From 589c1f4cbbeefb85e0a005b77aee818cbd41314e Mon Sep 17 00:00:00 2001 From: GunHog Date: Thu, 25 Sep 2014 09:44:41 -0500 Subject: [PATCH 1/7] Botcall, TG edition! --- code/_onclick/ai.dm | 4 + code/game/communications.dm | 3 + code/game/machinery/bots/bots.dm | 564 +++++++++++++++++- code/game/machinery/bots/cleanbot.dm | 322 +++++----- code/game/machinery/bots/ed209bot.dm | 584 +++++-------------- code/game/machinery/bots/floorbot.dm | 472 ++++++++------- code/game/machinery/bots/medbot.dm | 514 ++++++++-------- code/game/machinery/bots/mulebot.dm | 275 +++++---- code/game/machinery/bots/secbot.dm | 578 +++++------------- code/game/objects/items/devices/PDA/PDA.dm | 14 +- code/game/objects/items/devices/PDA/cart.dm | 163 ++++-- code/game/objects/items/devices/PDA/radio.dm | 113 ++-- code/modules/client/client procs.dm | 3 + code/modules/mob/living/silicon/ai/ai.dm | 78 ++- icons/obj/aibots.dmi | Bin 48259 -> 48936 bytes icons/pda_icons/pda_cleanbot.png | Bin 0 -> 183 bytes icons/pda_icons/pda_floorbot.png | Bin 0 -> 214 bytes icons/pda_icons/pda_medbot.png | Bin 0 -> 219 bytes 18 files changed, 1944 insertions(+), 1743 deletions(-) create mode 100644 icons/pda_icons/pda_cleanbot.png create mode 100644 icons/pda_icons/pda_floorbot.png create mode 100644 icons/pda_icons/pda_medbot.png diff --git a/code/_onclick/ai.dm b/code/_onclick/ai.dm index 87d1feeb1e4..e847cd47236 100644 --- a/code/_onclick/ai.dm +++ b/code/_onclick/ai.dm @@ -58,6 +58,10 @@ aicamera.camera_mode_off() aicamera.captureimage(A, usr) return + if(waypoint_mode) + set_waypoint(A) + waypoint_mode = 0 + return /* AI restrained() currently does nothing diff --git a/code/game/communications.dm b/code/game/communications.dm index ac4576042c2..3b5faf29f8b 100644 --- a/code/game/communications.dm +++ b/code/game/communications.dm @@ -176,6 +176,9 @@ var/const/RADIO_AIRLOCK = "6" var/const/RADIO_SECBOT = "7" var/const/RADIO_MULEBOT = "8" var/const/RADIO_MAGNETS = "9" +var/const/RADIO_CLEANBOT = "10" +var/const/RADIO_FLOORBOT = "11" +var/const/RADIO_MEDBOT = "12" var/global/datum/controller/radio/radio_controller diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm index 947bbe77df4..ae6d0f70325 100644 --- a/code/game/machinery/bots/bots.dm +++ b/code/game/machinery/bots/bots.dm @@ -6,6 +6,7 @@ luminosity = 3 use_power = 0 var/obj/item/weapon/card/id/botcard // the ID card that the bot "holds" + var/list/prev_access = list() var/on = 1 var/health = 0 //do not forget to set health for your bot! var/maxhealth = 0 @@ -14,7 +15,72 @@ var/open = 0//Maint panel var/locked = 1 var/hacked = 0 //Used to differentiate between being hacked by silicons and emagged by humans. + var/text_hack = "" //Custom text returned to a silicon upon hacking a bot. + var/text_dehack = "" //Text shown when resetting a bots hacked status to normal. + var/text_dehack_fail = "" //Shown when a silicon tries to reset a bot emagged with the emag item, which cannot be reset. + var/declare_message = "" //What the bot will display to the HUD user. + var/frustration = 0 //Used by some bots for tracking failures to reach their target. + var/list/call_path = list() //Path calculated by the AI and given to the bot to follow. + var/list/path = new() //Every bot has this, so it is best to put it here. + var/list/patrol_path = list() //The path a bot has while on patrol. + var/list/summon_path = list() //Path bot has while summoned. + var/pathset = 0 + var/mode = 0 //Standardizes the vars that indicate the bot is busy with its function. + var/tries = 0 //Number of times the bot tried and failed to move. + var/remote_disabled = 0 //If enabled, the AI cannot *Remotely* control a bot. It can still control it through cameras. + var/mob/living/silicon/ai/calling_ai //Links a bot to the AI calling it. + var/obj/item/device/radio/Radio //The bot's radio, for speaking to people. + var/radio_frequency = 1459 //The bot's default radio speaking freqency. Recommended to be on a department frequency. //var/emagged = 0 //Urist: Moving that var to the general /bot tree as it's used by most bots + var/auto_patrol = 0// set to make bot automatically patrol + var/turf/patrol_target // this is turf to navigate to (location of beacon) + var/turf/summon_target // The turf of a user summoning a bot. + var/new_destination // pending new destination (waiting for beacon response) + var/destination // destination description tag + var/next_destination // the next destination in the patrol route + + var/blockcount = 0 //number of times retried a blocked path + var/awaiting_beacon = 0 // count of pticks awaiting a beacon response + + var/nearest_beacon // the nearest beacon's tag + var/turf/nearest_beacon_loc // the nearest beacon's location + + var/beacon_freq = 1445 // navigation beacon frequency + var/control_freq = 1447 // bot control frequency + + var/bot_filter // The radio filter the bot uses to identify itself on the network. + + var/bot_type = 0 //The type of bot it is, for radio control. + #define SEC_BOT 1 // Secutritrons (Beepsky) and ED-209s + #define MULE_BOT 2 // MULEbots + #define FLOOR_BOT 3 // Floorbots + #define CLEAN_BOT 4 // Cleanbots + #define MED_BOT 5 // Medibots + + //Mode defines + #define BOT_IDLE 0 // idle + #define BOT_HUNT 1 // found target, hunting + #define BOT_PREP_ARREST 2 // at target, preparing to arrest + #define BOT_ARREST 3 // arresting target + #define BOT_START_PATROL 4 // start patrol + #define BOT_PATROL 5 // patrolling + #define BOT_SUMMON 6 // summoned by PDA + #define BOT_CLEANING 7 // cleaning (cleanbots) + #define BOT_REPAIRING 8 // repairing hull breaches (floorbots) + #define BOT_MOVING 9 // for clean/floor bots, when moving. + #define BOT_HEALING 10 // healing people (medbots) + #define BOT_RESPONDING 11 // responding to a call from the AI + #define BOT_LOADING 12 // loading/unloading + #define BOT_DELIVER 13 // moving to deliver + #define BOT_GO_HOME 14 // returning to home + #define BOT_BLOCKED 15 // blocked + #define BOT_NAV 16 // computing navigation + #define BOT_WAIT_FOR_NAV 17 // waiting for nav computation + #define BOT_NO_ROUTE 18 // no destination beacon found (or no route) + var/list/mode_name = list("In Pursuit","Preparing to Arrest","Arresting","Beginning Patrol","Patrolling","Summoned by PDA", \ + "Cleaning", "Repairing", "Proceeding to work site","Healing","Responding","Loading/Unloading","Navigating to Delivery Location","Navigating to Home", \ + "Waiting for clear path","Calculating navigation path","Pinging beacon network","Unable to reach destination") + //This holds text for what the bot is mode doing, reported on the AI's bot control interface. /obj/machinery/bot/proc/turn_on() @@ -26,6 +92,22 @@ /obj/machinery/bot/proc/turn_off() on = 0 SetLuminosity(0) + bot_reset() //Resets an AI's call, should it exist. + +/obj/machinery/bot/New() + ..() + botcard = new /obj/item/weapon/card/id(src) + set_custom_texts() + Radio = new /obj/item/device/radio(src) + Radio.listening = 0 //Makes bot radios transmit only so no one hears things while adjacent to one. + +/obj/machinery/bot/proc/add_to_beacons(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots. + if(radio_controller) + radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) + if(bot_filter) + radio_controller.add_object(src, control_freq, filter = bot_filter) + + /obj/machinery/bot/proc/explode() qdel(src) @@ -34,13 +116,19 @@ if (src.health <= 0) src.explode() -/obj/machinery/bot/proc/Emag(mob/user as mob) - if(locked) +/obj/machinery/bot/proc/Emag(mob/user as mob) //Master Emag proc. Ensure this is called in your bot before setting unique functions. + if(locked) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again. locked = 0 emagged = 1 user << "You bypass [src]'s controls." - if(!locked && open) + if(!locked && open) //Bot panel is unlocked by ID or emag, and the panel is screwed open. Ready for emagging. emagged = 2 + remote_disabled = 1 //Manually emagging the bot locks out the AI built in panel. + locked = 1 //Access denied forever! + bot_reset() + turn_on() //The bot automatically turns on when emagged, unless recently hit with EMP. + else //Bot is unlocked, but the maint panel has not been opened with a screwdriver yet. + user << "You need to open maintenance panel first." /obj/machinery/bot/examine() set src in view() @@ -63,7 +151,8 @@ /obj/machinery/bot/attack_animal(var/mob/living/simple_animal/M as mob) - if(M.melee_damage_upper == 0) return + if(M.melee_damage_upper == 0) + return M.changeNext_move(CLICK_CD_MELEE) src.health -= M.melee_damage_upper src.visible_message("[M] has [M.attacktext] [src]!") @@ -72,7 +161,55 @@ new /obj/effect/decal/cleanable/oil(src.loc) healthcheck() +/obj/machinery/bot/Topic(href, href_list) //Master Topic to handle common functions. + if(..()) + return + if(topic_denied()) + usr << "[src]'s interface is not responding!" + href_list = list() + return + + usr.set_machine(src) + add_fingerprint(usr) + if((href_list["power"]) && (allowed(usr) || !locked)) + if (on) + turn_off() + else + turn_on() + + switch(href_list["operation"]) + if("patrol") + auto_patrol = !auto_patrol + mode = BOT_IDLE + if("remote") + remote_disabled = !remote_disabled + if("hack") + if(emagged != 2) + emagged = 2 + hacked = 1 + remote_disabled = 0 + locked = 1 + usr << "[text_hack]" + bot_reset() + else if(!hacked) + usr << "[text_dehack_fail]" + else + emagged = 0 + hacked = 0 + usr << "[text_dehack]" + bot_reset() + updateUsrDialog() + +/obj/machinery/bot/proc/topic_denied() //Access check proc for bot topics! Remember to place in a bot's individual Topic if desired. + // 0 for access, 1 for denied. + if(emagged == 2) //An emagged bot cannot be controlled by humans, silicons can if one hacked it. + if(!hacked) //Manually emagged by a human - access denied to all. + return 1 + else if(!issilicon(usr)) //Bot is hacked, so only silicons are allowed access. + return 1 + else + return 0 /obj/machinery/bot/attackby(obj/item/weapon/W as obj, mob/user as mob) @@ -155,18 +292,425 @@ /obj/machinery/bot/proc/hack(mob/user) var/hack - if(issilicon(user)) - hack += "[src.emagged ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]
" - hack += "Harm Prevention Safety System: [src.emagged ? "DANGER" : "Engaged"]
" + if(issilicon(user)) //Allows silicons to toggle the emag status of a bot. + hack += "[emagged == 2 ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]
" + hack += "Harm Prevention Safety System: [emagged ? "DANGER" : "Engaged"]
" + else if(!locked) //Humans with access can use this option to hide a bot from the AI's remote control panel. + hack += "AI remote control network port: [remote_disabled ? "Closed" : "Open"]

" return hack +/obj/machinery/bot/proc/set_custom_texts() //Superclass for setting hack texts. Appears only if a set is not given to a bot locally. + text_hack = "You hack [name]." + text_dehack = "You reset [name]." + text_dehack_fail = "You fail to reset [name]." + /obj/machinery/bot/attack_ai(mob/user as mob) src.attack_hand(user) -/obj/machinery/bot/proc/speak(var/message) +/obj/machinery/bot/proc/speak(var/message, freq) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio. if((!src.on) || (!message)) return - for(var/mob/O in hearers(src, null)) - O.show_message("[src] beeps, \"[message]\"",2) + if(freq) + Radio.set_frequency(radio_frequency) + Radio.talk_into(src, message, radio_frequency) + else + say(message) return + +/obj/machinery/bot/proc/check_bot_access() + if(mode != BOT_SUMMON && mode != BOT_RESPONDING) + botcard.access = prev_access + +/obj/machinery/bot/proc/call_bot(var/caller, var/turf/waypoint) + bot_reset() //Reset a bot becore setting it to call mode. + var/area/end_area = get_area(waypoint) + + //For giving the bot temporary all-access. + var/obj/item/weapon/card/id/all_access = new /obj/item/weapon/card/id + var/datum/job/captain/All = new/datum/job/captain + all_access.access = All.get_access() + + call_path = AStar(src, waypoint, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 200, id=all_access) + calling_ai = caller //Link the AI to the bot! + + if(call_path && call_path.len) //Ensures that a valid path is calculated! + if(!on) + turn_on() //Saves the AI the hassle of having to activate a bot manually. + botcard = all_access //Give the bot all-access while under the AI's command. + calling_ai << "\icon[src] [name] called to [end_area.name]. [call_path.len-1] meters to destination." + pathset = 1 + mode = BOT_RESPONDING + tries = 0 + else + calling_ai << "Failed to calculate a valid route. Ensure destination is clear of obstructions and within range." + calling_ai = null + +/obj/machinery/bot/proc/call_mode() //Handles preparing a bot for a call, as well as calling the move proc. +//Handles the bot's movement during a call. + move_to_call() + sleep(5) + move_to_call() //Called twice so that the bot moves faster. + return + +/obj/machinery/bot/proc/move_to_call() + if(call_path && call_path.len && tries < 6) + step_towards(src, call_path[1]) + + if(loc == call_path[1])//Remove turfs from the path list if the bot moved there. + tries = 0 + call_path -= call_path[1] + else //Could not move because of an obstruction. + tries++ + else + if(calling_ai) + calling_ai << "\icon[src] [tries ? "[src] failed to reach waypoint." : "[src] successfully arrived to waypoint."]" + calling_ai = null + bot_reset() + +obj/machinery/bot/proc/bot_reset() + if(calling_ai) //Simple notification to the AI if it called a bot. It will not know the cause or identity of the bot. + calling_ai << "Call command to a bot has been reset." + calling_ai = null + call_path = null + path = new() + patrol_path = list() + summon_path = list() + summon_target = null + pathset = 0 + botcard.access = prev_access + tries = 0 + mode = BOT_IDLE + + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +//Patrol and summon code! +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +/obj/machinery/bot/proc/bot_patrol() + patrol_step() + spawn(5) + if(mode == BOT_PATROL) + patrol_step() + return + +obj/machinery/bot/proc/start_patrol() + + if(tries >= 4) //Bot is trapped, so stop trying to patrol. + auto_patrol = 0 + tries = 0 + speak("Unable to start patrol.") + + return + + if(!auto_patrol) //A bot not set to patrol should not be patrolling. + mode = BOT_IDLE + return + + if(patrol_path && patrol_path.len > 0 && patrol_target) // have a valid path, so just resume + mode = BOT_PATROL + return + + else if(patrol_target) // has patrol target already + spawn(0) + calc_path() // so just find a route to it + if(patrol_path.len == 0) + patrol_target = 0 + return + mode = BOT_PATROL + else // no patrol target, so need a new one + find_patrol_target() + speak("Engaging patrol mode.") + tries++ + return + +// perform a single patrol step + +/obj/machinery/bot/proc/patrol_step() + + if(loc == patrol_target) // reached target + + + at_patrol_target() + return + + else if(patrol_path.len > 0 && patrol_target) // valid path + var/turf/next = patrol_path[1] + if(next == loc) + patrol_path -= next + return + + + if(istype( next, /turf/simulated)) + + var/moved = step_towards(src, next) // attempt to move + if(moved) // successful move + blockcount = 0 + patrol_path -= loc + + else // failed to move + blockcount++ + + if(blockcount > 5) // attempt 5 times before recomputing + // find new path excluding blocked turf + + spawn(2) + calc_path(next) + if(patrol_path.len == 0) + find_patrol_target() + else + blockcount = 0 + tries = 0 + + return + + return + + else // not a valid turf + mode = BOT_IDLE + return + + else // no path, so calculate new one + mode = BOT_START_PATROL + + return + +// finds a new patrol target +/obj/machinery/bot/proc/find_patrol_target() + send_status() + if(awaiting_beacon) // awaiting beacon response + awaiting_beacon++ + if(awaiting_beacon > 5) // wait 5 secs for beacon response + find_nearest_beacon() // then go to nearest instead + return + if(next_destination) + set_destination(next_destination) + else + + find_nearest_beacon() + return + + +// finds the nearest beacon to self +// signals all beacons matching the patrol code +/obj/machinery/bot/proc/find_nearest_beacon() + nearest_beacon = null + new_destination = "__nearest__" + post_signal(beacon_freq, "findbeacon", "patrol") + awaiting_beacon = 1 + spawn(10) + awaiting_beacon = 0 + if(nearest_beacon) + set_destination(nearest_beacon) + tries = 0 + else + auto_patrol = 0 + mode = BOT_IDLE + speak("Disengaging patrol mode.") + send_status() + + +/obj/machinery/bot/proc/at_patrol_target() + + find_patrol_target() + return + + +// sets the current destination +// signals all beacons matching the patrol code +// beacons will return a signal giving their locations +/obj/machinery/bot/proc/set_destination(var/new_dest) + new_destination = new_dest + post_signal(beacon_freq, "findbeacon", "patrol") + awaiting_beacon = 1 + + +// receive a radio signal +// used for beacon reception + +/obj/machinery/bot/receive_signal(datum/signal/signal) + //log_admin("DEBUG \[[// world.timeofday]\]: /obj/machinery/bot/receive_signal([signal.debug_print()])") + if(!on) + return +/* + if(!signal.data["beacon"]) + + for(var/x in signal.data) + world << "* [x] = [signal.data[x]]" + */ + + var/recv = signal.data["command"] + // process all-bot input + + if(recv=="bot_status") + send_status() + + // check to see if we are the commanded bot + if(signal.data["active"] == src) + if(emagged == 2) //Emagged bots do not respect anyone's authority! + return + // process control input + switch(recv) + if("stop") + bot_reset() //HOLD IT!! + auto_patrol = 0 + return + + if("go") + auto_patrol = 1 + return + + if("summon") + bot_reset() + var/list/user_access = signal.data["useraccess"] + summon_target = signal.data["target"] //Location of the user + if(user_access.len != 0) + botcard.access = user_access + prev_access //Adds the user's access, if any. + mode = BOT_SUMMON + calc_summon_path() + speak("Responding.") + return + + // receive response from beacon + recv = signal.data["beacon"] + var/valid = signal.data["patrol"] + if(!recv || !valid) + return + if(recv == new_destination) // if the recvd beacon location matches the set destination + // the we will navigate there + destination = new_destination + patrol_target = signal.source.loc + next_destination = signal.data["next_patrol"] + awaiting_beacon = 0 + + // if looking for nearest beacon + else if(new_destination == "__nearest__") + var/dist = get_dist(src,signal.source.loc) + if(nearest_beacon) + // note we ignore the beacon we are located at + if(dist>1 && dist 1) + nearest_beacon = recv + nearest_beacon_loc = signal.source.loc + return + + +// send a radio signal with a single data key/value pair +/obj/machinery/bot/proc/post_signal(var/freq, var/key, var/value) + post_signal_multiple(freq, list("[key]" = value) ) + +// send a radio signal with multiple data key/values +/obj/machinery/bot/proc/post_signal_multiple(var/freq, var/list/keyval) + if(!z || z != 1) //Bot control will only work on station. + return + var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) + + if(!frequency) return + + var/datum/signal/signal = new() + signal.source = src + signal.transmission_method = 1 +// for(var/key in keyval) +// signal.data[key] = keyval[key] + signal.data = keyval +// world << "sent [key],[keyval[key]] on [freq]" + if(signal.data["findbeacon"]) + frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) + else if(signal.data["type"] == bot_type) + frequency.post_signal(src, signal, filter = bot_filter) + else + frequency.post_signal(src, signal) + +// signals bot status etc. to controller +/obj/machinery/bot/proc/send_status() + var/list/kv = list( + "type" = bot_type, + "name" = name, + "loca" = get_area(src), // area + "mode" = mode + ) + post_signal_multiple(control_freq, kv) + + +obj/machinery/bot/proc/bot_summon() + // summoned to PDA + summon_step() + spawn(4) + if(mode == BOT_SUMMON) + summon_step() + sleep(4) + summon_step() + return + +// calculates a path to the current destination +// given an optional turf to avoid +/obj/machinery/bot/proc/calc_path(var/turf/avoid = null) + check_bot_access() + patrol_path = AStar(loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 120, id=botcard, exclude=avoid) + if(!patrol_path) + patrol_path = list() + +/obj/machinery/bot/proc/calc_summon_path(var/turf/avoid = null) + check_bot_access() + summon_path = AStar(loc, summon_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 150, id=botcard, exclude=avoid) + if(!summon_path || tries >= 5) + bot_reset() + +/obj/machinery/bot/proc/summon_step() + + if(loc == summon_target) // Arrived to summon location. + bot_reset() + return + + else if(summon_path.len > 0 && summon_target) //Proper path acquired! + var/turf/next = summon_path[1] + if(next == loc) + summon_path -= next + return + + + if(istype( next, /turf/simulated)) + + var/moved = step_towards(src, next) // Move attempt + if(moved) + blockcount = 0 + summon_path -= loc + + else // failed to move + blockcount++ + + if(blockcount > 5) // attempt 5 times before recomputing + // find new path excluding blocked turf + spawn(2) + calc_summon_path(next) + tries++ + return + + return + + else // not a valid turf + bot_reset() + return + + else // no path, so calculate new one + calc_summon_path() + + return + + +/obj/machinery/bot/Bump(M as mob|obj) //Leave no door unopened! + if((istype(M, /obj/machinery/door/airlock) || istype(M, /obj/machinery/door/window)) && (!isnull(botcard))) + var/obj/machinery/door/D = M + if(D.check_access(botcard)) + D.open() + frustration = 0 + else if((istype(M, /mob/living/)) && (!anchored)) + var/mob/living/Mb = M + loc = Mb.loc + frustration = 0 + return diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm index a9c45b8dc5b..41f231919b9 100644 --- a/code/game/machinery/bots/cleanbot.dm +++ b/code/game/machinery/bots/cleanbot.dm @@ -24,51 +24,57 @@ //weight = 1.0E7 health = 25 maxhealth = 25 - var/cleaning = 0 var/blood = 1 var/list/target_types = list() var/obj/effect/decal/cleanable/target var/obj/effect/decal/cleanable/oldtarget + var/list/cleanbottargets = list() //Targets that the cleanbot cannot reach and will thus ignore. + var/max_targets = 50 //Maximum number of targets a cleanbot can ignore. var/oldloc = null - req_access = list(access_janitor) - var/path[] = new() - var/patrol_path[] = null - var/beacon_freq = 1445 // navigation beacon frequency + req_one_access = list(access_janitor, access_robotics) +// var/patrol_path[] = null +// var/beacon_freq = 1445 // navigation beacon frequency var/closest_dist var/closest_loc var/failed_steps - var/should_patrol var/next_dest var/next_dest_loc + bot_type = CLEAN_BOT + bot_filter = RADIO_CLEANBOT /obj/machinery/bot/cleanbot/New() ..() - src.get_targets() - src.icon_state = "cleanbot[src.on]" + get_targets() + icon_state = "cleanbot[on]" - should_patrol = 1 - - src.botcard = new /obj/item/weapon/card/id(src) var/datum/job/janitor/J = new/datum/job/janitor - src.botcard.access = J.get_access() - - if(radio_controller) - radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) + botcard.access = J.get_access() + prev_access = botcard.access + spawn(5) + add_to_beacons(bot_filter) /obj/machinery/bot/cleanbot/turn_on() . = ..() - src.icon_state = "cleanbot[src.on]" - src.updateUsrDialog() + icon_state = "cleanbot[on]" + updateUsrDialog() /obj/machinery/bot/cleanbot/turn_off() ..() - src.target = null - src.oldtarget = null - src.oldloc = null - src.icon_state = "cleanbot[src.on]" - src.path = new() - src.updateUsrDialog() + icon_state = "cleanbot[on]" + updateUsrDialog() + +/obj/machinery/bot/cleanbot/bot_reset() + ..() + cleanbottargets = list() //Allows the bot to clean targets it previously ignored due to being unreachable. + target = null + oldtarget = null + oldloc = null + +/obj/machinery/bot/cleanbot/set_custom_texts() + text_hack = "You corrupt [name]'s cleaning software." + text_dehack = "[name]'s software has been reset!" + text_dehack_fail = "[name] does not seem to respond to your repair code!" /obj/machinery/bot/cleanbot/attack_hand(mob/user as mob) . = ..() @@ -81,76 +87,55 @@ var/dat dat += hack(user) dat += text({" -Automatic Station Cleaner v1.0

+Cleaner v1.1 controls

Status: []
-Behaviour controls are [src.locked ? "locked" : "unlocked"]
-Maintenance panel panel is [src.open ? "opened" : "closed"]"}, -text("[src.on ? "On" : "Off"]")) - if(!src.locked || issilicon(user)) - dat += text({"
Cleans Blood: []
"}, text("[src.blood ? "Yes" : "No"]")) - dat += text({"
Patrol station: []
"}, text("[src.should_patrol ? "Yes" : "No"]")) - // dat += text({"
Beacon frequency: []
"}, text("[src.beacon_freq]")) -/* if(src.open && !src.locked) +Behaviour controls are [locked ? "locked" : "unlocked"]
+Maintenance panel panel is [open ? "opened" : "closed"]"}, +text("[on ? "On" : "Off"]")) + if(!locked || issilicon(user)) + dat += text({"
Cleans Blood: []
"}, text("[blood ? "Yes" : "No"]")) + dat += text({"
Patrol station: []
"}, text("[auto_patrol ? "Yes" : "No"]")) + // dat += text({"
Beacon frequency: []
"}, text("[beacon_freq]")) +/* if(open && !locked) dat += text({" Odd looking screw twiddled: []
Weird button pressed: []"}, -text("[src.screwloose ? "Yes" : "No"]"), -text("[src.oddbutton ? "Yes" : "No"]"))*/ +text("[screwloose ? "Yes" : "No"]"), +text("[oddbutton ? "Yes" : "No"]"))*/ - user << browse("Cleaner v1.0 controls[dat]", "window=autocleaner") - onclose(user, "autocleaner") + var/datum/browser/popup = new(user, "autoclean", "Automatic Station Cleaner v1.1") + popup.set_content(dat) + popup.open() return /obj/machinery/bot/cleanbot/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - src.add_fingerprint(usr) + + ..() switch(href_list["operation"]) - if("start") - if (src.on && !src.emagged) - turn_off() - else - turn_on() if("blood") - src.blood =!src.blood - src.get_targets() - src.updateUsrDialog() - if("patrol") - src.should_patrol =!src.should_patrol - src.patrol_path = null - src.updateUsrDialog() + blood =!blood + get_targets() + updateUsrDialog() if("freq") var/freq = text2num(input("Select frequency for navigation beacons", "Frequency", num2text(beacon_freq / 10))) * 10 if (freq > 0) - src.beacon_freq = freq - src.updateUsrDialog() + beacon_freq = freq + updateUsrDialog() + /* if("screw") - src.screwloose = !src.screwloose + screwloose = !screwloose usr << "You press the weird button." - src.updateUsrDialog() */ - if("hack") - if(!src.emagged) - src.emagged = 2 - src.hacked = 1 - usr << "You corrupt [src]'s cleaning software." - else if(!src.hacked) - usr << "[src] does not seem to respond to your repair code!" - else - src.hacked = 0 - src.emagged = 0 - usr << "[src]'s software has been reset!" - src.updateUsrDialog() + updateUsrDialog() */ /obj/machinery/bot/cleanbot/attackby(obj/item/weapon/W, mob/user as mob) if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if(src.allowed(usr) && !open && !emagged) - src.locked = !src.locked - user << "You [ src.locked ? "lock" : "unlock"] the [src] behaviour controls." + if(allowed(usr) && !open && !emagged) + locked = !locked + user << "You [ locked ? "lock" : "unlock"] the [src] behaviour controls." else if(emagged) user << "ERROR" @@ -163,135 +148,101 @@ text("[src.oddbutton ? "Yes" : "No" /obj/machinery/bot/cleanbot/Emag(mob/user as mob) ..() - if(open && !locked) - if(user) user << "[src] buzzes and beeps." - src.emagged = 2 + if(emagged == 2) + if(user) user << "[src] buzzes and beeps." /obj/machinery/bot/cleanbot/process() set background = BACKGROUND_ENABLED - if(!src.on) + if(!on) return - if(src.cleaning) - return - var/list/cleanbottargets = list() - if(!src.emagged && prob(5)) + if(call_path) + call_mode() + return + + if(mode == BOT_CLEANING) + return + + if(!emagged && prob(5)) visible_message("[src] makes an excited beeping booping sound!") - if(src.emagged && prob(10)) //Wets floors randomly + if(emagged == 2 && prob(10)) //Wets floors randomly if(istype(loc,/turf/simulated)) - var/turf/simulated/T = src.loc + var/turf/simulated/T = loc T.MakeSlippery() - if(src.emagged && prob(5)) //Spawns foam! + if(emagged == 2 && prob(5)) //Spawns foam! visible_message("[src] whirs and bubbles violently, before releasing a plume of froth!") - new /obj/effect/effect/foam(src.loc) + new /obj/effect/effect/foam(loc) - if(!src.target || src.target == null) + if(mode == BOT_SUMMON) + bot_summon() + return + + if(!target) //Search for cleanables it can see. for (var/obj/effect/decal/cleanable/D in view(7,src)) - for(var/T in src.target_types) - if(!(D in cleanbottargets) && (D.type == T || D.parent_type == T) && D != src.oldtarget) - src.oldtarget = D - src.target = D - return + for(var/T in target_types) + if(!(D in cleanbottargets) && (D.type == T || D.parent_type == T) && D != oldtarget) + oldtarget = D + target = D + break - if(!src.target || src.target == null) - if(src.loc != src.oldloc) - src.oldtarget = null + if(!target) + if(loc != oldloc) + oldtarget = null - if (!should_patrol) - return + if(auto_patrol) + if(mode == BOT_IDLE || mode == BOT_START_PATROL) + start_patrol() - if (!patrol_path || patrol_path.len < 1) - var/datum/radio_frequency/frequency = radio_controller.return_frequency(beacon_freq) + if(mode == BOT_PATROL) + bot_patrol() - if(!frequency) return - closest_dist = 9999 - closest_loc = null - next_dest_loc = null - - var/datum/signal/signal = new() - signal.source = src - signal.transmission_method = 1 - signal.data = list("findbeacon" = "patrol") - frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) - spawn(5) - if (!next_dest_loc) - next_dest_loc = closest_loc - if (next_dest_loc) - src.patrol_path = AStar(src.loc, next_dest_loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 120, id=botcard, exclude=null) - if(!patrol_path) - patrol_path = list() - else - patrol_move() return if(target && path.len == 0) spawn(0) - if(!src || !target) return - src.path = AStar(src.loc, src.target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 30) - if(!src.path) - src.path = list() - if(src.path.len == 0) - src.oldtarget = src.target - src.target = null + if(!src || !target) + return + //Try to produce a path to the target, and ignore airlocks to which it has access. + path = AStar(loc, target.loc, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 30, id=botcard) + if(!path) + path = list() + if(path.len == 0) //Target is unreachable, so add it to ignore list and prepare to find another target or remain idle/patrol. + add_to_ignore(target) + oldtarget = target + target = null + mode = BOT_IDLE return - if(src.path.len > 0 && src.target && (src.target != null)) - step_to(src, src.path[1]) - src.path -= src.path[1] - else if(src.path.len == 1) + if(path.len > 0 && target) + mode = BOT_MOVING + step_to(src, path[1]) + path -= path[1] + else if(path.len == 1) step_to(src, target) - if(src.target && (src.target != null)) - patrol_path = null - if(src.loc == src.target.loc) - clean(src.target) - src.path = new() - src.target = null + if(target) + if(loc == target.loc) + clean(target) + path = new() + target = null return - src.oldloc = src.loc + oldloc = loc -/obj/machinery/bot/cleanbot/proc/patrol_move() - if (src.patrol_path.len <= 0) - return - var/next = src.patrol_path[1] - src.patrol_path -= next - if (next == src.loc) - return - - var/moved = step_towards(src, next) - if (!moved) - failed_steps++ - if (failed_steps > 4) - patrol_path = null - next_dest = null - failed_steps = 0 - else - failed_steps = 0 - -/obj/machinery/bot/cleanbot/receive_signal(datum/signal/signal) - var/recv = signal.data["beacon"] - var/valid = signal.data["patrol"] - if(!recv || !valid) - return - - var/dist = get_dist(src, signal.source.loc) - if (dist < closest_dist && signal.source.loc != src.loc) - closest_dist = dist - closest_loc = signal.source.loc - next_dest = signal.data["next_patrol"] - - if (recv == next_dest) - next_dest_loc = signal.source.loc - next_dest = signal.data["next_patrol"] +/obj/machinery/bot/cleanbot/proc/add_to_ignore(target) + if(cleanbottargets.len < max_targets && !(target in cleanbottargets)) //Add the target to the ignore list if it is not full or already inside. + cleanbottargets += target + else if (cleanbottargets.len >= max_targets) + cleanbottargets -= cleanbottargets[1] // ignore list is full, so remove the oldest target. + cleanbottargets += target // then add the newest one. /obj/machinery/bot/cleanbot/proc/get_targets() - src.target_types = new/list() + target_types = new/list() target_types += /obj/effect/decal/cleanable/oil target_types += /obj/effect/decal/cleanable/vomit @@ -306,7 +257,7 @@ text("[src.oddbutton ? "Yes" : "No" target_types += /obj/effect/decal/cleanable/greenglow target_types += /obj/effect/decal/cleanable/dirt - if(src.blood) + if(blood) target_types += /obj/effect/decal/cleanable/xenoblood/ target_types += /obj/effect/decal/cleanable/xenoblood/xgibs target_types += /obj/effect/decal/cleanable/blood/ @@ -314,20 +265,21 @@ text("[src.oddbutton ? "Yes" : "No" target_types += /obj/effect/decal/cleanable/trail_holder /obj/machinery/bot/cleanbot/proc/clean(var/obj/effect/decal/cleanable/target) - src.anchored = 1 - src.icon_state = "cleanbot-c" + anchored = 1 + icon_state = "cleanbot-c" visible_message("[src] begins to clean up [target]") - src.cleaning = 1 + mode = BOT_CLEANING spawn(50) - src.cleaning = 0 - qdel(target) - src.icon_state = "cleanbot[src.on]" - src.anchored = 0 - src.target = null + if(mode == BOT_CLEANING) + qdel(target) + anchored = 0 + target = null + mode = BOT_IDLE + icon_state = "cleanbot[on]" /obj/machinery/bot/cleanbot/explode() - src.on = 0 - src.visible_message("[src] blows apart!", 1) + on = 0 + visible_message("[src] blows apart!", 1) var/turf/Tsec = get_turf(src) new /obj/item/weapon/reagent_containers/glass/bucket(Tsec) @@ -348,17 +300,17 @@ text("[src.oddbutton ? "Yes" : "No" if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm)) user.drop_item() qdel(W) - var/turf/T = get_turf(src.loc) + var/turf/T = get_turf(loc) var/obj/machinery/bot/cleanbot/A = new /obj/machinery/bot/cleanbot(T) - A.name = src.created_name + A.name = created_name user << "You add the robot arm to the bucket and sensor assembly. Beep boop!" user.unEquip(src, 1) qdel(src) else if (istype(W, /obj/item/weapon/pen)) - var/t = copytext(stripped_input(user, "Enter new robot name", src.name, src.created_name),1,MAX_NAME_LEN) + var/t = copytext(stripped_input(user, "Enter new robot name", name, created_name),1,MAX_NAME_LEN) if (!t) return - if (!in_range(src, usr) && src.loc != usr) + if (!in_range(src, usr) && loc != usr) return - src.created_name = t + created_name = t diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm index c8bc47d15a1..d5dcae89ad8 100644 --- a/code/game/machinery/bots/ed209bot.dm +++ b/code/game/machinery/bots/ed209bot.dm @@ -25,7 +25,6 @@ var/threatlevel = 0 var/target_lastloc //Loc of target when arrested. var/last_found //There's a delay - var/frustration = 0 //var/emagged = 0 //Emagged Secbots view everyone as a criminal var/declare_arrests = 1 //When making an arrest, should it notify everyone wearing sechuds? var/idcheck = 1 //If true, arrest people with no IDs @@ -33,39 +32,16 @@ var/check_records = 1 //Does it check security records? var/arrest_type = 0 //If true, don't handcuff var/projectile = null//Holder for projectile type, to avoid so many else if chains - - var/mode = 0 -#define SECBOT_IDLE 0 // idle -#define SECBOT_HUNT 1 // found target, hunting -#define SECBOT_PREP_ARREST 2 // at target, preparing to arrest -#define SECBOT_ARREST 3 // arresting target -#define SECBOT_START_PATROL 4 // start patrol -#define SECBOT_PATROL 5 // patrolling -#define SECBOT_SUMMON 6 // summoned by PDA - - var/auto_patrol = 0 // set to make bot automatically patrol - - var/beacon_freq = 1445 // navigation beacon frequency - var/control_freq = 1447 // bot control frequency - + radio_frequency = 1359 + bot_type = SEC_BOT + bot_filter = RADIO_SECBOT +/ //List of weapons that secbots will not arrest for var/safe_weapons = list(\ /obj/item/weapon/gun/energy/laser/bluetag,\ /obj/item/weapon/gun/energy/laser/redtag,\ /obj/item/weapon/gun/energy/laser/practice) - var/turf/patrol_target // this is turf to navigate to (location of beacon) - var/new_destination // pending new destination (waiting for beacon response) - var/destination // destination description tag - var/next_destination // the next destination in the patrol route - var/list/path = new // list of path turfs - - var/blockcount = 0 //number of times retried a blocked path - var/awaiting_beacon = 0 // count of pticks awaiting a beacon response - - var/nearest_beacon // the nearest beacon's tag - var/turf/nearest_beacon_loc // the nearest beacon's location - /obj/item/weapon/ed209_assembly name = "\improper ED-209 assembly" @@ -82,15 +58,14 @@ ..() if(created_name) name = created_name if(created_lasercolor) lasercolor = created_lasercolor - src.icon_state = "[lasercolor]ed209[src.on]" + icon_state = "[lasercolor]ed209[on]" spawn(3) - src.botcard = new /obj/item/weapon/card/id(src) var/datum/job/detective/J = new/datum/job/detective - src.botcard.access = J.get_access() + botcard.access = J.get_access() + prev_access = botcard.access - if(radio_controller) - radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT) - radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) + + add_to_beacons(bot_filter) if(lasercolor) shot_delay = 6//Longer shot delay because JESUS CHRIST check_records = 0//Don't actively target people set to arrest @@ -104,19 +79,28 @@ /obj/machinery/bot/ed209/turn_on() . = ..() - src.icon_state = "[lasercolor]ed209[src.on]" - src.mode = SECBOT_IDLE - src.updateUsrDialog() + icon_state = "[lasercolor]ed209[on]" + mode = BOT_IDLE + updateUsrDialog() /obj/machinery/bot/ed209/turn_off() ..() - src.target = null - src.oldtarget_name = null - src.anchored = 0 - src.mode = SECBOT_IDLE + icon_state = "[lasercolor]ed209[on]" + updateUsrDialog() + +/obj/machinery/bot/ed209/bot_reset() + ..() + target = null + oldtarget_name = null + anchored = 0 walk_to(src,0) - src.icon_state = "[lasercolor]ed209[src.on]" - src.updateUsrDialog() + last_found = world.time + projectile = null + +/obj/machinery/bot/ed209/set_custom_texts() + text_hack = "You disable [name]'s combat inhibitor." + text_dehack = "You restore [name]'s combat inhibitor." + text_dehack_fail = "[name] ignores your attempts to restrict him!" /obj/machinery/bot/ed209/attack_hand(mob/user as mob) . = ..() @@ -125,13 +109,14 @@ var/dat dat += hack(user) dat += text({" +Security Unit v2.6 controls

Status: []
Behaviour controls are [src.locked ? "locked" : "unlocked"]
-Maintenance panel panel is [src.open ? "opened" : "closed"]
"}, +Maintenance panel panel is [open ? "opened" : "closed"]
"}, -"
[src.on ? "On" : "Off"]" ) +"[on ? "On" : "Off"]" ) - if(!src.locked || issilicon(user)) + if(!locked || issilicon(user)) if(!lasercolor) dat += text({"
Arrest Unidentifiable Persons: []
@@ -142,74 +127,47 @@ Operating Mode: []
Report Arrests[]
Auto Patrol: []"}, -"[src.idcheck ? "Yes" : "No"]", -"[src.weaponscheck ? "Yes" : "No"]", -"[src.check_records ? "Yes" : "No"]", -"[src.arrest_type ? "Detain" : "Arrest"]", -"[src.declare_arrests ? "Yes" : "No"]", +"[idcheck ? "Yes" : "No"]", +"[weaponscheck ? "Yes" : "No"]", +"[check_records ? "Yes" : "No"]", +"[arrest_type ? "Detain" : "Arrest"]", +"[declare_arrests ? "Yes" : "No"]", "[auto_patrol ? "On" : "Off"]" ) - - var/datum/browser/popup = new(user, "autosec", "Securitron v2.0.9 controls") + var/datum/browser/popup = new(user, "autoed209", "Automatic Security Unit v2.6") popup.set_content(dat) popup.open() return /obj/machinery/bot/ed209/Topic(href, href_list) - if (..()) - return - usr.set_machine(src) - src.add_fingerprint(usr) if(lasercolor && (istype(usr,/mob/living/carbon/human))) var/mob/living/carbon/human/H = usr if((lasercolor == "b") && (istype(H.wear_suit, /obj/item/clothing/suit/redtag)))//Opposing team cannot operate it return else if((lasercolor == "r") && (istype(H.wear_suit, /obj/item/clothing/suit/bluetag))) return - if ((href_list["power"]) && (src.allowed(usr))) - if (src.on && !src.emagged) - turn_off() - else - turn_on() - return + ..() switch(href_list["operation"]) if ("idcheck") - src.idcheck = !src.idcheck - src.updateUsrDialog() + idcheck = !idcheck if("weaponscheck") - src.weaponscheck = !src.weaponscheck - src.updateUsrDialog() - if("ignorerec") - src.check_records = !src.check_records - src.updateUsrDialog() - if("switchmode") - src.arrest_type = !src.arrest_type - src.updateUsrDialog() - if("patrol") - auto_patrol = !auto_patrol - mode = SECBOT_IDLE + weaponscheck = !weaponscheck + updateUsrDialog() + if ("ignorerec") + check_records = !check_records + updateUsrDialog() + if ("switchmode") + arrest_type = !arrest_type updateUsrDialog() if("declarearrests") - src.declare_arrests = !src.declare_arrests - src.updateUsrDialog() - if("hack") - if(!src.emagged) - src.emagged = 2 - src.hacked = 1 - usr << "You disable [src]'s combat inhibitor." - else if(!src.hacked) - usr << "[src] ignores your attempts to restrict it!" - else - src.emagged = 0 - src.hacked = 0 - usr << "You restore [src]'s combat inhibitor." - src.updateUsrDialog() + declare_arrests = !declare_arrests + updateUsrDialog() /obj/machinery/bot/ed209/attackby(obj/item/weapon/W as obj, mob/user as mob) if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if (src.allowed(user) && !open && !emagged) - src.locked = !src.locked - user << "Controls are now [src.locked ? "locked" : "unlocked"]." + if (allowed(user) && !open && !emagged) + locked = !locked + user << "Controls are now [locked ? "locked" : "unlocked"]." else if(emagged) user << "ERROR" @@ -224,10 +182,10 @@ Auto Patrol: []"}, threatlevel = user.assess_threat(src) threatlevel += 6 if(threatlevel >= 4) - src.target = user + target = user if(lasercolor)//To make up for the fact that lasertag bots don't hunt - src.shootAt(user) - src.mode = SECBOT_HUNT + shootAt(user) + mode = BOT_HUNT /obj/machinery/bot/ed209/Emag(mob/user as mob) ..() @@ -236,22 +194,21 @@ Auto Patrol: []"}, spawn(0) for(var/mob/O in hearers(src, null)) O.show_message("[src] buzzes oddly!", 1) - src.target = null - if(user) src.oldtarget_name = user.name - src.last_found = world.time - src.anchored = 0 - src.declare_arrests = 0 - src.emagged = 2 - src.on = 1 - src.icon_state = "[lasercolor]ed209[src.on]" - src.projectile = null - mode = SECBOT_IDLE + if(user) + oldtarget_name = user.name + projectile = null + declare_arrests = 0 /obj/machinery/bot/ed209/process() set background = BACKGROUND_ENABLED - if (!src.on || src.disabled) + if(!on || disabled) return + + if(call_path) + call_mode() + return + var/list/targets = list() for (var/mob/living/carbon/C in view(9,src)) //Let's find us a target var/threatlevel = 0 @@ -259,7 +216,7 @@ Auto Patrol: []"}, continue if (istype(C, /mob/living/carbon/human)) threatlevel = C.assess_threat(src, lasercolor) - //src.speak(C.real_name + text(": threat: []", threatlevel)) + //speak(C.real_name + text(": threat: []", threatlevel)) if (threatlevel < 4 ) continue @@ -272,37 +229,33 @@ Auto Patrol: []"}, var/mob/t = pick(targets) if (istype(t, /mob/living)) if ((t.stat!=2) && (t.lying != 1)) - //src.speak("selected target: " + t.real_name) - src.shootAt(t) + //speak("selected target: " + t.real_name) + shootAt(t) switch(mode) - if(SECBOT_IDLE) // idle + if(BOT_IDLE) // idle walk_to(src,0) look_for_perp() // see if any criminals are in range if(!mode && auto_patrol) // still idle, and set to patrol - mode = SECBOT_START_PATROL // switch to patrol mode + mode = BOT_START_PATROL // switch to patrol mode - if(SECBOT_HUNT) // hunting for perp - if(src.lasercolor)//Lasertag bots do not tase or arrest anyone, just patrol and shoot and whatnot - mode = SECBOT_IDLE + if(BOT_HUNT) // hunting for perp + if(lasercolor)//Lasertag bots do not tase or arrest anyone, just patrol and shoot and whatnot + mode = BOT_IDLE return // if can't reach perp for long enough, go idle - if (src.frustration >= 8) + if (frustration >= 8) // for(var/mob/O in hearers(src, null)) // O << "[src] beeps, \"Backup requested! Suspect has evaded arrest.\"" - src.target = null - src.last_found = world.time - src.frustration = 0 - src.mode = 0 - walk_to(src,0) + bot_reset() if (target) // make sure target exists - if (get_dist(src, src.target) <= 1) // if right next to perp - playsound(src.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) - src.icon_state = "[lasercolor]ed209-c" + if (get_dist(src, target) <= 1) // if right next to perp + playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) + icon_state = "[lasercolor]ed209-c" spawn(2) - src.icon_state = "[lasercolor]ed209[src.on]" - var/mob/living/carbon/M = src.target + icon_state = "[lasercolor]ed209[on]" + var/mob/living/carbon/M = target var/maxstuns = 4 if (istype(M, /mob/living/carbon/human)) if (M.stuttering < 5 && (!(HULK in M.mutations)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) @@ -316,7 +269,7 @@ Auto Patrol: []"}, if(declare_arrests) var/area/location = get_area(src) - broadcast_hud_message("[src.name] is [arrest_type ? "detaining" : "arresting"] level [threatlevel] scumbag [target] in [location]", src) + speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].", radio_frequency) target.visible_message("[src.target] has been stunned by [src]!",\ "[src.target] has been stunned by [src]!") @@ -324,334 +277,89 @@ Auto Patrol: []"}, if(maxstuns <= 0) target = null - mode = SECBOT_PREP_ARREST - src.anchored = 1 - src.target_lastloc = M.loc + mode = BOT_PREP_ARREST + anchored = 1 + target_lastloc = M.loc return else // not next to perp - var/turf/olddist = get_dist(src, src.target) - walk_to(src, src.target,1,4) - if ((get_dist(src, src.target)) >= (olddist)) - src.frustration++ + var/turf/olddist = get_dist(src, target) + walk_to(src, target,1,4) + if ((get_dist(src, target)) >= (olddist)) + frustration++ else - src.frustration = 0 + frustration = 0 - if(SECBOT_PREP_ARREST) // preparing to arrest target - if(src.lasercolor) - mode = SECBOT_IDLE + if(BOT_PREP_ARREST) // preparing to arrest target + if(lasercolor) + mode = BOT_IDLE return if (!target) - mode = SECBOT_IDLE - src.anchored = 0 + mode = BOT_IDLE + anchored = 0 return // see if he got away - if ((get_dist(src, src.target) > 1) || ((src.target:loc != src.target_lastloc) && src.target:weakened < 2)) - src.anchored = 0 - mode = SECBOT_HUNT + if ((get_dist(src, target) > 1) || ((target.loc != target_lastloc) && target.weakened < 2)) + anchored = 0 + mode = BOT_HUNT return if(iscarbon(target) && target.canBeHandcuffed()) - if (!src.target.handcuffed && !src.arrest_type) - playsound(src.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2) - mode = SECBOT_ARREST - target.visible_message("[src] is trying to put handcuffs on [src.target]!",\ - "[src] is trying to put handcuffs on [src.target]!") + if (!target.handcuffed && !arrest_type) + playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2) + mode = BOT_ARREST + target.visible_message("[src] is trying to put handcuffs on [target]!",\ + "[src] is trying to put handcuffs on [target]!") spawn(60) - if (get_dist(src, src.target) <= 1) - if (src.target.handcuffed) + if (get_dist(src, target) <= 1) + if (target.handcuffed) return - if(istype(src.target,/mob/living/carbon)) - src.target.handcuffed = new /obj/item/weapon/handcuffs(src.target) + if(istype(target,/mob/living/carbon)) + target.handcuffed = new /obj/item/weapon/handcuffs(target) target.update_inv_handcuffed(0) //update handcuff overlays - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.frustration = 0 + mode = BOT_IDLE + target = null + anchored = 0 + last_found = world.time + frustration = 0 - // playsound(src.loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0) + // playsound(loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0) // var/arrest_message = pick("Have a secure day!","I AM THE LAW.", "God made tomorrow for the crooks we don't catch today.","You can't outrun a radio.") - // src.speak(arrest_message) + // speak(arrest_message) else - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.frustration = 0 + mode = BOT_IDLE + target = null + anchored = 0 + last_found = world.time + frustration = 0 - if(SECBOT_ARREST) // arresting - if(src.lasercolor) - mode = SECBOT_IDLE + if(BOT_ARREST) // arresting + if(lasercolor) + mode = BOT_IDLE return - if (!target || src.target.handcuffed) - src.anchored = 0 - mode = SECBOT_IDLE - return - - - if(SECBOT_START_PATROL) // start a patrol - - if(path.len > 0 && patrol_target) // have a valid path, so just resume - mode = SECBOT_PATROL - return - - else if(patrol_target) // has patrol target already - spawn(0) - calc_path() // so just find a route to it - if(path.len == 0) - patrol_target = 0 - return - mode = SECBOT_PATROL - - - else // no patrol target, so need a new one - find_patrol_target() - speak("Engaging patrol mode.") - - - if(SECBOT_PATROL) // patrol mode - patrol_step() - spawn(5) - if(mode == SECBOT_PATROL) - patrol_step() - - if(SECBOT_SUMMON) // summoned to PDA - patrol_step() - spawn(4) - if(mode == SECBOT_SUMMON) - patrol_step() - sleep(4) - patrol_step() - - return - - -// perform a single patrol step - -/obj/machinery/bot/ed209/proc/patrol_step() - - if(loc == patrol_target) // reached target - at_patrol_target() - return - - else if(path.len > 0 && patrol_target) // valid path - - var/turf/next = path[1] - if(next == loc) - path -= next - return - - - if(istype( next, /turf/simulated)) - - var/moved = step_towards(src, next) // attempt to move - if(moved) // successful move - blockcount = 0 - path -= loc - - look_for_perp() - if(lasercolor) - sleep(20) - else // failed to move - - blockcount++ - - if(blockcount > 5) // attempt 5 times before recomputing - // find new path excluding blocked turf - - spawn(2) - calc_path(next) - if(path.len == 0) - find_patrol_target() - else - blockcount = 0 - - return - - return - - else // not a valid turf - mode = SECBOT_IDLE - return - - else // no path, so calculate new one - mode = SECBOT_START_PATROL - - -// finds a new patrol target -/obj/machinery/bot/ed209/proc/find_patrol_target() - send_status() - if(awaiting_beacon) // awaiting beacon response - awaiting_beacon++ - if(awaiting_beacon > 5) // wait 5 secs for beacon response - find_nearest_beacon() // then go to nearest instead - return - - if(next_destination) - set_destination(next_destination) - else - find_nearest_beacon() - return - - -// finds the nearest beacon to self -// signals all beacons matching the patrol code -/obj/machinery/bot/ed209/proc/find_nearest_beacon() - nearest_beacon = null - new_destination = "__nearest__" - post_signal(beacon_freq, "findbeacon", "patrol") - awaiting_beacon = 1 - spawn(10) - awaiting_beacon = 0 - if(nearest_beacon) - set_destination(nearest_beacon) - else - auto_patrol = 0 - mode = SECBOT_IDLE - speak("Disengaging patrol mode.") - send_status() - - -/obj/machinery/bot/ed209/proc/at_patrol_target() - find_patrol_target() - return - - -// sets the current destination -// signals all beacons matching the patrol code -// beacons will return a signal giving their locations -/obj/machinery/bot/ed209/proc/set_destination(var/new_dest) - new_destination = new_dest - post_signal(beacon_freq, "findbeacon", "patrol") - awaiting_beacon = 1 - - -// receive a radio signal -// used for beacon reception - -/obj/machinery/bot/ed209/receive_signal(datum/signal/signal) - - if(!on) - return - - /* - world << "rec signal: [signal.source]" - for(var/x in signal.data) - world << "* [x] = [signal.data[x]]" - */ - - var/recv = signal.data["command"] - // process all-bot input - if(recv=="bot_status") - send_status() - - // check to see if we are the commanded bot - if(signal.data["active"] == src) - // process control input - switch(recv) - if("stop") - mode = SECBOT_IDLE - auto_patrol = 0 - return - - if("go") - mode = SECBOT_IDLE - auto_patrol = 1 - return - - if("summon") - patrol_target = signal.data["target"] - next_destination = destination - destination = null - awaiting_beacon = 0 - mode = SECBOT_SUMMON - calc_path() - speak("Responding.") - - return - - - - // receive response from beacon - recv = signal.data["beacon"] - var/valid = signal.data["patrol"] - if(!recv || !valid) - return - - if(recv == new_destination) // if the recvd beacon location matches the set destination - // the we will navigate there - destination = new_destination - patrol_target = signal.source.loc - next_destination = signal.data["next_patrol"] - awaiting_beacon = 0 - - // if looking for nearest beacon - else if(new_destination == "__nearest__") - var/dist = get_dist(src,signal.source.loc) - if(nearest_beacon) - - // note we ignore the beacon we are located at - if(dist>1 && dist 1) - nearest_beacon = recv - nearest_beacon_loc = signal.source.loc + mode = BOT_PREP_ARREST + anchored = 0 + + if(BOT_START_PATROL) + look_for_perp() + start_patrol() + + if(BOT_PATROL) + look_for_perp() + bot_patrol() + + if(BOT_SUMMON) + bot_summon() return - -// send a radio signal with a single data key/value pair -/obj/machinery/bot/ed209/proc/post_signal(var/freq, var/key, var/value) - post_signal_multiple(freq, list("[key]" = value) ) - -// send a radio signal with multiple data key/values -/obj/machinery/bot/ed209/proc/post_signal_multiple(var/freq, var/list/keyval) - - var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) - - if(!frequency) return - - var/datum/signal/signal = new() - signal.source = src - signal.transmission_method = 1 - //for(var/key in keyval) - // signal.data[key] = keyval[key] - //world << "sent [key],[keyval[key]] on [freq]" - signal.data = keyval - if (signal.data["findbeacon"]) - frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) - else if (signal.data["type"] == "secbot") - frequency.post_signal(src, signal, filter = RADIO_SECBOT) - else - frequency.post_signal(src, signal) - -// signals bot status etc. to controller -/obj/machinery/bot/ed209/proc/send_status() - var/list/kv = list( - "type" = "secbot", - "name" = name, - "loca" = loc.loc, // area - "mode" = mode, - ) - post_signal_multiple(control_freq, kv) - - - -// calculates a path to the current destination -// given an optional turf to avoid -/obj/machinery/bot/ed209/proc/calc_path(var/turf/avoid = null) - src.path = AStar(src.loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 120, id=botcard, exclude=avoid) - if(!src.path) - src.path = list() - - // look for a criminal in view of the bot /obj/machinery/bot/ed209/proc/look_for_perp() @@ -681,7 +389,7 @@ Auto Patrol: []"}, if(!src.lasercolor) playsound(src.loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/EDPlaceholder.ogg'), 50, 0) src.visible_message("[src] points at [C.name]!") - mode = SECBOT_HUNT + mode = BOT_HUNT spawn(0) process() // ensure bot quickly responds to a perp break @@ -807,7 +515,7 @@ Auto Patrol: []"}, ..() if (!isalien(target)) src.target = user - src.mode = SECBOT_HUNT + src.mode = BOT_HUNT /obj/machinery/bot/ed209/emp_act(severity) @@ -844,7 +552,7 @@ Auto Patrol: []"}, var/mob/toarrest = pick(targets) if (toarrest) src.target = toarrest - src.mode = SECBOT_HUNT + src.mode = BOT_HUNT @@ -958,14 +666,14 @@ Auto Patrol: []"}, return build_step++ user << "You add [W] to [src]." - src.item_state = "[lasercolor]ed209_taser" - src.icon_state = "[lasercolor]ed209_taser" + item_state = "[lasercolor]ed209_taser" + icon_state = "[lasercolor]ed209_taser" user.drop_item() qdel(W) if(8) if(istype(W, /obj/item/weapon/screwdriver)) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) + playsound(loc, 'sound/items/Screwdriver.ogg', 100, 1) var/turf/T = get_turf(user) user << "Now attaching the gun to the frame..." sleep(40) @@ -989,18 +697,18 @@ Auto Patrol: []"}, /obj/machinery/bot/ed209/bullet_act(var/obj/item/projectile/Proj) if(!disabled) var/lasertag_check = 0 - if((src.lasercolor == "b")) + if((lasercolor == "b")) if(istype(Proj, /obj/item/projectile/lasertag/redtag)) lasertag_check++ - else if((src.lasercolor == "r")) + else if((lasercolor == "r")) if(istype(Proj, /obj/item/projectile/lasertag/bluetag)) lasertag_check++ if(lasertag_check) icon_state = "[lasercolor]ed2090" - src.disabled = 1 + disabled = 1 target = null spawn(100) - src.disabled = 0 + disabled = 0 icon_state = "[lasercolor]ed2091" return 1 else diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index 8f0e67df3b4..ff349864af9 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -36,35 +36,56 @@ maxhealth = 25 //weight = 1.0E7 var/amount = 10 - var/repairing = 0 - var/improvefloors = 0 + var/replacetiles = 0 var/eattiles = 0 var/maketiles = 0 + var/fixfloors = 0 + var/autotile = 0 + var/nag_on_empty = 1 + var/nagged = 0 //Prevents the Floorbot nagging more than once per refill. + var/max_targets = 50 var/turf/target var/turf/oldtarget + var/list/ignore_list = list() //List of unreachable targets var/oldloc = null - req_access = list(access_construction) - var/path[] = new() + req_one_access = list(access_construction, access_robotics) var/targetdirection - + radio_frequency = 1357 //Engineering channel + bot_type = FLOOR_BOT + bot_filter = RADIO_FLOORBOT /obj/machinery/bot/floorbot/New() ..() - src.updateicon() + updateicon() + var/datum/job/engineer/J = new/datum/job/engineer + botcard.access = J.get_access() + prev_access = botcard.access + + spawn(5) + add_to_beacons(bot_filter) /obj/machinery/bot/floorbot/turn_on() . = ..() - src.updateicon() - src.updateUsrDialog() + updateicon() + updateUsrDialog() /obj/machinery/bot/floorbot/turn_off() ..() - src.target = null - src.oldtarget = null - src.oldloc = null - src.updateicon() - src.path = new() - src.updateUsrDialog() + updateicon() + updateUsrDialog() + +/obj/machinery/bot/floorbot/bot_reset() + ..() + target = null + oldtarget = null + oldloc = null + ignore_list = list() + nagged = 0 + +/obj/machinery/bot/floorbot/set_custom_texts() + text_hack = "You corrupt [name]'s construction protocols." + text_dehack = "You detect errors in [name] and reset his programming." + text_dehack_fail = "[name] is not responding to reset commands!" /obj/machinery/bot/floorbot/attack_hand(mob/user as mob) . = ..() @@ -76,44 +97,50 @@ /obj/machinery/bot/floorbot/interact(mob/user as mob) var/dat dat += hack(user) - dat += "Automatic Station Floor Repairer v1.0

" - dat += "Status: [src.on ? "On" : "Off"]
" - dat += "Maintenance panel panel is [src.open ? "opened" : "closed"]
" - dat += "Tiles left: [src.amount]
" - dat += "Behvaiour controls are [src.locked ? "locked" : "unlocked"]
" - if(!src.locked || issilicon(user)) - dat += "Improves floors: [src.improvefloors ? "Yes" : "No"]
" - dat += "Finds tiles: [src.eattiles ? "Yes" : "No"]
" - dat += "Make singles pieces of metal into tiles when empty: [src.maketiles ? "Yes" : "No"]
" + dat += "Floor Repairer Controls v1.1

" + dat += "Status: [on ? "On" : "Off"]
" + dat += "Maintenance panel panel is [open ? "opened" : "closed"]
" + dat += "Tiles left: [amount]
" + dat += "Behvaiour controls are [locked ? "locked" : "unlocked"]
" + if(!locked || issilicon(user)) + dat += "Add tiles to new hull plating: [autotile ? "Yes" : "No"]
" + dat += "Replace floor tiles: [replacetiles ? "Yes" : "No"]
" + dat += "Finds tiles: [eattiles ? "Yes" : "No"]
" + dat += "Make pieces of metal into tiles when empty: [maketiles ? "Yes" : "No"]
" + dat += "Transmit notice when empty: [nag_on_empty ? "Yes" : "No"]
" + dat += "Repair damaged tiles and platings: [fixfloors ? "Yes" : "No"]
" + dat += "Patrol Station: [auto_patrol ? "Yes" : "No"]
" var/bmode - if (src.targetdirection) - bmode = dir2text(src.targetdirection) + if (targetdirection) + bmode = dir2text(targetdirection) else bmode = "Disabled" dat += "

Bridge Mode : [bmode]
" - user << browse("Repairbot v1.0 controls[dat]", "window=autorepair") - onclose(user, "autorepair") + var/datum/browser/popup = new(user, "autofloor", "Automatic Station Floor Repairer v1.1") + popup.set_content(dat) + popup.open() return /obj/machinery/bot/floorbot/attackby(var/obj/item/W , mob/user as mob) if(istype(W, /obj/item/stack/tile/plasteel)) var/obj/item/stack/tile/plasteel/T = W - if(src.amount >= 50) + if(amount >= 50) return - var/loaded = min(50 - src.amount, T.get_amount()) + var/loaded = min(50-amount, T.amount) T.use(loaded) - src.amount += loaded + amount += loaded if (loaded > 0) - user << "You load [loaded] tiles into the floorbot. He now contains [src.amount] tiles." - src.updateicon() + user << "You load [loaded] tiles into the floorbot. He now contains [amount] tiles." + nagged = 0 + updateicon() else - user << "You need at least one floor tile to put into the floorbot." + user << "You need at least one floor tile to put into [src]" else if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if(src.allowed(usr) && !open && !emagged) - src.locked = !src.locked - user << "You [src.locked ? "lock" : "unlock"] the [src] behaviour controls." + if(allowed(user) && !open && !emagged) + locked = !locked + user << "You [locked ? "lock" : "unlock"] the [src] behaviour controls." else if(emagged) user << "ERROR" @@ -121,49 +148,33 @@ user << "Please close the access panel before locking it." else user << "Access denied." - src.updateUsrDialog() + updateUsrDialog() else ..() /obj/machinery/bot/floorbot/Emag(mob/user as mob) ..() - if(open && !locked) - if(user) user << "The [src] buzzes and beeps." + if(emagged == 2) + if(user) user << "[src] buzzes and beeps." /obj/machinery/bot/floorbot/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - src.add_fingerprint(usr) + ..() switch(href_list["operation"]) - if("start") - if (src.on && !src.emagged) - turn_off() - else - turn_on() - if("improve") - src.improvefloors = !src.improvefloors - src.updateUsrDialog() + if("replace") + replacetiles = !replacetiles if("tiles") - src.eattiles = !src.eattiles - src.updateUsrDialog() + eattiles = !eattiles if("make") - src.maketiles = !src.maketiles - src.updateUsrDialog() - if("hack") - if(!src.emagged) - src.emagged = 2 - src.hacked = 1 - usr << "You corrupt [src]'s construction protocols." - else if(!src.hacked) - usr << "[src] is not responding to reset commands!" - else - src.emagged = 0 - src.hacked = 0 - usr << "You detect errors in [src] and reset its programming." - src.updateUsrDialog() + maketiles = !maketiles + if("fix") + fixfloors = !fixfloors + if("autotile") + autotile = !autotile + if("emptynag") + nag_on_empty = !nag_on_empty + if("bridgemode") - switch(src.targetdirection) + switch(targetdirection) if(null) targetdirection = 1 if(1) @@ -176,210 +187,269 @@ targetdirection = null else targetdirection = null - src.updateUsrDialog() + updateUsrDialog() /obj/machinery/bot/floorbot/process() set background = BACKGROUND_ENABLED - if(!src.on) + if(!on) return - if(src.repairing) + + if(call_path) + call_mode() return - var/list/floorbottargets = list() - if(src.amount <= 0 && ((src.target == null) || !src.target)) - if(src.eattiles) + + if(mode == BOT_SUMMON) + bot_summon() + return + + if(amount <= 0 && !target) + if(eattiles) for(var/obj/item/stack/tile/plasteel/T in view(7, src)) - if(T != src.oldtarget && !(target in floorbottargets)) - src.oldtarget = T - src.target = T + if(T != oldtarget && !(target in ignore_list)) + oldtarget = T + target = T break - if(src.target == null || !src.target) - if(src.maketiles) - if(src.target == null || !src.target) + if(!target) + if(maketiles) + if(!target) for(var/obj/item/stack/sheet/metal/M in view(7, src)) - if(!(M in floorbottargets) && M != src.oldtarget && M.amount == 1 && !(istype(M.loc, /turf/simulated/wall))) - src.oldtarget = M - src.target = M + if(!(M in ignore_list) && M != oldtarget && !(istype(M.loc, /turf/simulated/wall))) + oldtarget = M + target = M break else return + if(nag_on_empty) //Floorbot is empty and cannot acquire more tiles, nag the engineers for more! + nag() if(prob(5)) visible_message("[src] makes an excited booping beeping sound!") - if((!src.target || src.target == null) && emagged < 2) - if(targetdirection != null) - /* + if(!target && emagged < 2 && amount > 0) + if(targetdirection != null) //The bot is in bridge mode. + for (var/turf/space/D in view(7,src)) - if(!(D in floorbottargets) && D != src.oldtarget) // Added for bridging mode + if(!(D in ignore_list) && D != oldtarget) // Added for bridging mode if(get_dir(src, D) == targetdirection) - src.oldtarget = D - src.target = D + oldtarget = D + target = D break - */ + var/turf/T = get_step(src, targetdirection) if(istype(T, /turf/space)) - src.oldtarget = T - src.target = T - if(!src.target || src.target == null) - for (var/turf/space/D in view(7,src)) - if(!(D in floorbottargets) && D != src.oldtarget && (D.loc.name != "Space")) - src.oldtarget = D - src.target = D + oldtarget = T + target = T + if(!target) + for (var/turf/space/D in view(7,src)) //Ensures the floorbot does not try to "fix" space areas or shuttle docking zones. + if(!(D in ignore_list) && D != oldtarget && is_hull_breach(D)) + mode = BOT_MOVING + oldtarget = D + target = D break - if((!src.target || src.target == null ) && src.improvefloors) + if(!target && replacetiles) //Finds a floor without a tile and gives it one. for (var/turf/simulated/floor/F in view(7,src)) - if(!(F in floorbottargets) && F != src.oldtarget && F.icon_state == "Floor1" && !(istype(F, /turf/simulated/floor/plating))) - src.oldtarget = F - src.target = F + //The target must be the floor and not a tile. The floor must not already have a floortile. + if(!(F in ignore_list) && F != oldtarget && F.is_plating()) + oldtarget = F + target = F + mode = BOT_MOVING break - if((!src.target || src.target == null) && src.eattiles) - for(var/obj/item/stack/tile/plasteel/T in view(7, src)) - if(!(T in floorbottargets) && T != src.oldtarget) - src.oldtarget = T - src.target = T + if(!target && fixfloors) //Repairs damaged floors and tiles. + for(var/turf/simulated/floor/F in view(7, src)) + if(!(F in ignore_list) && F != oldtarget && (F.broken || F.burnt)) + oldtarget = F + target = F + mode = BOT_MOVING break - if((!src.target || src.target == null) && emagged == 2) - if(!src.target || src.target == null) + if(!target && emagged == 2) + if(!target) for (var/turf/simulated/floor/D in view(7,src)) - if(!(D in floorbottargets) && D != src.oldtarget && D.floor_tile) - src.oldtarget = D - src.target = D + if(!(D in ignore_list) && D != oldtarget && D.floor_tile) + oldtarget = D + target = D + mode = BOT_MOVING break - if(!src.target || src.target == null) - if(src.loc != src.oldloc) - src.oldtarget = null + if(!target) + + if(auto_patrol) + if(mode == BOT_IDLE || mode == BOT_START_PATROL) + start_patrol() + + if(mode == BOT_PATROL) + bot_patrol() + + if(!target) + if(loc != oldloc) + oldtarget = null return - if(src.target && (src.target != null) && src.path.len == 0) + if(target && path.len == 0) spawn(0) - if(!istype(src.target, /turf/)) + if(!istype(target, /turf/)) var/turf/TL = get_turf(target) - src.path = AStar(src.loc, TL, /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30) + path = AStar(loc, TL, /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id=botcard) else - src.path = AStar(src.loc, src.target, /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30) - if(!src.path) - src.path = list() - if(src.path.len == 0) - src.oldtarget = src.target - src.target = null + path = AStar(loc, target, /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id=botcard) + if(!path) + path = list() + if(path.len == 0) + add_to_ignore(target) + oldtarget = target + target = null + mode = BOT_IDLE return - if(src.path.len > 0 && src.target && (src.target != null)) - step_to(src, src.path[1]) - src.path -= src.path[1] - else if(src.path.len == 1) + if(path.len > 0 && target) + step_to(src, path[1]) + path -= path[1] + else if(path.len == 1) step_to(src, target) - src.path = new() + path = new() - if(src.loc == src.target || src.loc == src.target.loc) - if(istype(src.target, /obj/item/stack/tile/plasteel)) - src.eattile(src.target) - else if(istype(src.target, /obj/item/stack/sheet/metal)) - src.maketile(src.target) - else if(istype(src.target, /turf/) && emagged < 2) - repair(src.target) - else if(emagged == 2 && istype(src.target,/turf/simulated/floor)) - var/turf/simulated/floor/F = src.target - src.anchored = 1 - src.repairing = 1 + if(loc == target || loc == target.loc) + if(istype(target, /obj/item/stack/tile/plasteel)) + eattile(target) + else if(istype(target, /obj/item/stack/sheet/metal)) + maketile(target) + else if(istype(target, /turf/) && emagged < 2) + repair(target) + else if(emagged == 2 && istype(target,/turf/simulated/floor)) + var/turf/simulated/floor/F = target + anchored = 1 + mode = BOT_REPAIRING if(prob(90)) F.break_tile_to_plating() else F.ReplaceWithLattice() visible_message("[src] makes an excited booping sound.") spawn(50) - src.amount ++ - src.anchored = 0 - src.repairing = 0 - src.target = null - src.path = new() + amount ++ + anchored = 0 + mode = BOT_IDLE + target = null + path = new() return - src.oldloc = src.loc + oldloc = loc +/obj/machinery/bot/floorbot/proc/nag() //Annoy everyone on the channel to refill us! + if(!nagged) + speak("Requesting refill at [get_area(src)]!", radio_frequency) + nagged = 1 + +/obj/machinery/bot/floorbot/proc/add_to_ignore(target) //Often objects may block access to damaged tiles or open space. + if(ignore_list.len < max_targets && !(target in ignore_list)) //This will help keep track of them, so the bot is always trying to reach a blocked spot. + ignore_list += target + else if (ignore_list.len >= max_targets) //If the list is full, insert newest, delete oldest. + ignore_list -= ignore_list[1] + ignore_list += target + +/obj/machinery/bot/floorbot/proc/is_hull_breach(var/turf/t) //Ignore space tiles not considered part of a structure, also ignores shuttle docking areas. + var/area/t_area = get_area(t) + if (t_area && (t_area.name == "Space" || findtext(t_area.name, "huttle"))) + return 0 + else + return 1 /obj/machinery/bot/floorbot/proc/repair(var/turf/target) + if(istype(target, /turf/space/)) - if(target.loc.name == "Space") + //Must be a hull breach or in bridge mode to continue. + if(!is_hull_breach(target) && !targetdirection) + target = null return else if(!istype(target, /turf/simulated/floor)) return - if(src.amount <= 0) + if(amount <= 0) + mode = BOT_IDLE + target = null return - src.anchored = 1 - src.icon_state = "floorbot-c" - if(istype(target, /turf/space/)) - visible_message("[src] begins to repair the hole.") - var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel - src.repairing = 1 + var/turf/simulated/floor/F + var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel + anchored = 1 + icon_state = "floorbot-c" + if(istype(target, /turf/space/)) //If we are fixing an area not part of pure space, it is + visible_message(" [targetdirection ? "[src] begins installing a bridge plating." : "[src] begins to repair the hole."] ") + mode = BOT_REPAIRING spawn(50) - T.build(src.loc) - src.repairing = 0 - src.amount -= 1 - src.updateicon() - src.anchored = 0 - src.target = null + if(mode == BOT_REPAIRING) + if(autotile) //Build the floor and include a tile. + F = target.ChangeTurf(/turf/simulated/floor) + else //Build a hull plating without a floor tile. + T.build(loc) + + mode = BOT_IDLE + amount -= 1 + updateicon() + anchored = 0 + src.target = null else - visible_message("[src] begins to improve the floor.") - src.repairing = 1 + + F = target + + mode = BOT_REPAIRING + visible_message(" [src] begins repairing the floor.") spawn(50) - src.loc.icon_state = "floor" - src.repairing = 0 - src.amount -= 1 - src.updateicon() - src.anchored = 0 - src.target = null + if(mode == BOT_REPAIRING) + F = target + F.make_plasteel_floor(T) + mode = BOT_IDLE + amount -= 1 + updateicon() + anchored = 0 + src.target = null /obj/machinery/bot/floorbot/proc/eattile(var/obj/item/stack/tile/plasteel/T) if(!istype(T, /obj/item/stack/tile/plasteel)) return - visible_message("[src] begins to collect tiles.") - src.repairing = 1 + visible_message(" [src] begins to collect tiles.") + mode = BOT_REPAIRING spawn(20) if(isnull(T)) - src.target = null - src.repairing = 0 + target = null + mode = BOT_IDLE return - if(src.amount + T.amount > 50) - var/i = 50 - src.amount - src.amount += i + if(amount + T.amount > 50) + var/i = 50 - amount + amount += i T.amount -= i else - src.amount += T.amount + amount += T.amount qdel(T) - src.updateicon() - src.target = null - src.repairing = 0 + updateicon() + target = null + mode = BOT_IDLE /obj/machinery/bot/floorbot/proc/maketile(var/obj/item/stack/sheet/metal/M) if(!istype(M, /obj/item/stack/sheet/metal)) return - if(M.amount > 1) - return - visible_message("[src] begins to create tiles.") - src.repairing = 1 + visible_message(" [src] begins to create tiles.") + mode = BOT_REPAIRING spawn(20) if(isnull(M)) - src.target = null - src.repairing = 0 + target = null + mode = BOT_IDLE return var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel T.amount = 4 T.loc = M.loc - qdel(M) - src.target = null - src.repairing = 0 + if(M.amount > 1) + M.amount-- + else + qdel(M) + target = null + mode = BOT_IDLE /obj/machinery/bot/floorbot/proc/updateicon() - if(src.amount > 0) - src.icon_state = "floorbot[src.on]" + if(amount > 0) + icon_state = "floorbot[on]" else - src.icon_state = "floorbot[src.on]e" + icon_state = "floorbot[on]e" /obj/machinery/bot/floorbot/explode() - src.on = 0 - src.visible_message("[src] blows apart!", 1) + on = 0 + visible_message("[src] blows apart!", 1) var/turf/Tsec = get_turf(src) var/obj/item/weapon/storage/toolbox/mechanical/N = new /obj/item/weapon/storage/toolbox/mechanical(Tsec) @@ -397,7 +467,7 @@ amount -= 16 else var/obj/item/stack/tile/plasteel/T = new (Tsec) - T.amount = src.amount + T.amount = amount amount = 0 var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread @@ -411,7 +481,7 @@ if(!istype(T, /obj/item/stack/tile/plasteel)) ..() return - if(src.contents.len >= 1) + if(contents.len >= 1) user << "They won't fit in, as there is already stuff inside." return if(T.use(10)) @@ -431,7 +501,7 @@ if(isprox(W)) qdel(W) var/obj/item/weapon/toolbox_tiles_sensor/B = new /obj/item/weapon/toolbox_tiles_sensor() - B.created_name = src.created_name + B.created_name = created_name user.put_in_hands(B) user << "You add the sensor to the toolbox and tiles!" user.unEquip(src, 1) @@ -441,10 +511,10 @@ var/t = copytext(stripped_input(user, "Enter new robot name", src.name, src.created_name),1,MAX_NAME_LEN) if (!t) return - if (!in_range(src, usr) && src.loc != usr) + if (!in_range(src, usr) && loc != usr) return - src.created_name = t + created_name = t /obj/item/weapon/toolbox_tiles_sensor/attackby(var/obj/item/W, mob/user as mob) ..() @@ -452,16 +522,16 @@ qdel(W) var/turf/T = get_turf(user.loc) var/obj/machinery/bot/floorbot/A = new /obj/machinery/bot/floorbot(T) - A.name = src.created_name + A.name = created_name user << "You add the robot arm to the odd looking toolbox assembly! Boop beep!" user.unEquip(src, 1) qdel(src) else if (istype(W, /obj/item/weapon/pen)) - var/t = stripped_input(user, "Enter new robot name", src.name, src.created_name) + var/t = stripped_input(user, "Enter new robot name", name, created_name) if (!t) return - if (!in_range(src, usr) && src.loc != usr) + if (!in_range(src, usr) && loc != usr) return - src.created_name = t \ No newline at end of file + created_name = t \ No newline at end of file diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm index a9754377d85..d4f7dd72c98 100644 --- a/code/game/machinery/bots/medbot.dm +++ b/code/game/machinery/bots/medbot.dm @@ -13,31 +13,34 @@ anchored = 0 health = 20 maxhealth = 20 - req_access =list(access_medical) + req_one_access =list(access_medical, access_robotics) var/stunned = 0 //It can be stunned by tasers. Delicate circuits. // var/emagged = 0 var/list/botcard_access = list(access_medical) var/obj/item/weapon/reagent_containers/glass/reagent_glass = null //Can be set to draw from this for reagents. var/skin = null //Set to "tox", "ointment" or "o2" for the other two firstaid kits. - var/frustration = 0 - var/path[] = new() var/mob/living/carbon/patient = null var/mob/living/carbon/oldpatient = null var/oldloc = null var/last_found = 0 var/last_newpatient_speak = 0 //Don't spam the "HEY I'M COMING" messages - 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. - + var/declare_crit = 1 //If active, the bot will transmit a critical patient alert to MedHUD users. + var/declare_cooldown = 0 //Prevents spam of critical patient alerts. + var/stationary_mode = 0 //If enabled, the Medibot will not move automatically. + radio_frequency = 1355 //Medical frequency //Setting which reagents to use to treat what by default. By id. var/treatment_brute = "tricordrazine" var/treatment_oxy = "tricordrazine" var/treatment_fire = "tricordrazine" var/treatment_tox = "tricordrazine" var/treatment_virus = "spaceacillin" + var/treat_virus = 1 //If on, the bot will attempt to treat viral infections, curing them if possible. var/shut_up = 0 //self explanatory :) + bot_type = MED_BOT + bot_filter = RADIO_MEDBOT /obj/machinery/bot/medbot/mysterious name = "\improper Mysterious Medibot" @@ -58,43 +61,63 @@ var/skin = null //Same as medbot, set to tox or ointment for the respective kits. w_class = 3.0 - New() + /obj/item/weapon/firstaid_arm_assembly/New() ..() spawn(5) - if(src.skin) - src.overlays += image('icons/obj/aibots.dmi', "kit_skin_[src.skin]") + if(skin) + overlays += image('icons/obj/aibots.dmi', "kit_skin_[skin]") +/obj/machinery/bot/medbot/proc/updateicon() + if(!on) + icon_state = "medibot0" + return + if(mode == BOT_HEALING) + icon_state = "medibots[stationary_mode]" + return + else if(stationary_mode) //Bot has yellow light to indicate stationary mode. + icon_state = "medibot2" + else + icon_state = "medibot1" /obj/machinery/bot/medbot/New() ..() - src.icon_state = "medibot[src.on]" + updateicon() spawn(4) - if(src.skin) - src.overlays += image('icons/obj/aibots.dmi', "medskin_[src.skin]") + if(skin) + overlays += image('icons/obj/aibots.dmi', "medskin_[skin]") - src.botcard = new /obj/item/weapon/card/id(src) - if(isnull(src.botcard_access) || (src.botcard_access.len < 1)) + if(isnull(botcard_access) || (botcard_access.len < 1)) var/datum/job/doctor/J = new/datum/job/doctor - src.botcard.access = J.get_access() + botcard.access = J.get_access() else - src.botcard.access = src.botcard_access + botcard.access = botcard_access + prev_access = botcard.access + add_to_beacons(bot_filter) /obj/machinery/bot/medbot/turn_on() . = ..() - src.icon_state = "medibot[src.on]" - src.updateUsrDialog() + updateicon() + updateUsrDialog() /obj/machinery/bot/medbot/turn_off() ..() - src.patient = null - src.oldpatient = null - src.oldloc = null - src.path = new() - src.currently_healing = 0 - src.last_found = world.time - src.icon_state = "medibot[src.on]" - src.updateUsrDialog() + updateicon() + updateUsrDialog() + +/obj/machinery/bot/medbot/bot_reset() + ..() + patient = null + oldpatient = null + oldloc = null + last_found = world.time + declare_cooldown = 0 + +/obj/machinery/bot/medbot/set_custom_texts() + + text_hack = "You corrupt [name]'s reagent processor circuits." + text_dehack = "You reset [name]'s reagent processor circuits." + text_dehack_fail = "[name] seems damaged and does not respond to reprogramming!" /obj/machinery/bot/medbot/attack_paw(mob/user as mob) return attack_hand(user) @@ -105,99 +128,93 @@ return var/dat dat += hack(user) - dat += "Automatic Medical Unit v1.0

" - dat += "Status: [src.on ? "On" : "Off"]
" - dat += "Maintenance panel panel is [src.open ? "opened" : "closed"]
" + dat += "Medical Unit Controls v1.1

" + dat += "Status: [on ? "On" : "Off"]
" + dat += "Maintenance panel panel is [open ? "opened" : "closed"]
" dat += "Beaker: " - if (src.reagent_glass) - dat += "Loaded \[[src.reagent_glass.reagents.total_volume]/[src.reagent_glass.reagents.maximum_volume]\]" + if (reagent_glass) + dat += "Loaded \[[reagent_glass.reagents.total_volume]/[reagent_glass.reagents.maximum_volume]\]" else dat += "None Loaded" - dat += "
Behaviour controls are [src.locked ? "locked" : "unlocked"]
" - if(!src.locked || issilicon(user)) + dat += "
Behaviour controls are [locked ? "locked" : "unlocked"]
" + if(!locked || issilicon(user)) dat += "Healing Threshold: " dat += "-- " dat += "- " - dat += "[src.heal_threshold] " + dat += "[heal_threshold] " dat += "+ " dat += "++" dat += "
" dat += "Injection Level: " dat += "- " - dat += "[src.injection_amount] " + dat += "[injection_amount] " dat += "+ " dat += "
" dat += "Reagent Source: " - dat += "[src.use_beaker ? "Loaded Beaker (When available)" : "Internal Synthesizer"]
" + dat += "[use_beaker ? "Loaded Beaker (When available)" : "Internal Synthesizer"]
" - dat += "The speaker switch is [src.shut_up ? "off" : "on"]. Toggle" + dat += "Treat Viral Infections: [treat_virus ? "Yes" : "No"]
" + dat += "The speaker switch is [shut_up ? "off" : "on"]. Toggle
" + dat += "Critical Patient Alerts: [declare_crit ? "Yes" : "No"]
" + dat += "Patrol Station: [auto_patrol ? "Yes" : "No"]
" + dat += "Stationary Mode: [stationary_mode ? "Yes" : "No"]
" - user << browse("Medibot v1.0 controls[dat]", "window=automed") - onclose(user, "automed") + var/datum/browser/popup = new(user, "automed", "Automatic Medical Unit v1.1") + popup.set_content(dat) + popup.open() return /obj/machinery/bot/medbot/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - src.add_fingerprint(usr) - if ((href_list["power"]) && (src.allowed(usr))) - if (src.on && !src.emagged) - turn_off() - else - turn_on() + ..() - else if((href_list["adj_threshold"]) && (!src.locked || issilicon(usr))) + if(href_list["adj_threshold"]) var/adjust_num = text2num(href_list["adj_threshold"]) - src.heal_threshold += adjust_num - if(src.heal_threshold < 5) - src.heal_threshold = 5 - if(src.heal_threshold > 75) - src.heal_threshold = 75 + heal_threshold += adjust_num + if(heal_threshold < 5) + heal_threshold = 5 + if(heal_threshold > 75) + heal_threshold = 75 - else if((href_list["adj_inject"]) && (!src.locked || issilicon(usr))) + else if(href_list["adj_inject"]) var/adjust_num = text2num(href_list["adj_inject"]) - src.injection_amount += adjust_num - if(src.injection_amount < 5) - src.injection_amount = 5 - if(src.injection_amount > 15) - src.injection_amount = 15 + injection_amount += adjust_num + if(injection_amount < 5) + injection_amount = 5 + if(injection_amount > 15) + injection_amount = 15 - else if((href_list["use_beaker"]) && (!src.locked || issilicon(usr))) - src.use_beaker = !src.use_beaker + else if(href_list["use_beaker"]) + use_beaker = !use_beaker - else if (href_list["eject"] && (!isnull(src.reagent_glass))) - if(!src.locked) - src.reagent_glass.loc = get_turf(src) - src.reagent_glass = null - else - usr << "You cannot eject the beaker because the panel is locked." + else if (href_list["eject"] && (!isnull(reagent_glass))) + reagent_glass.loc = get_turf(src) + reagent_glass = null - else if ((href_list["togglevoice"]) && (!src.locked || issilicon(usr))) - src.shut_up = !src.shut_up + else if (href_list["togglevoice"]) + shut_up = !shut_up - else if (href_list["operation"]) - if(!src.emagged) - src.emagged = 2 - src.hacked = 1 - usr << "You corrupt [src]'s reagent processor circuits." - else if(!src.hacked) - usr << "[src] seems damaged and does not respond to reprogramming!" - else - src.hacked = 0 - src.emagged = 0 - usr << "You reset [src]'s reagent circuits." - src.updateUsrDialog() + else if (href_list["critalerts"]) + declare_crit = !declare_crit + + else if (href_list["stationary"]) + stationary_mode = !stationary_mode + path = new() + updateicon() + + else if (href_list["virus"]) + treat_virus = !treat_virus + + updateUsrDialog() return /obj/machinery/bot/medbot/attackby(obj/item/weapon/W as obj, mob/user as mob) if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if (src.allowed(user) && !open && !emagged) - src.locked = !src.locked - user << "Controls are now [src.locked ? "locked." : "unlocked."]" - src.updateUsrDialog() + if (allowed(user) && !open && !emagged) + locked = !locked + user << "Controls are now [locked ? "locked." : "unlocked."]" + updateUsrDialog() else if(emagged) user << "ERROR" @@ -207,18 +224,18 @@ user << "Access denied." else if (istype(W, /obj/item/weapon/reagent_containers/glass)) - if(src.locked) + if(locked) user << "You cannot insert a beaker because the panel is locked." return - if(!isnull(src.reagent_glass)) + if(!isnull(reagent_glass)) user << "There is already a beaker loaded." return user.drop_item() W.loc = src - src.reagent_glass = W + reagent_glass = W user << "You insert [W]." - src.updateUsrDialog() + updateUsrDialog() return else @@ -228,106 +245,118 @@ /obj/machinery/bot/medbot/Emag(mob/user as mob) ..() - if(open && !locked) - if(user) user << "You short out [src]'s reagent synthesis circuits." + if(emagged == 2) + declare_crit = 0 + if(user) + user << "You short out [src]'s reagent synthesis circuits." spawn(0) - src.visible_message("[src] buzzes oddly!", 1) + visible_message("[src] buzzes oddly!", 1) flick("medibot_spark", src) - src.patient = null - if(user) src.oldpatient = user - src.currently_healing = 0 - src.last_found = world.time - src.anchored = 0 - src.emagged = 2 - src.on = 1 - src.icon_state = "medibot[src.on]" + if(user) + oldpatient = user /obj/machinery/bot/medbot/process() set background = BACKGROUND_ENABLED - if(!src.on) - src.stunned = 0 + if(!on) return - if(src.stunned) - src.icon_state = "medibota" - src.stunned-- - - src.oldpatient = src.patient - src.patient = null - src.currently_healing = 0 - - if(src.stunned <= 0) - src.icon_state = "medibot[src.on]" - src.stunned = 0 + if(call_path) + call_mode() return - if(src.frustration > 8) - src.oldpatient = src.patient - src.patient = null - src.currently_healing = 0 - src.last_found = world.time - src.path = new() + if(stunned) + icon_state = "medibota" + stunned-- - if(!src.patient) - if(!src.shut_up && prob(1)) + oldpatient = patient + patient = null + mode = BOT_IDLE + + if(stunned <= 0) + updateicon() + stunned = 0 + return + + if(frustration > 8) + oldpatient = patient + patient = null + mode = BOT_IDLE + last_found = world.time + path = new() + + if(!patient) + if(mode == BOT_SUMMON) //A medibot will not search for new patients when summoned! + bot_summon() + return + + if(!shut_up && 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!") - src.speak(message) + speak(message) - for (var/mob/living/carbon/C in view(7,src)) //Time to find a patient! + var/scan_range = (stationary_mode ? 1 : 7) //If in stationary mode, scan range is limited to adjacent patients. + for (var/mob/living/carbon/C in view(scan_range,src)) //Time to find a patient! if ((C.stat == 2) || !istype(C, /mob/living/carbon/human)) continue - if ((C == src.oldpatient) && (world.time < src.last_found + 100)) + if ((C == oldpatient) && (world.time < last_found + 100)) continue - if(src.assess_patient(C)) - src.patient = C - src.oldpatient = C - src.last_found = world.time - if((src.last_newpatient_speak + 300) < world.time) //Don't spam these messages! + if(assess_patient(C)) + patient = C + oldpatient = C + last_found = world.time + if((last_newpatient_speak + 300) < world.time) //Don't spam these messages! var/message = pick("Hey, [C.name]! Hold on, I'm coming.","Wait [C.name]! I want to help!","[C.name], you appear to be injured!") - src.speak(message) - src.last_newpatient_speak = world.time + speak(message) + last_newpatient_speak = world.time break else continue - if(src.patient && (get_dist(src,src.patient) <= 1)) - if(!src.currently_healing) - src.currently_healing = 1 - src.frustration = 0 - src.medicate_patient(src.patient) + if(patient && (get_dist(src,patient) <= 1)) + if(mode != BOT_HEALING) + mode = BOT_HEALING + updateicon() + frustration = 0 + medicate_patient(patient) return - else if(src.patient && (src.path.len) && (get_dist(src.patient,src.path[src.path.len]) > 2)) - src.path = new() - src.currently_healing = 0 - src.last_found = world.time + else if(patient && (path.len) && (get_dist(patient,path[path.len]) > 2)) + path = new() + mode = BOT_IDLE + last_found = world.time - if(src.patient && src.path.len == 0 && (get_dist(src,src.patient) > 1)) + if(!stationary_mode && patient && path.len == 0 && (get_dist(src,patient) > 1)) spawn(0) - src.path = AStar(src.loc, get_turf(src.patient), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 30,id=botcard) - if(!src.path) - src.path = list() - if(src.path.len == 0) - src.oldpatient = src.patient - src.patient = null - src.currently_healing = 0 - src.last_found = world.time + path = AStar(loc, get_turf(patient), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 30,id=botcard) + if(!path) + path = list() + if(path.len == 0) + oldpatient = patient + patient = null + mode = BOT_IDLE + last_found = world.time return - if(src.path.len > 0 && src.patient) - step_to(src, src.path[1]) - src.path -= src.path[1] + if(path.len > 0 && patient) + step_to(src, path[1]) + path -= path[1] spawn(3) - if(src.path.len) - step_to(src, src.path[1]) - src.path -= src.path[1] + if(path.len) + step_to(src, path[1]) + path -= path[1] - if(src.path.len > 8 && src.patient) - src.frustration++ + if(path.len > 8 && patient) + frustration++ + + if(auto_patrol && !stationary_mode && !patient) + if(mode == BOT_IDLE || mode == BOT_START_PATROL) + start_patrol() + + if(mode == BOT_PATROL) + bot_patrol() return @@ -339,113 +368,117 @@ if(C.suiciding) return 0 //Kevorkian school of robotic medical assistants. - if(src.emagged == 2) //Everyone needs our medicine. (Our medicine is toxins) + if(emagged == 2) //Everyone needs our medicine. (Our medicine is toxins) return 1 + if(declare_crit && C.health <= 0) //Critical condition! Call for help! + declare(C) + //If they're injured, we're using a beaker, and don't have one of our WONDERCHEMS. - if((src.reagent_glass) && (src.use_beaker) && ((C.getBruteLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getOxyLoss() >= (heal_threshold + 15)))) - for(var/datum/reagent/R in src.reagent_glass.reagents.reagent_list) + if((reagent_glass) && (use_beaker) && ((C.getBruteLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getToxLoss() >= heal_threshold) || (C.getOxyLoss() >= (heal_threshold + 15)))) + for(var/datum/reagent/R in reagent_glass.reagents.reagent_list) if(!C.reagents.has_reagent(R.id)) return 1 //They're injured enough for it! - if((C.getBruteLoss() >= heal_threshold) && (!C.reagents.has_reagent(src.treatment_brute))) + if((C.getBruteLoss() >= heal_threshold) && (!C.reagents.has_reagent(treatment_brute))) return 1 //If they're already medicated don't bother! - if((C.getOxyLoss() >= (15 + heal_threshold)) && (!C.reagents.has_reagent(src.treatment_oxy))) + if((C.getOxyLoss() >= (15 + heal_threshold)) && (!C.reagents.has_reagent(treatment_oxy))) return 1 - if((C.getFireLoss() >= heal_threshold) && (!C.reagents.has_reagent(src.treatment_fire))) + if((C.getFireLoss() >= heal_threshold) && (!C.reagents.has_reagent(treatment_fire))) return 1 - if((C.getToxLoss() >= heal_threshold) && (!C.reagents.has_reagent(src.treatment_tox))) + if((C.getToxLoss() >= heal_threshold) && (!C.reagents.has_reagent(treatment_tox))) return 1 + if(treat_virus) + for(var/datum/disease/D in C.viruses) + if((D.hidden[SCANNER]) || (D.hidden[PANDEMIC])) //the medibot can't detect viruses that are undetectable to Health Analyzers or Pandemic machines. + return 0 + if(D.severity == D.non_threat) // medibot doesn't try to heal truly harmless viruses + return 0 + if((D.stage > 1) || (D.spread_type == AIRBORNE)) // medibot can't detect a virus in its initial stage unless it spreads airborne. - for(var/datum/disease/D in C.viruses) - if((D.hidden[SCANNER]) || (D.hidden[PANDEMIC])) //the medibot can't detect viruses that are undetectable to Health Analyzers or Pandemic machines. - return 0 - if(D.severity == D.non_threat) // medibot doesn't try to heal truly harmless viruses - return 0 - if((D.stage > 1) || (D.spread_type == AIRBORNE)) // medibot can't detect a virus in its initial stage unless it spreads airborne. - - if (!C.reagents.has_reagent(src.treatment_virus)) - return 1 //STOP DISEASE FOREVER + if (!C.reagents.has_reagent(src.treatment_virus)) + return 1 //STOP DISEASE FOREVER return 0 /obj/machinery/bot/medbot/proc/medicate_patient(mob/living/carbon/C as mob) - if(!src.on) + if(!on) return if(!istype(C)) - src.oldpatient = src.patient - src.patient = null - src.currently_healing = 0 - src.last_found = world.time + oldpatient = patient + patient = null + mode = BOT_IDLE + last_found = world.time return if(C.stat == 2) var/death_message = pick("No! NO!","Live, damnit! LIVE!","I...I've never lost a patient before. Not today, I mean.") - src.speak(death_message) - src.oldpatient = src.patient - src.patient = null - src.currently_healing = 0 - src.last_found = world.time + speak(death_message) + oldpatient = patient + patient = null + mode = BOT_IDLE + last_found = world.time return var/reagent_id = null - if(src.emagged == 2) //Emagged! Time to poison everybody. + if(emagged == 2) //Emagged! Time to poison everybody. reagent_id = "toxin" else - var/virus = 0 - for(var/datum/disease/D in C.viruses) - if((!D.hidden[SCANNER]) && (!D.hidden[PANDEMIC])) //detectable virus - if(D.severity != D.non_threat) //virus is harmful - if((D.stage > 1) || (D.spread_type == AIRBORNE)) - virus = 1 + if(treat_virus) + var/virus = 0 + for(var/datum/disease/D in C.viruses) + if((!D.hidden[SCANNER]) && (!D.hidden[PANDEMIC])) //detectable virus + if(D.severity != D.non_threat) //virus is harmful + if((D.stage > 1) || (D.spread_type == AIRBORNE)) + virus = 1 - if (!reagent_id && (virus)) - if(!C.reagents.has_reagent(src.treatment_virus)) - reagent_id = src.treatment_virus + if (!reagent_id && (virus)) + if(!C.reagents.has_reagent(treatment_virus)) + reagent_id = treatment_virus if (!reagent_id && (C.getBruteLoss() >= heal_threshold)) - if(!C.reagents.has_reagent(src.treatment_brute)) - reagent_id = src.treatment_brute + if(!C.reagents.has_reagent(treatment_brute)) + reagent_id = treatment_brute if (!reagent_id && (C.getOxyLoss() >= (15 + heal_threshold))) - if(!C.reagents.has_reagent(src.treatment_oxy)) - reagent_id = src.treatment_oxy + if(!C.reagents.has_reagent(treatment_oxy)) + reagent_id = treatment_oxy if (!reagent_id && (C.getFireLoss() >= heal_threshold)) - if(!C.reagents.has_reagent(src.treatment_fire)) - reagent_id = src.treatment_fire + if(!C.reagents.has_reagent(treatment_fire)) + reagent_id = treatment_fire if (!reagent_id && (C.getToxLoss() >= heal_threshold)) - if(!C.reagents.has_reagent(src.treatment_tox)) - reagent_id = src.treatment_tox + if(!C.reagents.has_reagent(treatment_tox)) + reagent_id = treatment_tox //If the patient is injured but doesn't have our special reagent in them then we should give it to them first - if(reagent_id && src.use_beaker && src.reagent_glass && src.reagent_glass.reagents.total_volume) - for(var/datum/reagent/R in src.reagent_glass.reagents.reagent_list) + if(reagent_id && use_beaker && reagent_glass && reagent_glass.reagents.total_volume) + for(var/datum/reagent/R in reagent_glass.reagents.reagent_list) if(!C.reagents.has_reagent(R.id)) reagent_id = "internal_beaker" break if(!reagent_id) //If they don't need any of that they're probably cured! - src.oldpatient = src.patient - src.patient = null - src.currently_healing = 0 - src.last_found = world.time + oldpatient = patient + patient = null + mode = BOT_IDLE + last_found = world.time var/message = pick("All patched up!","An apple a day keeps me away.","Feel better soon!") - src.speak(message) + speak(message) + updateicon() return else - src.icon_state = "medibots" - C.visible_message("[src] is trying to inject [src.patient]!", \ - "[src] is trying to inject [src.patient]!") + C.visible_message("[src] is trying to inject [patient]!", \ + "[src] is trying to inject [patient]!") spawn(30) if ((get_dist(src, src.patient) <= 1) && (src.on)) @@ -454,12 +487,12 @@ src.reagent_glass.reagents.trans_to(src.patient,src.injection_amount) //Inject from beaker instead. src.reagent_glass.reagents.reaction(src.patient, 2) else - src.patient.reagents.add_reagent(reagent_id,src.injection_amount) - C.visible_message("[src] injects [src.patient] with the syringe!", \ - "[src] injects [src.patient] with the syringe!") + patient.reagents.add_reagent(reagent_id,injection_amount) + C.visible_message("[src] injects [patient] with the syringe!", \ + "[src] injects [patient] with the syringe!") - src.icon_state = "medibot[src.on]" - src.currently_healing = 0 + mode = BOT_IDLE + updateicon() return reagent_id = null @@ -467,11 +500,11 @@ /obj/machinery/bot/medbot/bullet_act(var/obj/item/projectile/Proj) if(Proj.flag == "taser") - src.stunned = min(stunned+10,20) + stunned = min(stunned+10,20) ..() /obj/machinery/bot/medbot/explode() - src.on = 0 + on = 0 visible_message("[src] blows apart!", 1) var/turf/Tsec = get_turf(src) @@ -481,9 +514,9 @@ new /obj/item/device/healthanalyzer(Tsec) - if(src.reagent_glass) - src.reagent_glass.loc = Tsec - src.reagent_glass = null + if(reagent_glass) + reagent_glass.loc = Tsec + reagent_glass = null if (prob(50)) new /obj/item/robot_parts/l_arm(Tsec) @@ -494,16 +527,15 @@ qdel(src) return -/obj/machinery/bot/medbot/Bump(M as mob|obj) //Leave no door unopened! - if ((istype(M, /obj/machinery/door)) && (!isnull(src.botcard))) - var/obj/machinery/door/D = M - if (!istype(D, /obj/machinery/door/firedoor) && D.check_access(src.botcard)) - D.open() - src.frustration = 0 - else if ((istype(M, /mob/living/)) && (!src.anchored)) - src.loc = M:loc - src.frustration = 0 - return +/obj/machinery/bot/medbot/proc/declare(var/crit_patient) + if(declare_cooldown) + return + var/area/location = get_area(src) + speak("Medical emergency! [crit_patient ? "[crit_patient]" : "A patient"] is in critical condition at [location]!",radio_frequency) + declare_cooldown = 1 + spawn(200) //Twenty seconds + declare_cooldown = 0 + /* terrible /obj/machinery/bot/medbot/Bumped(atom/movable/M as mob|obj) @@ -554,7 +586,7 @@ return //Making a medibot! - if(src.contents.len >= 1) + if(contents.len >= 1) user << "You need to empty [src] out first." return @@ -576,33 +608,33 @@ /obj/item/weapon/firstaid_arm_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) ..() if(istype(W, /obj/item/weapon/pen)) - var/t = copytext(stripped_input(user, "Enter new robot name", src.name, src.created_name),1,MAX_NAME_LEN) + var/t = copytext(stripped_input(user, "Enter new robot name", name, created_name),1,MAX_NAME_LEN) if (!t) return - if (!in_range(src, usr) && src.loc != usr) + if (!in_range(src, usr) && loc != usr) return - src.created_name = t + created_name = t else switch(build_step) if(0) if(istype(W, /obj/item/device/healthanalyzer)) user.drop_item() qdel(W) - src.build_step++ + build_step++ user << "You add the health sensor to [src]." - src.name = "First aid/robot arm/health analyzer assembly" - src.overlays += image('icons/obj/aibots.dmi', "na_scanner") + name = "First aid/robot arm/health analyzer assembly" + overlays += image('icons/obj/aibots.dmi', "na_scanner") if(1) if(isprox(W)) user.drop_item() qdel(W) - src.build_step++ + build_step++ user << "You complete the Medibot! Beep boop." var/turf/T = get_turf(src) var/obj/machinery/bot/medbot/S = new /obj/machinery/bot/medbot(T) - S.skin = src.skin - S.name = src.created_name + S.skin = skin + S.name = created_name user.unEquip(src, 1) qdel(src) diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index 696da1238b4..9863cf785fb 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -19,34 +19,30 @@ var/global/mulebot_count = 0 fire_dam_coeff = 0.7 brute_dam_coeff = 0.5 var/atom/movable/load = null // the loaded crate (usually) - var/beacon_freq = 1400 - var/control_freq = 1447 + var/list/delivery_beacons = list() //List of beacons that serve as delivery locations. + beacon_freq = 1400 + control_freq = 1447 + bot_type = MULE_BOT + bot_filter = RADIO_MULEBOT suffix = "" var/turf/target // this is turf to navigate to (location of beacon) var/loaddir = 0 // this the direction to unload onto/load from - var/new_destination = "" // pending new destination (waiting for beacon response) - var/destination = "" // destination description + new_destination = "" // pending new destination (waiting for beacon response) + destination = "" // destination description var/home_destination = "" // tag of home beacon req_access = list(access_cargo) // added robotics access so assembly line drop-off works properly -veyveyr //I don't think so, Tim. You need to add it to the MULE's hidden robot ID card. -NEO - var/path[] = new() - var/mode = 0 //0 = idle/ready - //1 = loading/unloading - //2 = moving to deliver - //3 = returning to home - //4 = blocked - //5 = computing navigation - //6 = waiting for nav computation - //7 = no destination beacon found (or no route) + mode = BOT_IDLE - var/blockcount = 0 //number of times retried a blocked path + blockcount = 0 //number of times retried a blocked path var/reached_target = 1 //true if already reached the target - var/refresh = 1 // true to refresh dialogue - var/auto_return = 1 // true if auto return to home beacon after unload - var/auto_pickup = 1 // true if auto-pickup at beacon + var/refresh = 1 // true to refresh dialogue + var/auto_return = 1 // true if auto return to home beacon after unload + var/auto_pickup = 1 // true if auto-pickup at beacon + var/report_delivery = 1 // true if bot will announce an arrival to a location. var/obj/item/weapon/stock_parts/cell/cell var/datum/wires/mulebot/wires = null @@ -67,18 +63,16 @@ var/global/mulebot_count = 0 /obj/machinery/bot/mulebot/New() ..() wires = new(src) - botcard = new(src) var/datum/job/cargo_tech/J = new/datum/job/cargo_tech botcard.access = J.get_access() + prev_access = botcard.access // botcard.access += access_robotics //Why --Ikki cell = new(src) cell.charge = 2000 cell.maxcharge = 2000 spawn(5) // must wait for map loading to finish - if(radio_controller) - radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT) - radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) + add_to_beacons(bot_filter) mulebot_count += 1 if(!suffix) @@ -100,8 +94,8 @@ var/global/mulebot_count = 0 locked = !locked user << "You [locked ? "lock" : "unlock"] the mulebot's controls!" flick("mulebot-emagged", src) - playsound(src.loc, 'sound/effects/sparks1.ogg', 100, 0) - else if(istype(I, /obj/item/weapon/card/id)) + playsound(loc, 'sound/effects/sparks1.ogg', 100, 0) + else if(istype(I, /obj/item/weapon/card/id) || istype(I, /obj/item/device/pda)) if(toggle_lock(user)) user << "Controls [(locked ? "locked" : "unlocked")]." @@ -118,30 +112,30 @@ var/global/mulebot_count = 0 open = !open if(open) - src.visible_message("[user] opens the maintenance hatch of [src]", "You open [src]'s maintenance hatch.") + visible_message("[user] opens the maintenance hatch of [src]", "You open [src]'s maintenance hatch.") on = 0 icon_state="mulebot-hatch" else - src.visible_message("[user] closes the maintenance hatch of [src]", "You close [src]'s maintenance hatch.") + visible_message("[user] closes the maintenance hatch of [src]", "You close [src]'s maintenance hatch.") icon_state = "mulebot0" updateDialog() else if (istype(I, /obj/item/weapon/wrench)) - if (src.health < maxhealth) - src.health = min(maxhealth, src.health+25) + if (health < maxhealth) + health = min(maxhealth, health+25) user.visible_message( - "[user] repairs [src]!", + "[user] repairs [src]!", "You repair [src]!" ) else - user << "[src] does not need a repair!" + user << " [src] does not need a repair!" else if(istype(I, /obj/item/device/multitool) || istype(I, /obj/item/weapon/wirecutters)) if(open) attack_hand(usr) else if(load && ismob(load)) // chance to knock off rider if(prob(1+I.force * 2)) unload(0) - user.visible_message("[user] knocks [load] off [src] with \the [I]!", "You knock [load] off [src] with \the [I]!") + user.visible_message(" [user] knocks [load] off [src] with \the [I]!", " You knock [load] off [src] with \the [I]!") else user << "You hit [src] with \the [I] but to no effect." else @@ -164,7 +158,7 @@ var/global/mulebot_count = 0 if(prob(50) && !isnull(load)) unload(0) if(prob(25)) - src.visible_message("Something shorts out inside [src]!") + visible_message(" Something shorts out inside [src]!") wires.RandomCut() ..() @@ -182,29 +176,30 @@ var/global/mulebot_count = 0 /obj/machinery/bot/mulebot/interact(var/mob/user, var/ai=0) var/dat - dat += "

Multiple Utility Load Effector Mk. IV

" + dat += "

Multiple Utility Load Effector Mk. V

" dat += "ID: [suffix]
" dat += "Power: [on ? "On" : "Off"]
" if(!open) dat += "

Status

" + dat += "
" switch(mode) - if(0) - dat += "Ready" - if(1) - dat += "Loading/Unloading" - if(2) - dat += "Navigating to Delivery Location" - if(3) - dat += "Navigating to Home" - if(4) - dat += "Waiting for clear path" - if(5,6) - dat += "Calculating navigation path" - if(7) - dat += "Unable to reach destination" + if(BOT_IDLE) + dat += "Ready" + if(BOT_LOADING) + dat += "[mode_name[BOT_LOADING]]" + if(BOT_DELIVER) + dat += "[mode_name[BOT_DELIVER]]" + if(BOT_GO_HOME) + dat += "[mode_name[BOT_GO_HOME]]" + if(BOT_BLOCKED) + dat += "[mode_name[BOT_BLOCKED]]" + if(BOT_NAV,BOT_WAIT_FOR_NAV) + dat += "[mode_name[BOT_NAV]]" + if(BOT_NO_ROUTE) + dat += "[mode_name[BOT_NO_ROUTE]]" dat += "
" dat += "Current Load: [load ? load.name : "none"]
" @@ -225,6 +220,7 @@ var/global/mulebot_count = 0 dat += "Set Home
" dat += "Toggle Auto Return Home ([auto_return ? "On":"Off"])
" dat += "Toggle Auto Pickup Crate ([auto_pickup ? "On":"Off"])
" + dat += "Toggle Delivery Reporting ([report_delivery ? "On" : "Off"])
" if(load) dat += "Unload Now
" @@ -247,7 +243,7 @@ var/global/mulebot_count = 0 //onclose(user, "mulebot") var/datum/browser/popup = new(user, "mulebot", "M.U.L.E. Mk. III [suffix ? "([suffix])" : ""]", 350, 500) popup.set_content(dat) - popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state)) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() return @@ -261,7 +257,7 @@ var/global/mulebot_count = 0 return if (usr.stat) return - if ((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon))) + if ((in_range(src, usr) && istype(loc, /turf)) || (istype(usr, /mob/living/silicon))) usr.set_machine(src) switch(href_list["op"]) @@ -269,7 +265,7 @@ var/global/mulebot_count = 0 toggle_lock(usr) if("power") - if (src.on) + if (on) turn_off() else if (cell && !open) if (!turn_on()) @@ -305,23 +301,23 @@ var/global/mulebot_count = 0 if("stop") - if(mode >=2) - mode = 0 + if(mode >= BOT_DELIVER) + bot_reset() updateDialog() if("go") - if(mode == 0) + if(mode == BOT_IDLE) start() updateDialog() if("home") - if(mode == 0 || mode == 2) + if(mode == BOT_IDLE || mode == BOT_DELIVER) start_home() updateDialog() if("destination") refresh=0 - var/new_dest = input("Enter new destination tag", "Mulebot [suffix ? "([suffix])" : ""]", destination) as text|null + var/new_dest = input("Select M.U.L.E. Destination", "Mulebot [suffix ? "([suffix])" : ""]", destination) as null|anything in delivery_beacons refresh=1 if(new_dest) set_destination(new_dest) @@ -357,12 +353,15 @@ var/global/mulebot_count = 0 if("autopick") auto_pickup = !auto_pickup + if("report") + report_delivery = !report_delivery + if("close") usr.unset_machine() usr << browse(null,"window=mulebot") updateDialog() - //src.updateUsrDialog() + //updateUsrDialog() else usr << browse(null, "window=mulebot") usr.unset_machine() @@ -375,7 +374,7 @@ var/global/mulebot_count = 0 return !open && cell && cell.charge > 0 && wires.HasPower() /obj/machinery/bot/mulebot/proc/toggle_lock(var/mob/user) - if(src.allowed(user)) + if(allowed(user)) locked = !locked updateDialog() return 1 @@ -403,8 +402,8 @@ var/global/mulebot_count = 0 // called to load a crate /obj/machinery/bot/mulebot/proc/load(var/atom/movable/C) if(wires.LoadCheck() && !istype(C,/obj/structure/closet/crate)) - src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) return // if not emagged, only allow crates to be loaded //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. @@ -416,16 +415,16 @@ var/global/mulebot_count = 0 if(get_dist(C, src) > 1 || load || !on) return - mode = 1 + mode = BOT_LOADING // if a create, close before loading var/obj/structure/closet/crate/crate = C if(istype(crate)) crate.close() - C.loc = src.loc + C.loc = loc sleep(2) - if(C.loc != src.loc) //To prevent you from going onto more thano ne bot. + if(C.loc != loc) //To prevent you from going onto more thano ne bot. return C.loc = src load = C @@ -441,7 +440,7 @@ var/global/mulebot_count = 0 M.client.perspective = EYE_PERSPECTIVE M.client.eye = src - mode = 0 + mode = BOT_IDLE send_status() // called to unload the bot @@ -451,7 +450,7 @@ var/global/mulebot_count = 0 if(!load) return - mode = 1 + mode = BOT_LOADING overlays.Cut() if(ismob(load)) @@ -461,11 +460,11 @@ var/global/mulebot_count = 0 M.client.eye = src - load.loc = src.loc + load.loc = loc load.pixel_y -= 9 load.layer = initial(load.layer) if(dirn) - var/turf/T = src.loc + var/turf/T = loc var/turf/newT = get_step(T,dirn) if(load.CanPass(load,newT)) //Can't get off onto anything that wouldn't let you pass normally step(load, dirn) @@ -477,9 +476,9 @@ var/global/mulebot_count = 0 // with items dropping as mobs are loaded for(var/atom/movable/AM in src) - if(AM == cell || AM == botcard) continue + if(AM == cell || istype(AM , botcard) || AM == Radio) continue - AM.loc = src.loc + AM.loc = loc AM.layer = initial(AM.layer) AM.pixel_y = initial(AM.pixel_y) if(ismob(AM)) @@ -487,8 +486,19 @@ var/global/mulebot_count = 0 if(M.client) M.client.perspective = MOB_PERSPECTIVE M.client.eye = src - mode = 0 + mode = BOT_IDLE +/obj/machinery/bot/mulebot/call_bot() + ..() + var/area/dest_area + if (call_path && call_path.len) + target = call_path[call_path.len] //Target is the end point of the path, the waypoint set by the AI. + dest_area = get_area(target) + destination = format_text(dest_area.name) + path = call_path + call_path = null //Once the MULE is commanded, follow normal procedures to reach the waypoint. + pathset = 1 //Indicates the AI's custom path is initialized. + start() /obj/machinery/bot/mulebot/process() if(!has_power()) @@ -520,13 +530,14 @@ var/global/mulebot_count = 0 /obj/machinery/bot/mulebot/proc/process_bot() //if(mode) world << "Mode: [mode]" + switch(mode) - if(0) // idle + if(BOT_IDLE) // idle icon_state = "mulebot0" return - if(1) // loading/unloading + if(BOT_LOADING) // loading/unloading return - if(2,3,4) // navigating to deliver,home, or blocked + if(BOT_DELIVER,BOT_GO_HOME,BOT_BLOCKED) // navigating to deliver,home, or blocked if(loc == target) // reached target at_target() @@ -569,14 +580,14 @@ var/global/mulebot_count = 0 path -= loc - if(mode==4) + if(mode == BOT_BLOCKED) spawn(1) send_status() if(destination == home_destination) - mode = 3 + mode = BOT_GO_HOME else - mode = 2 + mode = BOT_DELIVER else // failed to move @@ -585,73 +596,74 @@ var/global/mulebot_count = 0 blockcount++ - mode = 4 + mode = BOT_BLOCKED if(blockcount == 3) - src.visible_message("[src] makes an annoyed buzzing sound.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) + visible_message("[src] makes an annoyed buzzing sound.", "You hear an electronic buzzing sound.") + playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) if(blockcount > 10) // attempt 10 times before recomputing // find new path excluding blocked turf - src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) spawn(2) calc_path(next) if(path.len > 0) - src.visible_message("[src] makes a delighted ping!", "You hear a ping.") - playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) - mode = 4 - mode =6 + visible_message("[src] makes a delighted ping!", "You hear a ping.") + playsound(loc, 'sound/machines/ping.ogg', 50, 0) + mode = BOT_BLOCKED + mode = BOT_WAIT_FOR_NAV return return else - src.visible_message("[src] makes an annoyed buzzing sound.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-two.ogg', 50, 0) + visible_message("[src] makes an annoyed buzzing sound.", "You hear an electronic buzzing sound.") + playsound(loc, 'sound/machines/buzz-two.ogg', 50, 0) //world << "Bad turf." - mode = 5 + mode = BOT_NAV return else //world << "No path." - mode = 5 + mode = BOT_NAV return - if(5) // calculate new path + if(BOT_NAV) // calculate new path //world << "Calc new path." - mode = 6 + mode = BOT_WAIT_FOR_NAV spawn(0) calc_path() if(path.len > 0) blockcount = 0 - mode = 4 - src.visible_message("[src] makes a delighted ping!", "You hear a ping.") - playsound(src.loc, 'sound/machines/ping.ogg', 50, 0) + mode = BOT_BLOCKED + visible_message("[src] makes a delighted ping!", "You hear a ping.") + playsound(loc, 'sound/machines/ping.ogg', 50, 0) else - src.visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") - playsound(src.loc, 'sound/machines/buzz-sigh.ogg', 50, 0) + visible_message("[src] makes a sighing buzz.", "You hear an electronic buzzing sound.") + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0) - mode = 7 + mode = BOT_NO_ROUTE //if(6) //world << "Pending path calc." //if(7) //world << "No dest / no route." + return // calculates a path to the current destination // given an optional turf to avoid -/obj/machinery/bot/mulebot/proc/calc_path(var/turf/avoid = null) - src.path = AStar(src.loc, src.target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 250, id=botcard, exclude=avoid) - if(!src.path) - src.path = list() +/obj/machinery/bot/mulebot/calc_path(var/turf/avoid = null) + path = AStar(loc, target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 250, id=botcard, exclude=avoid) + if(!path) + path = list() // sets the current destination // signals all beacons matching the delivery code // beacons will return a signal giving their locations -/obj/machinery/bot/mulebot/proc/set_destination(var/new_dest) +/obj/machinery/bot/mulebot/set_destination(var/new_dest) spawn(0) new_destination = new_dest post_signal(beacon_freq, "findbeacon", "delivery") @@ -660,9 +672,9 @@ var/global/mulebot_count = 0 // starts bot moving to current destination /obj/machinery/bot/mulebot/proc/start() if(destination == home_destination) - mode = 3 + mode = BOT_GO_HOME else - mode = 2 + mode = BOT_DELIVER icon_state = "mulebot[(wires.MobAvoid() != 0)]" // starts bot moving to home @@ -670,17 +682,28 @@ var/global/mulebot_count = 0 /obj/machinery/bot/mulebot/proc/start_home() spawn(0) set_destination(home_destination) - mode = 4 + mode = BOT_BLOCKED icon_state = "mulebot[(wires.MobAvoid() != 0)]" // called when bot reaches current target /obj/machinery/bot/mulebot/proc/at_target() if(!reached_target) - src.visible_message("[src] makes a chiming sound!", "You hear a chime.") - playsound(src.loc, 'sound/machines/chime.ogg', 50, 0) + radio_frequency = 1347 //Supply channel + visible_message("[src] makes a chiming sound!", "You hear a chime.") + playsound(loc, 'sound/machines/chime.ogg', 50, 0) reached_target = 1 + if(pathset) //The AI called us here, so notify it of our arrival. + loaddir = dir //The MULE will attempt to load a crate in whatever direction the MULE is "facing". + if(calling_ai) + calling_ai << "\icon[src] [src] wirelessly plays a chiming sound!" + playsound(calling_ai, 'sound/machines/chime.ogg',40, 0) + calling_ai = null + radio_frequency = 1447 //Report on AI Private instead if the AI is controlling us. + //bot_reset() + if(load) // if loaded, unload at target + speak("Destination [destination] reached. Unloading [load].",radio_frequency) unload(loaddir) else // not loaded @@ -695,14 +718,16 @@ var/global/mulebot_count = 0 AM = locate(/obj/structure/closet/crate) in get_step(loc,loaddir) if(AM) load(AM) + if(report_delivery) + speak("Now loading [load] at [get_area(src)].", radio_frequency) // whatever happened, check to see if we return home if(auto_return && destination != home_destination) // auto return set and not at home already start_home() - mode = 4 + mode = BOT_BLOCKED else - mode = 0 // otherwise go idle + bot_reset() // otherwise go idle send_status() // report status to anyone listening @@ -714,13 +739,12 @@ var/global/mulebot_count = 0 var/mob/M = obs if(ismob(M)) if(istype(M,/mob/living/silicon/robot)) - src.visible_message("[src] bumps into [M]!") + visible_message("[src] bumps into [M]!") else - src.visible_message("[src] knocks over [M]!") + visible_message("[src] knocks over [M]!") M.stop_pulling() M.Stun(8) M.Weaken(5) - M.lying = 1 ..() /obj/machinery/bot/mulebot/alter_health() @@ -730,8 +754,8 @@ var/global/mulebot_count = 0 // called from mob/living/carbon/human/Crossed() // when mulebot is in the same loc /obj/machinery/bot/mulebot/proc/RunOver(var/mob/living/carbon/human/H) - src.visible_message("[src] drives over [H]!") - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) + visible_message("[src] drives over [H]!") + playsound(loc, 'sound/effects/splat.ogg', 50, 1) var/damage = rand(5,15) H.apply_damage(2*damage, BRUTE, "head") @@ -741,7 +765,7 @@ var/global/mulebot_count = 0 H.apply_damage(0.5*damage, BRUTE, "l_arm") H.apply_damage(0.5*damage, BRUTE, "r_arm") - var/obj/effect/decal/cleanable/blood/B = new(src.loc) + var/obj/effect/decal/cleanable/blood/B = new(loc) B.blood_DNA = list() B.blood_DNA[H.dna.unique_enzymes] = H.dna.blood_type @@ -779,7 +803,7 @@ var/global/mulebot_count = 0 // process control input switch(recv) if("stop") - mode = 0 + bot_reset() return if("go") @@ -815,6 +839,7 @@ var/global/mulebot_count = 0 // receive response from beacon recv = signal.data["beacon"] + if(wires.BeaconRX()) if(recv == new_destination) // if the recvd beacon location matches the set destination // the we will navigate there @@ -829,12 +854,18 @@ var/global/mulebot_count = 0 calc_path() updateDialog() + //Detects and stores current active delivery beacons. + if(signal.data["beacon"]) + if(!delivery_beacons) + delivery_beacons = new() + delivery_beacons[signal.data["beacon"] ] = signal.source + // send a radio signal with a single data key/value pair -/obj/machinery/bot/mulebot/proc/post_signal(var/freq, var/key, var/value) +/obj/machinery/bot/mulebot/post_signal(var/freq, var/key, var/value) post_signal_multiple(freq, list("[key]" = value) ) // send a radio signal with multiple data key/values -/obj/machinery/bot/mulebot/proc/post_signal_multiple(var/freq, var/list/keyval) +/obj/machinery/bot/mulebot/post_signal_multiple(var/freq, var/list/keyval) if(freq == beacon_freq && !(wires.BeaconRX())) return @@ -856,17 +887,17 @@ var/global/mulebot_count = 0 //world << "sent [key],[keyval[key]] on [freq]" if (signal.data["findbeacon"]) frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) - else if (signal.data["type"] == "mulebot") + else if (signal.data["type"] == MULE_BOT) frequency.post_signal(src, signal, filter = RADIO_MULEBOT) else frequency.post_signal(src, signal) // signals bot status etc. to controller -/obj/machinery/bot/mulebot/proc/send_status() +/obj/machinery/bot/mulebot/send_status() var/list/kv = list( - "type" = "mulebot", + "type" = MULE_BOT, "name" = suffix, - "loca" = (loc ? loc.loc : "Unknown"), // somehow loc can be null and cause a runtime - Quarxink + "loca" = get_area(src), "mode" = mode, "powr" = (cell ? cell.percent() : 0), "dest" = destination, @@ -886,7 +917,7 @@ var/global/mulebot_count = 0 /obj/machinery/bot/mulebot/explode() - src.visible_message("[src] blows apart!", 1) + visible_message(" [src] blows apart!", 1) var/turf/Tsec = get_turf(src) new /obj/item/device/assembly/prox_sensor(Tsec) @@ -902,6 +933,6 @@ var/global/mulebot_count = 0 s.set_up(3, 1, src) s.start() - new /obj/effect/decal/cleanable/oil(src.loc) + new /obj/effect/decal/cleanable/oil(loc) unload(0) qdel(src) diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index ae8f5927db7..406245fea17 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -17,27 +17,15 @@ var/threatlevel = 0 var/target_lastloc //Loc of target when arrested. var/last_found //There's a delay - var/frustration = 0 // var/emagged = 0 //Emagged Secbots view everyone as a criminal var/declare_arrests = 1 //When making an arrest, should it notify everyone wearing sechuds? var/idcheck = 0 //If true, arrest people with no IDs - var/weaponscheck = 0 //If true, arrest people for weapons if they don't have access + var/weaponscheck = 0 //If true, arrest people for weapons if they lack access var/check_records = 1 //Does it check security records? var/arrest_type = 0 //If true, don't handcuff - - var/mode = 0 -#define SECBOT_IDLE 0 // idle -#define SECBOT_HUNT 1 // found target, hunting -#define SECBOT_PREP_ARREST 2 // at target, preparing to arrest -#define SECBOT_ARREST 3 // arresting target -#define SECBOT_START_PATROL 4 // start patrol -#define SECBOT_PATROL 5 // patrolling -#define SECBOT_SUMMON 6 // summoned by PDA - - var/auto_patrol = 0 // set to make bot automatically patrol - - var/beacon_freq = 1445 // navigation beacon frequency - var/control_freq = 1447 // bot control frequency + radio_frequency = 1359 //Security channel + bot_type = SEC_BOT + bot_filter = RADIO_SECBOT //List of weapons that secbots will not arrest for var/safe_weapons = list(\ @@ -45,18 +33,6 @@ /obj/item/weapon/gun/energy/laser/redtag,\ /obj/item/weapon/gun/energy/laser/practice) - var/turf/patrol_target // this is turf to navigate to (location of beacon) - var/new_destination // pending new destination (waiting for beacon response) - var/destination // destination description tag - var/next_destination // the next destination in the patrol route - var/list/path = new // list of path turfs - - var/blockcount = 0 //number of times retried a blocked path - var/awaiting_beacon = 0 // count of pticks awaiting a beacon response - - var/nearest_beacon // the nearest beacon's tag - var/turf/nearest_beacon_loc // the nearest beacon's location - /obj/machinery/bot/secbot/beepsky name = "Officer Beep O'sky" @@ -83,30 +59,38 @@ /obj/machinery/bot/secbot/New() ..() - src.icon_state = "secbot[src.on]" + icon_state = "secbot[on]" spawn(3) - src.botcard = new /obj/item/weapon/card/id(src) - var/datum/job/detective/J = new /datum/job/detective - src.botcard.access = J.get_access() - if(radio_controller) - radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT) - radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) + + var/datum/job/detective/J = new/datum/job/detective + botcard.access = J.get_access() + prev_access = botcard.access + add_to_beacons(bot_filter) /obj/machinery/bot/secbot/turn_on() ..() - src.icon_state = "secbot[src.on]" - src.updateUsrDialog() + icon_state = "secbot[on]" + updateUsrDialog() /obj/machinery/bot/secbot/turn_off() ..() - src.target = null - src.oldtarget_name = null - src.anchored = 0 - src.mode = SECBOT_IDLE + icon_state = "secbot[on]" + updateUsrDialog() + +/obj/machinery/bot/secbot/bot_reset() + ..() + target = null + oldtarget_name = null + anchored = 0 walk_to(src,0) - src.icon_state = "secbot[src.on]" - src.updateUsrDialog() + last_found = world.time + +/obj/machinery/bot/secbot/set_custom_texts() + + text_hack = "You overload [name]'s target identification system." + text_dehack = "You reboot [name] and restore the target identification." + text_dehack_fail = "[name] refuses to accept your authority!" /obj/machinery/bot/secbot/attack_hand(mob/user as mob) . = ..() @@ -119,13 +103,14 @@ var/dat dat += hack(user) dat += text({" +Securitron v1.6 controls

Status: []
-Behaviour controls are [src.locked ? "locked" : "unlocked"]
-Maintenance panel panel is [src.open ? "opened" : "closed"]
"}, +Behaviour controls are [locked ? "locked" : "unlocked"]
+Maintenance panel panel is [open ? "opened" : "closed"]"}, -"[src.on ? "On" : "Off"]" ) +"[on ? "On" : "Off"]" ) - if(!src.locked || issilicon(user)) + if(!locked || issilicon(user)) dat += text({"
Arrest Unidentifiable Persons: []
Arrest for Unauthorized Weapons: []
@@ -135,136 +120,107 @@ Operating Mode: []
Report Arrests[]
Auto Patrol: []"}, -"[src.idcheck ? "Yes" : "No"]", -"[src.weaponscheck ? "Yes" : "No"]", -"[src.check_records ? "Yes" : "No"]", -"[src.arrest_type ? "Detain" : "Arrest"]", -"[src.declare_arrests ? "Yes" : "No"]", +"[idcheck ? "Yes" : "No"]", +"[weaponscheck ? "Yes" : "No"]", +"[check_records ? "Yes" : "No"]", +"[arrest_type ? "Detain" : "Arrest"]", +"[declare_arrests ? "Yes" : "No"]", "[auto_patrol ? "On" : "Off"]" ) - var/datum/browser/popup = new(user, "autosec", "Securitron v1.5.1 controls") + var/datum/browser/popup = new(user, "autosec", "Securitron v1.6 controls") popup.set_content(dat) popup.open() return /obj/machinery/bot/secbot/Topic(href, href_list) - if(..()) - return - usr.set_machine(src) - if((href_list["power"]) && (src.allowed(usr))) - if (src.on && !src.emagged) - turn_off() - else - turn_on() - return + + ..() switch(href_list["operation"]) if("idcheck") - src.idcheck = !src.idcheck - src.updateUsrDialog() + idcheck = !idcheck + updateUsrDialog() if("weaponscheck") - src.weaponscheck = !src.weaponscheck - src.updateUsrDialog() + weaponscheck = !weaponscheck + updateUsrDialog() if("ignorerec") - src.check_records = !src.check_records - src.updateUsrDialog() + check_records = !check_records + updateUsrDialog() if("switchmode") - src.arrest_type = !src.arrest_type - src.updateUsrDialog() - if("patrol") - auto_patrol = !auto_patrol - mode = SECBOT_IDLE + arrest_type = !arrest_type updateUsrDialog() if("declarearrests") - src.declare_arrests = !src.declare_arrests - src.updateUsrDialog() - if("hack") - if(!src.emagged) - src.emagged = 2 - src.hacked = 1 - usr << "You overload [src]'s target identification system." - else if(!src.hacked) - usr << "[src] refuses to accept your authority!" - else - src.emagged = 0 - src.hacked = 0 - usr << "You reboot [src] and restore the target identification." - src.updateUsrDialog() + declare_arrests = !declare_arrests + updateUsrDialog() + /obj/machinery/bot/secbot/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) - if(src.allowed(user) && !open && !emagged) - src.locked = !src.locked - user << "Controls are now [src.locked ? "locked." : "unlocked."]" + if(allowed(user) && !open && !emagged) + locked = !locked + user << "Controls are now [locked ? "locked." : "unlocked."]" else if(emagged) user << "ERROR" if(open) user << "Please close the access panel before locking it." else - user << "Access denied." + user << " Access denied." else ..() if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") // Any intent but harm will heal, so we shouldn't get angry. return - if(!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!src.target) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. + if(!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!target) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. threatlevel = user.assess_threat(src) threatlevel += 6 if(threatlevel >= 4) - src.target = user - src.mode = SECBOT_HUNT + target = user + mode = BOT_HUNT /obj/machinery/bot/secbot/Emag(mob/user as mob) ..() - if(open && !locked) - if(user) user << "You short out [src]'s target assessment circuits." + if(emagged == 2) + if(user) user << " You short out [src]'s target assessment circuits." spawn(0) for(var/mob/O in hearers(src, null)) - O.show_message("[src] buzzes oddly!", 1) - src.target = null - if(user) src.oldtarget_name = user.name - src.last_found = world.time - src.anchored = 0 - src.declare_arrests = 0 - src.emagged = 2 - src.on = 1 - src.icon_state = "secbot[src.on]" - mode = SECBOT_IDLE + O.show_message("[src] buzzes oddly!", 1) + if(user) oldtarget_name = user.name + declare_arrests = 0 /obj/machinery/bot/secbot/process() set background = BACKGROUND_ENABLED - if(!src.on) + if(!on) + return + + if(call_path) + call_mode() return switch(mode) - if(SECBOT_IDLE) // idle + if(BOT_IDLE) // idle walk_to(src,0) look_for_perp() // see if any criminals are in range if(!mode && auto_patrol) // still idle, and set to patrol - mode = SECBOT_START_PATROL // switch to patrol mode + mode = BOT_START_PATROL // switch to patrol mode - if(SECBOT_HUNT) // hunting for perp + if(BOT_HUNT) // hunting for perp // if can't reach perp for long enough, go idle - if(src.frustration >= 8) + if(frustration >= 8) // for(var/mob/O in hearers(src, null)) // O << "[src] beeps, \"Backup requested! Suspect has evaded arrest.\"" - src.target = null - src.last_found = world.time - src.frustration = 0 - src.mode = SECBOT_IDLE - walk_to(src,0) + bot_reset() if(target) // make sure target exists - if(src.Adjacent(target) && isturf(src.target.loc)) // if right next to perp - playsound(src.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) - src.icon_state = "secbot-c" + if(Adjacent(target) && isturf(target.loc)) // if right next to perp + playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) + icon_state = "secbot-c" spawn(2) - src.icon_state = "secbot[src.on]" - var/mob/living/carbon/M = src.target + icon_state = "secbot[on]" + var/mob/living/carbon/M = target var/maxstuns = 4 if(istype(M, /mob/living/carbon/human)) if(M.stuttering < 5 && (!(HULK in M.mutations))) @@ -278,7 +234,7 @@ Auto Patrol: []"}, if(declare_arrests) var/area/location = get_area(src) - broadcast_hud_message("[src.name] is [arrest_type ? "detaining" : "arresting"] level [threatlevel] scumbag [target] in [location]", src) + speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].", radio_frequency) target.visible_message("[src.target] has been stunned by [src]!",\ "[src.target] has been stunned by [src]!") @@ -286,369 +242,115 @@ Auto Patrol: []"}, if(maxstuns <= 0) target = null - mode = SECBOT_PREP_ARREST - src.anchored = 1 - src.target_lastloc = M.loc + mode = BOT_PREP_ARREST + anchored = 1 + target_lastloc = M.loc return else // not next to perp - var/turf/olddist = get_dist(src, src.target) - walk_to(src, src.target,1,4) - if((get_dist(src, src.target)) >= (olddist)) - src.frustration++ + var/turf/olddist = get_dist(src, target) + walk_to(src, target,1,4) + if((get_dist(src, target)) >= (olddist)) + frustration++ else - src.frustration = 0 + frustration = 0 - if(SECBOT_PREP_ARREST) // preparing to arrest target + if(BOT_PREP_ARREST) // preparing to arrest target // see if he got away - if((get_dist(src, src.target) > 1) || ((src.target:loc != src.target_lastloc) && src.target:weakened < 2)) - src.anchored = 0 - mode = SECBOT_HUNT + if((get_dist(src, target) > 1) || ((target.loc != target_lastloc) && target.weakened < 2)) + anchored = 0 + mode = BOT_HUNT return if(iscarbon(target) && target.canBeHandcuffed()) - if(!src.target.handcuffed && !src.arrest_type) - playsound(src.loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2) - mode = SECBOT_ARREST - target.visible_message("[src] is trying to put handcuffs on [src.target]!",\ + if(!target.handcuffed && !arrest_type) + playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2) + mode = BOT_ARREST + target.visible_message("[src] is trying to put handcuffs on [target]!",\ "[src] is trying to put handcuffs on [src.target]!") spawn(60) - if(get_dist(src, src.target) <= 1) - if(src.target.handcuffed) + if(get_dist(src, target) <= 1) + if(target.handcuffed) return - if(istype(src.target,/mob/living/carbon)) + if(istype(target,/mob/living/carbon)) target.handcuffed = new /obj/item/weapon/handcuffs(target) target.update_inv_handcuffed(0) //update the handcuffs overlay - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.frustration = 0 + mode = BOT_IDLE + target = null + anchored = 0 + last_found = world.time + frustration = 0 - playsound(src.loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0) + playsound(loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0) // var/arrest_message = pick("Have a secure day!","I AM THE LAW.", "God made tomorrow for the crooks we don't catch today.","You can't outrun a radio.") - // src.speak(arrest_message) + // speak(arrest_message) else - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.frustration = 0 + mode = BOT_IDLE + target = null + anchored = 0 + last_found = world.time + frustration = 0 - if(SECBOT_ARREST) // arresting + if(BOT_ARREST) // arresting if(!target || target.handcuffed) - src.anchored = 0 - mode = SECBOT_IDLE + anchored = 0 + mode = BOT_IDLE return + else //Try arresting again if the target escapes. + mode = BOT_PREP_ARREST + anchored = 0 + if(BOT_START_PATROL) + look_for_perp() + start_patrol() - if(SECBOT_START_PATROL) // start a patrol - - if(path.len > 0 && patrol_target) // have a valid path, so just resume - mode = SECBOT_PATROL - return - - else if(patrol_target) // has patrol target already - spawn(0) - calc_path() // so just find a route to it - if(path.len == 0) - patrol_target = 0 - return - mode = SECBOT_PATROL - - - else // no patrol target, so need a new one - find_patrol_target() - speak("Engaging patrol mode.") - - - if(SECBOT_PATROL) // patrol mode - - patrol_step() - spawn(5) - if(mode == SECBOT_PATROL) - patrol_step() - - if(SECBOT_SUMMON) // summoned to PDA - patrol_step() - spawn(4) - if(mode == SECBOT_SUMMON) - patrol_step() - sleep(4) - patrol_step() + if(BOT_PATROL) + look_for_perp() + bot_patrol() + if(BOT_SUMMON) + bot_summon() return - -// perform a single patrol step -/obj/machinery/bot/secbot/proc/patrol_step() - - if(loc == patrol_target) // reached target - at_patrol_target() - return - - else if(path.len > 0 && patrol_target) // valid path - - var/turf/next = path[1] - if(next == loc) - path -= next - return - - - if(istype( next, /turf/simulated)) - - var/moved = step_towards(src, next) // attempt to move - if(moved) // successful move - blockcount = 0 - path -= loc - - look_for_perp() - else // failed to move - - blockcount++ - - if(blockcount > 5) // attempt 5 times before recomputing - // find new path excluding blocked turf - - spawn(2) - calc_path(next) - if(path.len == 0) - find_patrol_target() - else - blockcount = 0 - - return - - return - - else // not a valid turf - mode = SECBOT_IDLE - return - - else // no path, so calculate new one - mode = SECBOT_START_PATROL - - -// finds a new patrol target -/obj/machinery/bot/secbot/proc/find_patrol_target() - send_status() - if(awaiting_beacon) // awaiting beacon response - awaiting_beacon++ - if(awaiting_beacon > 5) // wait 5 secs for beacon response - find_nearest_beacon() // then go to nearest instead - return - - if(next_destination) - set_destination(next_destination) - else - find_nearest_beacon() - return - - -// finds the nearest beacon to self -// signals all beacons matching the patrol code -/obj/machinery/bot/secbot/proc/find_nearest_beacon() - nearest_beacon = null - new_destination = "__nearest__" - post_signal(beacon_freq, "findbeacon", "patrol") - awaiting_beacon = 1 - spawn(10) - awaiting_beacon = 0 - if(nearest_beacon) - set_destination(nearest_beacon) - else - auto_patrol = 0 - mode = SECBOT_IDLE - speak("Disengaging patrol mode.") - send_status() - - -/obj/machinery/bot/secbot/proc/at_patrol_target() - find_patrol_target() - return - - -// sets the current destination -// signals all beacons matching the patrol code -// beacons will return a signal giving their locations -/obj/machinery/bot/secbot/proc/set_destination(var/new_dest) - new_destination = new_dest - post_signal(beacon_freq, "findbeacon", "patrol") - awaiting_beacon = 1 - - -// receive a radio signal -// used for beacon reception - -/obj/machinery/bot/secbot/receive_signal(datum/signal/signal) - //log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/bot/secbot/receive_signal([signal.debug_print()])") - if(!on) - return - - /* - world << "rec signal: [signal.source]" - for(var/x in signal.data) - world << "* [x] = [signal.data[x]]" - */ - - var/recv = signal.data["command"] - // process all-bot input - if(recv=="bot_status") - send_status() - - // check to see if we are the commanded bot - if(signal.data["active"] == src) - // process control input - switch(recv) - if("stop") - mode = SECBOT_IDLE - auto_patrol = 0 - return - - if("go") - mode = SECBOT_IDLE - auto_patrol = 1 - return - - if("summon") - patrol_target = signal.data["target"] - next_destination = destination - destination = null - awaiting_beacon = 0 - mode = SECBOT_SUMMON - calc_path() - speak("Responding.") - - return - - - - // receive response from beacon - recv = signal.data["beacon"] - var/valid = signal.data["patrol"] - if(!recv || !valid) - return - - if(recv == new_destination) // if the recvd beacon location matches the set destination - // the we will navigate there - destination = new_destination - patrol_target = signal.source.loc - next_destination = signal.data["next_patrol"] - awaiting_beacon = 0 - - // if looking for nearest beacon - else if(new_destination == "__nearest__") - var/dist = get_dist(src,signal.source.loc) - if(nearest_beacon) - - // note we ignore the beacon we are located at - if(dist>1 && dist 1) - nearest_beacon = recv - nearest_beacon_loc = signal.source.loc - return - - -// send a radio signal with a single data key/value pair -/obj/machinery/bot/secbot/proc/post_signal(var/freq, var/key, var/value) - post_signal_multiple(freq, list("[key]" = value) ) - -// send a radio signal with multiple data key/values -/obj/machinery/bot/secbot/proc/post_signal_multiple(var/freq, var/list/keyval) - - var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) - - if(!frequency) return - - var/datum/signal/signal = new() - signal.source = src - signal.transmission_method = 1 - //for(var/key in keyval) - // signal.data[key] = keyval[key] - signal.data = keyval - //world << "sent [key],[keyval[key]] on [freq]" - if(signal.data["findbeacon"]) - frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) - else if(signal.data["type"] == "secbot") - frequency.post_signal(src, signal, filter = RADIO_SECBOT) - else - frequency.post_signal(src, signal) - -// signals bot status etc. to controller -/obj/machinery/bot/secbot/proc/send_status() - var/list/kv = list( - "type" = "secbot", - "name" = name, - "loca" = loc.loc, // area - "mode" = mode - ) - post_signal_multiple(control_freq, kv) - - - -// calculates a path to the current destination -// given an optional turf to avoid -/obj/machinery/bot/secbot/proc/calc_path(var/turf/avoid = null) - src.path = AStar(src.loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 120, id=botcard, exclude=avoid) - if(!src.path) - src.path = list() - - // look for a criminal in view of the bot /obj/machinery/bot/secbot/proc/look_for_perp() - src.anchored = 0 + anchored = 0 for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal if((C.stat) || (C.handcuffed)) continue - if((C.name == src.oldtarget_name) && (world.time < src.last_found + 100)) + if((C.name == oldtarget_name) && (world.time < last_found + 100)) continue - src.threatlevel = C.assess_threat(src) + threatlevel = C.assess_threat(src) - if(!src.threatlevel) + if(!threatlevel) continue - else if(src.threatlevel >= 4) - src.target = C - src.oldtarget_name = C.name - src.speak("Level [src.threatlevel] infraction alert!") - playsound(src.loc, pick('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg'), 50, 0) - src.visible_message("[src] points at [C.name]!") - mode = SECBOT_HUNT + else if(threatlevel >= 4) + target = C + oldtarget_name = C.name + speak("Level [threatlevel] infraction alert!") + playsound(loc, pick('sound/voice/bcriminal.ogg', 'sound/voice/bjustice.ogg', 'sound/voice/bfreeze.ogg'), 50, 0) + visible_message("[src] points at [C.name]!") + mode = BOT_HUNT spawn(0) process() // ensure bot quickly responds to a perp break else continue - /obj/machinery/bot/secbot/proc/check_for_weapons(var/obj/item/slot_item) if(istype(slot_item, /obj/item/weapon/gun) || istype(slot_item, /obj/item/weapon/melee)) if(!(slot_item.type in safe_weapons)) return 1 return 0 -/obj/machinery/bot/secbot/Bump(M as mob|obj) //Leave no door unopened! - if((istype(M, /obj/machinery/door)) && (!isnull(src.botcard))) - var/obj/machinery/door/D = M - if(!istype(D, /obj/machinery/door/firedoor) && D.check_access(src.botcard)) - D.open() - src.frustration = 0 - else if((istype(M, /mob/living/)) && (!src.anchored)) - src.loc = M:loc - src.frustration = 0 - return /* terrible /obj/machinery/bot/secbot/Bumped(atom/movable/M as mob|obj) @@ -661,13 +363,13 @@ Auto Patrol: []"}, /obj/machinery/bot/secbot/explode() walk_to(src,0) - src.visible_message("[src] blows apart!", 1) + visible_message(" [src] blows apart!", 1) var/turf/Tsec = get_turf(src) var/obj/item/weapon/secbot_assembly/Sa = new /obj/item/weapon/secbot_assembly(Tsec) Sa.build_step = 1 Sa.overlays += "hs_hole" - Sa.created_name = src.name + Sa.created_name = name new /obj/item/device/assembly/prox_sensor(Tsec) new /obj/item/weapon/melee/baton(Tsec) @@ -678,14 +380,14 @@ Auto Patrol: []"}, s.set_up(3, 1, src) s.start() - new /obj/effect/decal/cleanable/oil(src.loc) + new /obj/effect/decal/cleanable/oil(loc) qdel(src) /obj/machinery/bot/secbot/attack_alien(var/mob/living/carbon/alien/user as mob) ..() if(!isalien(target)) - src.target = user - src.mode = SECBOT_HUNT + target = user + mode = BOT_HUNT //Secbot Construction @@ -695,7 +397,7 @@ Auto Patrol: []"}, ..() return - if(src.type != /obj/item/clothing/head/helmet) //Eh, but we don't want people making secbots out of space helmets. + if(type != /obj/item/clothing/head/helmet) //Eh, but we don't want people making secbots out of space helmets. return if(S.secured) diff --git a/code/game/objects/items/devices/PDA/PDA.dm b/code/game/objects/items/devices/PDA/PDA.dm index 3f239f3d591..d68d92464a4 100644 --- a/code/game/objects/items/devices/PDA/PDA.dm +++ b/code/game/objects/items/devices/PDA/PDA.dm @@ -297,12 +297,18 @@ var/global/list/obj/item/device/pda/PDAs = list() dat += "

Engineering Functions

" dat += "" if (cartridge.access_medical) dat += "

Medical Functions

" dat += "" + else dat += "" if (cartridge.access_security) dat += "

Security Functions

" @@ -325,6 +331,8 @@ var/global/list/obj/item/device/pda/PDAs = list() if (cartridge) if (cartridge.access_janitor) dat += "
  • Custodial Locator
  • " + if(istype(cartridge.radio, /obj/item/radio/integrated/cleanbot)) + dat += "
  • Cleanbot Access
  • " if (istype(cartridge.radio, /obj/item/radio/integrated/signal)) dat += "
  • Signaler System
  • " if (cartridge.access_reagent_scanner) @@ -467,9 +475,9 @@ var/global/list/obj/item/device/pda/PDAs = list() mode = 0 else mode = round(mode/10) - if(mode==4)//Fix for cartridges. Redirects to hub. + if(mode==4 || mode == 5)//Fix for cartridges. Redirects to hub. mode = 0 - else if(mode >= 40 && mode <= 49)//Fix for cartridges. Redirects to refresh the menu. + else if(mode >= 40 && mode <= 59)//Fix for cartridges. Redirects to refresh the menu. cartridge.mode = mode cartridge.unlock() if ("Authenticate")//Checks for ID @@ -1085,4 +1093,4 @@ var/global/list/obj/item/device/pda/PDAs = list() for(var/obj/item/device/pda/P in PDAs) if(!P.owner || P.toff || P.hidden) continue . += P - return . + return . \ No newline at end of file diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 082db03166a..3c1934f55a2 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -38,21 +38,37 @@ icon_state = "cart-e" access_engine = 1 + /obj/item/weapon/cartridge/engineering/New() + ..() + radio = new /obj/item/radio/integrated/floorbot(src) + /obj/item/weapon/cartridge/atmos name = "\improper BreatheDeep cartridge" icon_state = "cart-a" access_atmos = 1 + /obj/item/weapon/cartridge/atmos/New() + ..() + radio = new /obj/item/radio/integrated/floorbot(src) + /obj/item/weapon/cartridge/medical name = "\improper Med-U cartridge" icon_state = "cart-m" access_medical = 1 + /obj/item/weapon/cartridge/medical/New() + ..() + radio = new /obj/item/radio/integrated/medbot(src) + /obj/item/weapon/cartridge/chemistry name = "\improper ChemWhiz cartridge" icon_state = "cart-chem" access_reagent_scanner = 1 + /obj/item/weapon/cartridge/chemistry/New() + ..() + radio = new /obj/item/radio/integrated/medbot(src) + /obj/item/weapon/cartridge/security name = "\improper R.O.B.U.S.T. cartridge" icon_state = "cart-s" @@ -79,6 +95,10 @@ icon_state = "cart-j" access_janitor = 1 +/obj/item/weapon/cartridge/janitor/New() + ..() + radio = new /obj/item/radio/integrated/cleanbot(src) + /obj/item/weapon/cartridge/lawyer name = "\improper P.R.O.V.E. cartridge" icon_state = "cart-s" @@ -167,6 +187,10 @@ access_engine = 1 access_atmos = 1 +/obj/item/weapon/cartridge/ce/New() + ..() + radio = new /obj/item/radio/integrated/floorbot(src) + /obj/item/weapon/cartridge/cmo name = "\improper Med-U DELUXE cartridge" icon_state = "cart-cmo" @@ -175,6 +199,10 @@ access_reagent_scanner = 1 access_medical = 1 +/obj/item/weapon/cartridge/cmo/New() + ..() + radio = new /obj/item/radio/integrated/medbot(src) + /obj/item/weapon/cartridge/rd name = "\improper Signal Ace DELUXE cartridge" icon_state = "cart-rd" @@ -249,6 +277,66 @@ frequency.post_signal(src, status_signal) +/obj/item/weapon/cartridge/proc/bot_control(var/obj/item/radio/integrated/SC) + + + if(!SC) + menu = "Interlink Error - Please reinsert cartridge." + return + + if(!SC.active) + // list of bots + if(!SC.botlist || (SC.botlist && SC.botlist.len==0)) + menu += "No bots found.
    " + + else + for(var/obj/machinery/bot/B in SC.botlist) + menu += "[B] at [get_area(B)]
    " + + menu += "
    Scan for active bots
    " + + else // bot selected, control it + + menu += "[SC.active]
    Status: (refresh)
    " + + if(!SC.botstatus) + menu += "Waiting for response...
    " + else + + menu += "Location: [SC.botstatus["loca"] ]
    " + menu += "Mode: " + + switch(SC.botstatus["mode"]) + if(BOT_IDLE) + menu += "Ready" + if(BOT_HUNT) + menu += "Apprehending target" + if(BOT_PREP_ARREST,BOT_ARREST) + menu += "Arresting target" + if(BOT_START_PATROL) + menu += "Starting patrol" + if(BOT_PATROL) + menu += "On patrol" + if(BOT_SUMMON) + menu += "Responding to summons" + if(BOT_CLEANING) + menu += "Cleaning" + if(BOT_MOVING) + menu += "Proceeding to work site" + if(BOT_REPAIRING) + menu += "Performing repairs" + if(BOT_HEALING) + menu += "Medicating patient" + if(BOT_RESPONDING) + menu += "Proceeding to AI waypoint" + + menu += "
    \[Stop Patrol\] " + menu += "\[Start Patrol\] " + menu += "\[Summon Bot\]
    " + menu += "
    Return to bot list" + return menu + + /obj/item/weapon/cartridge/proc/generate_menu() switch(mode) if(40) //signaller @@ -457,52 +545,8 @@ Code: menu += "
    " if (46) //beepsky control var/obj/item/radio/integrated/beepsky/SC = radio - if(!SC) - menu = "Interlink Error - Please reinsert cartridge." - return - menu = "

    Securitron Interlink

    " - - if(!SC.active) - // list of bots - if(!SC.botlist || (SC.botlist && SC.botlist.len==0)) - menu += "No bots found.
    " - - else - for(var/obj/machinery/bot/secbot/B in SC.botlist) - menu += "[B] at [B.loc.loc]
    " - - menu += "
    Scan for active bots
    " - - else // bot selected, control it - - menu += "[SC.active]
    Status: (refresh)
    " - - if(!SC.botstatus) - menu += "Waiting for response...
    " - else - - menu += "Location: [SC.botstatus["loca"] ]
    " - menu += "Mode: " - - switch(SC.botstatus["mode"]) - if(0) - menu += "Ready" - if(1) - menu += "Apprehending target" - if(2,3) - menu += "Arresting target" - if(4) - menu += "Starting patrol" - if(5) - menu += "On patrol" - if(6) - menu += "Responding to summons" - - menu += "
    \[Stop Patrol\] " - menu += "\[Start Patrol\] " - menu += "\[Summon Bot\]
    " - menu += "
    Return to bot list" + bot_control(SC) if (47) //quartermaster order records menu = "

    Supply Record Interlink

    " @@ -526,7 +570,6 @@ Code: if(!QC) menu = "Interlink Error - Please reinsert cartridge." return - menu = "

    M.U.L.E. bot Interlink V0.8

    " if(!QC.active) @@ -552,19 +595,19 @@ Code: menu += "Mode: " switch(QC.botstatus["mode"]) - if(0) + if(BOT_IDLE) menu += "Ready" - if(1) + if(BOT_LOADING) menu += "Loading/Unloading" - if(2) + if(BOT_DELIVER) menu += "Navigating to Delivery Location" - if(3) + if(BOT_GO_HOME) menu += "Navigating to Home" - if(4) + if(BOT_BLOCKED) menu += "Waiting for clear path" - if(5,6) + if(BOT_NAV,BOT_WAIT_FOR_NAV) menu += "Calculating navigation path" - if(7) + if(BOT_NO_ROUTE) menu += "Unable to locate destination" var/obj/structure/closet/crate/C = QC.botstatus["load"] menu += "
    Current Load: [ !C ? "none" : "[C.name] (unload)" ]
    " @@ -641,6 +684,20 @@ Code: menu += "ERROR: Unable to determine current location." menu += "

    Refresh GPS Locator" + if (50) //Cleanbot control + menu = "

    Cleanbot Interlink

    " + var/obj/item/radio/integrated/cleanbot/SC = radio + bot_control(SC) + + if (51) //floorbot control + menu = "

    Floorbot Interlink

    " + var/obj/item/radio/integrated/floorbot/SC = radio + bot_control(SC) + + if (52) //Medibot control + menu = "

    Medibot Interlink

    " + var/obj/item/radio/integrated/medbot/SC = radio + bot_control(SC) /obj/item/weapon/cartridge/Topic(href, href_list) ..() diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm index e576cc2e109..ca45bc45567 100644 --- a/code/game/objects/items/devices/PDA/radio.dm +++ b/code/game/objects/items/devices/PDA/radio.dm @@ -4,6 +4,13 @@ icon = 'icons/obj/module.dmi' icon_state = "power_mod" var/obj/item/device/pda/hostpda = null + var/list/botlist = null // list of bots + var/obj/machinery/bot/active // the active bot; if null, show bot list + var/list/botstatus // the status signal sent by the bot + var/bot_type //The type of bot it is. + var/bot_filter //Determines which radio filter to use. + + var/control_freq = 1447 var/on = 0 //Are we currently active?? var/menu_message = "" @@ -12,13 +19,17 @@ ..() if (istype(loc.loc, /obj/item/device/pda)) hostpda = loc.loc + if (bot_filter) + spawn(5) + add_to_radio(bot_filter) -/obj/item/radio/integrated/proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3, s_filter) +/obj/item/radio/integrated/proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3,var/key4, var/value4, s_filter) //world << "Post: [freq]: [key]=[value], [key2]=[value2]" var/datum/radio_frequency/frequency = radio_controller.return_frequency(freq) - if(!frequency) return + if(!frequency) + return var/datum/signal/signal = new() signal.source = src @@ -28,6 +39,8 @@ signal.data[key2] = value2 if(key3) signal.data[key3] = value3 + if(key4) + signal.data[key4] = value4 frequency.post_signal(src, signal, filter = s_filter) @@ -44,33 +57,18 @@ /obj/item/radio/integrated/proc/generate_menu() -/obj/item/radio/integrated/beepsky - var/list/botlist = null // list of bots - var/obj/machinery/bot/secbot/active // the active bot; if null, show bot list - var/list/botstatus // the status signal sent by the bot - var/control_freq = 1447 -// create a new QM cartridge, and register to receive bot control & beacon message -/obj/item/radio/integrated/beepsky/New() - ..() - spawn(5) - if(radio_controller) - radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT) +/obj/item/radio/integrated/receive_signal(datum/signal/signal) + /*var/obj/item/device/pda/P = src.loc -// receive radio signals -// can detect bot status signals -// create/populate list as they are recvd -/obj/item/radio/integrated/beepsky/receive_signal(datum/signal/signal) -// var/obj/item/device/pda/P = src.loc - - /* world << "recvd:[P] : [signal.source]" for(var/d in signal.data) world << "- [d] = [signal.data[d]]" - */ - if (signal.data["type"] == "secbot") + */ + + if (signal.data["type"] == bot_type) if(!botlist) botlist = new() @@ -81,9 +79,7 @@ var/list/b = signal.data botstatus = b.Copy() -// if (istype(P)) P.updateSelfDialog() - -/obj/item/radio/integrated/beepsky/Topic(href, href_list) +/obj/item/radio/integrated/Topic(href, href_list) ..() var/obj/item/device/pda/PDA = src.hostpda @@ -91,32 +87,53 @@ if("control") active = locate(href_list["bot"]) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_SECBOT) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) if("scanbots") // find all bots botlist = null - post_signal(control_freq, "command", "bot_status", s_filter = RADIO_SECBOT) + post_signal(control_freq, "command", "bot_status", s_filter = bot_filter) if("botlist") active = null if("stop", "go") - post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = RADIO_SECBOT) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_SECBOT) + post_signal(control_freq, "command", href_list["op"], "active", active, s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) if("summon") - post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(PDA) , s_filter = RADIO_SECBOT) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_SECBOT) + post_signal(control_freq, "command", "summon", "active", active, "target", get_turf(PDA) , "useraccess", PDA.GetAccess(), s_filter = bot_filter) + post_signal(control_freq, "command", "bot_status", "active", active, s_filter = bot_filter) PDA.cartridge.unlock() +/obj/item/radio/integrated/proc/add_to_radio(bot_filter) //Master filter control for bots. Must be placed in the bot's local New() to support map spawned bots. + if(radio_controller) + radio_controller.add_object(src, control_freq, filter = bot_filter) + + +/obj/item/radio/integrated/beepsky + bot_filter = RADIO_SECBOT + bot_type = SEC_BOT + +/obj/item/radio/integrated/medbot + bot_filter = RADIO_MEDBOT + bot_type = MED_BOT + +/obj/item/radio/integrated/floorbot + bot_filter = RADIO_FLOORBOT + bot_type = FLOOR_BOT + +/obj/item/radio/integrated/cleanbot + bot_filter = RADIO_CLEANBOT + bot_type = CLEAN_BOT + /obj/item/radio/integrated/mule - var/list/botlist = null // list of bots - var/obj/machinery/bot/mulebot/active // the active bot; if null, show bot list - var/list/botstatus // the status signal sent by the bot + //var/list/botlist = null // list of bots + //var/obj/machinery/bot/mulebot/active // the active bot; if null, show bot list + //var/list/botstatus // the status signal sent by the bot var/list/beacons var/beacon_freq = 1400 - var/control_freq = 1447 + control_freq = 1447 // create a new QM cartridge, and register to receive bot control & beacon message /obj/item/radio/integrated/mule/New() @@ -141,7 +158,7 @@ for(var/d in signal.data) world << "- [d] = [signal.data[d]]" */ - if(signal.data["type"] == "mulebot") + if(signal.data["type"] == MULE_BOT) if(!botlist) botlist = new() @@ -162,52 +179,46 @@ // if(istype(P)) P.updateSelfDialog() /obj/item/radio/integrated/mule/Topic(href, href_list) - ..() + //..() var/obj/item/device/pda/PDA = src.hostpda var/cmd = "command" - if(active) cmd = "command [active.suffix]" + if(active) + cmd = "command [active.suffix]" switch(href_list["op"]) if("control") active = locate(href_list["bot"]) - post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) if("scanbots") // find all bots botlist = null - post_signal(control_freq, "command", "bot_status", s_filter = RADIO_MULEBOT) if("botlist") active = null - if("unload") post_signal(control_freq, cmd, "unload", s_filter = RADIO_MULEBOT) - post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) if("setdest") if(beacons) var/dest = input("Select Bot Destination", "Mulebot [active.suffix] Interlink", active.destination) as null|anything in beacons if(dest) post_signal(control_freq, cmd, "target", "destination", dest, s_filter = RADIO_MULEBOT) - post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) if("retoff") post_signal(control_freq, cmd, "autoret", "value", 0, s_filter = RADIO_MULEBOT) - post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) if("reton") post_signal(control_freq, cmd, "autoret", "value", 1, s_filter = RADIO_MULEBOT) - post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) if("pickoff") post_signal(control_freq, cmd, "autopick", "value", 0, s_filter = RADIO_MULEBOT) - post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) + if("pickon") post_signal(control_freq, cmd, "autopick", "value", 1, s_filter = RADIO_MULEBOT) - post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) if("stop", "go", "home") post_signal(control_freq, cmd, href_list["op"], s_filter = RADIO_MULEBOT) - post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) + + post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) PDA.cartridge.unlock() @@ -227,10 +238,10 @@ ..() if(radio_controller) initialize() - + /obj/item/radio/integrated/signal/initialize() if (src.frequency < 1441 || src.frequency > 1489) - src.frequency = sanitize_frequency(src.frequency) + src.frequency = sanitize_frequency(src.frequency) set_frequency(frequency) @@ -256,4 +267,4 @@ radio_connection.post_signal(src, signal) - return + return \ No newline at end of file diff --git a/code/modules/client/client procs.dm b/code/modules/client/client procs.dm index 3c597dd443e..dd2aa2b8caf 100644 --- a/code/modules/client/client procs.dm +++ b/code/modules/client/client procs.dm @@ -258,6 +258,9 @@ var/next_external_rsc = 0 'icons/pda_icons/pda_blank.png', 'icons/pda_icons/pda_boom.png', 'icons/pda_icons/pda_bucket.png', + 'icons/pda_icons/pda_medbot.png', + 'icons/pda_icons/pda_floorbot.png', + 'icons/pda_icons/pda_cleanbot.png', 'icons/pda_icons/pda_crate.png', 'icons/pda_icons/pda_cuffs.png', 'icons/pda_icons/pda_eject.png', diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm index ceea1384d12..66963138eb5 100644 --- a/code/modules/mob/living/silicon/ai/ai.dm +++ b/code/modules/mob/living/silicon/ai/ai.dm @@ -31,6 +31,7 @@ var/list/ai_list = list() var/obj/item/device/pda/ai/aiPDA = null var/obj/item/device/multitool/aiMulti = null var/obj/item/device/camera/siliconcam/aicamera = null + var/obj/machinery/bot/Bot //MALFUNCTION var/datum/module_picker/malf_picker @@ -54,6 +55,8 @@ var/list/ai_list = list() var/last_paper_seen = null var/can_shunt = 1 var/last_announcement = "" // For AI VOX, if enabled + var/turf/waypoint //Holds the turf of the currently selected waypoint. + var/waypoint_mode = 0 //Waypoint mode is for selecting a turf via clicking. /mob/living/silicon/ai/New(loc, var/datum/ai_laws/L, var/obj/item/device/mmi/B, var/safety = 0) var/list/possibleNames = ai_names @@ -94,7 +97,7 @@ var/list/ai_list = list() if (istype(loc, /turf)) verbs.Add(/mob/living/silicon/ai/proc/ai_network_change, \ /mob/living/silicon/ai/proc/ai_statuschange, /mob/living/silicon/ai/proc/ai_hologram_change, \ - /mob/living/silicon/ai/proc/toggle_camera_light) + /mob/living/silicon/ai/proc/toggle_camera_light, /mob/living/silicon/ai/proc/botcall) if(!safety)//Only used by AIize() to successfully spawn an AI. if (!B)//If there is no player/brain inside. @@ -356,6 +359,24 @@ var/list/ai_list = list() A.ai_actual_track(target) return + if (href_list["callbot"]) //Command a bot to move to a selected location. + Bot = locate(href_list["callbot"]) in machines + if(!Bot || Bot.remote_disabled || src.control_disabled) + return //True if there is no bot found, the bot is manually emagged, or the AI is carded with wireless off. + waypoint_mode = 1 + src << "Set your waypoint by clicking on a valid location free of obstructions." + return + + if (href_list["interface"]) //Remotely connect to a bot! + Bot = locate(href_list["interface"]) in machines + if(!Bot || Bot.remote_disabled || src.control_disabled) + return + Bot.attack_ai(src) + + if (href_list["botrefresh"]) //Refreshes the bot control panel. + botcall() + return + else if (href_list["faketrack"]) var/mob/target = locate(href_list["track"]) in mob_list var/mob/living/silicon/ai/A = locate(href_list["track2"]) in mob_list @@ -451,6 +472,61 @@ var/list/ai_list = list() return 1 +/mob/living/silicon/ai/proc/botcall() + set category = "AI Commands" + set name = "Access Robot Control" + set desc = "Wirelessly control various automatic robots." + if(stat == 2) + src << "Critical error. System offline." + return + + if(control_disabled) + src << "Wireless communication is disabled." + return + var/turf/ai_current_turf = get_turf(src) + var/ai_Zlevel = ai_current_turf.z + var/d + var/area/bot_area + d += "Query network status
    " + d += "" + + for (Bot in machines) + if(Bot.z == ai_Zlevel && !Bot.remote_disabled) //Only non-emagged bots on the same Z-level are detected! + bot_area = get_area(Bot) + d += "" + //If the bot is on, it will display the bot's current mode status. If the bot is not mode, it will just report "Idle". "Inactive if it is not on at all. + d += "" + d += "" + d += "" + d += "" + d += "" + d = format_text(d) + + var/datum/browser/popup = new(src, "botcall", "Remote Robot Control", 700, 400) + popup.set_content(d) + popup.open() + +/mob/living/silicon/ai/proc/set_waypoint(var/atom/A) + var/turf/turf_check = get_turf(A) + //The target must be in view of a camera or near the core. + if(turf_check in range(get_turf(src))) + call_bot(turf_check) + else if(cameranet && cameranet.checkTurfVis(turf_check)) + call_bot(turf_check) + else + src << "Selected location is not visible." + +/mob/living/silicon/ai/proc/call_bot(var/turf/waypoint) + + if(!Bot) + return + + if(Bot.calling_ai && Bot.calling_ai != src) //Prevents an override if another AI is controlling this bot. + src << "Interface error. Unit is already in use." + return + + Bot.call_bot(src, waypoint) + /mob/living/silicon/ai/triggerAlarm(var/class, area/A, var/O, var/alarmsource) if (stat == 2) return 1 diff --git a/icons/obj/aibots.dmi b/icons/obj/aibots.dmi index 7b559aad6349601f1e0380e3fea25632a53d6fb2..7039ec431037ef62c7f512ad83fdadd6f6ffa387 100644 GIT binary patch delta 9567 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 delta 8885 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 X@h5g#b{XE z)7O>#9;bwWrrI}y;u%1pFi#i95RLP5CmrNsP~dRB{^9W7FMN$2OEaxmdiTx{x^RVI zL6q-mr$4E4)!)k+YO|kNQvYDqxrVNk?4K=?yy9oisuVt*nZK9O4UtwEv b?_aR+wH8^GCM$kHpbZS3u6{1-oD!MwZLa?O zZuJJ<i$Lv74=;m7Ph+tbsxqo-U3d7N^$+dkP&;;OOSzfByXK z$;`JbJyCuqq&UnN#QBaeDm*gWQ~K@SuO3$So+{O2tF-R63ra9;U17|k)7Y!7yy94) zsln&StqQ4~X5MkjK5l1mdK II;Vst0ACYGQUCw| literal 0 HcmV?d00001 From c4375a5abc62f93971451515a4de790ecbe77479 Mon Sep 17 00:00:00 2001 From: GunHog Date: Thu, 25 Sep 2014 15:06:27 -0500 Subject: [PATCH 2/7] Minor fixes + Floorbot improvement - Floorbots are now anchored when moving to a hull breach. This prevents them being blown off course. - Fixes an accidental missing return for working mode in the floorbot and medibot. - Fixed the title of the Securitron's UI window. - Fixed the MULEbot's version number. --- code/game/machinery/bots/floorbot.dm | 7 +++++-- code/game/machinery/bots/medbot.dm | 2 +- code/game/machinery/bots/mulebot.dm | 4 +--- code/game/machinery/bots/secbot.dm | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index ff349864af9..a040c4260bd 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -81,6 +81,7 @@ oldloc = null ignore_list = list() nagged = 0 + anchored = 0 /obj/machinery/bot/floorbot/set_custom_texts() text_hack = "You corrupt [name]'s construction protocols." @@ -140,7 +141,7 @@ else if(istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) if(allowed(user) && !open && !emagged) locked = !locked - user << "You [locked ? "lock" : "unlock"] the [src] behaviour controls." + user << "You [locked ? "lock" : "unlock"] \the [src] behaviour controls." else if(emagged) user << "ERROR" @@ -192,7 +193,7 @@ /obj/machinery/bot/floorbot/process() set background = BACKGROUND_ENABLED - if(!on) + if(!on || mode == BOT_REPAIRING) return if(call_path) @@ -233,6 +234,7 @@ if(get_dir(src, D) == targetdirection) oldtarget = D target = D + anchored = 1 break var/turf/T = get_step(src, targetdirection) @@ -245,6 +247,7 @@ mode = BOT_MOVING oldtarget = D target = D + anchored = 1 //Prevent the floorbot being blown off-course while trying to reach a hull breach. break if(!target && replacetiles) //Finds a floor without a tile and gives it one. for (var/turf/simulated/floor/F in view(7,src)) diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm index d4f7dd72c98..a957349ddff 100644 --- a/code/game/machinery/bots/medbot.dm +++ b/code/game/machinery/bots/medbot.dm @@ -258,7 +258,7 @@ /obj/machinery/bot/medbot/process() set background = BACKGROUND_ENABLED - if(!on) + if(!on || mode == BOT_HEALING) return if(call_path) diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index 9863cf785fb..6229051ad8c 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -29,8 +29,6 @@ var/global/mulebot_count = 0 var/turf/target // this is turf to navigate to (location of beacon) var/loaddir = 0 // this the direction to unload onto/load from - new_destination = "" // pending new destination (waiting for beacon response) - destination = "" // destination description var/home_destination = "" // tag of home beacon req_access = list(access_cargo) // added robotics access so assembly line drop-off works properly -veyveyr //I don't think so, Tim. You need to add it to the MULE's hidden robot ID card. -NEO @@ -241,7 +239,7 @@ var/global/mulebot_count = 0 //user << browse("M.U.L.E. Mk. III [suffix ? "([suffix])" : ""][dat]", "window=mulebot;size=350x500") //onclose(user, "mulebot") - var/datum/browser/popup = new(user, "mulebot", "M.U.L.E. Mk. III [suffix ? "([suffix])" : ""]", 350, 500) + var/datum/browser/popup = new(user, "mulebot", "M.U.L.E. Mk. V [suffix ? "([suffix])" : ""]", 350, 500) popup.set_content(dat) popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) popup.open() diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index 406245fea17..057f4d55b21 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -127,7 +127,7 @@ Auto Patrol: []"}, "[declare_arrests ? "Yes" : "No"]", "[auto_patrol ? "On" : "Off"]" ) - var/datum/browser/popup = new(user, "autosec", "Securitron v1.6 controls") + var/datum/browser/popup = new(user, "autosec", "Automatic Security Unit v1.6") popup.set_content(dat) popup.open() return From 6d734c042c452e7510509c561ad469993aed9ba7 Mon Sep 17 00:00:00 2001 From: GunHog Date: Thu, 16 Oct 2014 16:52:13 -0500 Subject: [PATCH 3/7] Movement rebuilding and bot changes, Pt 1. - Created a proc to handle all bot movement, including patrols and PDA/AI summons. *NOT ALL BOTS ARE YET CONFIGURED TO USE IT!* That is for part 2. - Made/making bots faster while moving except for Secbots (balance issues) and MULEs (already super sanic). They will move twice per tick while moving/patrolling, and three times while summoned (PDA/AI) *PROCESS INCOMPLETE* - Gave bots a central ignore list for turfs they cannot reach, with a matching proc to use it. - New Scan() proc that will soon replace the highly similar ones in certain bots. Part of the effort to trim down on their repeated code. Added a define that defaults to 7 for view range. - Small tweak to the topic_denied() proc to use the user given by the primary topic. - Central process() proc so universal things for all bots can be easily added. (Except MULEs) Must return a 1 for child process() to run after it. - Corrected a spelling error. - Removed the extra path vars (summon_path, patrol_path, call_path) in favor of just using "path". - Compatibility changes to the summon and patrol procs. - Slightly tweaked cleanbot logic, removed some useless code, updated them to use the new scan() and bot_move() procs. Rest to follow. - Updated MULEbots to work with the new summon code. - Various small tweaks. --- code/game/machinery/bots/bots.dm | 251 ++++++++++++++++----------- code/game/machinery/bots/cleanbot.dm | 100 ++++------- code/game/machinery/bots/ed209bot.dm | 11 +- code/game/machinery/bots/floorbot.dm | 15 +- code/game/machinery/bots/medbot.dm | 46 +---- code/game/machinery/bots/mulebot.dm | 7 +- code/game/machinery/bots/secbot.dm | 12 +- 7 files changed, 194 insertions(+), 248 deletions(-) diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm index ae6d0f70325..b31b9b898a9 100644 --- a/code/game/machinery/bots/bots.dm +++ b/code/game/machinery/bots/bots.dm @@ -20,11 +20,11 @@ var/text_dehack_fail = "" //Shown when a silicon tries to reset a bot emagged with the emag item, which cannot be reset. var/declare_message = "" //What the bot will display to the HUD user. var/frustration = 0 //Used by some bots for tracking failures to reach their target. - var/list/call_path = list() //Path calculated by the AI and given to the bot to follow. - var/list/path = new() //Every bot has this, so it is best to put it here. - var/list/patrol_path = list() //The path a bot has while on patrol. - var/list/summon_path = list() //Path bot has while summoned. + var/speed = 2 //The speed at which the bot moves, or the number of times it moves per process() tick. + var/turf/ai_waypoint //The end point of a bot's path, or the target location. + var/list/path = list() //List of turfs through which a bot 'steps' to reach the waypoint. var/pathset = 0 + var/list/ignore_list //List of unreachable targets for an ignore-list enabled bot to ignore. var/mode = 0 //Standardizes the vars that indicate the bot is busy with its function. var/tries = 0 //Number of times the bot tried and failed to move. var/remote_disabled = 0 //If enabled, the AI cannot *Remotely* control a bot. It can still control it through cameras. @@ -57,6 +57,8 @@ #define CLEAN_BOT 4 // Cleanbots #define MED_BOT 5 // Medibots + #define DEFAULT_SCAN_RANGE 7 //default view range for finding targets. + //Mode defines #define BOT_IDLE 0 // idle #define BOT_HUNT 1 // found target, hunting @@ -107,8 +109,6 @@ if(bot_filter) radio_controller.add_object(src, control_freq, filter = bot_filter) - - /obj/machinery/bot/proc/explode() qdel(src) @@ -162,10 +162,10 @@ healthcheck() /obj/machinery/bot/Topic(href, href_list) //Master Topic to handle common functions. - if(..()) + . = ..() + if (.) return - - if(topic_denied()) + if(topic_denied(usr)) usr << "[src]'s interface is not responding!" href_list = list() return @@ -201,16 +201,28 @@ bot_reset() updateUsrDialog() -/obj/machinery/bot/proc/topic_denied() //Access check proc for bot topics! Remember to place in a bot's individual Topic if desired. +/obj/machinery/bot/proc/topic_denied(mob/user) //Access check proc for bot topics! Remember to place in a bot's individual Topic if desired. // 0 for access, 1 for denied. if(emagged == 2) //An emagged bot cannot be controlled by humans, silicons can if one hacked it. if(!hacked) //Manually emagged by a human - access denied to all. return 1 - else if(!issilicon(usr)) //Bot is hacked, so only silicons are allowed access. + else if(!issilicon(user)) //Bot is hacked, so only silicons are allowed access. return 1 else return 0 +/obj/machinery/bot/process() //Master process which handles code common across all bots. + + set background = BACKGROUND_ENABLED + + if(!on) + return + + if(mode == BOT_RESPONDING) + call_mode() + return + return 1 //Successful completion. Used to prevent child process() continuing if this one is ended early. + /obj/machinery/bot/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/screwdriver)) @@ -308,7 +320,7 @@ src.attack_hand(user) /obj/machinery/bot/proc/speak(var/message, freq) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio. - if((!src.on) || (!message)) + if((!on) || (!message)) return if(freq) Radio.set_frequency(radio_frequency) @@ -317,13 +329,84 @@ say(message) return + //Generalized behavior code, override where needed! + +/* +scan() will search for a given type (such as turfs, human mobs, or objects) in the bot's view range, and return a single result. +Arguments: The object type to be searched (such as "/mob/living/carbon/human"), the view range (usually 7, a full screen), +the old scan result to be ignored, if one exists, and a list of multiple objects to be ignored, for bots which support it. +*/ +obj/machinery/bot/proc/scan(var/scan_type, var/scan_range, var/old_target, var/list/ignorelist) + var/scan_result + var/final_result + ignorelist |= old_target + for (scan_type in view (scan_range ? scan_range : DEFAULT_SCAN_RANGE,src) ) + if( !(scan_type in ignorelist) ) + scan_result = process_scan(scan_type) + if( scan_result ) + final_result = scan_result + else + continue //The current element failed assessment, move on to the next. + return final_result + +//When the scan finds a target, run bot specific processing to select it for the next step. Empty by default. +obj/machinery/bot/proc/process_scan(var/scan_target) + return scan_target + + +/obj/machinery/bot/proc/add_to_ignore(var/subject) + if(ignore_list.len < 50) //This will help keep track of them, so the bot is always trying to reach a blocked spot. + ignore_list |= subject + else if (ignore_list.len >= subject) //If the list is full, insert newest, delete oldest. + ignore_list -= ignore_list[1] + ignore_list |= subject + +/* +Movement proc for stepping a bot through a path generated through A-star. +Pass a positive integer as an argument to override a bot's default speed. +*/ +obj/machinery/bot/proc/bot_move(var/dest, var/move_speed) + + if(!dest || !path || path.len == 0) //A-star failed or a path/destination was not set. + path = list() + return 0 + if(get_turf(src) == get_turf(dest)) //We have arrived, no need to move again. + return 1 + var/success + var/step_count = move_speed ? move_speed : speed //If a value is passed into move_speed, use that instead of the default speed var. + if(step_count >= 1 && tries < 4) + for(step_count, step_count >= 1,step_count--) + success = bot_step(dest) + if (success) + tries = 0 + else + tries++ + break + sleep(4) + else + return 0 + return 1 + + +obj/machinery/bot/proc/bot_step(var/dest) + if(path.len > 1) + step_to(src, path[1]) + if(get_turf(src) == path[1]) //Successful move + path -= path[1] + else + return 0 + else if(path.len == 1) + step_to(src, dest) + path = list() + return 1 + /obj/machinery/bot/proc/check_bot_access() if(mode != BOT_SUMMON && mode != BOT_RESPONDING) botcard.access = prev_access /obj/machinery/bot/proc/call_bot(var/caller, var/turf/waypoint) - bot_reset() //Reset a bot becore setting it to call mode. + bot_reset() //Reset a bot before setting it to call mode. var/area/end_area = get_area(waypoint) //For giving the bot temporary all-access. @@ -331,14 +414,15 @@ var/datum/job/captain/All = new/datum/job/captain all_access.access = All.get_access() - call_path = AStar(src, waypoint, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 200, id=all_access) + path = AStar(src, waypoint, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 200, id=all_access) calling_ai = caller //Link the AI to the bot! + ai_waypoint = waypoint - if(call_path && call_path.len) //Ensures that a valid path is calculated! + if(path && path.len) //Ensures that a valid path is calculated! if(!on) turn_on() //Saves the AI the hassle of having to activate a bot manually. botcard = all_access //Give the bot all-access while under the AI's command. - calling_ai << "\icon[src] [name] called to [end_area.name]. [call_path.len-1] meters to destination." + calling_ai << "\icon[src] [name] called to [end_area.name]. [path.len-1] meters to destination." pathset = 1 mode = BOT_RESPONDING tries = 0 @@ -348,23 +432,10 @@ /obj/machinery/bot/proc/call_mode() //Handles preparing a bot for a call, as well as calling the move proc. //Handles the bot's movement during a call. - move_to_call() - sleep(5) - move_to_call() //Called twice so that the bot moves faster. - return - -/obj/machinery/bot/proc/move_to_call() - if(call_path && call_path.len && tries < 6) - step_towards(src, call_path[1]) - - if(loc == call_path[1])//Remove turfs from the path list if the bot moved there. - tries = 0 - call_path -= call_path[1] - else //Could not move because of an obstruction. - tries++ - else + var/success = bot_move(ai_waypoint, 3) + if (!success) if(calling_ai) - calling_ai << "\icon[src] [tries ? "[src] failed to reach waypoint." : "[src] successfully arrived to waypoint."]" + calling_ai << "\icon[src] [get_turf(src) == ai_waypoint ? "[src] successfully arrived to waypoint." : "[src] failed to reach waypoint."]" calling_ai = null bot_reset() @@ -372,10 +443,7 @@ obj/machinery/bot/proc/bot_reset() if(calling_ai) //Simple notification to the AI if it called a bot. It will not know the cause or identity of the bot. calling_ai << "Call command to a bot has been reset." calling_ai = null - call_path = null - path = new() - patrol_path = list() - summon_path = list() + path = list() summon_target = null pathset = 0 botcard.access = prev_access @@ -383,6 +451,9 @@ obj/machinery/bot/proc/bot_reset() mode = BOT_IDLE + + + //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //Patrol and summon code! //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -407,14 +478,10 @@ obj/machinery/bot/proc/start_patrol() mode = BOT_IDLE return - if(patrol_path && patrol_path.len > 0 && patrol_target) // have a valid path, so just resume - mode = BOT_PATROL - return - - else if(patrol_target) // has patrol target already + if(patrol_target) // has patrol target spawn(0) - calc_path() // so just find a route to it - if(patrol_path.len == 0) + calc_path() // Find a route to it + if(path.len == 0) patrol_target = 0 return mode = BOT_PATROL @@ -430,44 +497,36 @@ obj/machinery/bot/proc/start_patrol() if(loc == patrol_target) // reached target - at_patrol_target() return - else if(patrol_path.len > 0 && patrol_target) // valid path - var/turf/next = patrol_path[1] + else if(path.len > 0 && patrol_target) // valid path + var/turf/next = path[1] if(next == loc) - patrol_path -= next + path -= next return - if(istype( next, /turf/simulated)) + var/moved = bot_move(patrol_target)//step_towards(src, next) // attempt to move + if(moved) // successful move + blockcount = 0 - var/moved = step_towards(src, next) // attempt to move - if(moved) // successful move - blockcount = 0 - patrol_path -= loc + else // failed to move + blockcount++ - else // failed to move - blockcount++ + if(blockcount > 5) // attempt 5 times before recomputing + // find new path excluding blocked turf - if(blockcount > 5) // attempt 5 times before recomputing - // find new path excluding blocked turf - - spawn(2) - calc_path(next) - if(patrol_path.len == 0) - find_patrol_target() - else - blockcount = 0 - tries = 0 - - return + spawn(2) + calc_path(next) + if(path.len == 0) + find_patrol_target() + else + blockcount = 0 + tries = 0 return - else // not a valid turf - mode = BOT_IDLE return else // no path, so calculate new one @@ -640,25 +699,20 @@ obj/machinery/bot/proc/start_patrol() obj/machinery/bot/proc/bot_summon() // summoned to PDA summon_step() - spawn(4) - if(mode == BOT_SUMMON) - summon_step() - sleep(4) - summon_step() return // calculates a path to the current destination // given an optional turf to avoid -/obj/machinery/bot/proc/calc_path(var/turf/avoid = null) +/obj/machinery/bot/proc/calc_path(var/turf/avoid) check_bot_access() - patrol_path = AStar(loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 120, id=botcard, exclude=avoid) - if(!patrol_path) - patrol_path = list() + path = AStar(loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 120, id=botcard, exclude=avoid) + if(!path) + path = list() -/obj/machinery/bot/proc/calc_summon_path(var/turf/avoid = null) +/obj/machinery/bot/proc/calc_summon_path(var/turf/avoid) check_bot_access() - summon_path = AStar(loc, summon_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 150, id=botcard, exclude=avoid) - if(!summon_path || tries >= 5) + path = AStar(loc, summon_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 150, id=botcard, exclude=avoid) + if(!path || tries >= 5) bot_reset() /obj/machinery/bot/proc/summon_step() @@ -667,36 +721,29 @@ obj/machinery/bot/proc/bot_summon() bot_reset() return - else if(summon_path.len > 0 && summon_target) //Proper path acquired! - var/turf/next = summon_path[1] + else if(path.len > 0 && summon_target) //Proper path acquired! + var/turf/next = path[1] if(next == loc) - summon_path -= next + path -= next return + var/moved = bot_move(summon_target, 3) // Move attempt + if(moved) + blockcount = 0 + path -= loc - if(istype( next, /turf/simulated)) + else // failed to move + blockcount++ - var/moved = step_towards(src, next) // Move attempt - if(moved) - blockcount = 0 - summon_path -= loc - - else // failed to move - blockcount++ - - if(blockcount > 5) // attempt 5 times before recomputing - // find new path excluding blocked turf - spawn(2) - calc_summon_path(next) - tries++ - return + if(blockcount > 5) // attempt 5 times before recomputing + // find new path excluding blocked turf + spawn(2) + calc_summon_path(next) + tries++ + return return - else // not a valid turf - bot_reset() - return - else // no path, so calculate new one calc_summon_path() diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm index 41f231919b9..7e18b9d8ce2 100644 --- a/code/game/machinery/bots/cleanbot.dm +++ b/code/game/machinery/bots/cleanbot.dm @@ -28,7 +28,6 @@ var/list/target_types = list() var/obj/effect/decal/cleanable/target var/obj/effect/decal/cleanable/oldtarget - var/list/cleanbottargets = list() //Targets that the cleanbot cannot reach and will thus ignore. var/max_targets = 50 //Maximum number of targets a cleanbot can ignore. var/oldloc = null req_one_access = list(access_janitor, access_robotics) @@ -55,7 +54,7 @@ add_to_beacons(bot_filter) /obj/machinery/bot/cleanbot/turn_on() - . = ..() + ..() icon_state = "cleanbot[on]" updateUsrDialog() @@ -66,7 +65,7 @@ /obj/machinery/bot/cleanbot/bot_reset() ..() - cleanbottargets = list() //Allows the bot to clean targets it previously ignored due to being unreachable. + ignore_list = list() //Allows the bot to clean targets it previously ignored due to being unreachable. target = null oldtarget = null oldloc = null @@ -95,13 +94,6 @@ text("[on ? "On" : "Off"]")) if(!locked || issilicon(user)) dat += text({"
    Cleans Blood: []
    "}, text("[blood ? "Yes" : "No"]")) dat += text({"
    Patrol station: []
    "}, text("[auto_patrol ? "Yes" : "No"]")) - // dat += text({"
    Beacon frequency: []
    "}, text("[beacon_freq]")) -/* if(open && !locked) - dat += text({" -Odd looking screw twiddled: []
    -Weird button pressed: []"}, -text("[screwloose ? "Yes" : "No"]"), -text("[oddbutton ? "Yes" : "No"]"))*/ var/datum/browser/popup = new(user, "autoclean", "Automatic Station Cleaner v1.1") popup.set_content(dat) @@ -122,15 +114,6 @@ text("[oddbutton ? "Yes" : "No"]You press the weird button." - updateUsrDialog() */ - /obj/machinery/bot/cleanbot/attackby(obj/item/weapon/W, mob/user as mob) if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) if(allowed(usr) && !open && !emagged) @@ -151,42 +134,41 @@ text("[oddbutton ? "Yes" : "No"][src] buzzes and beeps." +/obj/machinery/bot/cleanbot/process_scan(var/obj/effect/decal/cleanable/D) + for(var/T in target_types) + if(!(D in ignore_list) && istype(D, T)) + return D + /obj/machinery/bot/cleanbot/process() - set background = BACKGROUND_ENABLED - - if(!on) - return - - if(call_path) - call_mode() + if (!..()) return if(mode == BOT_CLEANING) return - if(!emagged && prob(5)) - visible_message("[src] makes an excited beeping booping sound!") - - if(emagged == 2 && prob(10)) //Wets floors randomly + if(emagged == 2) //Emag functions if(istype(loc,/turf/simulated)) - var/turf/simulated/T = loc - T.MakeSlippery() - if(emagged == 2 && prob(5)) //Spawns foam! - visible_message("[src] whirs and bubbles violently, before releasing a plume of froth!") - new /obj/effect/effect/foam(loc) + if(prob(10)) //Wets floors randomly + var/turf/simulated/T = loc + T.MakeSlippery() + + if(prob(5)) //Spawns foam! + visible_message("[src] whirs and bubbles violently, before releasing a plume of froth!") + new /obj/effect/effect/foam(loc) + + else if (prob(5)) + visible_message("[src] makes an excited beeping booping sound!") if(mode == BOT_SUMMON) bot_summon() return if(!target) //Search for cleanables it can see. - for (var/obj/effect/decal/cleanable/D in view(7,src)) - for(var/T in target_types) - if(!(D in cleanbottargets) && (D.type == T || D.parent_type == T) && D != oldtarget) - oldtarget = D - target = D - break + var/obj/effect/decal/cleanable/D + D = scan(D, DEFAULT_SCAN_RANGE, oldtarget, ignore_list) + oldtarget = D + target = D if(!target) if(loc != oldloc) @@ -199,48 +181,28 @@ text("[oddbutton ? "Yes" : "No"] 0 && target) - mode = BOT_MOVING - step_to(src, path[1]) - path -= path[1] - else if(path.len == 1) - step_to(src, target) + return + else + bot_move(target) - if(target) if(loc == target.loc) clean(target) - path = new() + path = list() target = null return oldloc = loc - -/obj/machinery/bot/cleanbot/proc/add_to_ignore(target) - if(cleanbottargets.len < max_targets && !(target in cleanbottargets)) //Add the target to the ignore list if it is not full or already inside. - cleanbottargets += target - else if (cleanbottargets.len >= max_targets) - cleanbottargets -= cleanbottargets[1] // ignore list is full, so remove the oldest target. - cleanbottargets += target // then add the newest one. - /obj/machinery/bot/cleanbot/proc/get_targets() target_types = new/list() diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm index d5dcae89ad8..f377ecf6977 100644 --- a/code/game/machinery/bots/ed209bot.dm +++ b/code/game/machinery/bots/ed209bot.dm @@ -188,7 +188,8 @@ Auto Patrol: []"}, mode = BOT_HUNT /obj/machinery/bot/ed209/Emag(mob/user as mob) - ..() + if (!..()) + return if(open && !locked) if(user) user << "You short out [src]'s target assessment circuits." spawn(0) @@ -200,13 +201,9 @@ Auto Patrol: []"}, declare_arrests = 0 /obj/machinery/bot/ed209/process() - set background = BACKGROUND_ENABLED + ..() - if(!on || disabled) - return - - if(call_path) - call_mode() + if(disabled) return var/list/targets = list() diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index a040c4260bd..5f1148fa82d 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -46,7 +46,6 @@ var/max_targets = 50 var/turf/target var/turf/oldtarget - var/list/ignore_list = list() //List of unreachable targets var/oldloc = null req_one_access = list(access_construction, access_robotics) var/targetdirection @@ -191,13 +190,10 @@ updateUsrDialog() /obj/machinery/bot/floorbot/process() - set background = BACKGROUND_ENABLED - - if(!on || mode == BOT_REPAIRING) + if (!..()) return - if(call_path) - call_mode() + if(mode == BOT_REPAIRING) return if(mode == BOT_SUMMON) @@ -341,13 +337,6 @@ speak("Requesting refill at [get_area(src)]!", radio_frequency) nagged = 1 -/obj/machinery/bot/floorbot/proc/add_to_ignore(target) //Often objects may block access to damaged tiles or open space. - if(ignore_list.len < max_targets && !(target in ignore_list)) //This will help keep track of them, so the bot is always trying to reach a blocked spot. - ignore_list += target - else if (ignore_list.len >= max_targets) //If the list is full, insert newest, delete oldest. - ignore_list -= ignore_list[1] - ignore_list += target - /obj/machinery/bot/floorbot/proc/is_hull_breach(var/turf/t) //Ignore space tiles not considered part of a structure, also ignores shuttle docking areas. var/area/t_area = get_area(t) if (t_area && (t_area.name == "Space" || findtext(t_area.name, "huttle"))) diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm index a957349ddff..4a88396df8d 100644 --- a/code/game/machinery/bots/medbot.dm +++ b/code/game/machinery/bots/medbot.dm @@ -256,13 +256,10 @@ oldpatient = user /obj/machinery/bot/medbot/process() - set background = BACKGROUND_ENABLED - - if(!on || mode == BOT_HEALING) + if (!..()) return - if(call_path) - call_mode() + if(mode == BOT_HEALING) return if(stunned) @@ -536,45 +533,6 @@ spawn(200) //Twenty seconds declare_cooldown = 0 - -/* terrible -/obj/machinery/bot/medbot/Bumped(atom/movable/M as mob|obj) - spawn(0) - if (M) - var/turf/T = get_turf(src) - M:loc = T -*/ - -/* - * Pathfinding procs, allow the medibot to path through doors it has access to. - */ - -//Pretty ugh -/* -/turf/proc/AdjacentTurfsAllowMedAccess() - var/L[] = new() - for(var/turf/t in oview(src,1)) - if(!t.density) - if(!LinkBlocked(src, t) && !TurfBlockedNonWindowNonDoor(t,get_access("Medical Doctor"))) - L.Add(t) - return L - - -//It isn't blocked if we can open it, man. -/proc/TurfBlockedNonWindowNonDoor(turf/loc, var/list/access) - for(var/obj/O in loc) - if(O.density && !istype(O, /obj/structure/window) && !istype(O, /obj/machinery/door)) - return 1 - - if (O.density && (istype(O, /obj/machinery/door)) && (access.len)) - var/obj/machinery/door/D = O - for(var/req in D.req_access) - if(!(req in access)) //doesn't have this access - return 1 - - return 0 -*/ - /* * Medbot Assembly -- Can be made out of all three medkits. */ diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index 6229051ad8c..335764f8d35 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -489,12 +489,10 @@ var/global/mulebot_count = 0 /obj/machinery/bot/mulebot/call_bot() ..() var/area/dest_area - if (call_path && call_path.len) - target = call_path[call_path.len] //Target is the end point of the path, the waypoint set by the AI. + if (path && path.len) + target = ai_waypoint //Target is the end point of the path, the waypoint set by the AI. dest_area = get_area(target) destination = format_text(dest_area.name) - path = call_path - call_path = null //Once the MULE is commanded, follow normal procedures to reach the waypoint. pathset = 1 //Indicates the AI's custom path is initialized. start() @@ -698,7 +696,6 @@ var/global/mulebot_count = 0 playsound(calling_ai, 'sound/machines/chime.ogg',40, 0) calling_ai = null radio_frequency = 1447 //Report on AI Private instead if the AI is controlling us. - //bot_reset() if(load) // if loaded, unload at target speak("Destination [destination] reached. Unloading [load].",radio_frequency) diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index 057f4d55b21..d8bc625230d 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -178,7 +178,9 @@ Auto Patrol: []"}, mode = BOT_HUNT /obj/machinery/bot/secbot/Emag(mob/user as mob) - ..() + if (..()) + return + if(emagged == 2) if(user) user << " You short out [src]'s target assessment circuits." spawn(0) @@ -188,13 +190,7 @@ Auto Patrol: []"}, declare_arrests = 0 /obj/machinery/bot/secbot/process() - set background = BACKGROUND_ENABLED - - if(!on) - return - - if(call_path) - call_mode() + if (!..()) return switch(mode) From cd3ec2ed47940a7c3328656756f5af474f4470b6 Mon Sep 17 00:00:00 2001 From: GunHog Date: Tue, 21 Oct 2014 13:19:23 -0500 Subject: [PATCH 4/7] Movement rebuilding and bot changes, Pt 2. - Added Both summon types to the super process(), since both summon types reset the bot. - Reworked the logic and arguments for the scan() proc. [NEEDS CAREFUL REVIEW]. - Updated cleanbots to properly use the scan() and bot_move() procs. - Floorbots will now have the correct icons when reset. - The direction and state for Bridge Mode can now be selected from a popup. - Reworked Floorbot logic and code to use the scan and bot_move procs. - Fixed Floorbot target not resetting properly. - Updated Medibots to use scan and bot_move. No functional changes should occur. --- code/game/machinery/bots/bots.dm | 37 ++-- code/game/machinery/bots/cleanbot.dm | 23 +-- code/game/machinery/bots/floorbot.dm | 244 ++++++++++++++------------- code/game/machinery/bots/medbot.dm | 68 +++----- 4 files changed, 186 insertions(+), 186 deletions(-) diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm index b31b9b898a9..ab1a2855848 100644 --- a/code/game/machinery/bots/bots.dm +++ b/code/game/machinery/bots/bots.dm @@ -24,7 +24,7 @@ var/turf/ai_waypoint //The end point of a bot's path, or the target location. var/list/path = list() //List of turfs through which a bot 'steps' to reach the waypoint. var/pathset = 0 - var/list/ignore_list //List of unreachable targets for an ignore-list enabled bot to ignore. + var/list/ignore_list = list() //List of unreachable targets for an ignore-list enabled bot to ignore. var/mode = 0 //Standardizes the vars that indicate the bot is busy with its function. var/tries = 0 //Number of times the bot tried and failed to move. var/remote_disabled = 0 //If enabled, the AI cannot *Remotely* control a bot. It can still control it through cameras. @@ -211,16 +211,20 @@ else return 0 -/obj/machinery/bot/process() //Master process which handles code common across all bots. +/obj/machinery/bot/process() //Master process which handles code common across most bots. set background = BACKGROUND_ENABLED if(!on) return - if(mode == BOT_RESPONDING) - call_mode() - return + switch(mode) //High-priority overrides are processed first. Bots can do nothing else while under direct command. + if(BOT_RESPONDING) //Called by the AI. + call_mode() + return + if(BOT_SUMMON) //Called by PDA + bot_summon() + return return 1 //Successful completion. Used to prevent child process() continuing if this one is ended early. @@ -333,21 +337,26 @@ /* scan() will search for a given type (such as turfs, human mobs, or objects) in the bot's view range, and return a single result. -Arguments: The object type to be searched (such as "/mob/living/carbon/human"), the view range (usually 7, a full screen), -the old scan result to be ignored, if one exists, and a list of multiple objects to be ignored, for bots which support it. +Arguments: The object type to be searched (such as "/mob/living/carbon/human"), the old scan result to be ignored, if one exists, +and the view range, which defaults to 7 (full screen) if an override is not passed. +If the bot maintains an ignore list, it is also checked here. + +Example usage: patient = scan(/mob/living/carbon/human, oldpatient, 1) +The proc would return a human next to the bot to be set to the patient var. +Pass the desired type path itself, declaring a temporary var beforehand is not required. */ -obj/machinery/bot/proc/scan(var/scan_type, var/scan_range, var/old_target, var/list/ignorelist) - var/scan_result +obj/machinery/bot/proc/scan(var/scan_type, var/old_target, var/scan_range) var/final_result - ignorelist |= old_target - for (scan_type in view (scan_range ? scan_range : DEFAULT_SCAN_RANGE,src) ) - if( !(scan_type in ignorelist) ) - scan_result = process_scan(scan_type) + for (var/scan in view (scan_range ? scan_range : DEFAULT_SCAN_RANGE, src) ) //Search for something in range! + if(!istype(scan, scan_type)) //Check that the thing we found is the type we want! + continue //If not, keep searching! + if( !(scan in ignore_list) && !(scan in old_target) ) //Filter for blacklisted elements, usually unreachable or previously processed oness + var/scan_result = process_scan(scan) //Some bots may require additional processing when a result is selected. if( scan_result ) final_result = scan_result else continue //The current element failed assessment, move on to the next. - return final_result + return final_result //When the scan finds a target, run bot specific processing to select it for the next step. Empty by default. obj/machinery/bot/proc/process_scan(var/scan_target) diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm index 7e18b9d8ce2..e9bfd865ecb 100644 --- a/code/game/machinery/bots/cleanbot.dm +++ b/code/game/machinery/bots/cleanbot.dm @@ -136,7 +136,7 @@ text("[on ? "On" : "Off"]")) /obj/machinery/bot/cleanbot/process_scan(var/obj/effect/decal/cleanable/D) for(var/T in target_types) - if(!(D in ignore_list) && istype(D, T)) + if(istype(D, T)) return D /obj/machinery/bot/cleanbot/process() @@ -160,15 +160,9 @@ text("[on ? "On" : "Off"]")) else if (prob(5)) visible_message("[src] makes an excited beeping booping sound!") - if(mode == BOT_SUMMON) - bot_summon() - return - if(!target) //Search for cleanables it can see. - var/obj/effect/decal/cleanable/D - D = scan(D, DEFAULT_SCAN_RANGE, oldtarget, ignore_list) - oldtarget = D - target = D + target = scan(/obj/effect/decal/cleanable/, oldtarget) + oldtarget = target if(!target) if(loc != oldloc) @@ -184,16 +178,17 @@ text("[on ? "On" : "Off"]")) return if(target) - if(!path || path.len == 0) - if(!src || !target) - return + if(!path || path.len == 0) //No path, need a new one //Try to produce a path to the target, and ignore airlocks to which it has access. path = AStar(loc, target.loc, /turf/proc/AdjacentTurfsWithAccess, /turf/proc/Distance, 0, 30, id=botcard) if (!bot_move(target)) add_to_ignore(target) + target = null return - else - bot_move(target) + mode = BOT_MOVING + else if (!bot_move(target)) + target = null + mode = BOT_IDLE if(loc == target.loc) clean(target) diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index 5f1148fa82d..dd920039701 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -52,6 +52,13 @@ radio_frequency = 1357 //Engineering channel bot_type = FLOOR_BOT bot_filter = RADIO_FLOORBOT + var/process_type //Determines what to do when process_scan() recieves a target. See process_scan() for details. + #define HULL_BREACH 1 + #define BRIDGE_MODE 2 + #define FIX_TILE 3 + #define AUTO_TILE 4 + #define REPLACE_TILE 5 + #define TILE_EMAG 6 /obj/machinery/bot/floorbot/New() ..() @@ -81,6 +88,7 @@ ignore_list = list() nagged = 0 anchored = 0 + update_icon() /obj/machinery/bot/floorbot/set_custom_texts() text_hack = "You corrupt [name]'s construction protocols." @@ -114,8 +122,8 @@ if (targetdirection) bmode = dir2text(targetdirection) else - bmode = "Disabled" - dat += "

    Bridge Mode : [bmode]
    " + bmode = "disabled" + dat += "Bridge Mode : [bmode]
    " var/datum/browser/popup = new(user, "autofloor", "Automatic Station Floor Repairer v1.1") popup.set_content(dat) @@ -174,18 +182,17 @@ nag_on_empty = !nag_on_empty if("bridgemode") - switch(targetdirection) - if(null) + var/setdir = input("Select construction direction:") as null|anything in list("north","east","south","west","disable") + switch(setdir) + if("north") targetdirection = 1 - if(1) + if("south") targetdirection = 2 - if(2) + if("east") targetdirection = 4 - if(4) + if("west") targetdirection = 8 - if(8) - targetdirection = null - else + if("disable") targetdirection = null updateUsrDialog() @@ -196,79 +203,57 @@ if(mode == BOT_REPAIRING) return - if(mode == BOT_SUMMON) - bot_summon() - return + if(amount <= 0 && !target) //Out of tiles! We must refill! + if(eattiles) //Configured to find and consume floortiles! + target = scan(/obj/item/stack/tile/plasteel, oldtarget) + process_type = null - if(amount <= 0 && !target) - if(eattiles) - for(var/obj/item/stack/tile/plasteel/T in view(7, src)) - if(T != oldtarget && !(target in ignore_list)) - oldtarget = T - target = T - break - if(!target) - if(maketiles) - if(!target) - for(var/obj/item/stack/sheet/metal/M in view(7, src)) - if(!(M in ignore_list) && M != oldtarget && !(istype(M.loc, /turf/simulated/wall))) - oldtarget = M - target = M - break - else + if(!target && maketiles) //We did not manage to find any floor tiles! Scan for metal stacks and make our own! + target = scan(/obj/item/stack/sheet/metal, oldtarget) + process_type = null return - if(nag_on_empty) //Floorbot is empty and cannot acquire more tiles, nag the engineers for more! - nag() + else + if(nag_on_empty) //Floorbot is empty and cannot acquire more tiles, nag the engineers for more! + nag() + if(prob(5)) visible_message("[src] makes an excited booping beeping sound!") + //Normal scanning procedure. We have tiles loaded, are not emagged. if(!target && emagged < 2 && amount > 0) if(targetdirection != null) //The bot is in bridge mode. - - for (var/turf/space/D in view(7,src)) - if(!(D in ignore_list) && D != oldtarget) // Added for bridging mode - if(get_dir(src, D) == targetdirection) - oldtarget = D - target = D - anchored = 1 - break - + //Try to find a space tile immediately in our selected direction. var/turf/T = get_step(src, targetdirection) if(istype(T, /turf/space)) - oldtarget = T target = T - if(!target) - for (var/turf/space/D in view(7,src)) //Ensures the floorbot does not try to "fix" space areas or shuttle docking zones. - if(!(D in ignore_list) && D != oldtarget && is_hull_breach(D)) - mode = BOT_MOVING - oldtarget = D - target = D - anchored = 1 //Prevent the floorbot being blown off-course while trying to reach a hull breach. - break - if(!target && replacetiles) //Finds a floor without a tile and gives it one. - for (var/turf/simulated/floor/F in view(7,src)) - //The target must be the floor and not a tile. The floor must not already have a floortile. - if(!(F in ignore_list) && F != oldtarget && F.is_plating()) - oldtarget = F - target = F - mode = BOT_MOVING - break - if(!target && fixfloors) //Repairs damaged floors and tiles. - for(var/turf/simulated/floor/F in view(7, src)) - if(!(F in ignore_list) && F != oldtarget && (F.broken || F.burnt)) - oldtarget = F - target = F - mode = BOT_MOVING - break - if(!target && emagged == 2) + else //Find a space tile farther way! + target = scan(/turf/space, oldtarget) + anchored = 1 + process_type = BRIDGE_MODE + return + if(!target) - for (var/turf/simulated/floor/D in view(7,src)) - if(!(D in ignore_list) && D != oldtarget && D.floor_tile) - oldtarget = D - target = D - mode = BOT_MOVING - break + process_type = HULL_BREACH //Ensures the floorbot does not try to "fix" space areas or shuttle docking zones. + target = scan(/turf/space, oldtarget) + anchored = 1 //Prevent the floorbot being blown off-course while trying to reach a hull breach. + return + + if(!target && replacetiles) //Finds a floor without a tile and gives it one. + process_type = REPLACE_TILE //The target must be the floor and not a tile. The floor must not already have a floortile. + //target = scan(/turf/simulated/floor, oldtarget) + return + + if(!target && fixfloors) //Repairs damaged floors and tiles. + process_type = FIX_TILE + target = scan(/turf/simulated/floor, oldtarget) + return + + if(!target && emagged == 2) //We are emagged! Time to rip up the floors! + process_type = TILE_EMAG + target = scan(/turf/simulated/floor, oldtarget) + return + if(!target) @@ -284,51 +269,49 @@ oldtarget = null return - if(target && path.len == 0) - spawn(0) + if(target) + if(path.len == 0) if(!istype(target, /turf/)) var/turf/TL = get_turf(target) path = AStar(loc, TL, /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id=botcard) else path = AStar(loc, target, /turf/proc/AdjacentTurfsSpace, /turf/proc/Distance, 0, 30, id=botcard) - if(!path) - path = list() - if(path.len == 0) + + if(!bot_move(target)) add_to_ignore(target) oldtarget = target target = null mode = BOT_IDLE - return - if(path.len > 0 && target) - step_to(src, path[1]) - path -= path[1] - else if(path.len == 1) - step_to(src, target) - path = new() + return + else if( !bot_move(target) ) + oldtarget = target + target = null + mode = BOT_IDLE + return - if(loc == target || loc == target.loc) - if(istype(target, /obj/item/stack/tile/plasteel)) - eattile(target) - else if(istype(target, /obj/item/stack/sheet/metal)) - maketile(target) - else if(istype(target, /turf/) && emagged < 2) - repair(target) - else if(emagged == 2 && istype(target,/turf/simulated/floor)) - var/turf/simulated/floor/F = target - anchored = 1 - mode = BOT_REPAIRING - if(prob(90)) - F.break_tile_to_plating() - else - F.ReplaceWithLattice() - visible_message("[src] makes an excited booping sound.") - spawn(50) - amount ++ - anchored = 0 - mode = BOT_IDLE - target = null - path = new() - return + if(loc == target || loc == target.loc) + if(istype(target, /obj/item/stack/tile/plasteel)) + eattile(target) + else if(istype(target, /obj/item/stack/sheet/metal)) + maketile(target) + else if(istype(target, /turf/) && emagged < 2) + repair(target) + else if(emagged == 2 && istype(target,/turf/simulated/floor)) + var/turf/simulated/floor/F = target + anchored = 1 + mode = BOT_REPAIRING + if(prob(90)) + F.break_tile_to_plating() + else + F.ReplaceWithLattice() + visible_message("[src] makes an excited booping sound.") + spawn(50) + amount ++ + anchored = 0 + mode = BOT_IDLE + target = null + path = new() + return oldloc = loc @@ -344,14 +327,41 @@ else return 1 -/obj/machinery/bot/floorbot/proc/repair(var/turf/target) +//Floorbots, having several functions, need sort out special conditions here. +obj/machinery/bot/floorbot/process_scan(var/scan_target) + var/result + var/turf/simulated/floor/F + switch(process_type) + if(HULL_BREACH) //The most common job, patching breaches in the station's hull. + if(is_hull_breach(scan_target)) //Ensure that the targeted space turf is actually part of the station, and not random space. + result = scan_target + if(BRIDGE_MODE) //Only space turfs in our chosen direction are considered. + if(get_dir(src, scan_target) == targetdirection) + result = scan_target + if(REPLACE_TILE) + F = scan_target + if(F.is_plating()) //The floor must not already have a tile. + result = F + if(FIX_TILE) //Selects only damaged floors. + F = scan_target + if(F.broken || F.burnt) + result = F + if(TILE_EMAG) //Emag mode! Rip up the floor and cause breaches to space! + F = scan_target + if(!F.is_plating()) + result = F + else //If no special processing is needed, simply return the result. + result = scan_target + return result - if(istype(target, /turf/space/)) +/obj/machinery/bot/floorbot/proc/repair(var/turf/target_turf) + + if(istype(target_turf, /turf/space/)) //Must be a hull breach or in bridge mode to continue. - if(!is_hull_breach(target) && !targetdirection) + if(!is_hull_breach(target_turf) && !targetdirection) target = null return - else if(!istype(target, /turf/simulated/floor)) + else if(!istype(target_turf, /turf/simulated/floor)) return if(amount <= 0) mode = BOT_IDLE @@ -361,36 +371,34 @@ var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel anchored = 1 icon_state = "floorbot-c" - if(istype(target, /turf/space/)) //If we are fixing an area not part of pure space, it is + if(istype(target_turf, /turf/space/)) //If we are fixing an area not part of pure space, it is visible_message(" [targetdirection ? "[src] begins installing a bridge plating." : "[src] begins to repair the hole."] ") mode = BOT_REPAIRING spawn(50) if(mode == BOT_REPAIRING) if(autotile) //Build the floor and include a tile. - F = target.ChangeTurf(/turf/simulated/floor) + F = target_turf.ChangeTurf(/turf/simulated/floor) else //Build a hull plating without a floor tile. T.build(loc) - mode = BOT_IDLE amount -= 1 updateicon() anchored = 0 - src.target = null + target = null else - - F = target + F = target_turf mode = BOT_REPAIRING visible_message(" [src] begins repairing the floor.") spawn(50) if(mode == BOT_REPAIRING) - F = target + F = target_turf F.make_plasteel_floor(T) mode = BOT_IDLE amount -= 1 updateicon() anchored = 0 - src.target = null + target = null /obj/machinery/bot/floorbot/proc/eattile(var/obj/item/stack/tile/plasteel/T) if(!istype(T, /obj/item/stack/tile/plasteel)) diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm index 4a88396df8d..fc4815121a1 100644 --- a/code/game/machinery/bots/medbot.dm +++ b/code/game/machinery/bots/medbot.dm @@ -255,6 +255,23 @@ if(user) oldpatient = user +/obj/machinery/bot/medbot/process_scan(var/mob/living/carbon/human/H) + if (H.stat == 2) + return + + if ((H == oldpatient) && (world.time < last_found + 100)) + return + + if(assess_patient(H)) + last_found = world.time + if((last_newpatient_speak + 300) < world.time) //Don't spam these messages! + 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!") + speak(message) + last_newpatient_speak = world.time + return H + else + return + /obj/machinery/bot/medbot/process() if (!..()) return @@ -283,36 +300,14 @@ path = new() if(!patient) - if(mode == BOT_SUMMON) //A medibot will not search for new patients when summoned! - bot_summon() - return - if(!shut_up && 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!") speak(message) + var/scan_range = (stationary_mode ? 1 : DEFAULT_SCAN_RANGE) //If in stationary mode, scan range is limited to adjacent patients. + patient = scan(/mob/living/carbon/human, oldpatient, scan_range) + oldpatient = patient - var/scan_range = (stationary_mode ? 1 : 7) //If in stationary mode, scan range is limited to adjacent patients. - for (var/mob/living/carbon/C in view(scan_range,src)) //Time to find a patient! - if ((C.stat == 2) || !istype(C, /mob/living/carbon/human)) - continue - - if ((C == oldpatient) && (world.time < last_found + 100)) - continue - - if(assess_patient(C)) - patient = C - oldpatient = C - last_found = world.time - if((last_newpatient_speak + 300) < world.time) //Don't spam these messages! - var/message = pick("Hey, [C.name]! Hold on, I'm coming.","Wait [C.name]! I want to help!","[C.name], you appear to be injured!") - speak(message) - last_newpatient_speak = world.time - break - else - continue - - - if(patient && (get_dist(src,patient) <= 1)) + if(patient && (get_dist(src,patient) <= 1)) //Patient is next to us, begin treatment! if(mode != BOT_HEALING) mode = BOT_HEALING updateicon() @@ -320,6 +315,7 @@ medicate_patient(patient) return + //Patient has moved away from us! else if(patient && (path.len) && (get_dist(patient,path[path.len]) > 2)) path = new() mode = BOT_IDLE @@ -328,22 +324,14 @@ if(!stationary_mode && patient && path.len == 0 && (get_dist(src,patient) > 1)) spawn(0) path = AStar(loc, get_turf(patient), /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 30,id=botcard) - if(!path) - path = list() - if(path.len == 0) - oldpatient = patient - patient = null - mode = BOT_IDLE - last_found = world.time - return if(path.len > 0 && patient) - step_to(src, path[1]) - path -= path[1] - spawn(3) - if(path.len) - step_to(src, path[1]) - path -= path[1] + if(!bot_move(patient)) + oldpatient = patient + patient = null + mode = BOT_IDLE + last_found = world.time + return if(path.len > 8 && patient) frustration++ From 8f4e6fcea1de19044f658460f96b0c2bb806915b Mon Sep 17 00:00:00 2001 From: GunHog Date: Tue, 21 Oct 2014 18:38:29 -0500 Subject: [PATCH 5/7] Merge conflict resolution - resolution Mini-fixes to make secbots compile and functional once again. A full conversion will follow soon, as well as addressing other issues. --- code/game/machinery/bots/ed209bot.dm | 8 +++++--- code/game/machinery/bots/secbot.dm | 10 ++-------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm index bb1c4d7c47c..030c741e226 100644 --- a/code/game/machinery/bots/ed209bot.dm +++ b/code/game/machinery/bots/ed209bot.dm @@ -99,7 +99,7 @@ anchored = 0 walk_to(src,0) last_found = world.time - projectile = null + set_weapon() /obj/machinery/bot/ed209/set_custom_texts() text_hack = "You disable [name]'s combat inhibitor." @@ -128,7 +128,8 @@ Arrest for Unauthorized Weapons: []
    Arrest for Warrant: []

    Operating Mode: []
    -Report Arrests[]"}, +Report Arrests[]
    +Auto Patrol[]"}, "[idcheck ? "Yes" : "No"]", "[weaponscheck ? "Yes" : "No"]", @@ -208,7 +209,8 @@ Report Arrests[]"}, src.set_weapon() /obj/machinery/bot/ed209/process() - ..() + if (!..()) + return if(disabled) return diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index 1a351a464a6..41173fb6bf5 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -115,7 +115,6 @@ Maintenance panel panel is [open ? "opened" : "closed"]"}, Arrest Unidentifiable Persons: []
    Arrest for Unauthorized Weapons: []
    Arrest for Warrant: []
    -
    Operating Mode: []
    Report Arrests[]
    Auto Patrol: []"}, @@ -281,11 +280,6 @@ Auto Patrol: []"}, return if(BOT_ARREST) - - playsound(loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0) - // var/arrest_message = pick("Have a secure day!","I AM THE LAW.", "God made tomorrow for the crooks we don't catch today.","You can't outrun a radio.") - // speak(arrest_message) - else if (!target) src.anchored = 0 mode = BOT_IDLE @@ -317,7 +311,7 @@ Auto Patrol: []"}, /obj/machinery/bot/secbot/proc/back_to_idle() src.anchored = 0 - mode = SECBOT_IDLE + mode = BOT_IDLE src.target = null src.last_found = world.time frustration = 0 @@ -327,7 +321,7 @@ Auto Patrol: []"}, /obj/machinery/bot/secbot/proc/back_to_hunt() src.anchored = 0 src.frustration = 0 - mode = SECBOT_HUNT + mode = BOT_HUNT spawn(0) process() //ensure bot quickly responds // look for a criminal in view of the bot From 921143c0784dcc8616a91a9063d235088c537715 Mon Sep 17 00:00:00 2001 From: GunHog Date: Wed, 22 Oct 2014 10:26:16 -0500 Subject: [PATCH 6/7] ED-209 balance changes and bot tweaks - ED-209's effective range has been reduced to 7, down from 12, to prevent them attacking people off-screen. - Ruthlessly nukes ".src" from the bot files, especially ed209bot.dm and secbot.dm! - Summoned bots now report on their department radio when summoned by PDA. - Bot radio frequencies now use the helpers instead! - Officer Pingsky (On the MiniSat) is now set to declare his arrests on the AI Private frequency, instead of Security. - The remote_disabled var now prevents bots from being summoned or controlled via PDA as well removing them from the AI's control panel. - The AI can no longer re-activate a bot's remote control radio by hacking it from the interface. - The telescopic baton is now considered a safe weapon to carry and wield by security bots. - Added a small reminder to PDA users that bots can download access from their ID to reach them. --- code/game/machinery/bots/bots.dm | 61 ++++---- code/game/machinery/bots/cleanbot.dm | 4 +- code/game/machinery/bots/ed209bot.dm | 149 ++++++++++---------- code/game/machinery/bots/floorbot.dm | 7 +- code/game/machinery/bots/medbot.dm | 12 +- code/game/machinery/bots/mulebot.dm | 4 +- code/game/machinery/bots/secbot.dm | 84 +++++------ code/game/objects/items/devices/PDA/cart.dm | 3 +- 8 files changed, 157 insertions(+), 167 deletions(-) diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm index 5966f51550f..49c8f205ef4 100644 --- a/code/game/machinery/bots/bots.dm +++ b/code/game/machinery/bots/bots.dm @@ -30,7 +30,7 @@ var/remote_disabled = 0 //If enabled, the AI cannot *Remotely* control a bot. It can still control it through cameras. var/mob/living/silicon/ai/calling_ai //Links a bot to the AI calling it. var/obj/item/device/radio/Radio //The bot's radio, for speaking to people. - var/radio_frequency = 1459 //The bot's default radio speaking freqency. Recommended to be on a department frequency. + var/radio_frequency //The bot's default radio speaking freqency. Recommended to be on a department frequency. //var/emagged = 0 //Urist: Moving that var to the general /bot tree as it's used by most bots var/auto_patrol = 0// set to make bot automatically patrol var/turf/patrol_target // this is turf to navigate to (location of beacon) @@ -113,8 +113,8 @@ qdel(src) /obj/machinery/bot/proc/healthcheck() - if (src.health <= 0) - src.explode() + if (health <= 0) + explode() /obj/machinery/bot/proc/Emag(mob/user as mob) //Master Emag proc. Ensure this is called in your bot before setting unique functions. if(locked) //First emag application unlocks the bot's interface. Apply a screwdriver to use the emag again. @@ -132,8 +132,8 @@ /obj/machinery/bot/examine(mob/user) ..() - if (src.health < maxhealth) - if (src.health > maxhealth/3) + if (health < maxhealth) + if (health > maxhealth/3) user << "[src]'s parts look loose." else user << "[src]'s parts look very loose." @@ -142,11 +142,11 @@ /obj/machinery/bot/attack_alien(var/mob/living/carbon/alien/user as mob) user.changeNext_move(CLICK_CD_MELEE) - src.health -= rand(15,30)*brute_dam_coeff - src.visible_message("[user] has slashed [src]!") - playsound(src.loc, 'sound/weapons/slice.ogg', 25, 1, -1) + health -= rand(15,30)*brute_dam_coeff + visible_message("[user] has slashed [src]!") + playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1) if(prob(10)) - new /obj/effect/decal/cleanable/oil(src.loc) + new /obj/effect/decal/cleanable/oil(loc) healthcheck() @@ -154,11 +154,11 @@ if(M.melee_damage_upper == 0) return M.changeNext_move(CLICK_CD_MELEE) - src.health -= M.melee_damage_upper - src.visible_message("[M] has [M.attacktext] [src]!") + health -= M.melee_damage_upper + visible_message("[M] has [M.attacktext] [src]!") add_logs(M, src, "attacked", admin=0) if(prob(10)) - new /obj/effect/decal/cleanable/oil(src.loc) + new /obj/effect/decal/cleanable/oil(loc) healthcheck() /obj/machinery/bot/Topic(href, href_list) //Master Topic to handle common functions. @@ -188,7 +188,6 @@ if(emagged != 2) emagged = 2 hacked = 1 - remote_disabled = 0 locked = 1 usr << "[text_hack]" bot_reset() @@ -232,7 +231,7 @@ if(istype(W, /obj/item/weapon/screwdriver)) if(!locked) open = !open - user << "Maintenance panel is now [src.open ? "opened" : "closed"]." + user << "Maintenance panel is now [open ? "opened" : "closed"]." else user << "Maintenance panel is locked." else if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") @@ -256,10 +255,10 @@ s.set_up(5, 1, src) switch(W.damtype) if("fire") - src.health -= W.force * fire_dam_coeff + health -= W.force * fire_dam_coeff s.start() if("brute") - src.health -= W.force * brute_dam_coeff + health -= W.force * brute_dam_coeff s.start() ..() healthcheck() @@ -277,24 +276,24 @@ return /obj/machinery/bot/blob_act() - src.health -= rand(20,40)*fire_dam_coeff + health -= rand(20,40)*fire_dam_coeff healthcheck() return /obj/machinery/bot/ex_act(severity) switch(severity) if(1.0) - src.explode() + explode() return if(2.0) - src.health -= rand(5,10)*fire_dam_coeff - src.health -= rand(10,20)*brute_dam_coeff + health -= rand(5,10)*fire_dam_coeff + health -= rand(10,20)*brute_dam_coeff healthcheck() return if(3.0) if (prob(50)) - src.health -= rand(1,5)*fire_dam_coeff - src.health -= rand(1,5)*brute_dam_coeff + health -= rand(1,5)*fire_dam_coeff + health -= rand(1,5)*brute_dam_coeff healthcheck() return return @@ -302,7 +301,7 @@ /obj/machinery/bot/emp_act(severity) var/was_on = on stat |= EMPED - var/obj/effect/overlay/pulse2 = new/obj/effect/overlay ( src.loc ) + var/obj/effect/overlay/pulse2 = new/obj/effect/overlay ( loc ) pulse2.icon = 'icons/effects/effects.dmi' pulse2.icon_state = "empdisable" pulse2.name = "emp sparks" @@ -323,8 +322,8 @@ if(issilicon(user)) //Allows silicons to toggle the emag status of a bot. hack += "[emagged == 2 ? "Software compromised! Unit may exhibit dangerous or erratic behavior." : "Unit operating normally. Release safety lock?"]
    " hack += "Harm Prevention Safety System: [emagged ? "DANGER" : "Engaged"]
    " - else if(!locked) //Humans with access can use this option to hide a bot from the AI's remote control panel. - hack += "AI remote control network port: [remote_disabled ? "Closed" : "Open"]

    " + else if(!locked) //Humans with access can use this option to hide a bot from the AI's remote control panel and PDA control. + hack += "Remote network control radio: [remote_disabled ? "Disconnected" : "Connected"]

    " return hack /obj/machinery/bot/proc/set_custom_texts() //Superclass for setting hack texts. Appears only if a set is not given to a bot locally. @@ -333,7 +332,7 @@ text_dehack_fail = "You fail to reset [name]." /obj/machinery/bot/attack_ai(mob/user as mob) - src.attack_hand(user) + attack_hand(user) /obj/machinery/bot/proc/speak(var/message, freq) //Pass a message to have the bot say() it. Pass a frequency to say it on the radio. if((!on) || (!message)) @@ -627,7 +626,7 @@ obj/machinery/bot/proc/start_patrol() // check to see if we are the commanded bot if(signal.data["active"] == src) - if(emagged == 2) //Emagged bots do not respect anyone's authority! + if(emagged == 2 || remote_disabled) //Emagged bots do not respect anyone's authority! Bots with their remote controls off cannot get commands. return // process control input switch(recv) @@ -648,7 +647,7 @@ obj/machinery/bot/proc/start_patrol() botcard.access = user_access + prev_access //Adds the user's access, if any. mode = BOT_SUMMON calc_summon_path() - speak("Responding.") + speak("Responding.", radio_frequency) return // receive response from beacon @@ -708,6 +707,8 @@ obj/machinery/bot/proc/start_patrol() // signals bot status etc. to controller /obj/machinery/bot/proc/send_status() + if(remote_disabled || emagged == 2) + return var/list/kv = list( "type" = bot_type, "name" = name, @@ -733,7 +734,8 @@ obj/machinery/bot/proc/bot_summon() /obj/machinery/bot/proc/calc_summon_path(var/turf/avoid) check_bot_access() path = AStar(loc, summon_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance_cardinal, 0, 150, id=botcard, exclude=avoid) - if(!path || tries >= 5) + if(!path || tries >= 5) //Cannot reach target. Give up and announce the issue. + speak("Summon command failed, destination unreachable.",radio_frequency) bot_reset() /obj/machinery/bot/proc/summon_step() @@ -751,7 +753,6 @@ obj/machinery/bot/proc/bot_summon() var/moved = bot_move(summon_target, 3) // Move attempt if(moved) blockcount = 0 - path -= loc else // failed to move blockcount++ diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm index ff461ed4820..abbbd289b62 100644 --- a/code/game/machinery/bots/cleanbot.dm +++ b/code/game/machinery/bots/cleanbot.dm @@ -38,6 +38,7 @@ var/failed_steps var/next_dest var/next_dest_loc + radio_frequency = SERV_FREQ //Service bot_type = CLEAN_BOT bot_filter = RADIO_CLEANBOT @@ -132,7 +133,8 @@ text("[on ? "On" : "Off"]")) /obj/machinery/bot/cleanbot/Emag(mob/user as mob) ..() if(emagged == 2) - if(user) user << "[src] buzzes and beeps." + if(user) + user << "[src] buzzes and beeps." /obj/machinery/bot/cleanbot/process_scan(var/obj/effect/decal/cleanable/D) for(var/T in target_types) diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm index 030c741e226..d27ae0a9a9d 100644 --- a/code/game/machinery/bots/ed209bot.dm +++ b/code/game/machinery/bots/ed209bot.dm @@ -33,7 +33,7 @@ var/arrest_type = 0 //If true, don't handcuff var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type var/shoot_sound = 'sound/weapons/Taser.ogg' - radio_frequency = 1359 + radio_frequency = SEC_FREQ bot_type = SEC_BOT bot_filter = RADIO_SECBOT / @@ -41,7 +41,8 @@ var/safe_weapons = list(\ /obj/item/weapon/gun/energy/laser/bluetag,\ /obj/item/weapon/gun/energy/laser/redtag,\ - /obj/item/weapon/gun/energy/laser/practice) + /obj/item/weapon/gun/energy/laser/practice,\ + /obj/item/weapon/melee/telebaton) /obj/item/weapon/ed209_assembly @@ -62,7 +63,7 @@ if(created_lasercolor) lasercolor = created_lasercolor icon_state = "[lasercolor]ed209[on]" - src.set_weapon() //giving it the right projectile and firing sound. + set_weapon() //giving it the right projectile and firing sound. spawn(3) var/datum/job/detective/J = new/datum/job/detective botcard.access = J.get_access() @@ -115,7 +116,7 @@ dat += text({" Security Unit v2.6 controls

    Status: []
    -Behaviour controls are [src.locked ? "locked" : "unlocked"]
    +Behaviour controls are [locked ? "locked" : "unlocked"]
    Maintenance panel panel is [open ? "opened" : "closed"]
    "}, "[on ? "On" : "Off"]" ) @@ -183,7 +184,7 @@ Auto Patrol[]"}, ..() if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") // Any intent but harm will heal, so we shouldn't get angry. return - if (!istype(W, /obj/item/weapon/screwdriver) && (!src.target)) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. + if (!istype(W, /obj/item/weapon/screwdriver) && (!target)) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. if(W.force && W.damtype != STAMINA)//If force is non-zero and damage type isn't stamina. threatlevel = user.assess_threat(src) threatlevel += 6 @@ -194,19 +195,15 @@ Auto Patrol[]"}, mode = BOT_HUNT /obj/machinery/bot/ed209/Emag(mob/user as mob) - if (!..()) - return - if(open && !locked) - if(user) user << "You short out [src]'s target assessment circuits." - spawn(0) - audible_message("[src] buzzes oddly!") - src.target = null - if(user) src.oldtarget_name = user.name - src.last_found = world.time - src.anchored = 0 - src.declare_arrests = 0 - src.icon_state = "[lasercolor]ed209[src.on]" - src.set_weapon() + ..() + if(emagged == 2) + if(user) + user << "You short out [src]'s target assessment circuits." + oldtarget_name = user.name + audible_message("[src] buzzes oddly!") + declare_arrests = 0 + icon_state = "[lasercolor]ed209[on]" + set_weapon() /obj/machinery/bot/ed209/process() if (!..()) @@ -216,7 +213,7 @@ Auto Patrol[]"}, return var/list/targets = list() - for (var/mob/living/carbon/C in view(9,src)) //Let's find us a target + for (var/mob/living/carbon/C in view(7,src)) //Let's find us a target var/threatlevel = 0 if ((C.stat) || (C.lying)) continue @@ -226,19 +223,19 @@ Auto Patrol[]"}, continue var/dst = get_dist(src, C) - if ( dst <= 1 || dst > 12) + if ( dst <= 1 || dst > 7) continue targets += C if (targets.len>0) var/mob/living/carbon/t = pick(targets) if ((t.stat!=2) && (t.lying != 1) && (!t.handcuffed)) //we don't shoot people who are dead, cuffed or lying down. - src.shootAt(t) + shootAt(t) switch(mode) if(BOT_IDLE) // idle walk_to(src,0) - if(!src.lasercolor) //lasertag bots don't want to arrest anyone + if(!lasercolor) //lasertag bots don't want to arrest anyone look_for_perp() // see if any criminals are in range if(!mode && auto_patrol) // still idle, and set to patrol mode = BOT_START_PATROL // switch to patrol mode @@ -250,9 +247,9 @@ Auto Patrol[]"}, back_to_idle() if(target) // make sure target exists - if(src.Adjacent(target) && isturf(src.target.loc)) // if right next to perp - playsound(src.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) - src.icon_state = "[lasercolor]ed209-c" + if(Adjacent(target) && isturf(target.loc)) // if right next to perp + playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) + icon_state = "[lasercolor]ed209-c" spawn(2) icon_state = "[lasercolor]ed209[on]" var/mob/living/carbon/M = target @@ -269,8 +266,8 @@ Auto Patrol[]"}, if(declare_arrests) var/area/location = get_area(src) speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].", radio_frequency) - target.visible_message("[src.target] has been stunned by [src]!",\ - "[src.target] has been stunned by [src]!") + target.visible_message("[target] has been stunned by [src]!",\ + "[target] has been stunned by [src]!") mode = BOT_PREP_ARREST anchored = 1 @@ -290,23 +287,23 @@ Auto Patrol[]"}, if(BOT_PREP_ARREST) // preparing to arrest target // see if he got away. If he's no no longer adjacent or inside a closet or about to get up, we hunt again. - if( !src.Adjacent(target) || !isturf(src.target.loc) || src.target.weakened < 2 ) + if( !Adjacent(target) || !isturf(target.loc) || target.weakened < 2 ) back_to_hunt() return if(iscarbon(target) && target.canBeHandcuffed()) - if(!src.arrest_type) - if(!src.target.handcuffed) //he's not cuffed? Try to cuff him! + if(!arrest_type) + if(!target.handcuffed) //he's not cuffed? Try to cuff him! mode = BOT_ARREST - playsound(src.loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) - target.visible_message("[src] is trying to put zipties on [src.target]!",\ - "[src] is trying to put zipties on [src.target]!") + playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) + target.visible_message("[src] is trying to put zipties on [target]!",\ + "[src] is trying to put zipties on [target]!") spawn(60) - if( !src.Adjacent(target) || !isturf(src.target.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. + if( !Adjacent(target) || !isturf(target.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. return - if(!src.target.handcuffed) - src.target.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(src.target) + if(!target.handcuffed) + target.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(target) target.update_inv_handcuffed(0) //update the handcuffs overlay back_to_idle() else @@ -318,17 +315,17 @@ Auto Patrol[]"}, if(BOT_ARREST) if (!target) - src.anchored = 0 + anchored = 0 mode = BOT_IDLE - src.last_found = world.time + last_found = world.time frustration = 0 return - if(src.target.handcuffed) //no target or target cuffed? back to idle. + if(target.handcuffed) //no target or target cuffed? back to idle. back_to_idle() return - if( !src.Adjacent(target) || !isturf(src.target.loc) || (src.target.loc != src.target_lastloc && src.target.weakened < 2) ) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again. + if( !Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.weakened < 2) ) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again. back_to_hunt() return else @@ -347,17 +344,17 @@ Auto Patrol[]"}, return /obj/machinery/bot/ed209/proc/back_to_idle() - src.anchored = 0 + anchored = 0 mode = BOT_IDLE - src.target = null - src.last_found = world.time + target = null + last_found = world.time frustration = 0 spawn(0) process() //ensure bot quickly responds /obj/machinery/bot/ed209/proc/back_to_hunt() - src.anchored = 0 - src.frustration = 0 + anchored = 0 + frustration = 0 mode = BOT_HUNT spawn(0) process() //ensure bot quickly responds @@ -365,28 +362,28 @@ Auto Patrol[]"}, // look for a criminal in view of the bot /obj/machinery/bot/ed209/proc/look_for_perp() - if(src.disabled) + if(disabled) return - src.anchored = 0 - src.threatlevel = 0 - for (var/mob/living/carbon/C in view(12,src)) //Let's find us a criminal + anchored = 0 + threatlevel = 0 + for (var/mob/living/carbon/C in view(7,src)) //Let's find us a criminal if ((C.stat) || (C.handcuffed)) continue - if ((C.name == src.oldtarget_name) && (world.time < src.last_found + 100)) + if ((C.name == oldtarget_name) && (world.time < last_found + 100)) continue - src.threatlevel = C.assess_threat(src, lasercolor) + threatlevel = C.assess_threat(src, lasercolor) - if (!src.threatlevel) + if (!threatlevel) continue - else if (src.threatlevel >= 4) - src.target = C - src.oldtarget_name = C.name - src.speak("Level [src.threatlevel] infraction alert!") - playsound(src.loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/EDPlaceholder.ogg'), 50, 0) - src.visible_message("[src] points at [C.name]!") + else if (threatlevel >= 4) + target = C + oldtarget_name = C.name + speak("Level [threatlevel] infraction alert!") + playsound(loc, pick('sound/voice/ed209_20sec.ogg', 'sound/voice/EDPlaceholder.ogg'), 50, 0) + visible_message("[src] points at [C.name]!") mode = BOT_HUNT spawn(0) process() // ensure bot quickly responds to a perp @@ -401,14 +398,14 @@ Auto Patrol[]"}, return 0 /obj/machinery/bot/ed209/Bump(M as mob|obj) //Leave no door unopened! - if ((istype(M, /obj/machinery/door)) && (!isnull(src.botcard))) + if ((istype(M, /obj/machinery/door)) && (!isnull(botcard))) var/obj/machinery/door/D = M - if (!istype(D, /obj/machinery/door/firedoor) && D.check_access(src.botcard)) + if (!istype(D, /obj/machinery/door/firedoor) && D.check_access(botcard)) D.open() - src.frustration = 0 - else if ((istype(M, /mob/living/)) && (!src.anchored)) - src.loc = M:loc - src.frustration = 0 + frustration = 0 + else if ((istype(M, /mob/living/)) && (!anchored)) + loc = M:loc + frustration = 0 return /* terrible @@ -421,13 +418,13 @@ Auto Patrol[]"}, /obj/machinery/bot/ed209/explode() walk_to(src,0) - src.visible_message("[src] blows apart!") + visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) var/obj/item/weapon/ed209_assembly/Sa = new /obj/item/weapon/ed209_assembly(Tsec) Sa.build_step = 1 Sa.overlays += image('icons/obj/aibots.dmi', "hs_hole") - Sa.created_name = src.name + Sa.created_name = name new /obj/item/device/assembly/prox_sensor(Tsec) if(!lasercolor) @@ -462,12 +459,12 @@ Auto Patrol[]"}, s.set_up(3, 1, src) s.start() - new /obj/effect/decal/cleanable/oil(src.loc) + new /obj/effect/decal/cleanable/oil(loc) qdel(src) /obj/machinery/bot/ed209/proc/set_weapon() //used to update the projectile type and firing sound shoot_sound = 'sound/weapons/laser.ogg' - if(src.emagged == 2) + if(emagged == 2) if(lasercolor) projectile = /obj/item/projectile/lasertag else @@ -502,7 +499,7 @@ Auto Patrol[]"}, if (!( istype(U, /turf) )) return var/obj/item/projectile/A = new projectile (loc) - playsound(src.loc, shoot_sound, 50, 1) + playsound(loc, shoot_sound, 50, 1) A.current = U A.yo = U.y - T.y A.xo = U.x - T.x @@ -514,8 +511,8 @@ Auto Patrol[]"}, /obj/machinery/bot/ed209/attack_alien(var/mob/living/carbon/alien/user as mob) ..() if (!isalien(target)) - src.target = user - src.mode = BOT_HUNT + target = user + mode = BOT_HUNT /obj/machinery/bot/ed209/emp_act(severity) @@ -523,7 +520,7 @@ Auto Patrol[]"}, if(severity==2 && prob(70)) ..(severity-1) else - var/obj/effect/overlay/pulse2 = new/obj/effect/overlay ( src.loc ) + var/obj/effect/overlay/pulse2 = new/obj/effect/overlay ( loc ) pulse2.icon = 'icons/effects/effects.dmi' pulse2.icon_state = "empdisable" pulse2.name = "emp sparks" @@ -553,8 +550,8 @@ Auto Patrol[]"}, if(targets.len) var/mob/toarrest = pick(targets) if (toarrest) - src.target = toarrest - src.mode = BOT_HUNT + target = toarrest + mode = BOT_HUNT @@ -562,9 +559,9 @@ Auto Patrol[]"}, ..() if(istype(W, /obj/item/weapon/pen)) - var/t = copytext(stripped_input(user, "Enter new robot name", src.name, src.created_name),1,MAX_NAME_LEN) + var/t = copytext(stripped_input(user, "Enter new robot name", name, created_name),1,MAX_NAME_LEN) if(!t) return - if(!in_range(src, usr) && src.loc != usr) return + if(!in_range(src, usr) && loc != usr) return created_name = t return diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index 1febaf5acb3..37f35e71252 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -49,7 +49,7 @@ var/oldloc = null req_one_access = list(access_construction, access_robotics) var/targetdirection - radio_frequency = 1357 //Engineering channel + radio_frequency = ENG_FREQ //Engineering channel bot_type = FLOOR_BOT bot_filter = RADIO_FLOORBOT var/process_type //Determines what to do when process_scan() recieves a target. See process_scan() for details. @@ -163,7 +163,8 @@ /obj/machinery/bot/floorbot/Emag(mob/user as mob) ..() if(emagged == 2) - if(user) user << "[src] buzzes and beeps." + if(user) + user << "[src] buzzes and beeps." /obj/machinery/bot/floorbot/Topic(href, href_list) ..() @@ -508,7 +509,7 @@ obj/machinery/bot/floorbot/process_scan(var/scan_target) qdel(src) else if (istype(W, /obj/item/weapon/pen)) - var/t = copytext(stripped_input(user, "Enter new robot name", src.name, src.created_name),1,MAX_NAME_LEN) + var/t = copytext(stripped_input(user, "Enter new robot name", name, created_name),1,MAX_NAME_LEN) if (!t) return if (!in_range(src, usr) && loc != usr) diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm index 22caac819bb..91accb8bcaf 100644 --- a/code/game/machinery/bots/medbot.dm +++ b/code/game/machinery/bots/medbot.dm @@ -30,7 +30,7 @@ var/declare_crit = 1 //If active, the bot will transmit a critical patient alert to MedHUD users. var/declare_cooldown = 0 //Prevents spam of critical patient alerts. var/stationary_mode = 0 //If enabled, the Medibot will not move automatically. - radio_frequency = 1355 //Medical frequency + radio_frequency = MED_FREQ //Medical frequency //Setting which reagents to use to treat what by default. By id. var/treatment_brute = "tricordrazine" var/treatment_oxy = "tricordrazine" @@ -387,7 +387,7 @@ return 0 if((D.stage > 1) || (D.spread_type == AIRBORNE)) // medibot can't detect a virus in its initial stage unless it spreads airborne. - if (!C.reagents.has_reagent(src.treatment_virus)) + if (!C.reagents.has_reagent(treatment_virus)) return 1 //STOP DISEASE FOREVER return 0 @@ -467,11 +467,11 @@ "[src] is trying to inject [patient]!") spawn(30) - if ((get_dist(src, src.patient) <= 1) && (src.on)) + if ((get_dist(src, patient) <= 1) && (on)) if(reagent_id == "internal_beaker") - if(src.use_beaker && src.reagent_glass && src.reagent_glass.reagents.total_volume) - src.reagent_glass.reagents.trans_to(src.patient,src.injection_amount) //Inject from beaker instead. - src.reagent_glass.reagents.reaction(src.patient, 2) + if(use_beaker && reagent_glass && reagent_glass.reagents.total_volume) + reagent_glass.reagents.trans_to(patient,injection_amount) //Inject from beaker instead. + reagent_glass.reagents.reaction(patient, 2) else patient.reagents.add_reagent(reagent_id,injection_amount) C.visible_message("[src] injects [patient] with the syringe!", \ diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index 7fbb4cad654..e796be551c6 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -684,7 +684,7 @@ var/global/mulebot_count = 0 // called when bot reaches current target /obj/machinery/bot/mulebot/proc/at_target() if(!reached_target) - radio_frequency = 1347 //Supply channel + radio_frequency = SUPP_FREQ //Supply channel visible_message("[src] makes a chiming sound!", "You hear a chime.") playsound(loc, 'sound/machines/chime.ogg', 50, 0) reached_target = 1 @@ -695,7 +695,7 @@ var/global/mulebot_count = 0 calling_ai << "\icon[src] [src] wirelessly plays a chiming sound!" playsound(calling_ai, 'sound/machines/chime.ogg',40, 0) calling_ai = null - radio_frequency = 1447 //Report on AI Private instead if the AI is controlling us. + radio_frequency = AIPRIV_FREQ //Report on AI Private instead if the AI is controlling us. if(load) // if loaded, unload at target speak("Destination [destination] reached. Unloading [load].",radio_frequency) diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index 41173fb6bf5..ad9518802b3 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -18,12 +18,12 @@ var/target_lastloc //Loc of target when arrested. var/last_found //There's a delay // var/emagged = 0 //Emagged Secbots view everyone as a criminal - var/declare_arrests = 1 //When making an arrest, should it notify everyone wearing sechuds? + var/declare_arrests = 1 //When making an arrest, should it notify everyone on the security channel? var/idcheck = 0 //If true, arrest people with no IDs var/weaponscheck = 0 //If true, arrest people for weapons if they lack access var/check_records = 1 //Does it check security records? var/arrest_type = 0 //If true, don't handcuff - radio_frequency = 1359 //Security channel + radio_frequency = SEC_FREQ //Security channel bot_type = SEC_BOT bot_filter = RADIO_SECBOT @@ -31,7 +31,8 @@ var/safe_weapons = list(\ /obj/item/weapon/gun/energy/laser/bluetag,\ /obj/item/weapon/gun/energy/laser/redtag,\ - /obj/item/weapon/gun/energy/laser/practice) + /obj/item/weapon/gun/energy/laser/practice,\ + /obj/item/weapon/melee/telebaton) /obj/machinery/bot/secbot/beepsky @@ -44,7 +45,7 @@ /obj/machinery/bot/secbot/pingsky name = "Officer Pingsky" desc = "It's Officer Pingsky! Delegated to satellite guard duty for harbouring anti-human sentiment." - declare_arrests = 0 + radio_frequency = AIPRIV_FREQ /obj/item/weapon/secbot_assembly name = "incomplete securitron assembly" @@ -169,7 +170,7 @@ Auto Patrol: []"}, ..() if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") // Any intent but harm will heal, so we shouldn't get angry. return - if(!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!src.target) && (W.damtype != STAMINA) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. + if(!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!target) && (W.damtype != STAMINA) ) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. threatlevel = user.assess_threat(src) threatlevel += 6 if(threatlevel >= 4) @@ -177,19 +178,15 @@ Auto Patrol: []"}, mode = BOT_HUNT /obj/machinery/bot/secbot/Emag(mob/user as mob) - if (..()) - return + ..() if(emagged == 2) - if(user) user << " You short out [src]'s target assessment circuits." - spawn(0) - audible_message("[src] buzzes oddly!") - src.target = null - if(user) src.oldtarget_name = user.name - src.last_found = world.time - src.anchored = 0 - src.declare_arrests = 0 - src.icon_state = "secbot[src.on]" + if(user) + user << " You short out [src]'s target assessment circuits." + oldtarget_name = user.name + audible_message("[src] buzzes oddly!") + declare_arrests = 0 + icon_state = "secbot[on]" /obj/machinery/bot/secbot/process() if (!..()) @@ -213,9 +210,9 @@ Auto Patrol: []"}, return if(target) // make sure target exists - if(src.Adjacent(target) && isturf(src.target.loc)) // if right next to perp - playsound(src.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) - src.icon_state = "secbot-c" + if(Adjacent(target) && isturf(target.loc)) // if right next to perp + playsound(loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) + icon_state = "secbot-c" spawn(2) icon_state = "secbot[on]" var/mob/living/carbon/M = target @@ -232,8 +229,8 @@ Auto Patrol: []"}, if(declare_arrests) var/area/location = get_area(src) speak("[arrest_type ? "Detaining" : "Arresting"] level [threatlevel] scumbag [target] in [location].", radio_frequency) - target.visible_message("[src.target] has been stunned by [src]!",\ - "[src.target] has been stunned by [src]!") + target.visible_message("[target] has been stunned by [src]!",\ + "[target] has been stunned by [src]!") mode = BOT_PREP_ARREST anchored = 1 @@ -253,24 +250,24 @@ Auto Patrol: []"}, if(BOT_PREP_ARREST) // preparing to arrest target // see if he got away. If he's no no longer adjacent or inside a closet or about to get up, we hunt again. - if( !src.Adjacent(target) || !isturf(src.target.loc) || src.target.weakened < 2 ) + if( !Adjacent(target) || !isturf(target.loc) || target.weakened < 2 ) back_to_hunt() return if(iscarbon(target) && target.canBeHandcuffed()) - if(!src.arrest_type) - if(!src.target.handcuffed) //he's not cuffed? Try to cuff him! + if(!arrest_type) + if(!target.handcuffed) //he's not cuffed? Try to cuff him! mode = BOT_ARREST - playsound(src.loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) - target.visible_message("[src] is trying to put zipties on [src.target]!",\ - "[src] is trying to put zipties on [src.target]!") + playsound(loc, 'sound/weapons/cablecuff.ogg', 30, 1, -2) + target.visible_message("[src] is trying to put zipties on [target]!",\ + "[src] is trying to put zipties on [target]!") spawn(60) - if( !src.Adjacent(target) || !isturf(src.target.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. + if( !Adjacent(target) || !isturf(target.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. return - if(!src.target.handcuffed) + if(!target.handcuffed) target.handcuffed = new /obj/item/weapon/restraints/handcuffs/cable/zipties/used(target) target.update_inv_handcuffed(0) //update the handcuffs overlay - playsound(src.loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0) + playsound(loc, pick('sound/voice/bgod.ogg', 'sound/voice/biamthelaw.ogg', 'sound/voice/bsecureday.ogg', 'sound/voice/bradio.ogg', 'sound/voice/binsult.ogg', 'sound/voice/bcreep.ogg'), 50, 0) back_to_idle() else back_to_idle() @@ -281,17 +278,17 @@ Auto Patrol: []"}, if(BOT_ARREST) if (!target) - src.anchored = 0 + anchored = 0 mode = BOT_IDLE - src.last_found = world.time + last_found = world.time frustration = 0 return - if(src.target.handcuffed) //no target or target cuffed? back to idle. + if(target.handcuffed) //no target or target cuffed? back to idle. back_to_idle() return - if( !src.Adjacent(target) || !isturf(src.target.loc) || (src.target.loc != src.target_lastloc && src.target.weakened < 2) ) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again. + if( !Adjacent(target) || !isturf(target.loc) || (target.loc != target_lastloc && target.weakened < 2) ) //if he's changed loc and about to get up or not adjacent or got into a closet, we prep arrest again. back_to_hunt() return else //Try arresting again if the target escapes. @@ -310,17 +307,17 @@ Auto Patrol: []"}, return /obj/machinery/bot/secbot/proc/back_to_idle() - src.anchored = 0 + anchored = 0 mode = BOT_IDLE - src.target = null - src.last_found = world.time + target = null + last_found = world.time frustration = 0 spawn(0) process() //ensure bot quickly responds /obj/machinery/bot/secbot/proc/back_to_hunt() - src.anchored = 0 - src.frustration = 0 + anchored = 0 + frustration = 0 mode = BOT_HUNT spawn(0) process() //ensure bot quickly responds @@ -358,15 +355,6 @@ Auto Patrol: []"}, return 1 return 0 - -/* terrible -/obj/machinery/bot/secbot/Bumped(atom/movable/M as mob|obj) - spawn(0) - if(M) - var/turf/T = get_turf(src) - M:loc = T -*/ - /obj/machinery/bot/secbot/explode() walk_to(src,0) diff --git a/code/game/objects/items/devices/PDA/cart.dm b/code/game/objects/items/devices/PDA/cart.dm index 3c1934f55a2..e42c3cf453a 100644 --- a/code/game/objects/items/devices/PDA/cart.dm +++ b/code/game/objects/items/devices/PDA/cart.dm @@ -333,7 +333,8 @@ menu += "
    \[Stop Patrol\] " menu += "\[Start Patrol\] " menu += "\[Summon Bot\]
    " - menu += "
    Return to bot list" + menu += "
    Return to bot list
    " + menu += "Keep an ID inserted to upload access codes upon summoning." return menu From 7479e4074683466dac130880e6158d5194dc1409 Mon Sep 17 00:00:00 2001 From: GunHog Date: Fri, 24 Oct 2014 08:41:17 -0500 Subject: [PATCH 7/7] Add the Kinetic Accelerator to safe list - Fixes #5442 --- code/game/machinery/bots/ed209bot.dm | 3 ++- code/game/machinery/bots/secbot.dm | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm index d27ae0a9a9d..fd42c38f7b1 100644 --- a/code/game/machinery/bots/ed209bot.dm +++ b/code/game/machinery/bots/ed209bot.dm @@ -42,7 +42,8 @@ /obj/item/weapon/gun/energy/laser/bluetag,\ /obj/item/weapon/gun/energy/laser/redtag,\ /obj/item/weapon/gun/energy/laser/practice,\ - /obj/item/weapon/melee/telebaton) + /obj/item/weapon/melee/telebaton,\ + /obj/item/weapon/gun/energy/kinetic_accelerator) /obj/item/weapon/ed209_assembly diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index ad9518802b3..25c76ba6872 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -32,7 +32,8 @@ /obj/item/weapon/gun/energy/laser/bluetag,\ /obj/item/weapon/gun/energy/laser/redtag,\ /obj/item/weapon/gun/energy/laser/practice,\ - /obj/item/weapon/melee/telebaton) + /obj/item/weapon/melee/telebaton,\ + /obj/item/weapon/gun/energy/kinetic_accelerator) /obj/machinery/bot/secbot/beepsky

    Name

    Status

    Location

    Control

    [Bot.hacked ? "(!) [Bot.name]" : Bot.name][Bot.on ? "[Bot.mode ? "[ Bot.mode_name[Bot.mode] ]": "Idle"]" : "Inactive"][bot_area.name]InterfaceCall