diff --git a/code/WorkInProgress/Mini/atmos_control.dm b/code/WorkInProgress/Mini/atmos_control.dm index c56397dfc5d..c8a18d2dd6c 100644 --- a/code/WorkInProgress/Mini/atmos_control.dm +++ b/code/WorkInProgress/Mini/atmos_control.dm @@ -66,6 +66,8 @@ continue if(!alarm.remote_control) continue + if(alarm.hidden) + continue var/turf/pos = get_turf(alarm) var/list/alarm_data=list() alarm_data["ID"]="\ref[alarm]" diff --git a/code/controllers/master_controller.dm b/code/controllers/master_controller.dm index 4e62555e369..9d9f3a80b85 100644 --- a/code/controllers/master_controller.dm +++ b/code/controllers/master_controller.dm @@ -21,6 +21,7 @@ datum/controller/game_controller var/mobs_cost = 0 var/diseases_cost = 0 var/machines_cost = 0 + var/aibots_cost = 0 var/objects_cost = 0 var/networks_cost = 0 var/powernets_cost = 0 @@ -188,6 +189,13 @@ datum/controller/game_controller/proc/process() processMachines() machines_cost = (world.timeofday - timer) / 10 + sleep(breather_ticks) + + //BOTS + timer = world.timeofday + process_bots() + aibots_cost = (world.timeofday - timer) / 10 + sleep(breather_ticks) //OBJECTS @@ -244,7 +252,7 @@ datum/controller/game_controller/proc/process() gc_cost = (world.timeofday - timer) / 10 //TIMING - total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + puddles_cost + ticker_cost + gc_cost + total_cost = air_cost + sun_cost + mobs_cost + diseases_cost + machines_cost + aibots_cost + objects_cost + networks_cost + powernets_cost + nano_cost + events_cost + puddles_cost + ticker_cost + gc_cost var/end_time = world.timeofday if(end_time < start_time) //why not just use world.time instead? @@ -278,6 +286,15 @@ datum/controller/game_controller/proc/process() Machinery.removeAtProcessing() +/datum/controller/game_controller/proc/process_bots() + for(var/obj/machinery/bot/Bot in aibots) + if(!Bot.gc_destroyed) + last_thing_processed = Bot.type + spawn(0) + Bot.bot_process() + continue + aibots -= Bot + /datum/controller/game_controller/proc/processObjects() for (var/obj/Object in processing_objects) if (Object && Object.loc) diff --git a/code/defines/procs/AStar.dm b/code/defines/procs/AStar.dm index acf759e4f9b..1c5a40b5e60 100644 --- a/code/defines/procs/AStar.dm +++ b/code/defines/procs/AStar.dm @@ -172,7 +172,7 @@ proc/AStar(start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minn //then run the main loop while(!open.IsEmpty() && !path) { - //get the lower f node on the open list + //get the lower f node on the open list cur = open.Dequeue() //get the lower f turf in the open list closed.Add(cur.source) //and tell we've processed it @@ -236,3 +236,10 @@ proc/AStar(start,end,adjacent,dist,maxnodes,maxnodedepth = 30,mintargetdist,minn path.Swap(i,path.len-i+1) return path + +//wrapper that returns an empty list if A* failed to find a path +/proc/get_path_to(var/start,var/end,var/adjacent,var/dist,var/maxnodes,var/maxnodedepth = 30,var/mintargetdist,var/minnodedist,id=null, var/turf/exclude=null) + var/list/path = AStar(start, end, adjacent, dist, maxnodes, maxnodedepth, mintargetdist, minnodedist,id, exclude) + if(!path) + path = list() + return path diff --git a/code/game/communications.dm b/code/game/communications.dm index c874f37adf1..374b212098c 100644 --- a/code/game/communications.dm +++ b/code/game/communications.dm @@ -61,6 +61,33 @@ */ +/* the radio controller is a confusing piece of shit and didnt work + so i made radios not use the radio controller. +*/ +var/list/all_radios = list() + +/proc/add_radio(var/obj/item/radio, freq) + if(!freq || !radio) + return + if(!all_radios["[freq]"]) + all_radios["[freq]"] = list(radio) + return freq + + all_radios["[freq]"] |= radio + return freq + +/proc/remove_radio(var/obj/item/radio, freq) + if(!freq || !radio) + return + if(!all_radios["[freq]"]) + return + + all_radios["[freq]"] -= radio + +/proc/remove_radio_all(var/obj/item/radio) + for(var/freq in all_radios) + all_radios["[freq]"] -= radio + /* Frequency range: 1200 to 1600 Radiochat range: 1441 to 1489 (most devices refuse to be tune to other frequency, even during mapmaking) @@ -108,7 +135,8 @@ var/list/radiochannels = list( "Deathsquad" = 1441, "Syndicate" = 1213, "Supply" = 1347, - "Service" = 1349 + "Service" = 1349, + "AI Private" = 1447 ) //depenging helpers var/list/DEPT_FREQS = list(1351, 1355, 1357, 1359, 1213, 1443, 1441, 1347, 1349) @@ -127,6 +155,9 @@ var/const/MED_FREQ = 1355 var/const/SUP_FREQ = 1347 var/const/SRV_FREQ = 1349 +// other channels +var/const/AIPRIV_FREQ = 1447 + #define TRANSMISSION_WIRE 0 #define TRANSMISSION_RADIO 1 @@ -140,6 +171,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 @@ -150,7 +184,7 @@ var/global/datum/controller/radio/radio_controller datum/controller/radio var/list/datum/radio_frequency/frequencies = list() - proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null) + proc/add_object(obj/device as obj, var/new_frequency as num, var/filter = null as text|null) var/f_text = num2text(new_frequency) var/datum/radio_frequency/frequency = frequencies[f_text] diff --git a/code/game/machinery/alarm.dm b/code/game/machinery/alarm.dm index 0cbb1ccd1e3..75b33bf83fe 100644 --- a/code/game/machinery/alarm.dm +++ b/code/game/machinery/alarm.dm @@ -82,7 +82,7 @@ var/datum/radio_frequency/radio_connection var/list/TLV = list() - + var/hidden = 0 /obj/machinery/alarm/server preset = AALARM_PRESET_SERVER @@ -626,6 +626,8 @@ // Not sent from atmos console AND // Not silicon AND locked. data["locked"]=!fromAtmosConsole && (!(istype(user, /mob/living/silicon)) && locked) + if(fromAtmosConsole && remote_control < 2) + data["locked"] = 1 data["rcon"]=rcon_setting data["target_temp"] = target_temperature - T0C data["atmos_alarm"] = alarm_area.atmosalm diff --git a/code/game/machinery/bots/bots.dm b/code/game/machinery/bots/bots.dm index b0016ddf1ce..41cf368ecef 100644 --- a/code/game/machinery/bots/bots.dm +++ b/code/game/machinery/bots/bots.dm @@ -1,11 +1,11 @@ // AI (i.e. game AI, not the AI player) controlled bots - /obj/machinery/bot icon = 'icons/obj/aibots.dmi' layer = MOB_LAYER 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,8 +14,74 @@ 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/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() //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. + 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 //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 + + #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 + #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() if(stat) return 0 @@ -26,109 +92,209 @@ /obj/machinery/bot/proc/turn_off() on = 0 SetLuminosity(0) + bot_reset() //Resets an AI's call, should it exist. + +/obj/machinery/bot/New() + ..() + aibots += src //Global bot list + 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. + spawn(20) + if(radio_controller) + radio_controller.add_object(src, beacon_freq, RADIO_NAVBEACONS) + if(bot_filter) + radio_controller.add_object(src, control_freq, bot_filter) /obj/machinery/bot/proc/explode() + aibots -= src 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) - if(locked && user) +/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) || !user) //Ion storm does not require unlocking for emagging + 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() +/obj/machinery/bot/examine(mob/user) ..() - if (src.health < maxhealth) - if (src.health > maxhealth/3) - usr << "[src]'s parts look loose." + if (health < maxhealth) + if (health > maxhealth/3) + user << "[src]'s parts look loose." else - usr << "[src]'s parts look very loose!" - return + user << "[src]'s parts look very loose." + else + user << "[src] is in pristine condition." /obj/machinery/bot/attack_alien(var/mob/living/carbon/alien/user as mob) - src.health -= rand(15,30)*brute_dam_coeff - src.visible_message("\red [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() /obj/machinery/bot/attack_animal(var/mob/living/simple_animal/M as mob) - if(M.melee_damage_upper == 0) return - src.health -= M.melee_damage_upper - src.visible_message("\red [M] has [M.attacktext] [src]!") - add_logs(M, src, "attacked") + if(M.melee_damage_upper == 0) + return + 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. + . = ..() + if (.) + return + if(topic_denied(usr)) + 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 + 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(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(user)) //Bot is hacked, so only silicons are allowed access. + return 1 + else + return 0 + +/obj/machinery/bot/proc/bot_process() //Master process which handles code common across most bots. + + set background = BACKGROUND_ENABLED + + if(!on) + 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. /obj/machinery/bot/attackby(obj/item/weapon/W as obj, mob/user as mob) if(istype(W, /obj/item/weapon/screwdriver)) if(!locked) open = !open - user << "Maintenance panel is now [src.open ? "opened" : "closed"]." - else if(istype(W, /obj/item/weapon/weldingtool)) - if(health < maxhealth) - if(open) - health = min(maxhealth, health+10) - user.visible_message("\red [user] repairs [src]!","\blue You repair [src]!") - else - user << "Unable to repair with the maintenance panel closed." + user << "Maintenance panel is now [open ? "opened" : "closed"]." else - user << "[src] does not need a repair." + user << "Maintenance panel is locked." else if (istype(W, /obj/item/weapon/card/emag) && emagged < 2) Emag(user) else - if(hasvar(W,"force") && hasvar(W,"damtype")) - switch(W.damtype) - if("fire") - src.health -= W.force * fire_dam_coeff - if("brute") - src.health -= W.force * brute_dam_coeff - ..() - healthcheck() + if(istype(W, /obj/item/weapon/weldingtool) && user.a_intent != "harm") + if(health >= maxhealth) + user << "[src] does not need a repair." + return + if(!open) + user << "Unable to repair with the maintenance panel closed." + return + var/obj/item/weapon/weldingtool/WT = W + if(WT.remove_fuel(0, user)) + health = min(maxhealth, health+10) + user.visible_message("[user] repairs [src]!","You repair [src]!") + else + user << "The welder must be on for this task." else - ..() + if(W.force) //if force is non-zero + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + switch(W.damtype) + if("fire") + health -= W.force * fire_dam_coeff + s.start() + if("brute") + health -= W.force * brute_dam_coeff + s.start() + ..() + healthcheck() + /obj/machinery/bot/bullet_act(var/obj/item/projectile/Proj) - health -= Proj.damage - ..() - healthcheck() - -/obj/machinery/bot/meteorhit() - src.explode() + if((Proj.damage_type == BRUTE || Proj.damage_type == BURN)) + health -= Proj.damage + if(prob(75) && Proj.damage > 0) + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(5, 1, src) + s.start() + ..() + healthcheck() 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 @@ -136,7 +302,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" @@ -152,15 +318,471 @@ if (was_on) turn_on() +/obj/machinery/bot/proc/hack(mob/user) + var/hack + 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 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. + 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.add_hiddenprint(user) - src.attack_hand(user) + attack_hand(user) -/obj/machinery/bot/proc/speak(var/message) - if((!src.on) || (!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((!on) || (!message)) return - for(var/mob/O in hearers(src, null)) - O.show_message("[src] beeps, \"[message]\"",2) + if(freq) + return + // Commenting out the radio code until our say code's compatible with /tg/'s + /* Radio.set_frequency(radio_frequency) + Radio.talk_into_atom(src, message, radio_frequency) */ + else + for(var/mob/O in hearers(src, null)) + O.show_message("[src] beeps, \"[message]\"",2) 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 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/old_target, var/scan_range) + var/final_result + 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 + +//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 && 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 before 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() + + 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(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]. [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 + path = list() + +/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. + var/success = bot_move(ai_waypoint, 3) + if (!success) + if(calling_ai) + calling_ai << "\icon[src] [get_turf(src) == ai_waypoint ? "[src] successfully arrived to waypoint." : "[src] failed to reach 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 + 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_target) // has patrol target + spawn(0) + calc_path() // Find a route to it + if(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(path.len > 0 && patrol_target) // valid path + var/turf/next = path[1] + if(next == loc) + path -= next + return + + + var/moved = bot_move(patrol_target)//step_towards(src, next) // attempt to move + if(moved) // successful move + blockcount = 0 + + 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 + tries = 0 + + return + + 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 || 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) + 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."/*, radio_frequency*/) + 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() + if(remote_disabled || emagged == 2) + return + 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() + return + +// calculates a path to the current destination +// given an optional turf to avoid +/obj/machinery/bot/proc/calc_path(var/turf/avoid) + check_bot_access() + 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) + 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) //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() + + if(loc == summon_target) // Arrived to summon location. + bot_reset() + return + + else if(path.len > 0 && summon_target) //Proper path acquired! + var/turf/next = path[1] + if(next == loc) + path -= next + return + + var/moved = bot_move(summon_target, 3) // Move attempt + if(moved) + blockcount = 0 + + 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 // 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 \ No newline at end of file diff --git a/code/game/machinery/bots/cleanbot.dm b/code/game/machinery/bots/cleanbot.dm index fe8a0a34a99..08f0e6e16d2 100644 --- a/code/game/machinery/bots/cleanbot.dm +++ b/code/game/machinery/bots/cleanbot.dm @@ -5,17 +5,16 @@ icon = 'icons/obj/aibots.dmi' icon_state = "bucket_proxy" force = 3.0 - throwforce = 10.0 + throwforce = 5.0 throw_speed = 2 throw_range = 5 w_class = 3.0 - flags = TABLEPASS var/created_name = "Cleanbot" //Cleanbot /obj/machinery/bot/cleanbot - name = "Cleanbot" + name = "\improper Cleanbot" desc = "A little cleaning robot, he looks so excited!" icon = 'icons/obj/aibots.dmi' icon_state = "cleanbot0" @@ -25,55 +24,57 @@ //weight = 1.0E7 health = 25 maxhealth = 25 - var/cleaning = 0 - var/screwloose = 0 - var/oddbutton = 0 var/blood = 1 var/list/target_types = list() var/obj/effect/decal/cleanable/target var/obj/effect/decal/cleanable/oldtarget + var/max_targets = 50 //Maximum number of targets a cleanbot can ignore. var/oldloc = null - req_access = list(access_janitor, access_robotics) - 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 + radio_frequency = SRV_FREQ //Service + 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() ..() - if(!isnull(src.target)) - target.targeted_by = null - 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() + ..() + ignore_list = 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) . = ..() @@ -84,65 +85,41 @@ /obj/machinery/bot/cleanbot/interact(mob/user as mob) 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 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) - dat += text({" -Odd looking screw twiddled: []
-Weird button pressed: []"}, -text("[src.screwloose ? "Yes" : "No"]"), -text("[src.oddbutton ? "Yes" : "No"]")) +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"]")) - 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) - 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", "Frequnecy", num2text(beacon_freq / 10))) * 10 + var/freq = text2num(input("Select frequency for navigation beacons", "Frequency", num2text(beacon_freq / 10))) * 10 if (freq > 0) - src.beacon_freq = freq - src.updateUsrDialog() - if("screw") - src.screwloose = !src.screwloose - usr << "You press the weird button." - src.updateUsrDialog() + beacon_freq = freq + 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" @@ -155,186 +132,113 @@ text("[src.oddbutton ? "Yes" : "No" /obj/machinery/bot/cleanbot/Emag(mob/user as mob) ..() - if((!locked && open) || !user) - if(user) user << "The [src] buzzes and beeps." - src.oddbutton = 1 - src.screwloose = 1 + if(emagged == 2) + if(user) + user << "[src] buzzes and beeps." -/obj/machinery/bot/cleanbot/process() - //set background = 1 +/obj/machinery/bot/cleanbot/process_scan(var/obj/effect/decal/cleanable/D) + for(var/T in target_types) + if(istype(D, T)) + return D - if(!src.on) - return - if(src.cleaning) +/obj/machinery/bot/cleanbot/bot_process() + if (!..()) return - if(!src.screwloose && !src.oddbutton && prob(5)) + if(mode == BOT_CLEANING) + return + + if(emagged == 2) //Emag functions + if(istype(loc,/turf/simulated)) + + 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(src.screwloose && prob(5)) - if(istype(loc,/turf/simulated)) - var/turf/simulated/T = src.loc - if(T.wet < 1) - T.wet = 1 - if(T.wet_overlay) - T.overlays -= T.wet_overlay - T.wet_overlay = null - T.wet_overlay = image('icons/effects/water.dmi',T,"wet_floor") - T.overlays += T.wet_overlay - spawn(800) - if (istype(T) && T.wet < 2) - T.wet = 0 - if(T.wet_overlay) - T.overlays -= T.wet_overlay - T.wet_overlay = null - if(src.oddbutton && prob(5)) - visible_message("Something flies out of [src]. He seems to be acting oddly.") - var/obj/effect/decal/cleanable/blood/gibs/gib = new /obj/effect/decal/cleanable/blood/gibs(src.loc) - //gib.streak(list(NORTH, SOUTH, EAST, WEST, NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST)) - src.oldtarget = gib - if(!src.target || src.target == null) - for (var/obj/effect/decal/cleanable/D in view(7,src)) - for(var/T in src.target_types) - if(isnull(D.targeted_by) && (D.type == T || D.parent_type == T) && D != src.oldtarget) // If the mess isn't targeted - src.oldtarget = D // or if it is but the bot is gone. - src.target = D // and it's stuff we clean? Clean it. - D.targeted_by = src // Claim the mess we are targeting. - return + if(!target) //Search for cleanables it can see. + target = scan(/obj/effect/decal/cleanable/, oldtarget) + oldtarget = target - if(!src.target || src.target == null) - if(src.loc != src.oldloc) - src.oldtarget = null + if(!target) + if(loc != oldloc) + oldtarget = null - if (!should_patrol) + if(auto_patrol) + if(mode == BOT_IDLE || mode == BOT_START_PATROL) + start_patrol() + + if(mode == BOT_PATROL) + bot_patrol() + + return + + if(target) + 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 + path = list() + return + mode = BOT_MOVING + else if (!bot_move(target)) + target = null + mode = BOT_IDLE return - if (!patrol_path || patrol_path.len < 1) - var/datum/radio_frequency/frequency = radio_controller.return_frequency(beacon_freq) + if(target && loc == target.loc) + clean(target) + path = list() + target = null - 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, 0, 120, id=botcard, exclude=null) - else - patrol_move() - - return - - if(!path) - path = new() - 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 - target.targeted_by = null - src.target = null - 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) - 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 - return - - src.oldloc = src.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"] + oldloc = loc /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 - target_types += /obj/effect/decal/cleanable/poop target_types += /obj/effect/decal/cleanable/robot_debris target_types += /obj/effect/decal/cleanable/crayon - target_types += /obj/effect/decal/cleanable/liquid_fuel - target_types += /obj/effect/decal/cleanable/mucus + target_types += /obj/effect/decal/cleanable/molten_item + target_types += /obj/effect/decal/cleanable/tomato_smudge + target_types += /obj/effect/decal/cleanable/egg_smudge + target_types += /obj/effect/decal/cleanable/pie_smudge + target_types += /obj/effect/decal/cleanable/flour + target_types += /obj/effect/decal/cleanable/ash + target_types += /obj/effect/decal/cleanable/greenglow + target_types += /obj/effect/decal/cleanable/dirt - - if(src.blood) - target_types += /obj/effect/decal/cleanable/xenoblood + if(blood) + target_types += /obj/effect/decal/cleanable/xenoblood/ target_types += /obj/effect/decal/cleanable/xenoblood/xgibs target_types += /obj/effect/decal/cleanable/blood/ - target_types += /obj/effect/decal/cleanable/blood/green - target_types += /obj/effect/decal/cleanable/blood/tracks target_types += /obj/effect/decal/cleanable/blood/gibs/ - target_types += /obj/effect/decal/cleanable/dirt /obj/machinery/bot/cleanbot/proc/clean(var/obj/effect/decal/cleanable/target) - src.anchored = 1 - src.icon_state = "cleanbot-c" - visible_message("\red [src] begins to clean up the [target]") - src.cleaning = 1 - var/cleantime = 50 - if(istype(target,/obj/effect/decal/cleanable/dirt)) // Clean Dirt much faster - cleantime = 10 - spawn(cleantime) - src.cleaning = 0 - del(target) - src.icon_state = "cleanbot[src.on]" - src.anchored = 0 - src.target = null + anchored = 1 + icon_state = "cleanbot-c" + visible_message("[src] begins to clean up [target]") + mode = BOT_CLEANING + spawn(50) + 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("\red [src] blows apart!", 1) + on = 0 + visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) new /obj/item/weapon/reagent_containers/glass/bucket(Tsec) @@ -347,25 +251,25 @@ text("[src.oddbutton ? "Yes" : "No" var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, src) s.start() - del(src) + qdel(src) return /obj/item/weapon/bucket_sensor/attackby(var/obj/item/W, mob/user as mob) ..() if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm)) user.drop_item() - del(W) - var/turf/T = get_turf(src.loc) + qdel(W) + 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.drop_from_inventory(src) - del(src) + user.before_take_item(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 = stripped_input(user, "Enter new robot name", name, created_name,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 4e864670857..15cb278b37c 100644 --- a/code/game/machinery/bots/ed209bot.dm +++ b/code/game/machinery/bots/ed209bot.dm @@ -1,5 +1,5 @@ /obj/machinery/bot/ed209 - name = "ED-209 Security Robot" + name = "\improper ED-209 Security Robot" desc = "A security robot. He looks less than thrilled." icon = 'icons/obj/aibots.dmi' icon_state = "ed2090" @@ -25,51 +25,29 @@ 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 + var/idcheck = 0 //If true, arrest people with no IDs var/weaponscheck = 1 //If true, arrest people for weapons if they don't have access 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 + var/projectile = /obj/item/projectile/energy/electrode //Holder for projectile type + var/shoot_sound = 'sound/weapons/Taser.ogg' + radio_frequency = SEC_FREQ + 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,\ - /obj/item/weapon/melee/telebaton) - - 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/melee/telebaton,\ + /obj/item/weapon/gun/energy/kinetic_accelerator) /obj/item/weapon/ed209_assembly - name = "ED-209 assembly" + name = "\improper ED-209 assembly" desc = "Some sort of bizarre assembly." icon = 'icons/obj/aibots.dmi' icon_state = "ed209_frame" @@ -81,136 +59,122 @@ /obj/machinery/bot/ed209/New(loc,created_name,created_lasercolor) ..() - if(created_name) name = created_name - if(created_lasercolor) lasercolor = created_lasercolor - src.icon_state = "[lasercolor]ed209[src.on]" + if(created_name) + name = created_name + if(created_lasercolor) + lasercolor = created_lasercolor + icon_state = "[lasercolor]ed209[on]" + set_weapon() //giving it the right projectile and firing sound. 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 arrest_type = 1//Don't even try to cuff - req_access = list(access_maint_tunnels) + req_access = list(access_maint_tunnels, access_theatre) arrest_type = 1 - if((lasercolor == "b") && (name == "ED-209 Security Robot"))//Picks a name if there isn't already a custome one + if((lasercolor == "b") && (name == "\improper ED-209 Security Robot"))//Picks a name if there isn't already a custome one name = pick("BLUE BALLER","SANIC","BLUE KILLDEATH MURDERBOT") - if((lasercolor == "r") && (name == "ED-209 Security Robot")) + if((lasercolor == "r") && (name == "\improper ED-209 Security Robot")) name = pick("RED RAMPAGE","RED ROVER","RED KILLDEATH MURDERBOT") /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 + set_weapon() + +/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) . = ..() if (.) return 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"]
"}, +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)) if(!lasercolor) dat += text({"
-Arrest for No ID: []
+Arrest Unidentifiable Persons: []
Arrest for Unauthorized Weapons: []
Arrest for Warrant: []

Operating Mode: []
Report Arrests[]
-Auto Patrol: []"}, +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.6 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) - 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"]." + updateUsrDialog() else if(emagged) user << "ERROR" @@ -220,467 +184,211 @@ Auto Patrol: []"}, user << "Access denied." else ..() - if (!istype(W, /obj/item/weapon/screwdriver) && (!src.target)) - if(hasvar(W,"force") && W.force)//If force is defined and non-zero - src.target = user - if(lasercolor)//To make up for the fact that lasertag bots don't hunt - src.shootAt(user) - src.mode = SECBOT_HUNT + 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) && (!target)) // Added check for welding tool to fix #2432. Welding tool behavior is handled in superclass. + if(W.force)//If force is non-zero and damage type isn't stamina. + threatlevel = user.assess_threat(src) + threatlevel += 6 + if(threatlevel >= 4) + target = user + if(lasercolor)//To make up for the fact that lasertag bots don't hunt + shootAt(user) + mode = BOT_HUNT /obj/machinery/bot/ed209/Emag(mob/user as mob) ..() - if((!locked && open) || !user) - if(user) user << "You short out [src]'s target assessment circuits." - spawn(0) - for(var/mob/O in hearers(src, null)) - O.show_message("\red [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(emagged == 2) + if(user) + user << "You short out [src]'s target assessment circuits." + oldtarget_name = user.name + visible_message("[src] buzzes oddly!") + declare_arrests = 0 + icon_state = "[lasercolor]ed209[on]" + set_weapon() -/obj/machinery/bot/ed209/process() - //set background = 1 - - if (!src.on) +/obj/machinery/bot/ed209/bot_process() + if (!..()) return + + if(disabled) + return + var/list/targets = list() - for (var/mob/living/carbon/C in view(12,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 - if (istype(C, /mob/living/carbon/human)) - threatlevel = C.assess_threat(src, lasercolor) - //src.speak(C.real_name + text(": threat: []", threatlevel)) + threatlevel = C.assess_threat(src, lasercolor) + //speak(C.real_name + text(": threat: []", threatlevel)) if (threatlevel < 4 ) 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/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) + 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. + 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(!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 = 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 - return + if(BOT_HUNT) // hunting for perp // if can't reach perp for long enough, go idle - if (src.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 + if (frustration >= 8) walk_to(src,0) + back_to_idle() - if (target) // make sure target exists - if (Adjacent(target)) // if right next to perp - playsound(src.loc, 'sound/weapons/Egloves.ogg', 50, 1, -1) - src.icon_state = "[lasercolor]ed209-c" + if(target) // make sure target exists + 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) - src.icon_state = "[lasercolor]ed209[src.on]" - var/mob/living/carbon/M = src.target - var/maxstuns = 4 - if (istype(M, /mob/living/carbon/human)) - if (M.stuttering < 10 && (!(M_HULK in M.mutations)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) - M.stuttering = 10 - M.Stun(10) - M.Weaken(10) + icon_state = "[lasercolor]ed209[on]" + var/mob/living/carbon/M = target + if(istype(M, /mob/living/carbon/human)) + if( M.stuttering < 5 && !(M_HULK in M.mutations) ) + M.stuttering = 5 + M.Stun(5) + M.Weaken(5) else M.Weaken(5) M.stuttering = 5 M.Stun(5) if(declare_arrests) + //var/area/location = get_area(src) declare_arrest() - 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]!") - maxstuns-- - 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 + else + back_to_idle() - if(SECBOT_PREP_ARREST) // preparing to arrest target - if(src.lasercolor) - mode = SECBOT_IDLE - return - if (!target) - mode = SECBOT_IDLE - src.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(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( !Adjacent(target) || !isturf(target.loc) || target.weakened < 2 ) + back_to_hunt() return - if(istype(src.target,/mob/living/carbon)) - 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(iscarbon(target) && target.canBeHandcuffed()) + if(!arrest_type) + if(!target.handcuffed) //he's not cuffed? Try to cuff him! + mode = BOT_ARREST + playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2) + 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) + spawn(30) + if( !Adjacent(target) || !isturf(target.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. return - - if(istype(src.target,/mob/living/carbon)) - src.target.handcuffed = new /obj/item/weapon/handcuffs(src.target) - target.update_inv_handcuffed() //update handcuff overlays - - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.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) - // 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) - else - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.frustration = 0 - - if(SECBOT_ARREST) // arresting - if(src.lasercolor) - mode = SECBOT_IDLE - return - if (!target || src.target.handcuffed) - src.anchored = 0 - mode = SECBOT_IDLE - return - - - if(SECBOT_START_PATROL) // start a patrol - if(!path || !istype(path)) - path = list() - 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 + if(!target.handcuffed) + target.handcuffed = new /obj/item/weapon/handcuffs(target) + target.update_inv_handcuffed(0) //update the handcuffs overlay + back_to_idle() + else + back_to_idle() 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 - if(!path || !istype(path)) path = list() - 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 - + else + back_to_idle() 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 + if(BOT_ARREST) + if (!target) + anchored = 0 + mode = BOT_IDLE + last_found = world.time + frustration = 0 return - if("go") - mode = SECBOT_IDLE - auto_patrol = 1 + if(target.handcuffed) //no target or target cuffed? back to idle. + back_to_idle() 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() + + return +/obj/machinery/bot/ed209/proc/back_to_idle() + anchored = 0 + mode = BOT_IDLE + target = null + last_found = world.time + frustration = 0 + spawn(0) + bot_process() //ensure bot quickly responds -// 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, 0, 120, id=botcard, exclude=avoid) - if (!src.path) src.path = list() - +/obj/machinery/bot/ed209/proc/back_to_hunt() + anchored = 0 + frustration = 0 + mode = BOT_HUNT + spawn(0) + bot_process() //ensure bot quickly responds // 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((src.lasercolor) && (C.lying)) - continue//Does not shoot at people lyind down when in lasertag mode, because it's just annoying, and they can fire once they get up. - - 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!") - 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 + 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 + bot_process() // ensure bot quickly responds to a perp break else continue @@ -692,14 +400,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) && !istype(D,/obj/machinery/door/poddoor)) + 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 @@ -710,27 +418,29 @@ Auto Patrol: []"}, M:loc = T */ - /obj/machinery/bot/ed209/explode() walk_to(src,0) - src.visible_message("\red [src] blows apart!", 1) + 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) var/obj/item/weapon/gun/energy/taser/G = new /obj/item/weapon/gun/energy/taser(Tsec) G.power_supply.charge = 0 + G.update_icon() else if(lasercolor == "b") var/obj/item/weapon/gun/energy/laser/bluetag/G = new /obj/item/weapon/gun/energy/laser/bluetag(Tsec) G.power_supply.charge = 0 + G.update_icon() else if(lasercolor == "r") var/obj/item/weapon/gun/energy/laser/redtag/G = new /obj/item/weapon/gun/energy/laser/redtag(Tsec) G.power_supply.charge = 0 + G.update_icon() if (prob(50)) new /obj/item/robot_parts/l_leg(Tsec) @@ -751,9 +461,24 @@ Auto Patrol: []"}, s.set_up(3, 1, src) s.start() - new /obj/effect/decal/cleanable/oil(src.loc) - del(src) + 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(emagged == 2) + if(lasercolor) + projectile = /obj/item/projectile/lasertag + else + projectile = /obj/item/projectile/beam + else + if(!lasercolor) + shoot_sound = 'sound/weapons/Taser.ogg' + projectile = /obj/item/projectile/energy/electrode + else if(lasercolor == "b") + projectile = /obj/item/projectile/lasertag/blue + else if(lasercolor == "r") + projectile = /obj/item/projectile/lasertag/red /obj/machinery/bot/ed209/proc/shootAt(var/mob/target) if(lastfired && world.time - lastfired < shot_delay) @@ -769,30 +494,14 @@ Auto Patrol: []"}, return //if(lastfired && world.time - lastfired < 100) - var/shoot_sound = 'sound/weapons/laser.ogg' if(!projectile) - if(!lasercolor) - if (src.emagged == 2) - projectile = /obj/item/projectile/beam - else - projectile = /obj/item/projectile/energy/electrode - shoot_sound = 'sound/weapons/Taser.ogg' - else if(lasercolor == "b") - if (src.emagged == 2) - projectile = /obj/item/projectile/lasertag/omni - else - projectile = /obj/item/projectile/lasertag/blue - else if(lasercolor == "r") - if (src.emagged == 2) - projectile = /obj/item/projectile/lasertag/omni - else - projectile = /obj/item/projectile/lasertag/red + return 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 @@ -804,8 +513,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 = SECBOT_HUNT + target = user + mode = BOT_HUNT /obj/machinery/bot/ed209/emp_act(severity) @@ -813,7 +522,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" @@ -833,16 +542,18 @@ Auto Patrol: []"}, targets-=toshoot if (prob(50) && emagged < 2) emagged = 2 + set_weapon() shootAt(toshoot) emagged = 0 + set_weapon() else shootAt(toshoot) else if(prob(50)) if(targets.len) var/mob/toarrest = pick(targets) if (toarrest) - src.target = toarrest - src.mode = SECBOT_HUNT + target = toarrest + mode = BOT_HUNT @@ -850,17 +561,17 @@ 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 = stripped_input(user, "Enter new robot name", name, created_name,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 switch(build_step) if(0,1) - if( istype(W, /obj/item/robot_parts/l_leg) || istype(W, /obj/item/robot_parts/r_leg) ) + if(istype(W, /obj/item/robot_parts/l_leg) || istype(W, /obj/item/robot_parts/r_leg)) user.drop_item() - del(W) + qdel(W) build_step++ user << "You add the robot leg to [src]." name = "legs/frame assembly" @@ -872,13 +583,13 @@ Auto Patrol: []"}, icon_state = "ed209_legs" if(2) - if( istype(W, /obj/item/clothing/suit/redtag) ) + if(istype(W, /obj/item/clothing/suit/redtag)) lasercolor = "r" - else if( istype(W, /obj/item/clothing/suit/bluetag) ) + else if(istype(W, /obj/item/clothing/suit/bluetag)) lasercolor = "b" - if( lasercolor || istype(W, /obj/item/clothing/suit/armor/vest) ) + if(lasercolor || istype(W, /obj/item/clothing/suit/armor/vest)) user.drop_item() - del(W) + qdel(W) build_step++ user << "You add the armor to [src]." name = "vest/legs/frame assembly" @@ -886,26 +597,38 @@ Auto Patrol: []"}, icon_state = "[lasercolor]ed209_shell" if(3) - if( istype(W, /obj/item/weapon/weldingtool) ) + if(istype(W, /obj/item/weapon/weldingtool)) var/obj/item/weapon/weldingtool/WT = W if(WT.remove_fuel(0,user)) build_step++ name = "shielded frame assembly" user << "You welded the vest to [src]." if(4) - if( istype(W, /obj/item/clothing/head/helmet) ) - user.drop_item() - del(W) - build_step++ - user << "You add the helmet to [src]." - name = "covered and shielded frame assembly" - item_state = "[lasercolor]ed209_hat" - icon_state = "[lasercolor]ed209_hat" + switch(lasercolor) + //if("b") + //if(!istype(W, /obj/item/clothing/head/helmet/bluetaghelm)) + //return + + //if("r") + //if(!istype(W, /obj/item/clothing/head/helmet/redtaghelm)) + //return + + if("") + if(!istype(W, /obj/item/clothing/head/helmet)) + return + + user.drop_item() + qdel(W) + build_step++ + user << "You add the helmet to [src]." + name = "covered and shielded frame assembly" + item_state = "[lasercolor]ed209_hat" + icon_state = "[lasercolor]ed209_hat" if(5) - if( isprox(W) ) + if(isprox(W)) user.drop_item() - del(W) + qdel(W) build_step++ user << "You add the prox sensor to [src]." name = "covered, shielded and sensored frame assembly" @@ -913,43 +636,45 @@ Auto Patrol: []"}, icon_state = "[lasercolor]ed209_prox" if(6) - if( istype(W, /obj/item/stack/cable_coil) ) + if(istype(W, /obj/item/stack/cable_coil)) var/obj/item/stack/cable_coil/coil = W - var/turf/T = get_turf(user) + if (coil.amount < 1) + user << "You need one length of cable to wire the ED-209." + return user << "You start to wire [src]..." - sleep(40) - if(get_turf(user) == T) - coil.use(1) - build_step++ - user << "You wire the ED-209 assembly." - name = "wired ED-209 assembly" + if (do_after(user, 40)) + if (coil.amount >= 1 && build_step == 6) + coil.use(1) + build_step = 7 + user << "You wire the ED-209 assembly." + name = "wired ED-209 assembly" if(7) switch(lasercolor) if("b") - if( !istype(W, /obj/item/weapon/gun/energy/laser/bluetag) ) + if(!istype(W, /obj/item/weapon/gun/energy/laser/bluetag)) return name = "bluetag ED-209 assembly" if("r") - if( !istype(W, /obj/item/weapon/gun/energy/laser/redtag) ) + if(!istype(W, /obj/item/weapon/gun/energy/laser/redtag)) return name = "redtag ED-209 assembly" if("") - if( !istype(W, /obj/item/weapon/gun/energy/taser) ) + if(!istype(W, /obj/item/weapon/gun/energy/taser)) return name = "taser ED-209 assembly" else 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() - del(W) + qdel(W) if(8) - if( istype(W, /obj/item/weapon/screwdriver) ) - playsound(src.loc, 'sound/items/Screwdriver.ogg', 100, 1) + if(istype(W, /obj/item/weapon/screwdriver)) + playsound(loc, 'sound/items/Screwdriver.ogg', 100, 1) var/turf/T = get_turf(user) user << "Now attaching the gun to the frame..." sleep(40) @@ -959,49 +684,50 @@ Auto Patrol: []"}, user << "Taser gun attached." if(9) - if( istype(W, /obj/item/weapon/cell) ) + if(istype(W, /obj/item/weapon/cell)) build_step++ user << "You complete the ED-209." var/turf/T = get_turf(src) new /obj/machinery/bot/ed209(T,created_name,lasercolor) user.drop_item() - del(W) - user.drop_from_inventory(src) - del(src) + qdel(W) + user.before_take_item(src, 1) + qdel(src) /obj/machinery/bot/ed209/bullet_act(var/obj/item/projectile/Proj) - if((src.lasercolor == "b") && (src.disabled == 0)) - if(istype(Proj, /obj/item/projectile/lasertag/red)) - src.disabled = 1 - del (Proj) - sleep(100) - src.disabled = 0 + if(!disabled) + var/lasertag_check = 0 + if((lasercolor == "b")) + if(istype(Proj, /obj/item/projectile/lasertag/red)) + lasertag_check++ + else if((lasercolor == "r")) + if(istype(Proj, /obj/item/projectile/lasertag/blue)) + lasertag_check++ + if(lasertag_check) + icon_state = "[lasercolor]ed2090" + disabled = 1 + target = null + spawn(100) + disabled = 0 + icon_state = "[lasercolor]ed2091" + return 1 else - ..() - else if((src.lasercolor == "r") && (src.disabled == 0)) - if(istype(Proj, /obj/item/projectile/lasertag/blue)) - src.disabled = 1 - del (Proj) - sleep(100) - src.disabled = 0 - else - ..() + ..(Proj) else - ..() + ..(Proj) /obj/machinery/bot/ed209/bluetag/New()//If desired, you spawn red and bluetag bots easily new /obj/machinery/bot/ed209(get_turf(src),null,"b") - del(src) + qdel(src) /obj/machinery/bot/ed209/redtag/New() new /obj/machinery/bot/ed209(get_turf(src),null,"r") qdel(src) - /obj/machinery/bot/ed209/proc/declare_arrest() - var/area/location = get_area(src) + var/area/location = get_area(src) for(var/mob/living/carbon/human/human in world) if((human.z == src.z) && istype(human.glasses, /obj/item/clothing/glasses/hud/security) || istype(human.glasses, /obj/item/clothing/glasses/sunglasses/sechud) && !human.blinded) - human << "\icon[human.glasses] [src.name] is [arrest_type ? "detaining" : "arresting"] level [threatlevel] [target] in [location]" + human << "\icon[human.glasses] [src.name] is [arrest_type ? "detaining" : "arresting"] level [threatlevel] threat [target] in [location]" diff --git a/code/game/machinery/bots/floorbot.dm b/code/game/machinery/bots/floorbot.dm index 78c418adb8c..f85d57657f3 100644 --- a/code/game/machinery/bots/floorbot.dm +++ b/code/game/machinery/bots/floorbot.dm @@ -9,7 +9,6 @@ throw_speed = 2 throw_range = 5 w_class = 3.0 - flags = TABLEPASS var/created_name = "Floorbot" /obj/item/weapon/toolbox_tiles_sensor @@ -22,15 +21,11 @@ throw_speed = 2 throw_range = 5 w_class = 3.0 - flags = TABLEPASS var/created_name = "Floorbot" -// Tell other floorbots what we're fucking with so two floorbots don't dick with the same tile. -var/global/list/floorbot_targets=list() - //Floorbot /obj/machinery/bot/floorbot - name = "Floorbot" + name = "\improper Floorbot" desc = "A little floor repairing robot, he looks so excited!" icon = 'icons/obj/aibots.dmi' icon_state = "floorbot0" @@ -40,61 +35,65 @@ var/global/list/floorbot_targets=list() health = 25 maxhealth = 25 //weight = 1.0E7 - var/mode = 0 -#define FLOORBOT_IDLE 0 // idle -#define FLOORBOT_FIXING_SHIT 1 -#define FLOORBOT_START_PATROL 2 // start patrol -#define FLOORBOT_PATROL 3 // patrolling - - var/auto_patrol = 0 // set to make bot automatically patrol 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/oldloc = null - req_access = list(access_construction) - var/path[] = new() + req_one_access = list(access_construction, access_robotics) var/targetdirection - var/beacon_freq = 1445 // navigation beacon frequency - - - 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/patpath = 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 - + 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. + #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() ..() - 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() ..() - if(!isnull(src.target)) - floorbot_targets -= src.target - src.target = null - src.oldtarget = null - src.oldloc = null - src.updateicon() - src.path = new() - src.patpath = new() - src.updateUsrDialog() - src.mode=FLOORBOT_IDLE + updateicon() + updateUsrDialog() + +/obj/machinery/bot/floorbot/bot_reset() + ..() + target = null + oldtarget = null + oldloc = null + ignore_list = list() + nagged = 0 + anchored = 0 + update_icon() + +/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) . = ..() @@ -105,36 +104,52 @@ var/global/list/floorbot_targets=list() /obj/machinery/bot/floorbot/interact(mob/user as mob) var/dat - 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 single pieces of metal into tiles when empty: [src.maketiles ? "Yes" : "No"]
" + dat += hack(user) + dat += "Floor Repairer Controls v1.1

" + dat += "Status: [on ? "On" : "Off"]
" + dat += "Maintenance panel panel is [open ? "opened" : "closed"]
" + dat += "Tiles left: [amount]
" + dat += "Behaviour 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 += "Traction Magnets: [anchored ? "Engaged" : "Disengaged"]
" + dat += "Patrol Station: [auto_patrol ? "Yes" : "No"]
" + var/bmode + 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.amount) + var/loaded = min(50-amount, T.amount) T.use(loaded) - src.amount += loaded - user << "You load [loaded] tiles into the floorbot. He now contains [src.amount] tiles." - src.updateicon() + amount += loaded + if (loaded > 0) + 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 [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" @@ -142,528 +157,310 @@ var/global/list/floorbot_targets=list() 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((!locked && open) || !user) - 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) - 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() + maketiles = !maketiles + if("fix") + fixfloors = !fixfloors + if("autotile") + autotile = !autotile + if("emptynag") + nag_on_empty = !nag_on_empty + if("anchor") + anchored = !anchored -/obj/machinery/bot/floorbot/proc/is_obj_valid_target(var/atom/T,var/list/floorbottargets) - if(T in floorbottargets) - return 0 - if(T == src.oldtarget) - return 0 - if(istype(T.loc, /turf/simulated/wall)) - return 0 - if(!T.loc.Enter(src)) - return 0 - return 1 + if("bridgemode") + var/setdir = input("Select construction direction:") as null|anything in list("north","east","south","west","disable") + switch(setdir) + if("north") + targetdirection = 1 + if("south") + targetdirection = 2 + if("east") + targetdirection = 4 + if("west") + targetdirection = 8 + if("disable") + targetdirection = null + updateUsrDialog() -/obj/machinery/bot/floorbot/proc/hunt_for_tiles(var/list/shit_in_view, var/list/floorbottargets) - for(var/obj/item/stack/tile/plasteel/T in shit_in_view) - if(!(T in floorbot_targets) && src.is_obj_valid_target(T,floorbottargets)) - src.oldtarget = T - src.target = T - floorbot_targets +=T - mode=FLOORBOT_FIXING_SHIT - return - -/obj/machinery/bot/floorbot/proc/hunt_for_metal(var/list/shit_in_view, var/list/floorbottargets) - for(var/obj/item/stack/sheet/metal/M in shit_in_view) - if(!(M in floorbot_targets) && src.is_obj_valid_target(M) && M.amount == 1) - src.oldtarget = M - src.target = M - floorbot_targets += M - mode=FLOORBOT_FIXING_SHIT - return - -/obj/machinery/bot/floorbot/proc/have_target() - return (src.target != null) - -/obj/machinery/bot/floorbot/process() - //set background = 1 - - if(!src.on) - return - if(src.repairing) +/obj/machinery/bot/floorbot/bot_process() + if (!..()) return - switch(mode) - if(FLOORBOT_IDLE) // idle - walk_to(src,0) - if(checkforwork()) // see if any criminals are in range - return - if(!mode && auto_patrol) // still idle, and set to patrol - mode = FLOORBOT_START_PATROL // switch to patrol mode - if(FLOORBOT_FIXING_SHIT) - src.fix_shit() + if(mode == BOT_REPAIRING) + 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(!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(FLOORBOT_START_PATROL) // start a patrol - if(patpath.len > 0 && patrol_target) // have a valid path, so just resume - mode = FLOORBOT_PATROL - return - - else if(patrol_target) // has patrol target already - spawn(0) - calc_path() // so just find a route to it - if(patpath.len == 0) - patrol_target = 0 - return - mode = FLOORBOT_PATROL - - - else // no patrol target, so need a new one - find_patrol_target() - speak("That's done, what's next?") - - - if(FLOORBOT_PATROL) // patrol mode - patrol_step() - spawn(5) - if(mode == FLOORBOT_PATROL) - patrol_step() - -/obj/machinery/bot/floorbot/proc/fix_shit() - if(!src.have_target()) - if(src.loc != src.oldloc) - src.oldtarget = null - return 0 - if(!src.path) - src.path = new() - if(src.target && (src.target != null) && src.path.len == 0) - spawn(0) - if(!istype(src.target, /turf/)) - src.path = AStar(src.loc, src.target.loc, /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, id=botcard) - if (!src.path) src.path = list() - if(src.path.len == 0) - src.oldtarget = src.target - floorbot_targets -= src.target - src.target = null - return 1 - 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) - step_to(src, target) - src.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) - mode=FLOORBOT_IDLE - else if(istype(src.target, /obj/item/stack/sheet/metal)) - src.maketile(src.target) - mode=FLOORBOT_IDLE - //else if(istype(src.target, /turf/) && emagged < 2) - else if((src.target.is_plating() || istype(src.target,/turf/space/)) && emagged < 2) - repair(src.target) - mode=FLOORBOT_IDLE - else if(src.target.is_plasteel_floor() && (src.target:broken || src.target:burnt) && emagged < 2) - var/turf/simulated/floor/F = src.target - src.anchored = 1 - src.repairing = 1 - F.break_tile_to_plating() - spawn(50) - src.anchored = 0 - src.repairing = 0 - src.target = null - floorbot_targets -= src.target - mode=FLOORBOT_IDLE - else if(src.target.is_plating() && emagged == 2) - var/turf/simulated/floor/F = src.target - src.anchored = 1 - src.repairing = 1 - if(prob(90)) - F.break_tile_to_plating() - else - F.ReplaceWithLattice() - visible_message("\red [src] makes an excited booping sound.") - spawn(50) - src.amount ++ - src.anchored = 0 - src.repairing = 0 - src.target = null - floorbot_targets -= src.target - mode=FLOORBOT_IDLE - src.path = new() - mode=FLOORBOT_IDLE - return 1 - - src.oldloc = src.loc - return 1 - -/obj/machinery/bot/floorbot/proc/checkforwork() - if(src.have_target()) - return 0 - var/list/floorbottargets = list() - - // Needed because we used to look this up 15 goddamn times per process. - Nexypoo - var/list/shitICanSee = view(7, src) - - if(src.amount <= 0 && !src.have_target()) - if(src.eattiles) - if(src.hunt_for_tiles(shitICanSee, floorbottargets)) - return 1 - if(src.maketiles && !src.have_target()) - if(src.hunt_for_metal(shitICanSee, floorbottargets)) - return 1 else - return 0 + 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.have_target() && emagged < 2) - if(targetdirection != null) - /* - for (var/turf/space/D in shitICanSee) - if(!(D in floorbottargets) && D != src.oldtarget) // Added for bridging mode -- TLE - if(get_dir(src, D) == targetdirection) - src.oldtarget = D - src.target = D - break - */ + //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. + //Try to find a space tile immediately in our selected direction. var/turf/T = get_step(src, targetdirection) - if(istype(T, /turf/space) && !(T in floorbot_targets)) - src.oldtarget = T - src.target = T - floorbot_targets+=T - mode=FLOORBOT_FIXING_SHIT - return 1 - if(!src.have_target()) - for (var/turf/space/D in shitICanSee) - if(!(D in floorbottargets) && D != src.oldtarget && (D.loc.name != "Space") && !(D in floorbot_targets)) - src.oldtarget = D - src.target = D - floorbot_targets += D - mode=FLOORBOT_FIXING_SHIT - return 1 - if((!src.target || src.target == null ) && src.improvefloors) - for (var/turf/simulated/floor/F in shitICanSee) - // So, what the dick are we doing, here? - // ORIGINAL: if(!(F in floorbottargets) && F != src.oldtarget && F.icon_state == "Floor1" && !(istype(F, /turf/simulated/floor/plating))) - // Using new erro flags: - if(!(F in floorbottargets) && F != src.oldtarget && F.is_plating() && !(istype(F, /turf/simulated/wall)) && !(F in floorbot_targets)) - if(!F.broken && !F.burnt) - src.oldtarget = F - src.target = F - floorbot_targets += F - mode=FLOORBOT_FIXING_SHIT - return 1 - if(!(F in floorbottargets) && !(F in floorbot_targets) && F != src.oldtarget && F.is_plasteel_floor() && (F.broken||F.burnt)) - src.oldtarget = F - src.target = F - floorbot_targets += F - mode=FLOORBOT_FIXING_SHIT - return 1 + if(istype(T, /turf/space)) + target = T - if(!src.have_target() && emagged == 2) - for (var/turf/simulated/floor/D in shitICanSee) - //if(!(D in floorbottargets) && D != src.oldtarget && D.floor_tile) - if(!(D in floorbottargets) && D != src.oldtarget && D.is_plasteel_floor() && !(D in floorbot_targets)) - src.oldtarget = D - src.target = D - floorbot_targets += D - mode=FLOORBOT_FIXING_SHIT - return 1 - return 0 + else //Find a space tile farther way! + target = scan(/turf/space, oldtarget) + process_type = BRIDGE_MODE -/obj/machinery/bot/floorbot/proc/repair(var/turf/target) - if(istype(target, /turf/space/)) - if(target.loc.name == "Space") + if(!target) + process_type = HULL_BREACH //Ensures the floorbot does not try to "fix" space areas or shuttle docking zones. + target = scan(/turf/space, oldtarget) + + 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) + + if(!target && fixfloors) //Repairs damaged floors and tiles. + process_type = FIX_TILE + target = scan(/turf/simulated/floor, oldtarget) + + if(!target && emagged == 2) //We are emagged! Time to rip up the floors! + process_type = TILE_EMAG + target = scan(/turf/simulated/floor, oldtarget) + + + 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(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(!bot_move(target)) + add_to_ignore(target) + oldtarget = target + target = null + mode = BOT_IDLE + return + else if( !bot_move(target) ) + oldtarget = target + target = null + mode = BOT_IDLE return - else if(!istype(target, /turf/simulated/floor)) - return - if(src.amount <= 0) - return - src.anchored = 1 - src.icon_state = "floorbot-c" - if(istype(target, /turf/space/)) - visible_message("\red [src] begins to repair the hole") - var/obj/item/stack/tile/plasteel/T = new /obj/item/stack/tile/plasteel - src.repairing = 1 - spawn(50) - T.build(src.loc) - src.repairing = 0 - src.amount -= 1 - src.updateicon() - src.anchored = 0 - floorbot_targets -= src.target - src.target = null + + 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 + +/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/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 - var/turf/simulated/floor/F = src.loc - if(!F.broken && !F.burnt) - visible_message("\red [src] begins to improve the floor.") - src.repairing = 1 + return 1 + +//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 + anchored = 1 //Prevent the floorbot being blown off-course while trying to reach a hull breach. + if(BRIDGE_MODE) //Only space turfs in our chosen direction are considered. + if(get_dir(src, scan_target) == targetdirection) + result = scan_target + anchored = 1 + if(REPLACE_TILE) + F = scan_target + if(istype(F, /turf/simulated/floor/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(!istype(F, /turf/simulated/floor/plating)) + result = F + else //If no special processing is needed, simply return the result. + result = scan_target + return result + +/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_turf) && !targetdirection) + target = null + return + else if(!istype(target_turf, /turf/simulated/floor)) + return + if(amount <= 0) + mode = BOT_IDLE + target = null + return + anchored = 1 + icon_state = "floorbot-c" + 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. + target_turf.ChangeTurf(/turf/simulated/floor) + else //Build a hull plating without a floor tile. + target_turf.ChangeTurf(/turf/simulated/floor/plating) + mode = BOT_IDLE + amount -= 1 + updateicon() + anchored = 0 + target = null + else + var/turf/simulated/floor/F = target_turf + if(F.burnt || F.broken) + mode = BOT_REPAIRING + visible_message(" [src] begins repairing the floor.") spawn(50) - F.make_plasteel_floor() - src.repairing = 0 - src.amount -= 1 - src.updateicon() - src.anchored = 0 - floorbot_targets -= src.target - src.target = null - else - if(F.is_plating()) - visible_message("\red [src] begins to fix dents in the floor.") - src.repairing = 1 - spawn(20) - src.repairing = 0 - // Cheap, and does the job. - F.icon_state = "plating" + if(mode == BOT_REPAIRING) F.burnt = 0 F.broken = 0 - floorbot_targets -= src.target - src.target = null + F.icon_state = "floor" + mode = BOT_IDLE + amount -= 1 + updateicon() + anchored = 0 + target = null + else + mode = BOT_REPAIRING + visible_message(" [src] begins repairing the floor.") + spawn(50) + if(mode == BOT_REPAIRING) + F.burnt = 0 + F.broken = 0 + target_turf.ChangeTurf(/turf/simulated/floor) + mode = BOT_IDLE + amount -= 1 + updateicon() + anchored = 0 + 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("\red [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 - del(T) - src.updateicon() - floorbot_targets -= src.target - src.target = null - src.repairing = 0 + amount += T.amount + qdel(T) + 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("\red [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 - if(M.amount==1) - del(M) - else + if(M.amount > 1) M.amount-- - floorbot_targets -= src.target - src.target = null - src.repairing = 0 + 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" - - -/obj/machinery/bot/floorbot/proc/calc_path(var/turf/avoid = null) - src.path = AStar(src.loc, patrol_target, /turf/proc/CardinalTurfsWithAccess, /turf/proc/Distance, 0, 120, id=botcard, exclude=avoid) - src.path = reverselist(src.path) - -// perform a single patrol step - -/obj/machinery/bot/floorbot/proc/patrol_step() - - if(loc == patrol_target) // reached target - at_patrol_target() - return - - else if(patpath.len > 0 && patrol_target) // valid path - - var/turf/next = patpath[1] - if(next == loc) - patpath -= next - return - - - if(istype( next, /turf/simulated)) - - var/moved = step_towards(src, next) // attempt to move - if(moved) // successful move - blockcount = 0 - patpath -= loc - - checkforwork() - 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(patpath.len == 0) - find_patrol_target() - else - blockcount = 0 - - return - - return - - else // not a valid turf - mode = FLOORBOT_IDLE - return - - else // no path, so calculate new one - mode = FLOORBOT_START_PATROL - - -// finds a new patrol target -/obj/machinery/bot/floorbot/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/floorbot/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 = FLOORBOT_IDLE - speak("Disengaging patrol mode.") - //send_status() - - -/obj/machinery/bot/floorbot/proc/at_patrol_target() - find_patrol_target() - return - -/obj/machinery/bot/floorbot/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() - return - -/obj/machinery/bot/floorbot/receive_signal(datum/signal/signal) - //log_admin("DEBUG \[[world.timeofday]\]: /obj/machinery/bot/secbot/receive_signal([signal.debug_print()])") - if(!on) - return - - var/recv = signal.data["command"] - - // 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/floorbot/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/floorbot/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 - frequency.post_signal(src, signal) - -// sets the current destination -// signals all beacons matching the patrol code -// beacons will return a signal giving their locations -/obj/machinery/bot/floorbot/proc/set_destination(var/new_dest) - new_destination = new_dest - post_signal(beacon_freq, "findbeacon", "patrol") - awaiting_beacon = 1 + icon_state = "floorbot[on]e" /obj/machinery/bot/floorbot/explode() - src.on = 0 - src.visible_message("\red [src] blows apart!", 1) + on = 0 + visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) var/obj/item/weapon/storage/toolbox/mechanical/N = new /obj/item/weapon/storage/toolbox/mechanical(Tsec) @@ -674,9 +471,6 @@ var/global/list/floorbot_targets=list() if (prob(50)) new /obj/item/robot_parts/l_arm(Tsec) - if(src.target) - floorbot_targets -= src.target - while (amount)//Dumps the tiles into the appropriate sized stacks if(amount >= 16) var/obj/item/stack/tile/plasteel/T = new (Tsec) @@ -684,13 +478,13 @@ var/global/list/floorbot_targets=list() 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 s.set_up(3, 1, src) s.start() - del(src) + qdel(src) return @@ -698,54 +492,56 @@ var/global/list/floorbot_targets=list() if(!istype(T, /obj/item/stack/tile/plasteel)) ..() return - if(src.contents.len >= 1) - user << "They wont fit in as there is already stuff inside." + if(contents.len >= 1) + user << "They won't fit in, as there is already stuff inside." + return + if(T.use(10)) + if(user.s_active) + user.s_active.close(user) + var/obj/item/weapon/toolbox_tiles/B = new /obj/item/weapon/toolbox_tiles + user.put_in_hands(B) + user << "You add the tiles into the empty toolbox. They protrude from the top." + user.before_take_item(src, 1) + qdel(src) + else + user << "You need 10 floor tiles to start building a floorbot." return - if(user.s_active) - user.s_active.close(user) - del(T) - var/obj/item/weapon/toolbox_tiles/B = new /obj/item/weapon/toolbox_tiles - user.put_in_hands(B) - user << "You add the tiles into the empty toolbox. They protrude from the top." - user.drop_from_inventory(src) - del(src) /obj/item/weapon/toolbox_tiles/attackby(var/obj/item/W, mob/user as mob) ..() if(isprox(W)) - del(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.drop_from_inventory(src) - del(src) + user.before_take_item(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 = stripped_input(user, "Enter new robot name", name, created_name,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) ..() if(istype(W, /obj/item/robot_parts/l_arm) || istype(W, /obj/item/robot_parts/r_arm)) - del(W) + 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.drop_from_inventory(src) - del(src) + user.before_take_item(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,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 \ No newline at end of file + created_name = t diff --git a/code/game/machinery/bots/medbot.dm b/code/game/machinery/bots/medbot.dm index 8d0c72f3758..232df34fba9 100644 --- a/code/game/machinery/bots/medbot.dm +++ b/code/game/machinery/bots/medbot.dm @@ -4,7 +4,7 @@ /obj/machinery/bot/medbot - name = "Medibot" + name = "\improper Medibot" desc = "A little medical robot. He looks somewhat underwhelmed." icon = 'icons/obj/aibots.dmi' icon_state = "medibot0" @@ -13,30 +13,34 @@ anchored = 0 health = 20 maxhealth = 20 - req_access =list(access_medical, access_robotics) + req_one_access =list(access_medical, access_robotics) var/stunned = 0 //It can be stunned by tasers. Delicate circuits. -//var/emagged = 0 +// 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 = MED_FREQ //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 = 0 //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/syndicate name = "Suspicious Medibot" @@ -45,10 +49,19 @@ treatment_oxy = "dexalin" treatment_brute = "bicaridine" treatment_fire = "kelotane" + treatment_tox = "anti_toxin" + +/obj/machinery/bot/medbot/mysterious + name = "\improper Mysterious Medibot" + desc = "International Medibot of mystery." + skin = "bezerk" + treatment_oxy = "dexalinp" + treatment_brute = "bicaridine" + treatment_fire = "kelotane" treatment_tox = "anti_toxin" /obj/item/weapon/firstaid_arm_assembly - name = "first aid/robot arm assembly" + name = "incomplete medibot assembly." desc = "A first aid kit with a robot arm permanently grafted to it." icon = 'icons/obj/aibots.dmi' icon_state = "firstaid_arm" @@ -57,45 +70,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(src.skin == "bezerk") - created_name = "Suspicious Medibot" + 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,88 +136,94 @@ if (.) return var/dat - dat += "Automatic Medical Unit v1.0

" - dat += "Status: [src.on ? "On" : "Off"]
" - dat += "Maintenance panel panel is [src.open ? "opened" : "closed"]
" + dat += hack(user) + 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) - 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 - src.updateUsrDialog() + else if (href_list["critalerts"]) + declare_crit = !declare_crit + + else if (href_list["stationary"]) + stationary_mode = !stationary_mode + path = list() + 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" @@ -196,131 +233,127 @@ 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 + var/current_health = health ..() - if (health < maxhealth && !istype(W, /obj/item/weapon/screwdriver) && W.force) + if (health < current_health) //if medbot took some damage step_to(src, (get_step_away(src,user))) /obj/machinery/bot/medbot/Emag(mob/user as mob) ..() - if((!locked && open) || !user) - 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) - for(var/mob/O in hearers(src, null)) - O.show_message("\red [src] buzzes oddly!", 1) + visible_message("[src] buzzes oddly!") 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 = 1 - - if(!src.on) - src.stunned = 0 +/obj/machinery/bot/medbot/process_scan(var/mob/living/carbon/human/H) + if (H.stat == 2) 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 ((H == oldpatient) && (world.time < last_found + 100)) 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(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 - if(!src.patient) - if(!src.shut_up && prob(1)) +/obj/machinery/bot/medbot/bot_process() + if (!..()) + return + + if(mode == BOT_HEALING) + return + + if(stunned) + icon_state = "medibota" + stunned-- + + 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 = list() + + if(!patient) + 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) + 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 - for (var/mob/living/carbon/C in view(7,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)) - continue - - if(src.assess_patient(C)) - src.patient = C - src.oldpatient = C - src.last_found = world.time - spawn(0) - if((src.last_newpatient_speak + 100) < world.time) //Don't spam these messages! - var/message = pick("Hey, you! Hold on, I'm coming.","Wait! I want to help!","You appear to be injured!") - src.speak(message) - src.last_newpatient_speak = world.time - src.visible_message("[src] points at [C.name]!") - break - else - continue - - if(!src.path) - src.path = new() - - if(src.patient && Adjacent(patient)) - if(!src.currently_healing) - src.currently_healing = 1 - src.frustration = 0 - src.medicate_patient(src.patient) + if(patient && (get_dist(src,patient) <= 1)) //Patient is next to us, begin treatment! + 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 + //Patient has moved away from us! + else if(patient && path && path.len && (get_dist(patient,path[path.len]) > 2)) + path = list() + 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, 0, 30,id=botcard) - if (!path) 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 && patient) + if(!bot_move(patient)) + 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] - spawn(3) - if(src.path.len) - step_to(src, src.path[1]) - src.path -= src.path[1] + if(path.len > 8 && patient) + frustration++ - if(src.path.len > 8 && src.patient) - src.frustration++ + if(auto_patrol && !stationary_mode && !patient) + if(mode == BOT_IDLE || mode == BOT_START_PATROL) + start_patrol() + + if(mode == BOT_PATROL) + bot_patrol() return @@ -332,121 +365,124 @@ 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((skin == "bezerk") && (!("syndicate" in C.faction))) // No healing non-operatives as a SyndiBot. + + if((skin == "bezerk") && (!("syndicate" in C.faction))) return 0 + + 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 - 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 - 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" - visible_message("\red [src] is trying to inject [src.patient]!") - spawn(30) - if ((get_dist(src, src.patient) <= 1) && (src.on)) - if((reagent_id == "internal_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) - else - src.patient.reagents.add_reagent(reagent_id,src.injection_amount) - visible_message("\red [src] injects [src.patient] with the syringe!") + C.visible_message("[src] is trying to inject [patient]!", \ + "[src] is trying to inject [patient]!") - src.icon_state = "medibot[src.on]" - src.currently_healing = 0 + spawn(30) + if ((get_dist(src, patient) <= 1) && (on)) + if(reagent_id == "internal_beaker") + 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!", \ + "[src] injects [patient] with the syringe!") + + mode = BOT_IDLE + updateicon() return reagent_id = null return - - /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 - visible_message("\red [src] blows apart!", 1) + on = 0 + visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) new /obj/item/weapon/storage/firstaid(Tsec) @@ -455,9 +491,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) @@ -465,57 +501,17 @@ var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread s.set_up(3, 1, src) s.start() - del(src) + 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) && !istype(D,/obj/machinery/door/poddoor)) - 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/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 -*/ +/obj/machinery/bot/medbot/proc/declare(var/crit_patient) + if(declare_cooldown) + return + //var/area/location = get_area(src) + declare_critical(crit_patient) + declare_cooldown = 1 + spawn(200) //Twenty seconds + declare_cooldown = 0 /* * Medbot Assembly -- Can be made out of all three medkits. @@ -528,7 +524,7 @@ return //Making a medibot! - if(src.contents.len >= 1) + if(contents.len >= 1) user << "You need to empty [src] out first." return @@ -539,55 +535,49 @@ A.skin = "tox" else if(istype(src,/obj/item/weapon/storage/firstaid/o2)) A.skin = "o2" - else if(istype(src,/obj/item/weapon/storage/firstaid/tactical)) - A.skin = "bezerk" - del(S) + qdel(S) user.put_in_hands(A) user << "You add the robot arm to the first aid kit." - user.drop_from_inventory(src) - del(src) + user.before_take_item(src, 1) + qdel(src) /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 = stripped_input(user, "Enter new robot name", name, created_name,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() - del(W) - src.build_step++ + qdel(W) + 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() - del(W) - src.build_step++ + qdel(W) + 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 = skin + S.name = created_name + user.before_take_item(src, 1) + qdel(src) - if (src.skin == "bezerk") - var/obj/machinery/bot/medbot/syndicate/S = new /obj/machinery/bot/medbot/syndicate(T) - S.skin = src.skin - S.name = src.created_name - S.req_access = list("150") - - else - var/obj/machinery/bot/medbot/S = new /obj/machinery/bot/medbot(T) - S.skin = src.skin - S.name = src.created_name - - user.drop_from_inventory(src) - del(src) - +/obj/machinery/bot/medbot/proc/declare_critical(var/crit_patient) + var/area/location = get_area(src) + for(var/mob/living/carbon/human/human in world) + if((human.z == src.z) && istype(human.glasses, /obj/item/clothing/glasses/hud/health) | istype(human.glasses, /obj/item/clothing/glasses/hud/health_advanced) && !human.blinded) + human << "\icon[human.glasses] Medical emergency! [crit_patient ? "[crit_patient]" : "A patient"] is in critical condition at [location]!" \ No newline at end of file diff --git a/code/game/machinery/bots/mulebot.dm b/code/game/machinery/bots/mulebot.dm index 8ee227f7a2b..0a6f58b0f31 100644 --- a/code/game/machinery/bots/mulebot.dm +++ b/code/game/machinery/bots/mulebot.dm @@ -19,34 +19,28 @@ 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 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/cell/cell var/datum/wires/mulebot/wires = null @@ -68,18 +62,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) @@ -87,9 +79,6 @@ var/global/mulebot_count = 0 name = "\improper Mulebot ([suffix])" - verbs -= /atom/movable/verb/pull - - // attack by item // emag : lock/unlock, @@ -99,12 +88,12 @@ var/global/mulebot_count = 0 /obj/machinery/bot/mulebot/attackby(var/obj/item/I, var/mob/user) if(istype(I,/obj/item/weapon/card/emag)) locked = !locked - user << "\blue You [locked ? "lock" : "unlock"] the mulebot's controls!" + 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 << "\blue Controls [(locked ? "locked" : "unlocked")]." + user << "Controls [(locked ? "locked" : "unlocked")]." else if(istype(I,/obj/item/weapon/cell) && open && !cell) var/obj/item/weapon/cell/C = I @@ -114,32 +103,35 @@ var/global/mulebot_count = 0 updateDialog() else if(istype(I,/obj/item/weapon/screwdriver)) if(locked) - user << "\blue The maintenance hatch cannot be opened or closed while the controls are locked." + user << "The maintenance hatch cannot be opened or closed while the controls are locked." return open = !open if(open) - src.visible_message("[user] opens the maintenance hatch of [src]", "\blue 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]", "\blue 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( - "\red [user] repairs [src]!", - "\blue You repair [src]!" + "[user] repairs [src]!", + "You repair [src]!" ) else - user << "\blue [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("\red [user] knocks [load] off [src] with \the [I]!", "\red 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 @@ -162,13 +154,12 @@ var/global/mulebot_count = 0 if(prob(50) && !isnull(load)) unload(0) if(prob(25)) - src.visible_message("\red Something shorts out inside [src]!") + visible_message(" Something shorts out inside [src]!") wires.RandomCut() ..() /obj/machinery/bot/mulebot/attack_ai(var/mob/user) - src.add_hiddenprint(user) user.set_machine(src) interact(user, 1) @@ -181,38 +172,40 @@ var/global/mulebot_count = 0 /obj/machinery/bot/mulebot/interact(var/mob/user, var/ai=0) var/dat - dat += "Multiple Utility Load Effector Mk. III

" - dat += "ID: [suffix]
" - dat += "Power: [on ? "On" : "Off"]
" + dat += "

Multiple Utility Load Effector Mk. V

" + dat += "ID: [suffix]
" + dat += "Power: [on ? "On" : "Off"]
" if(!open) - dat += "Status: " + 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 locate 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"]
" - dat += "Destination: [!destination ? "none" : destination]
" - dat += "Power level: [cell ? cell.percent() : 0]%
" + dat += "Current Load: [load ? load.name : "none"]
" + dat += "Destination: [!destination ? "none" : destination]
" + dat += "Power level: [cell ? cell.percent() : 0]%" if(locked && !ai) - dat += "
Controls are locked (unlock)" + dat += " 
Controls are locked
Unlock Controls" else - dat += "
Controls are unlocked (lock)

" + dat += " 
Controls are unlocked
Lock Controls

" dat += "Toggle Power
" dat += "Stop
" @@ -223,15 +216,16 @@ 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
" - dat += "
The maintenance hatch is closed.
" + dat += "
The maintenance hatch is closed.
" else if(!ai) - dat += "The maintenance hatch is open.

" - dat += "Power cell: " + dat += "
The maintenance hatch is open.

" + dat += "Power cell: " if(cell) dat += "Installed
" else @@ -239,10 +233,14 @@ var/global/mulebot_count = 0 dat += wires() else - dat += "The bot is in maintenance mode and cannot be controlled.
" + dat += "
The bot is in maintenance mode and cannot be controlled.

" - user << browse("Mulebot [suffix ? "([suffix])" : ""][dat]", "window=mulebot;size=350x500") - onclose(user, "mulebot") + //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. V [suffix ? "([suffix])" : ""]", 350, 500) + popup.set_content(dat) + popup.set_title_image(user.browse_rsc_icon(icon, icon_state)) + popup.open() return // returns the wire panel text @@ -255,7 +253,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"]) @@ -263,11 +261,11 @@ var/global/mulebot_count = 0 toggle_lock(usr) if("power") - if (src.on) + if (on) turn_off() else if (cell && !open) if (!turn_on()) - usr << "\red You can't switch on [src]." + usr << "You can't switch on [src]." return else return @@ -282,7 +280,7 @@ var/global/mulebot_count = 0 cell.add_fingerprint(usr) cell = null - usr.visible_message("\blue [usr] removes the power cell from [src].", "\blue You remove the power cell from [src].") + usr.visible_message("[usr] removes the power cell from [src].", "You remove the power cell from [src].") updateDialog() if("cellinsert") @@ -294,28 +292,28 @@ var/global/mulebot_count = 0 C.loc = src C.add_fingerprint(usr) - usr.visible_message("\blue [usr] inserts a power cell into [src].", "\blue You insert the power cell into [src].") + usr.visible_message("[usr] inserts a power cell into [src].", "You insert the power cell into [src].") updateDialog() 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) @@ -351,12 +349,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() @@ -369,12 +370,12 @@ 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 else - user << "\red Access denied." + user << "Access denied." return 0 // mousedrop a crate to load the bot @@ -397,8 +398,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. @@ -410,19 +411,16 @@ var/global/mulebot_count = 0 if(get_dist(C, src) > 1 || load || !on) return - for(var/obj/structure/plasticflaps/P in src.loc)//Takes flaps into account - if(!CanPass(C,P)) - 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 @@ -438,22 +436,19 @@ 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 // argument is optional direction to unload // if zero, unload at bot's location -/obj/machinery/bot/mulebot/proc/unload(var/dirn = 0) +/obj/machinery/bot/mulebot/proc/unload(var/dirn) if(!load) return - mode = 1 + mode = BOT_LOADING overlays.Cut() - load.loc = src.loc - load.pixel_y -= 9 - load.layer = initial(load.layer) if(ismob(load)) var/mob/M = load if(M.client) @@ -461,13 +456,14 @@ var/global/mulebot_count = 0 M.client.eye = src + load.loc = loc + load.pixel_y -= 9 + load.layer = initial(load.layer) if(dirn) - var/turf/T = src.loc - T = get_step(T,dirn) - if(CanPass(load,T))//Can't get off onto anything that wouldn't let you pass normally + 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) - else - load.loc = src.loc//Drops you right there, so you shouldn't be able to get yourself stuck load = null @@ -476,9 +472,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)) @@ -486,47 +482,56 @@ 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 (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) + pathset = 1 //Indicates the AI's custom path is initialized. + start() -/obj/machinery/bot/mulebot/process() +/obj/machinery/bot/mulebot/bot_process() if(!has_power()) on = 0 return if(on) var/speed = (wires.Motor1() ? 1 : 0) + (wires.Motor2() ? 2 : 0) //world << "speed: [speed]" + var/num_steps = 0 switch(speed) if(0) // do nothing if(1) - process_bot() - spawn(2) - process_bot() - sleep(2) - process_bot() - sleep(2) - process_bot() - sleep(2) - process_bot() + num_steps = 10 if(2) - process_bot() - spawn(4) - process_bot() + num_steps = 5 if(3) - process_bot() + num_steps = 3 + + if(num_steps) + process_bot() + num_steps-- + spawn(0) + for(var/i=num_steps,i>0,i--) + sleep(2) + process_bot() if(refresh) updateDialog() /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() @@ -539,26 +544,28 @@ var/global/mulebot_count = 0 if(next == loc) path -= next return + + if(istype( next, /turf/simulated)) //world << "at ([x],[y]) moving to ([next.x],[next.y])" - if(bloodiness) - var/turf/simulated/T=loc - if(istype(T)) - var/goingdir=0 - var/newdir = get_dir(next, loc) - if(newdir == dir) - goingdir = newdir - else - newdir = newdir | dir - if(newdir == 3) - newdir = 1 - else if(newdir == 12) - newdir = 4 - goingdir = newdir - T.AddTracks(/obj/effect/decal/cleanable/blood/tracks/wheels,list(),0,goingdir,currentBloodColor) + + if(bloodiness) + var/obj/effect/decal/cleanable/blood/tracks/B = new(loc) + var/newdir = get_dir(next, loc) + if(newdir == dir) + B.dir = newdir + else + newdir = newdir | dir + if(newdir == 3) + newdir = 1 + else if(newdir == 12) + newdir = 4 + B.dir = newdir bloodiness-- + + var/moved = step_towards(src, next) // attempt to move if(cell) cell.use(1) if(moved) // successful move @@ -567,14 +574,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 @@ -583,73 +590,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 > 5) // attempt 5 times before recomputing + 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") @@ -658,9 +666,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 @@ -668,17 +676,27 @@ 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 = SUP_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 + 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 = 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*/) unload(loaddir) else // not loaded @@ -693,14 +711,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 @@ -712,23 +732,22 @@ var/global/mulebot_count = 0 var/mob/M = obs if(ismob(M)) if(istype(M,/mob/living/silicon/robot)) - src.visible_message("\red [src] bumps into [M]!") + visible_message("[src] bumps into [M]!") else - src.visible_message("\red [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() return get_turf(src) -// called from mob/living/carbon/human/HasEntered() +// 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("\red [src] drives over [H]!") + visible_message("\red [src] drives over [H]!") playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) var/damage = rand(5,15) @@ -746,12 +765,6 @@ var/global/mulebot_count = 0 bloodiness += 4 currentBloodColor="#A10808" // For if species get different blood colors. -/obj/machinery/bot/mulebot/proc/RunOverCreature(var/mob/living/H,var/bloodcolor) - src.visible_message("\red [src] drives over [H]!") - playsound(src.loc, 'sound/effects/splat.ogg', 50, 1) - bloodiness += 4 - currentBloodColor=bloodcolor // For if species get different blood colors. - // player on mulebot attempted to move /obj/machinery/bot/mulebot/relaymove(var/mob/user) if(user.stat) @@ -784,7 +797,7 @@ var/global/mulebot_count = 0 // process control input switch(recv) if("stop") - mode = 0 + bot_reset() return if("go") @@ -820,6 +833,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 @@ -834,12 +848,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 @@ -861,17 +881,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, @@ -891,7 +911,7 @@ var/global/mulebot_count = 0 /obj/machinery/bot/mulebot/explode() - src.visible_message("\red [src] blows apart!", 1) + visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) new /obj/item/device/assembly/prox_sensor(Tsec) @@ -907,6 +927,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) - del(src) + qdel(src) diff --git a/code/game/machinery/bots/secbot.dm b/code/game/machinery/bots/secbot.dm index 7e22805b5f9..0afd2215f3a 100644 --- a/code/game/machinery/bots/secbot.dm +++ b/code/game/machinery/bots/secbot.dm @@ -1,5 +1,5 @@ /obj/machinery/bot/secbot - name = "Securitron" + name = "\improper Securitron" desc = "A little security robot. He looks less than thrilled." icon = 'icons/obj/aibots.dmi' icon_state = "secbot0" @@ -17,41 +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/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 = 1 //If true, arrest people for weapons if they don't have access + var/weaponscheck = 1 //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/next_harm_time = 0 - - 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 - - - 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 + radio_frequency = SEC_FREQ //Security channel + bot_type = SEC_BOT + bot_filter = RADIO_SECBOT //List of weapons that secbots will not arrest for var/safe_weapons = list(\ @@ -59,24 +33,24 @@ /obj/item/weapon/gun/energy/laser/redtag,\ /obj/item/weapon/gun/energy/laser/practice,\ /obj/item/weapon/melee/telebaton,\ - /obj/item/weapon/melee/baton/loaded/ntcane) + /obj/item/weapon/gun/energy/kinetic_accelerator) /obj/machinery/bot/secbot/beepsky name = "Officer Beep O'sky" desc = "It's Officer Beep O'sky! Powered by a potato and a shot of whiskey." idcheck = 0 - weaponscheck = 1 + weaponscheck = 0 auto_patrol = 1 /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 = "helmet/signaler assembly" - desc = "Some sort of bizarre assembly." + name = "incomplete securitron assembly" + desc = "Some sort of bizarre assembly made from a proximity sensor, helmet, and signaler." icon = 'icons/obj/aibots.dmi' icon_state = "helmet_signaler" item_state = "helmet" @@ -87,30 +61,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) . = ..() @@ -121,151 +103,123 @@ /obj/machinery/bot/secbot/interact(mob/user as mob) 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 for No ID: []
+Arrest Unidentifiable Persons: []
Arrest for Unauthorized Weapons: []
Arrest for Warrant: []
-
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 controls") + var/datum/browser/popup = new(user, "autosec", "Automatic Security Unit v1.6") popup.set_content(dat) popup.open() return /obj/machinery/bot/secbot/Topic(href, href_list) - usr.set_machine(src) - src.add_fingerprint(usr) - if((href_list["power"]) && (src.allowed(usr))) - if(src.on) - 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."]" + updateUsrDialog() else if(emagged) user << "ERROR" if(open) - user << "\red Please close the access panel before locking it." + user << "Please close the access panel before locking it." else - user << "\red Access denied." + user << " Access denied." else ..() - if(!istype(W, /obj/item/weapon/screwdriver) && (W.force) && (!src.target)) - src.target = user - src.mode = SECBOT_HUNT + 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) && (!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) + target = user + mode = BOT_HUNT /obj/machinery/bot/secbot/Emag(mob/user as mob) ..() - if((!locked && open) || !user) - if(user) user << "\red You short out [src]'s target assessment circuits." - spawn(0) - for(var/mob/O in hearers(src, null)) - O.show_message("\red [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 -/obj/machinery/bot/secbot/process() - //set background = 1 + if(emagged == 2) + if(user) + user << " You short out [src]'s target assessment circuits." + oldtarget_name = user.name + visible_message("[src] buzzes oddly!") + declare_arrests = 0 + icon_state = "secbot[on]" - if(!src.on) +/obj/machinery/bot/secbot/bot_process() + if (!..()) 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) - // 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 + if(frustration >= 8) walk_to(src,0) + back_to_idle() + 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) - src.icon_state = "secbot[src.on]" - var/mob/living/carbon/M = src.target - var/maxstuns = 4 + icon_state = "secbot[on]" + var/mob/living/carbon/M = target if(istype(M, /mob/living/carbon/human)) - if(M.stuttering < 5 && (!(M_HULK in M.mutations))) + if( M.stuttering < 5 && !(M_HULK in M.mutations) ) M.stuttering = 5 M.Stun(5) M.Weaken(5) @@ -275,412 +229,146 @@ Auto Patrol: []"}, M.Stun(5) if(declare_arrests) + //var/area/location = get_area(src) declare_arrest() - 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]!") - maxstuns-- - 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 else - src.frustration = 8 + back_to_idle() - 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 + // 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( !Adjacent(target) || !isturf(target.loc) || target.weakened < 2 ) + back_to_hunt() return - if(istype(src.target,/mob/living/carbon)) - var/mob/living/carbon/C = target - if(!C.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]!") - - spawn(60) - if(get_dist(src, src.target) <= 1) - /*if(src.target.handcuffed) - return*/ - - if(istype(src.target,/mob/living/carbon)) - C = target - if(!C.handcuffed) - C.handcuffed = new /obj/item/weapon/handcuffs(target) - C.update_inv_handcuffed() //update the handcuffs overlay - - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.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) - // 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) - + if(iscarbon(target) && target.canBeHandcuffed()) + if(!arrest_type) + if(!target.handcuffed) //he's not cuffed? Try to cuff him! + mode = BOT_ARREST + playsound(loc, 'sound/weapons/handcuffs.ogg', 30, 1, -2) + target.visible_message("[src] is trying to put handcuffs on [target]!",\ + "[src] is trying to put handcuffs on [target]!") + spawn(80) + if( !Adjacent(target) || !isturf(target.loc) ) //if he's in a closet or not adjacent, we cancel cuffing. + return + if(!target.handcuffed) + target.handcuffed = new /obj/item/weapon/handcuffs(target) + target.update_inv_handcuffed(0) //update the handcuffs overlay + 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() + return else - mode = SECBOT_IDLE - src.target = null - src.anchored = 0 - src.last_found = world.time - src.frustration = 0 - - if(SECBOT_ARREST) // arresting - - if(!target || !istype(target, /mob/living/carbon)) - src.anchored = 0 - mode = SECBOT_IDLE + back_to_idle() return - else - var/mob/living/carbon/C = target - if(!C.handcuffed) - src.anchored = 0 - mode = SECBOT_IDLE - return + if(BOT_ARREST) + if (!target) + anchored = 0 + mode = BOT_IDLE + last_found = world.time + frustration = 0 + return - if(SECBOT_START_PATROL) // start a patrol - if(path != null) - if(path.len > 0 && patrol_target) // have a valid path, so just resume - mode = SECBOT_PATROL - return + if(target.handcuffed) //no target or target cuffed? back to idle. + back_to_idle() + 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 + 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. + mode = BOT_PREP_ARREST + anchored = 0 + if(BOT_START_PATROL) + look_for_perp() + start_patrol() - else // no patrol target, so need a new one - find_patrol_target() - speak("Engaging patrol mode.") + if(BOT_PATROL) + look_for_perp() + bot_patrol() - 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 +/obj/machinery/bot/secbot/proc/back_to_idle() + anchored = 0 + mode = BOT_IDLE + target = null + last_found = world.time + frustration = 0 + spawn(0) + bot_process() //ensure bot quickly responds -// 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() - - +/obj/machinery/bot/secbot/proc/back_to_hunt() + anchored = 0 + frustration = 0 + mode = BOT_HUNT + spawn(0) + bot_process() //ensure bot quickly responds // 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 + bot_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) && !istype(D,/obj/machinery/door/poddoor)) - 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) - spawn(0) - if(M) - var/turf/T = get_turf(src) - M:loc = T -*/ - - - /obj/machinery/bot/secbot/explode() walk_to(src,0) - src.visible_message("\red [src] blows apart!", 1) + visible_message("[src] blows apart!") 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 += image('icons/obj/aibots.dmi', "hs_hole") - Sa.created_name = src.name + Sa.overlays += "hs_hole" + Sa.created_name = name new /obj/item/device/assembly/prox_sensor(Tsec) - new /obj/item/weapon/melee/baton/loaded(Tsec) + new /obj/item/weapon/melee/baton(Tsec) if(prob(50)) new /obj/item/robot_parts/l_arm(Tsec) @@ -689,14 +377,14 @@ Auto Patrol: []"}, s.set_up(3, 1, src) s.start() - new /obj/effect/decal/cleanable/oil(src.loc) - del(src) + 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 @@ -706,63 +394,70 @@ 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) - del(S) + qdel(S) var/obj/item/weapon/secbot_assembly/A = new /obj/item/weapon/secbot_assembly user.put_in_hands(A) - user << "You add the signaler to the helmet." - user.drop_from_inventory(src) - del(src) + user << "You add the signaler to the helmet." + user.before_take_item(src, 1) + qdel(src) else return -/obj/item/weapon/secbot_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) +/obj/item/weapon/secbot_assembly/attackby(obj/item/I, mob/user) ..() - if((istype(W, /obj/item/weapon/weldingtool)) && (!src.build_step)) - var/obj/item/weapon/weldingtool/WT = W - if(WT.remove_fuel(0,user)) - src.build_step++ - src.overlays += image('icons/obj/aibots.dmi', "hs_hole") - user << "You weld a hole in [src]!" + if(istype(I, /obj/item/weapon/weldingtool)) + if(!build_step) + var/obj/item/weapon/weldingtool/WT = I + if(WT.remove_fuel(0, user)) + build_step++ + overlays += "hs_hole" + user << "You weld a hole in [src]!" + else if(build_step == 1) + var/obj/item/weapon/weldingtool/WT = I + if(WT.remove_fuel(0, user)) + build_step-- + overlays -= "hs_hole" + user << "You weld the hole in [src] shut!" - else if(isprox(W) && (src.build_step == 1)) + else if(isprox(I) && (build_step == 1)) user.drop_item() - src.build_step++ - user << "You add the prox sensor to [src]!" - src.overlays += image('icons/obj/aibots.dmi', "hs_eye") - src.name = "helmet/signaler/prox sensor assembly" - del(W) + build_step++ + user << "You add the prox sensor to [src]!" + overlays += "hs_eye" + name = "helmet/signaler/prox sensor assembly" + qdel(I) - else if(((istype(W, /obj/item/robot_parts/l_arm)) || (istype(W, /obj/item/robot_parts/r_arm))) && (src.build_step == 2)) + else if(((istype(I, /obj/item/robot_parts/l_arm)) || (istype(I, /obj/item/robot_parts/r_arm))) && (build_step == 2)) user.drop_item() - src.build_step++ - user << "You add the robot arm to [src]!" - src.name = "helmet/signaler/prox sensor/robot arm assembly" - src.overlays += image('icons/obj/aibots.dmi', "hs_arm") - del(W) + build_step++ + user << "You add the robot arm to [src]!" + name = "helmet/signaler/prox sensor/robot arm assembly" + overlays += "hs_arm" + qdel(I) - else if((istype(W, /obj/item/weapon/melee/baton)) && (src.build_step >= 3)) + else if((istype(I, /obj/item/weapon/melee/baton)) && (build_step >= 3)) user.drop_item() - src.build_step++ - user << "You complete the Securitron! Beep boop." + build_step++ + user << "You complete the Securitron! Beep boop." var/obj/machinery/bot/secbot/S = new /obj/machinery/bot/secbot S.loc = get_turf(src) - S.name = src.created_name - del(W) - del(src) + S.name = created_name + qdel(I) + 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) + else if(istype(I, /obj/item/weapon/pen)) + var/t = stripped_input(user, "Enter new robot name", name, created_name,MAX_NAME_LEN) if(!t) return - if(!in_range(src, usr) && src.loc != usr) + if(!in_range(src, usr) && loc != usr) return created_name = t - else if(istype(W, /obj/item/weapon/screwdriver)) + else if(istype(I, /obj/item/weapon/screwdriver)) if(!build_step) new /obj/item/device/assembly/signaler(get_turf(src)) new /obj/item/clothing/head/helmet(get_turf(src)) @@ -780,9 +475,9 @@ Auto Patrol: []"}, new /obj/item/robot_parts/l_arm(get_turf(src)) user << "You remove the robot arm from [src]." build_step-- - -/obj/machinery/bot/secbot/proc/declare_arrest() + +/obj/machinery/bot/secbot/proc/declare_arrest() var/area/location = get_area(src) for(var/mob/living/carbon/human/human in world) if((human.z == src.z) && istype(human.glasses, /obj/item/clothing/glasses/hud/security) || istype(human.glasses, /obj/item/clothing/glasses/sunglasses/sechud) && !human.blinded) - human << "\icon[human.glasses] [src.name] is [arrest_type ? "detaining" : "arresting"] level [threatlevel] [target] in [location]" + human << "\icon[human.glasses] [src.name] is [arrest_type ? "detaining" : "arresting"] level [threatlevel] threat [target] in [location]" \ No newline at end of file diff --git a/code/game/machinery/navbeacon.dm b/code/game/machinery/navbeacon.dm index 71f450e70fe..59bf0f2ea17 100644 --- a/code/game/machinery/navbeacon.dm +++ b/code/game/machinery/navbeacon.dm @@ -20,144 +20,146 @@ req_access = list(access_engine) - New() - ..() +/obj/machinery/navbeacon/New() + ..() - set_codes() + set_codes() - var/turf/T = loc - hide(T.intact) + var/turf/T = loc + hide(T.intact) - spawn(5) // must wait for map loading to finish - if(radio_controller) - radio_controller.add_object(src, freq, RADIO_NAVBEACONS) + spawn(50) // must wait for map loading to finish + if(radio_controller) + radio_controller.add_object(src, freq, RADIO_NAVBEACONS) - // set the transponder codes assoc list from codes_txt - proc/set_codes() - if(!codes_txt) - return +/obj/machinery/navbeacon/Destroy() + if(radio_controller) + radio_controller.remove_object(src, freq) + ..() - codes = new() +// set the transponder codes assoc list from codes_txt +/obj/machinery/navbeacon/proc/set_codes() + if(!codes_txt) + return - var/list/entries = text2list(codes_txt, ";") // entries are separated by semicolons + codes = new() - for(var/e in entries) - var/index = findtext(e, "=") // format is "key=value" - if(index) - var/key = copytext(e, 1, index) - var/val = copytext(e, index+1) - codes[key] = val - else - codes[e] = "1" + var/list/entries = text2list(codes_txt, ";") // entries are separated by semicolons + + for(var/e in entries) + var/index = findtext(e, "=") // format is "key=value" + if(index) + var/key = copytext(e, 1, index) + var/val = copytext(e, index+1) + codes[key] = val + else + codes[e] = "1" - // called when turf state changes - // hide the object if turf is intact - hide(var/intact) - invisibility = intact ? 101 : 0 +// called when turf state changes +// hide the object if turf is intact +/obj/machinery/navbeacon/hide(var/intact) + invisibility = intact ? 101 : 0 + updateicon() + +// update the icon_state +/obj/machinery/navbeacon/proc/updateicon() + var/state="navbeacon[open]" + + if(invisibility) + icon_state = "[state]-f" // if invisible, set icon to faded version + // in case revealed by T-scanner + else + icon_state = "[state]" + + +// look for a signal of the form "findbeacon=X" +// where X is any +// or the location +// or one of the set transponder keys +// if found, return a signal +/obj/machinery/navbeacon/receive_signal(datum/signal/signal) + var/request = signal.data["findbeacon"] + if(request && ((request in codes) || request == "any" || request == location)) + spawn(1) + post_signal() + +// return a signal giving location and transponder codes + +/obj/machinery/navbeacon/proc/post_signal() + 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 + signal.data["beacon"] = location + + for(var/key in codes) + signal.data[key] = codes[key] + frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) + + +/obj/machinery/navbeacon/attackby(var/obj/item/I, var/mob/user) + var/turf/T = loc + if(T.intact) + return // prevent intraction when T-scanner revealed + + if(istype(I, /obj/item/weapon/screwdriver)) + open = !open + + user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") + updateicon() - // update the icon_state - proc/updateicon() - var/state="navbeacon[open]" - - if(invisibility) - icon_state = "[state]-f" // if invisible, set icon to faded version - // in case revealed by T-scanner - else - icon_state = "[state]" - - - // look for a signal of the form "findbeacon=X" - // where X is any - // or the location - // or one of the set transponder keys - // if found, return a signal - receive_signal(datum/signal/signal) - - var/request = signal.data["findbeacon"] - if(request && ((request in codes) || request == "any" || request == location)) - spawn(1) - post_signal() - - // return a signal giving location and transponder codes - - proc/post_signal() - - 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 - signal.data["beacon"] = location - - for(var/key in codes) - signal.data[key] = codes[key] - - frequency.post_signal(src, signal, filter = RADIO_NAVBEACONS) - - - attackby(var/obj/item/I, var/mob/user) - var/turf/T = loc - if(T.intact) - return // prevent intraction when T-scanner revealed - - if(istype(I, /obj/item/weapon/screwdriver)) - open = !open - - user.visible_message("[user] [open ? "opens" : "closes"] the beacon's cover.", "You [open ? "open" : "close"] the beacon's cover.") - - updateicon() - - else if (istype(I, /obj/item/weapon/card/id)||istype(I, /obj/item/device/pda)) - if(open) - if (src.allowed(user)) - src.locked = !src.locked - user << "Controls are now [src.locked ? "locked." : "unlocked."]" - else - user << "\red Access denied." - updateDialog() + else if (istype(I, /obj/item/weapon/card/id)||istype(I, /obj/item/device/pda)) + if(open) + if (src.allowed(user)) + src.locked = !src.locked + user << "Controls are now [src.locked ? "locked." : "unlocked."]" else - user << "You must open the cover first!" + user << "Access denied." + updateDialog() + else + user << "You must open the cover first!" + return + +/obj/machinery/navbeacon/attack_ai(var/mob/user) + interact(user, 1) + +/obj/machinery/navbeacon/attack_paw() + return + +/obj/machinery/navbeacon/attack_hand(var/mob/user) + interact(user, 0) + +/obj/machinery/navbeacon/interact(var/mob/user, var/ai = 0) + var/turf/T = loc + if(T.intact) + return // prevent intraction when T-scanner revealed + + if(!open && !ai) // can't alter controls if not open, unless you're an AI + user << "The beacon's control cover is closed." return - attack_ai(var/mob/user) - interact(user, 1) - attack_paw() - return + var/t - attack_hand(var/mob/user) - interact(user, 0) - - interact(var/mob/user, var/ai = 0) - var/turf/T = loc - if(T.intact) - return // prevent intraction when T-scanner revealed - - if(!open && !ai) // can't alter controls if not open, unless you're an AI - user << "The beacon's control cover is closed." - return - - - var/t - - if(locked && !ai) - t = {"Navigation Beacon

+ if(locked && !ai) + t = {"Navigation Beacon

(swipe card to unlock controls)
Frequency: [format_frequency(freq)]

Location: [location ? location : "(none)"]
Transponder Codes:
    "} - for(var/key in codes) - t += "
  • [key] ... [codes[key]]" - t+= "
      " + for(var/key in codes) + t += "
    • [key] ... [codes[key]]" + t+= "
        " - else + else - t = {"Navigation Beacon

        + t = {"Navigation Beacon

        (swipe card to lock controls)
        Frequency: - @@ -169,76 +171,70 @@ Frequency: Location: [location ? location : "(none)"]
        Transponder Codes:
          "} - for(var/key in codes) - t += "
        • [key] ... [codes[key]]" - t += " (edit)" - t += " (delete)
          " - t += "(add new)
          " - t+= "
            " + for(var/key in codes) + t += "
          • [key] ... [codes[key]]" + t += " (edit)" + t += " (delete)
            " + t += "(add new)
            " + t+= "
              " - user << browse(t, "window=navbeacon") - onclose(user, "navbeacon") + user << browse(t, "window=navbeacon") + onclose(user, "navbeacon") + return + +/obj/machinery/navbeacon/Topic(href, href_list) + if(..()) return + if(open && !locked) + usr.set_machine(src) - Topic(href, href_list) - ..() - if (usr.stat) - return - if ((in_range(src, usr) && istype(src.loc, /turf)) || (istype(usr, /mob/living/silicon))) - if(open && !locked) - usr.set_machine(src) + if (href_list["freq"]) + freq = sanitize_frequency(freq + text2num(href_list["freq"])) + updateDialog() - if (href_list["freq"]) - freq = sanitize_frequency(freq + text2num(href_list["freq"])) - updateDialog() + else if(href_list["locedit"]) + var/newloc = copytext(sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null),1,MAX_MESSAGE_LEN) + if(newloc) + location = newloc + updateDialog() - else if(href_list["locedit"]) - var/newloc = copytext(sanitize(input("Enter New Location", "Navigation Beacon", location) as text|null),1,MAX_MESSAGE_LEN) - if(newloc) - location = newloc - updateDialog() + else if(href_list["edit"]) + var/codekey = href_list["code"] - else if(href_list["edit"]) - var/codekey = href_list["code"] + var/newkey = input("Enter Transponder Code Key", "Navigation Beacon", codekey) as text|null + if(!newkey) + return - var/newkey = input("Enter Transponder Code Key", "Navigation Beacon", codekey) as text|null - if(!newkey) - return + var/codeval = codes[codekey] + var/newval = input("Enter Transponder Code Value", "Navigation Beacon", codeval) as text|null + if(!newval) + newval = codekey + return - var/codeval = codes[codekey] - var/newval = input("Enter Transponder Code Value", "Navigation Beacon", codeval) as text|null - if(!newval) - newval = codekey - return + codes.Remove(codekey) + codes[newkey] = newval - codes.Remove(codekey) - codes[newkey] = newval + updateDialog() - updateDialog() + else if(href_list["delete"]) + var/codekey = href_list["code"] + codes.Remove(codekey) + updateDialog() - else if(href_list["delete"]) - var/codekey = href_list["code"] - codes.Remove(codekey) - updateDialog() + else if(href_list["add"]) - else if(href_list["add"]) - - var/newkey = input("Enter New Transponder Code Key", "Navigation Beacon") as text|null - if(!newkey) - return - - var/newval = input("Enter New Transponder Code Value", "Navigation Beacon") as text|null - if(!newval) - newval = "1" - return - - if(!codes) - codes = new() - - codes[newkey] = newval - - updateDialog() + var/newkey = input("Enter New Transponder Code Key", "Navigation Beacon") as text|null + if(!newkey) + return + var/newval = input("Enter New Transponder Code Value", "Navigation Beacon") as text|null + if(!newval) + newval = "1" + return + if(!codes) + codes = new() + codes[newkey] = newval + updateDialog() \ No newline at end of file diff --git a/code/game/objects/items/devices/PDA/radio.dm b/code/game/objects/items/devices/PDA/radio.dm index 616584b0429..63f1082884f 100644 --- a/code/game/objects/items/devices/PDA/radio.dm +++ b/code/game/objects/items/devices/PDA/radio.dm @@ -1,209 +1,218 @@ /obj/item/radio/integrated - name = "PDA radio module" - desc = "An electronic radio system of nanotrasen origin." + name = "\improper PDA radio module" + desc = "An electronic radio system of Nanotrasen origin." icon = 'icons/obj/module.dmi' icon_state = "power_mod" var/obj/item/device/pda/hostpda = null - - var/on = 0 //Are we currently active? - var/menu_message = "" - - New() - ..() - if (istype(loc.loc, /obj/item/device/pda)) - hostpda = loc.loc - - proc/post_signal(var/freq, var/key, var/value, var/key2, var/value2, var/key3, var/value3, s_filter) - - //world << "Post: [freq]: [key]=[value], [key2]=[value2]" - 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 - signal.data[key] = value - if(key2) - signal.data[key2] = value2 - if(key3) - signal.data[key3] = value3 - - frequency.post_signal(src, signal, filter = s_filter) - - return - - 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/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 - // create a new QM cartridge, and register to receive bot control & beacon message - New() - ..() + var/on = 0 //Are we currently active?? + var/menu_message = "" + +/obj/item/radio/integrated/New() + ..() + if (istype(loc.loc, /obj/item/device/pda)) + hostpda = loc.loc + if (bot_filter) spawn(5) - if(radio_controller) - radio_controller.add_object(src, control_freq, filter = RADIO_SECBOT) - Destroy() - if(radio_controller) - radio_controller.remove_object(src, control_freq) - ..() - // receive radio signals - // can detect bot status signals - // create/populate list as they are recvd + add_to_radio(bot_filter) - receive_signal(datum/signal/signal) -// var/obj/item/device/pda/P = src.loc +/obj/item/radio/integrated/Destroy() + if(radio_controller) + radio_controller.remove_object(src, control_freq) + ..() - /* - world << "recvd:[P] : [signal.source]" - for(var/d in signal.data) - world << "- [d] = [signal.data[d]]" +/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 + + var/datum/signal/signal = new() + signal.source = src + signal.transmission_method = 1 + signal.data[key] = value + if(key2) + signal.data[key2] = value2 + if(key3) + signal.data[key3] = value3 + if(key4) + signal.data[key4] = value4 + + frequency.post_signal(src, signal, filter = s_filter) + +/obj/item/radio/integrated/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(!botlist) - botlist = new() - if(!(signal.source in botlist)) - botlist += signal.source + if (signal.data["type"] == bot_type) + if(!botlist) + botlist = new() - if(active == signal.source) - var/list/b = signal.data - botstatus = b.Copy() + if(!(signal.source in botlist)) + botlist += signal.source -// if (istype(P)) P.updateSelfDialog() + if(active == signal.source) + var/list/b = signal.data + botstatus = b.Copy() - Topic(href, href_list) - ..() - var/obj/item/device/pda/PDA = src.hostpda +/obj/item/radio/integrated/Topic(href, href_list) + ..() + var/obj/item/device/pda/PDA = src.hostpda - switch(href_list["op"]) + switch(href_list["op"]) - if("control") - active = locate(href_list["bot"]) - post_signal(control_freq, "command", "bot_status", "active", active, s_filter = RADIO_SECBOT) + if("control") + active = locate(href_list["bot"]) + 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) + if("scanbots") // find all bots + botlist = null + post_signal(control_freq, "command", "bot_status", s_filter = bot_filter) - if("botlist") - active = null + 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) + if("stop", "go") + 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) + if("summon") + 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) + +/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 - New() - ..() - spawn(5) - if(radio_controller) - radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT) - radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) - spawn(10) - post_signal(beacon_freq, "findbeacon", "delivery", s_filter = RADIO_NAVBEACONS) - - Destroy() +// create a new QM cartridge, and register to receive bot control & beacon message +/obj/item/radio/integrated/mule/New() + ..() + spawn(5) if(radio_controller) - radio_controller.remove_object(src, control_freq) - radio_controller.remove_object(src, beacon_freq) - // receive radio signals - // can detect bot status signals - // and beacon locations - // create/populate lists as they are recvd + radio_controller.add_object(src, control_freq, filter = RADIO_MULEBOT) + radio_controller.add_object(src, beacon_freq, filter = RADIO_NAVBEACONS) + spawn(10) + post_signal(beacon_freq, "findbeacon", "delivery", s_filter = RADIO_NAVBEACONS) - receive_signal(datum/signal/signal) -// var/obj/item/device/pda/P = src.loc +/obj/item/radio/integrated/mule/Destroy() + if(radio_controller) + radio_controller.remove_object(src,beacon_freq) + ..() - /* - world << "recvd:[P] : [signal.source]" - for(var/d in signal.data) - world << "- [d] = [signal.data[d]]" - */ - if(signal.data["type"] == "mulebot") - if(!botlist) - botlist = new() +// receive radio signals +// can detect bot status signals +// and beacon locations +// create/populate lists as they are recvd - if(!(signal.source in botlist)) - botlist += signal.source +/obj/item/radio/integrated/mule/receive_signal(datum/signal/signal) +// var/obj/item/device/pda/P = src.loc - if(active == signal.source) - var/list/b = signal.data - botstatus = b.Copy() + /* + world << "recvd:[P] : [signal.source]" + for(var/d in signal.data) + world << "- [d] = [signal.data[d]]" + */ + if(signal.data["type"] == MULE_BOT) + if(!botlist) + botlist = new() - else if(signal.data["beacon"]) - if(!beacons) - beacons = new() + if(!(signal.source in botlist)) + botlist += signal.source - beacons[signal.data["beacon"] ] = signal.source + if(active == signal.source) + var/list/b = signal.data + botstatus = b.Copy() + + else if(signal.data["beacon"]) + if(!beacons) + beacons = new() + + beacons[signal.data["beacon"] ] = signal.source // if(istype(P)) P.updateSelfDialog() - Topic(href, href_list) - ..() - var/cmd = "command" - if(active) cmd = "command [active.suffix]" +/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]" - switch(href_list["op"]) + switch(href_list["op"]) - if("control") - active = locate(href_list["bot"]) - post_signal(control_freq, cmd, "bot_status", s_filter = RADIO_MULEBOT) + if("control") + active = locate(href_list["bot"]) - if("scanbots") // find all bots - botlist = null - post_signal(control_freq, "command", "bot_status", s_filter = RADIO_MULEBOT) + if("scanbots") // find all bots + botlist = null - if("botlist") - active = null + if("botlist") + active = null + if("unload") + post_signal(control_freq, cmd, "unload", 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) - 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) + if("reton") + post_signal(control_freq, cmd, "autoret", "value", 1, 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) - 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("pickon") + post_signal(control_freq, cmd, "autopick", "value", 1, 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) + 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) @@ -218,37 +227,42 @@ var/last_transmission var/datum/radio_frequency/radio_connection - New() - ..() - if(radio_controller) - initialize() +/obj/item/radio/integrated/signal/New() + ..() + if(radio_controller) + initialize() - initialize() - if (src.frequency < 1441 || src.frequency > 1489) - src.frequency = sanitize_frequency(src.frequency) - - set_frequency(frequency) - - proc/set_frequency(new_frequency) +/obj/item/radio/integrated/signal/Destroy() + if(radio_controller) radio_controller.remove_object(src, frequency) - frequency = new_frequency - radio_connection = radio_controller.add_object(src, frequency) + ..() - proc/send_signal(message="ACTIVATE") +/obj/item/radio/integrated/signal/initialize() + if (src.frequency < 1441 || src.frequency > 1489) + src.frequency = sanitize_frequency(src.frequency) - if(last_transmission && world.time < (last_transmission + 5)) - return - last_transmission = world.time + set_frequency(frequency) - var/time = time2text(world.realtime,"hh:mm:ss") - var/turf/T = get_turf(src) - lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") +/obj/item/radio/integrated/signal/proc/set_frequency(new_frequency) + radio_controller.remove_object(src, frequency) + frequency = new_frequency + radio_connection = radio_controller.add_object(src, frequency) - var/datum/signal/signal = new - signal.source = src - signal.encryption = code - signal.data["message"] = message - - radio_connection.post_signal(src, signal) +/obj/item/radio/integrated/signal/proc/send_signal(message="ACTIVATE") + if(last_transmission && world.time < (last_transmission + 5)) return + last_transmission = world.time + + var/time = time2text(world.realtime,"hh:mm:ss") + var/turf/T = get_turf(src) + lastsignalers.Add("[time] : [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) : [format_frequency(frequency)]/[code]") + + var/datum/signal/signal = new + signal.source = src + signal.encryption = code + signal.data["message"] = message + + radio_connection.post_signal(src, signal) + + return \ No newline at end of file diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 76b5c509f6f..a129d887128 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -10,9 +10,29 @@ var/max_fire_temperature_sustained = 0 //The max temperature of the fire which it was subjected to var/dirt = 0 var/dirtoverlay = null + /turf/simulated/New() ..() levelupdate() + +/turf/simulated/proc/MakeSlippery(var/wet_setting = 1) // 1 = Water, 2 = Lube + if(wet >= wet_setting) + return + wet = wet_setting + if(wet_setting == 1) + if(wet_overlay) + overlays -= wet_overlay + wet_overlay = null + wet_overlay = image('icons/effects/water.dmi', src, "wet_floor_static") + overlays += wet_overlay + + spawn(rand(790, 820)) // Purely so for visual effect + if(!istype(src, /turf/simulated)) //Because turfs don't get deleted, they change, adapt, transform, evolve and deform. they are one and they are all. + return + if(wet > wet_setting) return + wet = 0 + if(wet_overlay) + overlays -= wet_overlay /turf/simulated/proc/AddTracks(var/typepath,var/mob,var/bloodDNA,var/comingdir,var/goingdir) var/obj/effect/decal/cleanable/blood/tracks/tracks = locate(typepath) in src diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 07c144f1c08..0a656b903fb 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -218,7 +218,7 @@ return var/old_lumcount = lighting_lumcount - initial(lighting_lumcount) - + var/old_opacity = opacity //world << "Replacing [src.type] with [N]" if(connections) connections.erase_all() @@ -246,6 +246,11 @@ W.lighting_changed = 1 lighting_controller.changed_turfs += W + + if(old_opacity != W.opacity) //opacity has changed. Need to update surrounding lights + if(W.lighting_lumcount) //unless we're being illuminated, don't bother (may be buggy, hard to test) + W.UpdateAffectingLights() + if (istype(W,/turf/simulated/floor)) W.RemoveLattice() diff --git a/code/global.dm b/code/global.dm index 249946633a9..7f4e9487828 100644 --- a/code/global.dm +++ b/code/global.dm @@ -319,3 +319,6 @@ var/recall_time_limit=72000 //added for Xenoarchaeology, might be useful for other stuff var/global/list/alphabet_uppercase = list("A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z") + +// AI controlled bots +var/global/list/aibots = list() \ No newline at end of file diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index 8a1bea5d268..882d972ddd7 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -572,8 +572,5 @@ src << "You do not have enough chemicals stored to reproduce." return -/mob/living/carbon/proc/assess_threat() - return - /mob/living/carbon/proc/canBeHandcuffed() return 0 \ No newline at end of file diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm index 703df1732f1..250e17f4101 100644 --- a/code/modules/mob/living/carbon/human/human.dm +++ b/code/modules/mob/living/carbon/human/human.dm @@ -1555,7 +1555,7 @@ //Check for ID var/obj/item/weapon/card/id/idcard = get_idcard() - if(judgebot.idcheck && !idcard) + if(judgebot.idcheck && !idcard && name=="Unknown") threatcount += 4 //Check for weapons @@ -1595,7 +1595,7 @@ //Agent cards lower threatlevel. if(istype(idcard, /obj/item/weapon/card/id/syndicate)) - threatcount -= 2 + threatcount -= 5 return threatcount diff --git a/code/modules/mob/living/carbon/monkey/monkey.dm b/code/modules/mob/living/carbon/monkey/monkey.dm index 04b3e404b80..5c8345f6c5f 100644 --- a/code/modules/mob/living/carbon/monkey/monkey.dm +++ b/code/modules/mob/living/carbon/monkey/monkey.dm @@ -507,6 +507,10 @@ return 10 //Everyone is a criminal! var/threatcount = 0 + //Securitrons can't identify monkeys + if(!lasercolor && judgebot.idcheck ) + threatcount += 4 + //Lasertag bullshit if(lasercolor) if(lasercolor == "b")//Lasertag turrets target the opposing team, how great is that? -Sieve diff --git a/code/modules/mob/living/silicon/silicon.dm b/code/modules/mob/living/silicon/silicon.dm index 9289de9139b..d2e067f8cc8 100644 --- a/code/modules/mob/living/silicon/silicon.dm +++ b/code/modules/mob/living/silicon/silicon.dm @@ -233,4 +233,5 @@ step(AM, t) now_pushing = null - +/mob/living/silicon/assess_threat() //Secbots won't hunt silicon units + return -10 diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm index 97b1fb52146..7842bc402f6 100644 --- a/code/modules/mob/mob.dm +++ b/code/modules/mob/mob.dm @@ -869,6 +869,7 @@ var/list/slot_equipment_priority = list( \ stat(null, "Mob-[master_controller.mobs_cost]\t#[mob_list.len]") stat(null, "Dis-[master_controller.diseases_cost]\t#[active_diseases.len]") stat(null, "Mch-[master_controller.machines_cost]\t#[machines.len]") + stat(null, "Bots-[master_controller.aibots_cost]\t#[aibots.len]") stat(null, "Obj-[master_controller.objects_cost]\t#[processing_objects.len]") stat(null, "PiNet-[master_controller.networks_cost]\t#[pipe_networks.len]") stat(null, "Ponet-[master_controller.powernets_cost]\t#[powernets.len]") @@ -1308,3 +1309,6 @@ mob/proc/yank_out_object() if(paralysis) AdjustParalysis(-1) return paralysis + +/mob/proc/assess_threat() //For sec bot threat assessment + return \ No newline at end of file diff --git a/code/setup.dm b/code/setup.dm index 32be9d5476c..8073c8c7648 100644 --- a/code/setup.dm +++ b/code/setup.dm @@ -913,4 +913,6 @@ var/list/RESTRICTED_CAMERA_NETWORKS = list( //Those networks can only be accesse #define IS_SKRELL 3 #define IS_UNATHI 4 -var/list/hit_appends = list("-OOF", "-ACK", "-UGH", "-HRNK", "-HURGH", "-GLORF") \ No newline at end of file +var/list/hit_appends = list("-OOF", "-ACK", "-UGH", "-HRNK", "-HURGH", "-GLORF") + +#define BACKGROUND_ENABLED 0 // The default value for all uses of set background. Set background can cause gradual lag and is recommended you only turn this on if necessary. \ No newline at end of file diff --git a/icons/obj/aibots.dmi b/icons/obj/aibots.dmi index 4c2a228f43a..00826a8344b 100644 Binary files a/icons/obj/aibots.dmi and b/icons/obj/aibots.dmi differ diff --git a/maps/cyberiad.dmm.backup b/maps/cyberiad.dmm.backup index 8533e6ae483..19efc00b530 100644 --- a/maps/cyberiad.dmm.backup +++ b/maps/cyberiad.dmm.backup @@ -1953,8 +1953,8 @@ "aLC" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 6},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aLD" = (/obj/structure/cable{d1 = 2; d2 = 4; icon_state = "2-4"; tag = ""},/obj/structure/disposalpipe/segment{dir = 4; icon_state = "pipe-c"},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aLE" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0; tag = ""},/obj/structure/disposalpipe/segment{dir = 4},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) -"aLF" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 6},/turf/simulated/floor{icon_state = "dark"},/area/chapel/office) -"aLG" = (/obj/structure/disposalpipe/segment,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/machinery/crema_switch{pixel_x = 25},/obj/machinery/light/small{dir = 4},/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 9; pixel_y = 0},/turf/simulated/floor{icon_state = "dark"},/area/chapel/office) +"aLF" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 10},/turf/simulated/floor{icon_state = "dark"},/area/chapel/office) +"aLG" = (/obj/structure/disposalpipe/segment,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/machinery/crema_switch{pixel_x = 25},/obj/machinery/light/small{dir = 4},/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/turf/simulated/floor{icon_state = "dark"},/area/chapel/office) "aLH" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aLI" = (/obj/machinery/atmospherics/pipe/manifold/supply/hidden{tag = "icon-manifold-b-f (NORTH)"; icon_state = "manifold-b-f"; dir = 1},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aLJ" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 10},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) @@ -2009,8 +2009,8 @@ "aMG" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0; tag = ""},/obj/structure/disposalpipe/segment{dir = 4},/obj/machinery/door/airlock/maintenance{req_access_txt = "12"},/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{dir = 4},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aMH" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_y = 0; tag = ""},/obj/structure/disposalpipe/segment{dir = 4},/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{dir = 4},/obj/machinery/atmospherics/pipe/simple/supply/hidden,/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aMI" = (/obj/structure/cable{d1 = 1; d2 = 8; icon_state = "1-8"; tag = ""},/obj/structure/disposalpipe/segment{dir = 8; icon_state = "pipe-c"},/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{dir = 4},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) -"aMJ" = (/obj/structure/cable{d1 = 2; d2 = 8; icon_state = "2-8"; tag = ""},/obj/structure/disposalpipe/segment{dir = 2; icon_state = "pipe-c"},/obj/machinery/atmospherics/pipe/manifold/supply/hidden{tag = "icon-manifold-b-f (NORTH)"; icon_state = "manifold-b-f"; dir = 1},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) -"aMK" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{tag = "icon-manifold-r-f (NORTH)"; icon_state = "manifold-r-f"; dir = 1},/obj/machinery/atmospherics/pipe/simple/supply/hidden,/turf/simulated/floor/plating,/area/maintenance/fsmaint2) +"aMJ" = (/obj/structure/cable{d1 = 4; d2 = 8; icon_state = "4-8"; pixel_x = 0; tag = ""},/obj/structure/disposalpipe/segment{dir = 4},/obj/machinery/atmospherics/pipe/manifold/supply/hidden{tag = "icon-manifold-b-f (NORTH)"; icon_state = "manifold-b-f"; dir = 1},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) +"aMK" = (/obj/structure/cable{d1 = 2; d2 = 8; icon_state = "2-8"; tag = ""},/obj/structure/disposalpipe/segment{dir = 2; icon_state = "pipe-c"},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) "aML" = (/obj/machinery/atmospherics/pipe/simple{_color = "blue"; icon_state = "intact-b-f"; level = 1; name = "pipe"},/obj/machinery/atmospherics/pipe/simple{_color = "red"; dir = 4; icon_state = "intact-r-f"; level = 1; name = "pipe"},/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{dir = 4},/turf/simulated/wall,/area) "aMM" = (/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{tag = "icon-manifold-r-f (NORTH)"; icon_state = "manifold-r-f"; dir = 1},/turf/simulated/wall,/area) "aMN" = (/obj/machinery/door/airlock/maintenance{name = "Chapel Maintenance"; req_access_txt = "12"},/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{dir = 10},/turf/simulated/floor/plating,/area/chapel/main) @@ -2203,8 +2203,8 @@ "aQs" = (/obj/structure/stool/bed/chair/office/dark,/obj/structure/disposalpipe/segment{dir = 4; icon_state = "pipe-c"},/turf/simulated/floor/wood,/area/library) "aQt" = (/obj/structure/disposalpipe/segment{dir = 4},/turf/simulated/floor/wood,/area/library) "aQu" = (/obj/structure/disposalpipe/segment{dir = 8; icon_state = "pipe-c"},/turf/simulated/floor/wood,/area/library) -"aQv" = (/obj/machinery/door/airlock/maintenance{name = "Crematorium Maintenance"; req_access_txt = "27"},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/obj/machinery/atmospherics/pipe/simple/supply/hidden,/turf/simulated/floor/plating,/area/chapel/office) -"aQw" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/obj/machinery/firealarm{dir = 4; pixel_x = 24},/obj/machinery/atmospherics/pipe/simple/supply/hidden,/turf/simulated/floor{icon_state = "dark"},/area/chapel/office) +"aQv" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/manifold/scrubbers/hidden{tag = "icon-manifold-r-f (NORTH)"; icon_state = "manifold-r-f"; dir = 1},/turf/simulated/floor/plating,/area/maintenance/fsmaint2) +"aQw" = (/obj/machinery/door/airlock/maintenance{name = "Crematorium Maintenance"; req_access_txt = "27"},/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/turf/simulated/floor/plating,/area/chapel/office) "aQx" = (/obj/machinery/disposal,/obj/structure/disposalpipe/trunk,/turf/simulated/floor{icon_state = "grimy"},/area/chapel/office) "aQy" = (/obj/effect/landmark/start{name = "Chaplain"},/obj/structure/stool/bed/chair,/turf/simulated/floor{icon_state = "grimy"},/area/chapel/office) "aQz" = (/obj/structure/table/woodentable,/obj/item/weapon/paper_bin{pixel_x = -2; pixel_y = 5},/obj/item/weapon/storage/fancy/crayons,/turf/simulated/floor{icon_state = "grimy"},/area/chapel/office) @@ -4327,8 +4327,8 @@ "bFk" = (/obj/structure/closet/wardrobe/genetics_white,/obj/machinery/status_display{layer = 4; pixel_x = 0; pixel_y = 32},/turf/simulated/floor{tag = "icon-whitepurple (NORTH)"; icon_state = "whitepurple"; dir = 1},/area/medical/genetics) "bFl" = (/obj/machinery/atmospherics/unary/vent_pump{dir = 4; external_pressure_bound = 101; on = 1; pressure_checks = 1},/turf/simulated/floor{tag = "icon-whitepurple (NORTHEAST)"; icon_state = "whitepurple"; dir = 5},/area/medical/genetics) "bFm" = (/obj/machinery/door/firedoor/border_only,/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/obj/machinery/door/poddoor{density = 0; icon_state = "pdoor0"; id = "Biohazard_medi"; name = "Quarantine Lockdown"; opacity = 0},/obj/machinery/door/airlock/research{name = "Genetics Research"; req_access_txt = "47;9"},/turf/simulated/floor{icon_state = "white"},/area/medical/research{name = "Research Division"}) -"bFn" = (/obj/machinery/light{dir = 1},/obj/machinery/atmospherics/pipe/manifold{_color = "blue"; icon_state = "manifold-b-f"; level = 1; name = "pipe manifold"},/obj/machinery/status_display{layer = 4; pixel_x = 0; pixel_y = 32},/obj/machinery/door/firedoor/border_only,/turf/simulated/floor{dir = 8; icon_state = "whiteblue"},/area/medical/research{name = "Research Division"}) -"bFo" = (/obj/machinery/camera{c_tag = "Research Hallway West"; dir = 2; network = list("Research","SS13")},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/simulated/floor{icon_state = "white"},/area/medical/research{name = "Research Division"}) +"bFn" = (/obj/machinery/camera{c_tag = "Research Hallway West"; dir = 2; network = list("Research","SS13")},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/obj/machinery/status_display{layer = 4; pixel_x = 0; pixel_y = 32},/turf/simulated/floor{icon_state = "white"},/area/medical/research{name = "Research Division"}) +"bFo" = (/obj/machinery/light{dir = 1},/obj/machinery/atmospherics/pipe/manifold{_color = "blue"; icon_state = "manifold-b-f"; level = 1; name = "pipe manifold"},/obj/machinery/door/firedoor/border_only,/turf/simulated/floor{dir = 8; icon_state = "whiteblue"},/area/medical/research{name = "Research Division"}) "bFp" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/obj/item/device/radio/intercom{pixel_y = 25},/turf/simulated/floor{icon_state = "white"},/area/medical/research{name = "Research Division"}) "bFq" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/obj/structure/extinguisher_cabinet{pixel_x = -5; pixel_y = 30},/turf/simulated/floor{icon_state = "white"},/area/medical/research{name = "Research Division"}) "bFr" = (/obj/machinery/atmospherics/pipe/manifold{_color = "blue"; dir = 1; icon_state = "manifold-b-f"; level = 1; name = "pipe manifold"},/turf/simulated/floor{icon_state = "white"},/area/medical/research{name = "Research Division"}) @@ -5587,6 +5587,7 @@ "cdw" = (/obj/machinery/door/firedoor,/obj/machinery/door/airlock/glass_medical{id_tag = null; name = "Recovery Ward"; req_access_txt = "0"},/turf/simulated/floor{icon_state = "white"},/area/medical/ward) "cdx" = (/obj/machinery/door/firedoor,/obj/machinery/door/airlock/glass_medical{id_tag = null; name = "Recovery Ward"; req_access_txt = "0"},/obj/structure/disposalpipe/segment,/turf/simulated/floor{icon_state = "white"},/area/medical/ward) "cdy" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/simulated/wall,/area) +"cdz" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{dir = 4},/obj/machinery/computer/atmos_alert,/obj/machinery/alarm{dir = 8; icon_state = "alarm0"; pixel_x = 24},/turf/simulated/floor,/area/atmos/control) "cdA" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/simulated/floor{icon_state = "white"},/area/medical/medbay2) "cdB" = (/obj/structure/grille,/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced{dir = 8},/obj/machinery/door/poddoor{density = 0; icon_state = "pdoor0"; id = "scanhide"; name = "Scanning Room Shutters"; opacity = 0},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/simulated/floor/plating,/area/medical/medbay2) "cdC" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 4},/turf/simulated/floor{tag = "icon-whitebluefull"; icon_state = "whitebluefull"},/area/medical/virology) @@ -5810,6 +5811,7 @@ "chM" = (/obj/structure/table,/obj/item/weapon/reagent_containers/blood/AMinus,/obj/item/weapon/reagent_containers/blood/APlus,/obj/item/weapon/reagent_containers/blood/BMinus,/obj/item/weapon/reagent_containers/blood/BPlus,/obj/item/weapon/reagent_containers/blood/OPlus,/obj/item/weapon/reagent_containers/blood/OMinus,/turf/simulated/floor{icon_state = "white"},/area/medical/medbay2) "chN" = (/obj/machinery/iv_drip,/turf/simulated/floor{icon_state = "white"},/area/medical/medbay2) "chO" = (/obj/machinery/alarm{dir = 4; icon_state = "alarm0"; pixel_x = -22},/turf/simulated/floor{tag = "icon-whitebluefull"; icon_state = "whitebluefull"},/area/medical/virology) +"chP" = (/obj/machinery/computer/general_air_control{frequency = 1441; name = "Tank Monitor"; sensors = list("n2_sensor" = "Nitrogen", "o2_sensor" = "Oxygen", "co2_sensor" = "Carbon Dioxide", "tox_sensor" = "Toxins", "n2o_sensor" = "Nitrous Oxide", "waste_sensor" = "Gas Mix Tank")},/turf/simulated/floor,/area/atmos/control) "chQ" = (/obj/structure/table,/obj/item/weapon/storage/firstaid/adv,/obj/item/weapon/storage/firstaid/fire,/obj/item/weapon/storage/firstaid/o2,/obj/item/weapon/storage/firstaid/regular,/obj/item/weapon/storage/firstaid/toxin,/turf/simulated/floor{icon_state = "blue"; dir = 8},/area/medical/biostorage) "chR" = (/obj/machinery/atmospherics/unary/vent_scrubber{dir = 1; level = 2; on = 1; scrub_N2O = 1; scrub_Toxins = 1},/turf/simulated/floor,/area/medical/biostorage) "chS" = (/obj/machinery/power/apc{dir = 4; name = "Medbay Storage APC"; pixel_x = 25},/obj/structure/cable,/turf/simulated/floor{dir = 4; icon_state = "blue"},/area/medical/biostorage) @@ -6048,7 +6050,7 @@ "cmr" = (/obj/structure/table,/obj/machinery/media/receiver/boombox,/turf/simulated/floor,/area/engine/break_room) "cms" = (/obj/machinery/light{dir = 4; icon_state = "tube1"},/obj/machinery/atmospherics/pipe/simple{_color = "blue"; icon_state = "intact-b-f"; level = 1; name = "pipe"},/obj/structure/table,/turf/simulated/floor,/area/engine/break_room) "cmt" = (/obj/machinery/atmospherics/pipe/simple{_color = "red"; icon_state = "intact-r-f"; level = 1; name = "pipe"},/turf/simulated/wall/r_wall,/area) -"cmu" = (/obj/machinery/computer/general_air_control{frequency = 1441; name = "Tank Monitor"; sensors = list("n2_sensor" = "Nitrogen", "o2_sensor" = "Oxygen", "co2_sensor" = "Carbon Dioxide", "tox_sensor" = "Toxins", "n2o_sensor" = "Nitrous Oxide", "waste_sensor" = "Gas Mix Tank")},/obj/machinery/alarm{frequency = 1439; pixel_y = 23},/turf/simulated/floor,/area/atmos/control) +"cmu" = (/obj/machinery/atmospherics/binary/volume_pump/on{dir = 8; name = "Filter to External"},/turf/simulated/floor,/area/atmos) "cmv" = (/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/obj/machinery/computer/security/engineering,/obj/machinery/camera{c_tag = "Atmospherics Control Room"; network = list("SS13")},/obj/structure/cable{icon_state = "0-4"; d2 = 4},/obj/machinery/power/apc{dir = 1; name = "Atmospherics Control APC"; pixel_y = 26},/turf/simulated/floor,/area/atmos/control) "cmw" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 5},/obj/machinery/computer/general_air_control{frequency = 1443; level = 3; name = "Distribution and Waste Monitor"; sensors = list("mair_in_meter" = "Mixed Air In", "air_sensor" = "Mixed Air Supply Tank", "mair_out_meter" = "Mixed Air Out", "dloop_atm_meter" = "Distribution Loop", "wloop_atm_meter" = "Waste Loop")},/obj/structure/cable{d1 = 2; d2 = 8; icon_state = "2-8"; tag = ""},/turf/simulated/floor,/area/atmos/control) "cmx" = (/obj/machinery/atmospherics/pipe/manifold{_color = "blue"; dir = 1; icon_state = "manifold-b"; level = 2; tag = "icon-manifold-b (NORTH)"},/turf/simulated/floor/plating,/area/maintenance/asmaint) @@ -6056,8 +6058,10 @@ "cmz" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 9; pixel_y = 0},/turf/simulated/floor{icon_state = "white"},/area/medical/surgery) "cmA" = (/obj/machinery/computer/operating,/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/turf/simulated/floor{icon_state = "white"},/area/medical/surgery) "cmB" = (/obj/machinery/optable,/turf/simulated/floor{icon_state = "white"},/area/medical/surgery) +"cmC" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/structure/disposalpipe/segment,/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/obj/machinery/firealarm{dir = 4; pixel_x = 24},/turf/simulated/floor{icon_state = "dark"},/area/chapel/office) "cmD" = (/obj/structure/table,/obj/item/weapon/circular_saw,/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/obj/item/weapon/FixOVein,/turf/simulated/floor{icon_state = "white"},/area/medical/surgery) "cmE" = (/obj/machinery/computer/operating,/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden,/turf/simulated/floor{icon_state = "white"},/area/medical/surgery) +"cmF" = (/obj/structure/closet/emcloset,/turf/simulated/floor{icon_state = "cafeteria"; dir = 5},/area/djstation) "cmG" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 6},/turf/simulated/floor/plating,/area/maintenance/asmaint) "cmH" = (/obj/structure/sign/biohazard,/obj/machinery/atmospherics/pipe/manifold{_color = "blue"; dir = 1; icon_state = "manifold-b-f"; level = 1; name = "pipe manifold"},/turf/simulated/wall/r_wall,/area) "cmI" = (/obj/machinery/atmospherics/unary/vent_pump{dir = 8; on = 1},/obj/item/device/radio/intercom{dir = 1; name = "Station Intercom (General)"; pixel_y = -28},/turf/simulated/floor{tag = "icon-whitebluefull"; icon_state = "whitebluefull"},/area/medical/virology) @@ -6102,7 +6106,7 @@ "cnv" = (/obj/machinery/atmospherics/pipe/simple{_color = "red"; icon_state = "intact-r-f"; level = 1; name = "pipe"},/obj/structure/grille,/obj/structure/window/reinforced{dir = 8},/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced{dir = 4},/obj/machinery/door/poddoor{density = 0; icon_state = "pdoor0"; id = "atmos"; name = "Atmos Blast Door"; opacity = 0},/turf/simulated/floor/plating,/area/atmos/control) "cnw" = (/obj/machinery/computer/atmoscontrol,/turf/simulated/floor,/area/atmos/control) "cnx" = (/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{dir = 5},/obj/structure/stool/bed/chair/office/dark{dir = 1},/turf/simulated/floor,/area/atmos/control) -"cny" = (/obj/structure/cable{d1 = 1; d2 = 2; icon_state = "1-2"; tag = ""},/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{dir = 4},/obj/machinery/computer/atmos_alert,/turf/simulated/floor,/area/atmos/control) +"cny" = (/obj/machinery/status_display{pixel_x = 32; pixel_y = 0},/turf/simulated/floor{icon_state = "cafeteria"; dir = 5},/area/djstation) "cnz" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden,/obj/machinery/atmospherics/pipe/simple/scrubbers/hidden{dir = 4},/turf/simulated/floor/plating,/area/maintenance/asmaint) "cnA" = (/obj/structure/closet/secure_closet/medical3,/turf/simulated/floor{icon_state = "white"},/area/medical/surgery) "cnB" = (/obj/structure/table,/obj/item/weapon/retractor,/obj/item/weapon/cautery,/obj/machinery/atmospherics/pipe/manifold{_color = "red"; dir = 8; icon_state = "manifold-r-f"; initialize_directions = 11; level = 1; name = "pipe manifold"},/turf/simulated/floor{icon_state = "white"},/area/medical/surgery) @@ -6204,6 +6208,7 @@ "cpt" = (/obj/machinery/atmospherics/pipe/simple/supply/hidden{dir = 9; pixel_y = 0},/turf/simulated/floor/plating,/area/maintenance/asmaint) "cpu" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced{dir = 8},/obj/machinery/atmospherics/pipe/simple/supply/hidden,/obj/machinery/door/poddoor{density = 0; icon_state = "pdoor0"; id = "Biohazard_medi"; name = "Quarantine Lockdown"; opacity = 0},/turf/simulated/floor/plating,/area) "cpv" = (/turf/simulated/floor{dir = 8; icon_state = "warnwhite"; tag = "icon-warnwhite (NORTH)"},/area/medical/virology) +"cpw" = (/obj/machinery/newscaster{pixel_y = 32},/obj/machinery/media/jukebox/dj,/obj/machinery/status_display{layer = 4; pixel_x = 32; pixel_y = 0},/turf/simulated/floor{tag = "icon-cafeteria (NORTHEAST)"; icon_state = "cafeteria"; dir = 5},/area/djstation) "cpx" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 8},/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 1},/obj/machinery/door/poddoor{density = 0; icon_state = "pdoor0"; id = "Biohazard_medi"; name = "Quarantine Lockdown"; opacity = 0},/turf/simulated/floor/plating,/area) "cpy" = (/obj/structure/grille,/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 1},/obj/machinery/door/poddoor{density = 0; icon_state = "pdoor0"; id = "Biohazard_medi"; name = "Quarantine Lockdown"; opacity = 0},/turf/simulated/floor/plating,/area) "cpz" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced,/obj/structure/window/reinforced{dir = 1},/obj/machinery/door/poddoor{density = 0; icon_state = "pdoor0"; id = "Biohazard_medi"; name = "Quarantine Lockdown"; opacity = 0},/turf/simulated/floor/plating,/area) @@ -9701,7 +9706,7 @@ "dEH" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced{dir = 8},/turf/simulated/floor/plating,/area/djstation) "dEI" = (/obj/machinery/computer/drone_control,/turf/simulated/floor/plating,/area/djstation) "dEJ" = (/obj/machinery/light{tag = "icon-tube1 (NORTH)"; icon_state = "tube1"; dir = 1},/obj/machinery/drone_fabricator,/turf/simulated/floor/plating,/area/djstation) -"dEK" = (/obj/structure/closet/emcloset,/obj/machinery/status_display{pixel_x = 0; pixel_y = 32},/turf/simulated/floor{icon_state = "cafeteria"; dir = 5},/area/djstation) +"dEK" = (/obj/structure/closet/emcloset,/turf/simulated/floor{tag = "icon-cafeteria (NORTHEAST)"; icon_state = "cafeteria"; dir = 5},/area/djstation) "dEL" = (/turf/simulated/wall,/area/constructionsite/ai) "dEM" = (/turf/simulated/floor/plating/airless,/area/constructionsite/ai) "dEN" = (/turf/simulated/floor/airless{tag = "icon-bcircuit"; icon_state = "bcircuit"},/area/constructionsite/ai) @@ -11247,7 +11252,6 @@ "eit" = (/obj/machinery/conveyor_switch{id = "mining_external"},/turf/simulated/floor/airless{tag = "icon-asteroidwarning"; icon_state = "asteroidwarning"; dir = 2},/area/mine/explored) "eiu" = (/obj/machinery/atmospherics/pipe/tank/air{dir = 1; initialize_directions = 0; level = 1},/turf/simulated/floor/plating,/area/mine/west_outpost) "eiv" = (/obj/machinery/power/port_gen/pacman{anchored = 1},/obj/structure/cable{icon_state = "0-4"; d2 = 4},/obj/machinery/alarm{dir = 1; icon_state = "alarm0"; pixel_y = -22},/turf/simulated/floor/plating,/area/mine/west_outpost) -"eiw" = (/obj/machinery/atmospherics/binary/volume_pump/on{dir = 4; name = "External"},/turf/simulated/floor,/area/atmos) "eix" = (/obj/machinery/power/smes,/obj/structure/cable,/turf/simulated/floor/plating,/area/mine/west_outpost) "eiy" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 1},/obj/structure/window/reinforced{dir = 4},/obj/structure/window/reinforced{dir = 8},/obj/structure/window/reinforced,/turf/simulated/floor/plating,/area/mine/living_quarters) "eiz" = (/obj/machinery/atmospherics/pipe/simple,/obj/machinery/door/airlock/glass_medical{id_tag = null; name = "Infirmary"; req_access_txt = "0"},/obj/machinery/door/firedoor/border_only{dir = 4; layer = 2.6; name = "Firelock"},/turf/simulated/floor{icon_state = "white"},/area/mine/living_quarters) @@ -11382,7 +11386,6 @@ "ele" = (/obj/structure/rack,/obj/item/clothing/suit/space/syndicate,/obj/item/clothing/head/helmet/space/syndicate,/obj/item/clothing/mask/breath,/obj/item/weapon/tank/jetpack/oxygen,/turf/simulated/floor/plating,/area/djstation) "elf" = (/obj/structure/window/reinforced{dir = 1},/obj/structure/rack{dir = 4},/obj/item/clothing/under/soviet,/obj/item/clothing/head/ushanka,/obj/machinery/status_display{layer = 4; pixel_x = 32; pixel_y = 0},/turf/simulated/floor{tag = "icon-cafeteria (NORTHEAST)"; icon_state = "cafeteria"; dir = 5},/area/djstation) "elg" = (/turf/simulated/floor{tag = "icon-cafeteria (NORTHEAST)"; icon_state = "cafeteria"; dir = 5},/area/djstation) -"eli" = (/obj/machinery/newscaster{pixel_y = 32},/obj/machinery/media/jukebox/dj,/turf/simulated/floor{tag = "icon-cafeteria (NORTHEAST)"; icon_state = "cafeteria"; dir = 5},/area/djstation) "elj" = (/obj/machinery/light{dir = 4},/turf/simulated/floor{tag = "icon-cafeteria (NORTHEAST)"; icon_state = "cafeteria"; dir = 5},/area/djstation) "elk" = (/obj/machinery/status_display{layer = 4; pixel_x = 0; pixel_y = 32},/turf/simulated/floor{icon_state = "grimy"},/area/djstation) "ell" = (/obj/structure/table,/obj/structure/safe/floor,/obj/item/weapon/gun/projectile/automatic/pistol,/turf/simulated/floor{icon_state = "grimy"},/area/djstation) @@ -11406,7 +11409,6 @@ "elE" = (/turf/simulated/wall,/area/derelict/solar_control) "elF" = (/turf/simulated/floor,/area/derelict/solar_control) "elG" = (/obj/machinery/light/small{dir = 1},/turf/simulated/floor,/area/derelict/solar_control) -"elH" = (/obj/structure/closet/emcloset,/obj/machinery/status_display{layer = 4; pixel_x = 0; pixel_y = 32},/turf/simulated/floor{tag = "icon-cafeteria (NORTHEAST)"; icon_state = "cafeteria"; dir = 5},/area/djstation) "elI" = (/obj/structure/table,/obj/item/weapon/paper_bin{pixel_x = 1; pixel_y = 9},/obj/item/weapon/pen,/turf/simulated/floor{tag = "icon-cafeteria (NORTHEAST)"; icon_state = "cafeteria"; dir = 5},/area/djstation) "elJ" = (/obj/structure/sign/securearea{desc = "A warning sign which reads 'EXTERNAL AIRLOCK'"; icon_state = "space"; layer = 4; name = "EXTERNAL AIRLOCK"; pixel_x = 0; pixel_y = -32},/turf/simulated/floor{tag = "icon-cafeteria (NORTHEAST)"; icon_state = "cafeteria"; dir = 5},/area/djstation) "elK" = (/obj/structure/grille,/obj/structure/window/reinforced{dir = 5; health = 1e+007},/turf/simulated/floor/plating,/area/derelict/solar_control) @@ -12480,11 +12482,11 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaEhaEhaEhaEhaEhaEhaEhaEhaEhaHMaHNaHOaHPaDeaDeaDeaDfaEYaaiaHQaHRaHSaHTaHUaaqaHVaHWaHXaHYaHYaHZaDlaDlaIaaaiaFgaGaaIbaFgaFgaaiaIcayVaIdaGVayVayVaDsaaiaIeaIeaIfaIgaIhaIiaIjaIiaIkaIlaImaInaInaaiaHjavVaIoaaqaHlaouaEwaBhaBiaaqaCIaIpaIqaIraIsaItaItaIuaIvaItaIvaIwaIxaIyaIzaIAaIBaICaIDaIEaIFaIGaIHaaqaIIaIJaIIaaqaIKaAwaAwaAwaAwaAwaILaaiaabaoJaCoaIMaINazTaaiaIOaIPaIQaBPaBPaBPaBPaBPaBPaBPaBPaBPaATaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazMaaiaaiaaiaaiaaiaaiaaiaDbaIRaISaaiaDbaIRaISaaiaaiaaiaaiaITaDeaDeaDeaDeaDfaIUaaiaIVaIWaIXaIYaIZaaqaJaaGPaEoaJbaGPaGPaGPaJcaJdaaiaJeaJfaJgaJhaFgaaiayVayVaJiaGVayVayVaDsaaiaaiaaiaaiaJjaJkaGYaJlaGYaJkaJmaaiaaiaaiaaiaHjavVaJnaaqaHlaouaDLaBhaBiaaqaJoaJpaaqaaqaaqaaqaaqaaqaaqaJqaaqaaqaaqaaqaaqaaqaJraaqaJsaJtaJtaJtaJuaaqaJvaJwaJxaaqaIKaAwaAwaAwaAwaAwaILaaiaaaaaiaJyaJzaJAaaiaaiaJBaJCaJDaBPaJEaBPaBPaBPaBPaJFaBPaBPaERaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaiaaiaJGaJHaJIaAGaAGaJJaJKaAGaAGaJLaJMaJNaaiaCZaJOaEjaDeaDeaDeaDfaEYaaiaaqaaqaouaJPaaqaaqaJaaGPaEoaaiaaiaaiaaiaaiaaiaaiaaiabnaJQaaiaaiaaiaaiaaiaaiaaiaaiaaiaDsaaiaJRaJRaIfaJSaJTaIiaIjaIiaJTaJUaJVaJWaJXaaiaHjavVaHkaaqaJYaAlaJZaBhaBiaaqaKaaJpaaqaKbaKcaKdaKdaaqaKeaKcaKfaaqaKgaKhaKiaaqaKjaaqaJsaJtaJtaJtaJuaaqaKkaKlaKmaaqaKnaKoaKoaKoaKoaKoaKpaaaaaaaaiaKqaKraKsaaiaaiaKtaKuaKvaaqaaqaaqaaqaaqaaqaaqaaqaaqaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaDbaKwaKxaDeaDeaDeaDeaKyaDeaDeaDeaKyaDeaKzaKAaKBaKCaKDaKEaKEaKEaKFaEYaaiaKGaKHaKIaKJaaqaaqaKKaGPaEoaaiaKLaKMaKNaKMaKOaaiaKPaKQaKRaKSaKTaaiaKUaKUaKVaKUaKWaaiaDsaaiaGXaGYaKXaKYaJkaGYaKZaGYaJkaLaaLbaGYaHiaaiaHjavVaLcaaqaHlaCGaCHaBhaBiaaqaLdaLeaaqaLfaKcaKcaKcaLgaLhaLiaLjaaqaLkaLlaLmaLnaLoaaqaLpaLqaLraLqaLsaaqaLtaLuaLvaaiaaaaaaaaaaaaaabaaaaaaaaaaaaaaiaLwaLxaLyaaiaaiaLzaLAaLBaaqaLCaLDaLEaLEaLEaLEaLEaLEaMJaLHaLHaLIaLHaLHaLHaLHaLJaLKaLLaaiaLMaaiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaiaaiaLNaCbaCbaCbaCbaLOaCbaCbaCbaLOaLPaLQaLRaLSaLRaLTaLUaLUaLUaLVaLWaaiaLXaLYaLZaMaaMbaaqaJaaGPaEoaaiaMcaKOaKOaKOaKOaaiaMdaMeaMfaMgaMhacCaMiaMiaMjaMiaMkaaiaDsaaiaJRaJRaIfaMlaJTaIiaMmaIiaJTaMnaImaMoaMpaaiaMqaysaMraaqaHlaCGabXabXabXabXaMsaMtaMuaMvaMwabXabXabXaaEabXabtaMxaMyaMzaMAabXaMBaaqaaqaaqaaqaaqaaqaaqaaqaaqaaqaaiaaiaaiaaiaoJaoLaaiaaiaaiaaiaaiaaiaMCaamaaiaaiaMDaMEaMFaMGaMHaMIabXabXabXabXabXaOxaMKabXabXaMLabXaMMabXabXaaEaMNaaqaaqaMOaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiabAacMaaaaaaaaaaaaaaaaaaaaaaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaMQaMRaMRaMSaMRaMRaMTaMUaMRaMRaMSaMVaEXaMWaMXaMYaMZaNaaNbaNbaNbaNcaNdaaiaNeaNfaNgaNhaNiaNjaNkaNlaIaaaiaNmaNnaNoaNpaNqaNraNsaNtaNuaNvaNwaNxaNyaNzaNAaNBaNCacfaNDaaiaaiaaiaaiaNEaJkaGYaJlaGYaJkaNFaaiaaiaaiaaiaNGaNHaNIaaqaJYaNJasTasTasTaNKaNLaNMabIaNNaNOabIaNPabIaNQabIaNRaAsaNSaNTaNUaaqaNVaNWaNXaNYaNYaNWaNZaOaaOaaOaaOaaOaaObaOcaOaaOdaOeaOfaOgaOhaOiaOiaOjaOkaOlaOmaOnaOoaLAaOpaOqaOraOsaaqaOtaOuaOvaaqaaqaQvaaqaOyaOzbjaaOBaOCaODaOEaOFaOGaOHaOIaaiaOJaOKaOLaOMaONaOOaOPaOQaORaOSaaaaaaaaaaaaaaaaaaaaaaMPaMPaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaOTaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaMQaOUaOVaOVaOVaOWaOXaOYaOZaPaaOWaOVaPbaPcaPdaaiaERaJGaPeaDeaDeaDeaPfaPgaaiaPhaPiaPjaPkaPlaaqaPmaPnaGPaaiaPoaPpaPqaPraPsacCaPtaPuaPvaPwaPxaaiaPyaPzaPAaKUaPBaaiayVaaiaGXaGYaPCaKYaJkaGYaJlaGYaJkaLaaPDaaiaaiaaiaPEaPFaPGaPHaPIabnaaiaaiaaiaaqaPJaPKaaqaPLaKcaaqaPMaaqaPNaaqaPOaPPaPQaPRaPSaPTaPUaPVaPWaPXaPYaAnaPZaPYaPYaQaaPYaPVaQbaPVaQcaPVaPVaQdaQeaQfaQgaQgaQhaQiaQjaQkaQlaQmaQnaQnaQoaQpaQqaQraQsaQtaQuaaqaRRaQwaaqaQxaQyaQzaQAaOCaQBaOEaOFaOGaQCaQDaaiaQEaQFaQFaQGaOQaOQaOQaOQaORaOSaaaaaaaaaaaaaaaaaaaMPaMPaMPaMPaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaQHaPbaQIaQJaQKaQIaQKaOVaQKaQIaQKaQJaQLaMRaMVaaiaaiaQMaDeaDeaDeaDeaQNaQOaaiaQPaQQaQRaQSaQTabXaQUaQVaaqaaiaQWaQXaQYaKOaKOaaiaQZaPuaPvaPwaRaaaiaRbaPzaRcaRdaKUaaiayVaaiaReaRfaIfaRgaRhaIiaRiaIiaRjaRkaRlaaiaRmaRnaRoaRpaRqaRraRsaRtaaaaaaaaiaRuaRvaRwaaqaRxaKcaKcaKcaRyaKcaKcaKcaaJaRzaRAaaqaOwaouaaqaRBaaqaaqaRCaRDaREaRFaRGaaqaRHaRIaaqaOwaaqaaqaaqaaqaRJaaqaaqaouaaqaaqaaqaaqaRKaRLaRLaaJaQqaRMaRNaROaRPaRQaaqaLFaLGaaqaRTaRUaRVaQAaRWaQBaOEaOFaOGaRXaRYaaiaRZaSaaSaaSbaOQaOQaOQaOQaORaOSaaaaaaaaaaaaaaaaaaaMPaMPaMPaMPaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaDbaKwaKxaDeaDeaDeaDeaKyaDeaDeaDeaKyaDeaKzaKAaKBaKCaKDaKEaKEaKEaKFaEYaaiaKGaKHaKIaKJaaqaaqaKKaGPaEoaaiaKLaKMaKNaKMaKOaaiaKPaKQaKRaKSaKTaaiaKUaKUaKVaKUaKWaaiaDsaaiaGXaGYaKXaKYaJkaGYaKZaGYaJkaLaaLbaGYaHiaaiaHjavVaLcaaqaHlaCGaCHaBhaBiaaqaLdaLeaaqaLfaKcaKcaKcaLgaLhaLiaLjaaqaLkaLlaLmaLnaLoaaqaLpaLqaLraLqaLsaaqaLtaLuaLvaaiaaaaaaaaaaaaaabaaaaaaaaaaaaaaiaLwaLxaLyaaiaaiaLzaLAaLBaaqaLCaLDaLEaLEaLEaLEaMJaLEaMKaLHaLHaLIaLHaLHaLHaLHaLJaLKaLLaaiaLMaaiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaiaaiaLNaCbaCbaCbaCbaLOaCbaCbaCbaLOaLPaLQaLRaLSaLRaLTaLUaLUaLUaLVaLWaaiaLXaLYaLZaMaaMbaaqaJaaGPaEoaaiaMcaKOaKOaKOaKOaaiaMdaMeaMfaMgaMhacCaMiaMiaMjaMiaMkaaiaDsaaiaJRaJRaIfaMlaJTaIiaMmaIiaJTaMnaImaMoaMpaaiaMqaysaMraaqaHlaCGabXabXabXabXaMsaMtaMuaMvaMwabXabXabXaaEabXabtaMxaMyaMzaMAabXaMBaaqaaqaaqaaqaaqaaqaaqaaqaaqaaqaaiaaiaaiaaiaoJaoLaaiaaiaaiaaiaaiaaiaMCaamaaiaaiaMDaMEaMFaMGaMHaMIabXabXabXabXaaEaOxaQvabXabXaMLabXaMMabXabXaaEaMNaaqaaqaMOaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiabAacMaaaaaaaaaaaaaaaaaaaaaaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaMQaMRaMRaMSaMRaMRaMTaMUaMRaMRaMSaMVaEXaMWaMXaMYaMZaNaaNbaNbaNbaNcaNdaaiaNeaNfaNgaNhaNiaNjaNkaNlaIaaaiaNmaNnaNoaNpaNqaNraNsaNtaNuaNvaNwaNxaNyaNzaNAaNBaNCacfaNDaaiaaiaaiaaiaNEaJkaGYaJlaGYaJkaNFaaiaaiaaiaaiaNGaNHaNIaaqaJYaNJasTasTasTaNKaNLaNMabIaNNaNOabIaNPabIaNQabIaNRaAsaNSaNTaNUaaqaNVaNWaNXaNYaNYaNWaNZaOaaOaaOaaOaaOaaObaOcaOaaOdaOeaOfaOgaOhaOiaOiaOjaOkaOlaOmaOnaOoaLAaOpaOqaOraOsaaqaOtaOuaOvaaJaaqaQwaaqaOyaOzbjaaOBaOCaODaOEaOFaOGaOHaOIaaiaOJaOKaOLaOMaONaOOaOPaOQaORaOSaaaaaaaaaaaaaaaaaaaaaaMPaMPaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaOTaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaMQaOUaOVaOVaOVaOWaOXaOYaOZaPaaOWaOVaPbaPcaPdaaiaERaJGaPeaDeaDeaDeaPfaPgaaiaPhaPiaPjaPkaPlaaqaPmaPnaGPaaiaPoaPpaPqaPraPsacCaPtaPuaPvaPwaPxaaiaPyaPzaPAaKUaPBaaiayVaaiaGXaGYaPCaKYaJkaGYaJlaGYaJkaLaaPDaaiaaiaaiaPEaPFaPGaPHaPIabnaaiaaiaaiaaqaPJaPKaaqaPLaKcaaqaPMaaqaPNaaqaPOaPPaPQaPRaPSaPTaPUaPVaPWaPXaPYaAnaPZaPYaPYaQaaPYaPVaQbaPVaQcaPVaPVaQdaQeaQfaQgaQgaQhaQiaQjaQkaQlaQmaQnaQnaQoaQpaQqaQraQsaQtaQuaaJaRRcmCaaqaQxaQyaQzaQAaOCaQBaOEaOFaOGaQCaQDaaiaQEaQFaQFaQGaOQaOQaOQaOQaORaOSaaaaaaaaaaaaaaaaaaaMPaMPaMPaMPaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaQHaPbaQIaQJaQKaQIaQKaOVaQKaQIaQKaQJaQLaMRaMVaaiaaiaQMaDeaDeaDeaDeaQNaQOaaiaQPaQQaQRaQSaQTabXaQUaQVaaqaaiaQWaQXaQYaKOaKOaaiaQZaPuaPvaPwaRaaaiaRbaPzaRcaRdaKUaaiayVaaiaReaRfaIfaRgaRhaIiaRiaIiaRjaRkaRlaaiaRmaRnaRoaRpaRqaRraRsaRtaaaaaaaaiaRuaRvaRwaaqaRxaKcaKcaKcaRyaKcaKcaKcaaJaRzaRAaaqaOwaouaaqaRBaaqaaqaRCaRDaREaRFaRGaaqaRHaRIaaqaOwaaqaaqaaqaaqaRJaaqaaqaouaaqaaqaaqaaqaRKaRLaRLaaJaQqaRMaRNaROaRPaRQbMZaLFaLGaaqaRTaRUaRVaQAaRWaQBaOEaOFaOGaRXaRYaaiaRZaSaaSaaSbaOQaOQaOQaOQaORaOSaaaaaaaaaaaaaaaaaaaMPaMPaMPaMPaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaScaSdaQIaQJaQKaQIaQKaOVaQKaQIaQKaSeaPbaOVaSfaMVaaiaSgaDeaDeaDeaDeaShaSiaAMaSjaSkaSlaSkaSmaSnaSoaSpaSqaaiaaiafjaaiaaiaaiaaiaGGaSraSsaGGaStaaiaaiafjaaiaaiaaiaaiaSuaaiaaiaaiaaiaSvaSwaSxaSyaSxaSzaSAaaiaaiaSBaRpaRpaRpaRpaSCaSDaSEaSFaSGaaiaSHaRvaSIaaqaaqaaqaaqaaqaaqaaqaaqaaqaaJaSJaSKaSLaSMajCaSOaSPaSQaaqaSRaSSaSTaSUaSVaaqaSWaSXaSYaSZaSYaTaaTbaTcaTdaTeaTfaTgaThaTiaTjaaqaTkaTlaTmaTnaToaRMaTpaROaRPaTqaaqaTraTsaTtaTuaTvaQAaQAaaqaQDaOEaOFaOGaTwaTxaaiaTyaTzaTAaTBaTCaTDaOQaOQaTEaOSaaaaaaaaaaaaaaaaMPaMPaMPaMPaMPaMPaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaScaSdaTFaQJaQKaQIaQKaTGaQKaQIaQKaQJaMSaOVaTHaTIaaiaTJaDeaDeaDeaDeaDfaTKaDeaTLaTMaTNaTOaTPaSqaTQaTRaSqaTSaTTaTUaSqaTVaSqaTWaSqaTQaTXaSqaTYaTZaSqaUaaSqaUbaTTaUcaUdaUeaUfaUgaUhaUiaUjaUjaUjaUjaUjaUjaUkaUlaUmaUnaUoaUpaUqaUraUsaUtaRpaRpaUuaUvaUwaUvaUxaUyaUzaUAaUBaUCaUDaaqaUEaUFaUGaUHaUIaUIaUJaUKaULaUMaaqaUNaUOaUPaUPaUQaaqaURaUSaUTaUUaUVaUVaUWaUXaUYaUXaUZaVaaVbaVcaVdaaqaRKaRLaVeaVfaVgaVhaViaVjaVhaVkaaqaVlaVmaaqaVnaVoaQAaVpaaqaVqaVraVsaOGaQDaQDaaiaVtaVuabnafjaaiaaiaaiaaiaaiabAacMaaaaaaaaaaaaaMPaMPaMPaMPaMPaMPaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaScaSdaQIaQJaQKaQIaQKaOVaQKaQIaQKaQJaPbaOVaVvaVwaaiaVxaDeaDeaDeaDeaDfaVyaLUaVzaVAaVBaVAaVCaVDaTQaVEaVFaVGaVHaVIaVFaVFaVFaSoaVFaSoaVJaVFaVKaVFaVFaVLaSqaUbaTTaUcaUdaUdaUdaUdaVMaUdaUdaUdaUdaUdaUdaVNaVOaVPaVQaVRaVSaVTaVUaVVaVWaVXaVXaVXaVYaVZaVZaVZaWaaUDaUDaUDaWbaWcaUDaaqaWdaWeaUIaUIaWfaUIaWgaWhaULaWiaaqaWjaWkaWlaWmaWnaaqaWoaWpaWqaWraWsaWtaaqaWuaWvaWuaaqaaqaaqaouaaqaaqaRKaRLaRLaaJaWwaQqaQqaWxaQqaWyaaqaaqaaqaaqaaqaaqaaqaWzaaqaOGaOGaOFaOGaWAaWBaaqaWCaWDaWEaWFaWGaWHaWIaWJazWaaaaaaaaaaaaaMPaMPaMPaMPaMPaMPaMPaMPaMPaMPaMPaMPaMPaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -12512,7 +12514,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbsHaGGaGHbvMbobbnobnobpKbpLbnoaaibyUbjJbjKbjKbyVabIaSobjMbdXaaqbyWbqabyXbyYbyZbzabzbbzcbzdbzdbzebzfbzgbzhbyYbzibzjaaqbwdboRbzkbzlbzmbxVbzobzpbzqbzrbzsaaibuDaaiaaibztbzubzvbtlbzwbzxacfbzyaaiabnaaiaaiaaiaaiaaibzzaaibzAboRbybaaibzBbzCbzCbzDbzEaaqbzFbzGbzHbzIbzJbzKbzLbzHbzMbzNbzObzPbzQbzRbzSbzTbzUbzVbzWbzXbzYbzZbAabAbbAcbAdbAebAfbAfbAgbAhbAiaaqbAjbqVbqVbAkbAlbwYbAmaaqbAnbAobApaaqbxJbxLaSGbArbjxaaiaaibAsaaiaaibAtbAubAubAubAvaaibxibxlbxkbxlbxlbAwbxiaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabjHbjHbjHbjHbjHbjHbjHbjHbjHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAxbAxbAxbAxbAxbAxbAxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbAybAzbpIbpJbocbocbodboebocaaibAAbiCbiDbABbACbADbAEbAFaSqaaqbAGbqabyXbyYbAHaaqbAIbAJbqbbqbbAKbALbAMbzhbyYbANbAObAPbwdboRbAQbadbzmbwxbASbATbAUbAVbAWbAXabubzxacfarzbAYbxTbAYbzxbAZaaiaaiaaibBabBbbBcbBdbBebBfbBgacfbBhboRbybbuTbBibBjbBkbBlbBmaaqbBnbBobBpbBqbBrbBsbBtbBubBvbBwbBxbBybBzbBAbBBbarbBDbBEbBFbBGbBHaaibBIbBJbBKbBLaaibBMbBNbBObBPbBPaaqbBQbBRbBSaaqbBTbwYbBUaCZbBVbBWbBXbBYbBZbCabCbbCcbCdbCebwvbCgbChbwwbCjbCjbCkbClbCmbCnbCobvJbvKbCpbvKbvJbCqaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabjHbjHbjHbjHbjHbjHbjHbjHbjHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAxbAxbAxbAxbAxbAxbAxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbsHbsHaaabCrbobbnobnobnobCsbCtbCuaaibCvbCwbCxbCybCzaoibCAbCBaSqaaqbCCbCDbCEbCFbCGbCHbCIbCJbCKbCKbCLbCHbCMbCNbyYbCObCPaaqbCQboRbAQbadbzmaaibCRbCSbCTbCUbCVabnbCWbCXbCYbgPacfbCZbDabgPbDbbDcbDdaaibDebDfbDgbDhbDibDibDjbDkbDlboRbDmaaiaaqbDnbjxbDobDpaaqbBnbDqaaqbDrbDsbBsbDtaaqbBnbDubDvaaqaaqaaqaouaaqaaqaaqbDwamwaaqaaiaaibDxaaqaaqaaiaaiaaqaaJaaqaaqaaqaaqaaqaaqaaqbDybDzaCnaCobDAbDBbDCbDDbDDbDEbDFbDGbDHbDIbDJbDKbDLbuLbDNbDNbDObBZbDPaaibDQbDRbDSbwgaaibDUaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabjHbjHbjHbjHbjHbjHbjHbjHbjHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAxbAxbAxbAxbAxbAxbAxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazMaaiaaiaaiaoJaoLazMaoJaoLaaiaoJaoLaaiaaibDVafjaaibxAaaqaaqaaqaaqazlbDWaoiaoibDXaoibDYbDZbEabEbbEcbEdbEebqbbEfbqbbEgbzfbEhbqbbyYbqbbEibjxbwdboRbEjbEkbElaaibEmbEnbEobEpbEqabnbErbEsbEtbEubnFbEwbExbEybEzbEAbEBaaibECbEDbEEbEFbEGbEHbEIaaibEJbEKbybbELbEMbENbEObEPbEQbERbESbBobjxbETbEUbEVbEWbjxbBnbDubDvbEXbEYbEZbFabFbbFcbFdbFebFfaaqbFgbFhbFibFjbFkbFlbFmbCebFnbDDbDDbFobFpbDDbFqbFrbFsbFtbFubDDbFvbFwbFxbFybFzbFAaaiabnaaiaaiaaibFBaaiaaibAtbAvaaibDMbFDbFEbFFbFGbFHbFIbFJaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabjHbjHbjHbjHbjHbjHbjHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAxbAxbAxbAxbAxbAxbAxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazMaaiaaiaaiaoJaoLazMaoJaoLaaiaoJaoLaaiaaibDVafjaaibxAaaqaaqaaqaaqazlbDWaoiaoibDXaoibDYbDZbEabEbbEcbEdbEebqbbEfbqbbEgbzfbEhbqbbyYbqbbEibjxbwdboRbEjbEkbElaaibEmbEnbEobEpbEqabnbErbEsbEtbEubnFbEwbExbEybEzbEAbEBaaibECbEDbEEbEFbEGbEHbEIaaibEJbEKbybbELbEMbENbEObEPbEQbERbESbBobjxbETbEUbEVbEWbjxbBnbDubDvbEXbEYbEZbFabFbbFcbFdbFebFfaaqbFgbFhbFibFjbFkbFlbFmbCebFobDDbDDbFnbFpbDDbFqbFrbFsbFtbFubDDbFvbFwbFxbFybFzbFAaaiabnaaiaaiaaibFBaaiaaibAtbAvaaibDMbFDbFEbFFbFGbFHbFIbFJaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabjHbjHbjHbjHbjHbjHbjHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAxbAxbAxbAxbAxbAxbAxbAxbAxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabFKbFKbFKbFKbFKbFKbFKaaaaaabzlbFLbFMbFMbFMbFNbFObFPbFPbFPbFPbFQbFPbFPbFRbFPbFPbFPbFPbFPbFSbFTbFUbFPamwbFVbqbbqbbqbbEgbFWbFXbFYbyYbFZbGabGbbwdboRbGcbCibzmaaibGebGfbGgbCSbGhabnbGibGjbGkaaibrRbGmbnEacCbGobGpbGqaaibGrbGsbGtbGubEGbEHbEIaaibGvbGwbGxbGybGzbGAbGBbGCbGDbGEbGFbGGaaqaaqbGHbGIaaqaaqbBnbDubDvbjxbGJbGKbGKbGKbGLbGKbGMbGNbGObGPbGQbGRbGRbGSbGTbGUbGVbGWbBZbBZbGXbBZbGYbBZbGZbBZbHabHbbHbbHcbHdbHebHfbHgbHhbHibHjbHkbHlaaibHmbHnaaaaaaaaaaaibHobHpbHqbHrbHsbHtbCfaaiaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabjHbjHbjHbjHbjHaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAxbAxbAxbAxbAxbAxbAxbAxbAxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabFKbFKbFKbFKbFKbFKbFKaaaaaabadbHvbHwbHwbHwbHxbHybFPbFPbFPbFPbFPbFPbFPbFRbFPbFPbFPbFPbFPbFSbFTbFUbFPamwbHzbHAbHBbHCbHDbHEbHFbHGbHHbHIbHJbjxbwdboRbHKaaqaaqaaiaaiaaiaaiaaibHLabnaaibHMabnacCabnbHNaaiaaiaaiaaiaaiaaiabnaaiaaiaaiaaiaaiaaiaaibHOboRbHPbnSaaqbHQbHRaaqaaqbBnbHSbHTbEQbHUbHVbHWbHXbHYbESbHZbIabIbbIcbIdbIdbIdbIebIdbIfbIgbIhbIibIjbIkbFibIlaaiaaiaaiaaiaaiaaibImaaiaaiaaiaaJbInaaqaaqbIobIpbIqbIrbHfbIsbItbIubIvbIwbIxaaibHmbIyaaaaaaaaaaaibIzbIAaaibCnaaiaaibCnaaiaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabAxbAxbAxbAxbAxbAxbAxbAxbAxaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabFKbFKbFKbFKbFKbFKbFKbIBaoKaCobICbIDbIDbIDbIEbIFbIDbHwbHwbHwbHwbHwbIGbIHbHwbHwbIGbHwbHwbHybIIbIJbFPazlaoiaoiaoiaoiaoiaoibIKbEdaaqaaqaaqaaqbwdboRbILbIMbINbIObIPbIQbIRbISbITbIUbIVbIWbIXbIYbIZbJabJbbJcbJdbJebJfbJebJgbJhbJibnQbJjboRbJkbJlbJmboRbJnaaqbJobJpbJqbJrbJsbBnbJtbJubJvbJwbJxbJybJzbJAbJBbJCbJDaaqbJEbJFbJGbJHbJIbGKbJJbJKaaqbJLbFibFibFibJMaaibJNbJObJPbJQbJRbJSbJTbJUaaicbObJWbJXaaqaaqbJYbIqbJZbKabKbbKcbKdbKebKfbKgaaibKhaaiaaaaaaaaaaaiaaiaaiaaiaaaaaaaabaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaOTaaaaaaaaaaaaaaaaaaaaaaaa @@ -12536,8 +12538,8 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpaaaaaaaaacjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjqcjqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachdcjrcjscjtcjucjvcjwcjxcjycjzcjAaaqbYHcjBaaiaaaaaaaaaaaaaaicjCcjDcjEcjEcjFcjGaaqcjHbYTbTWcjIcjJcjKcjLcjKcjKcjKcjMcjNcjOcjOcjPcjQaaibBJbBJaaqcjRcjScjTcjUcjVcjWaaqcjVcjWcjUcjXcjYcjZaaichMckachNaaickbcfccgBaaiaaiaaiaaiaaiaaiaaiaaibBJbBJbBJaaiaabaaaaaaaaaaabaabaabaaaaaaaaaaaaaaaaaackcckdckeckfaaiaaickgckhckiaaiaaiaaickjckkcklckkckmaaiaaiaaaaaaaaacknaaaaaaaaaaabaaaaaaaaaaaqaaqaaqaabaaaaaaaabaaaaaaaabaabaabckockpckqbRTaaqaaiaaibQCbQDbQCaaiaaiaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaackrcksckscksckscksckscksckscksckscksckscktaaiaaqaaqaaqaaqaaqaaqckuckvaaiaaiaaiaaiaaiaaiaaqaaqaaqaaqaaqaaqaaqckwbYTbTWckxckyckzckAckBckCckDckEckFckGckHckIckJaaibBJbBJaaqckKckLcjTcjUcjUckMaaqckNcjUcjUcjXcjTckOaaiaaiaaiaaiaaickbcfcckPaaibBJbBJbBJbBJbBJbBJbBJbBJbBJbBJaaiaaaaaaaaaaaaaaaaabaabaaaaaaaaaaaaaaaaaackQckRckSckTaaiaaickUckVckWaaiaaaaaackXckXckYckXckXaaaaaaaaaaaaaaacknaaaaaaaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabckZbAubAubAvaaqaaqaaiaaiaaiaaibOJaabaabaaaaaaaagaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpaaacjpaaaaaacjpcjpcjpcjpcjpcjpcjpaaaaaacjpaaacjpaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaagaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaclaclbclccldcldclecldclfclgclhclicljclkcllclmclmclncloclpclqclqclrclscltcluclvclucluclucltclucluclwclxclyclzbYTbTWclAclBclCafjaaichzacCauZclDclEacCauaccaaaibBJbBJaaqclFclGclHclIcjUclJaaqclKcjUclLclMclHclNaaibBJbBJbBJclOckbcfcckbclObBJbBJbBJbBJbBJbBJbBJbBJbBJbBJaaiaaaaaaaaaaaaaaaaabaabaabaaaaaaaaaaaaaaaccoaaiclPafjccoazSclQclRbfcazTaaaclSclTclUclVclUclTclSaaacjicjjcjjclWaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaabaaaaaaaaaaaaaabaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpaaacjpcjpcjpcjpcjpcjpcjpcjpcjpaaacjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaclaclXclYclZclYcmaclYclXclYcmbclZclYcmccmdbTXcmdcmdcmebTXbTXbTXbTXcmfcmdcmdcmdcmdcmdcmdcmdcmdcmdcmdcmgcmhcmicmjcmkcmlcmmcmncmoaaicmpcmqcmrcmscmtcmucmvcmwacfcgGcmxabIcmycmzcmAcmBcjUcmDaaqcmDcjUcmBcmEcjTbBCaaicmGacfacfcmHcmIcfccmJccoazSaoKaoKbckaaiaaiaaiaoJaoKaoLaaiaaaaaaaaaaaaaaaaaaaabaabaaaaaaaaaaaaaaaaaicmKcmLcmMaaiaabaATcmNaATaabaaaclScmOcmPcmQcmPcmOclSaaacknaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpaaacjpcjpcjpcjpcjpcjpcjpcjpcjpaaacjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacmRclYcmScmTcmUcmVcmUcmWcmXcmYcmZcnacnbcmhcnccndcndcneabIcnfabIcngcnhcngcngcngcngcngcngcngcngcngcngcnicmdcnjcnkcnlcnmcnncnocwNcnqcnrcnscntcnucnvcnwcnxcnyacCbXxcnzaQUcnAcjUcjTcjUcjUcnBabXcnCcjUcjUcjXcjTcnDaaicnEaaiaaaafjaaicnFaaiaaiaaaaaaaaaaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaiaaiaaiaaiaaiaaiaaiaaiaaicnGcnHcnIaaiaabaERcnJaERaabaaaclScnKcnLcnMcnNcnKclSaaacknaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaagaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaOTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpaaacjpcjpcjpcjpcjpcjpcjpcjpcjpaaacjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaclaclXclYclZclYcmaclYclXclYcmbclZclYcmccmdbTXcmdcmdcmebTXbTXbTXbTXcmfcmdcmdcmdcmdcmdcmdcmdcmdcmdcmdcmgcmhcmicmjcmkcmlcmmcmncmoaaicmpcmqcmrcmscmtchPcmvcmwacfcgGcmxabIcmycmzcmAcmBcjUcmDaaqcmDcjUcmBcmEcjTbBCaaicmGacfacfcmHcmIcfccmJccoazSaoKaoKbckaaiaaiaaiaoJaoKaoLaaiaaaaaaaaaaaaaaaaaaaabaabaaaaaaaaaaaaaaaaaicmKcmLcmMaaiaabaATcmNaATaabaaaclScmOcmPcmQcmPcmOclSaaacknaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpaaacjpcjpcjpcjpcjpcjpcjpcjpcjpaaacjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacmRclYcmScmTcmUcmVcmUcmWcmXcmYcmZcnacnbcmhcnccndcndcneabIcnfabIcngcnhcngcngcngcngcngcngcngcngcngcngcnicmdcnjcnkcnlcnmcnncnocwNcnqcnrcnscntcnucnvcnwcnxcdzacCbXxcnzaQUcnAcjUcjTcjUcjUcnBabXcnCcjUcjUcjXcjTcnDaaicnEaaiaaaafjaaicnFaaiaaiaaaaaaaaaaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaiaaiaaiaaiaaiaaiaaiaaiaaicnGcnHcnIaaiaabaERcnJaERaabaaaclScnKcnLcnMcnNcnKclSaaacknaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaagaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaOTaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpaaacjpcjpcjpcjpcjpcjpcjpcjpcjpaaacjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaazMaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaicnOaaqaaqbyNaaqcnQaaqaaqcnRaoLaaqaaqaoJaoKaoLaaqaaqaaqaaqcnScnTcnUcnVcnWcnXcnYcnZcoacnZcnZcobcnZcoccodczUcofcogcohbBJcnEaoucoicojcokcolcomconaaqcoocomcopcjXcoqcoraaicnEaaiaaacoscotcoucovaaiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaicowcoxcoycozcBccoBcoCaaqcoDcoEcoFacCcoGaaacoHaaaaaaaaaclScoIcoJcoJcoJcoKclSaaacknaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafoafoafoafpafpafoafpafpafpafXafpafpafpafoafoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaCZcoLcoMcoNcoOcoPcoQcoRcoScoTcoUcoVcoWcoUcoXcoYcoZcoZcpaaaqcpbcpccpdcpebTWcpfcpgcphcpicpjcpkcpjcplcpmcpncpocppcpqaaiaaicnEaouaaiaaicpraaiaaiabnbzxacfacfacfcpsacfbgPacfcptaaiaaacpucpvcoubZtaaicpxcpycpycpzccoaaibAXacCacCcpAacCacCcpAcpBaaaaaaaaicpCcpDcpCcpEcpFckdcnHaaqaaJcpGaaJaaiaaiaaacpHaaaaabaaaclSclSclScpIclSclSclSaaacknaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaafoaaaaaaaabaabaaaaabaabaaaaaaaabaabaabaaaafoaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaATcoLcpJcpKcpLcpMcpNcpNcpNcpNcpOcpPcpQcoZcpRcoZcoZcpScoZbuIbTXcpUcpVcpebTWcpWaaqcpXcpicpYcpZcqacpicqbcqccqdcqecqfcqgcqhcqicqjcqkcqkcqlbSEcgGcqmcqnbBJcqobXxcqpbBJbBJbVTbBJaaiaaacqqcpvcoucqraaqcqscqtcqucqvcqwcqxcqycqwcqxcqzcqwcqAcqBabnaaaaaaaaicpCcpCcpCcqCcqDcoBcqEcqFcqGcqHcqIcqJaaiaaacpHaabaabaaaaaaaaaafoaaaafoaaaaaaaaacknaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -12550,7 +12552,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpaaacjpcjpcjpcjpcjpcjpcj aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpaaaaaacjpcjpcjpcjpcjpcjpcjpcjpcjpaaaaaacjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacrwcrwcrwcrwcrwcrwcrwcrwcrwaaaaaaaaaafpaabcwqcwrcwrcwscwtcwucwucwucwtcwucwucwucwtcwucwucwvcwrcwrcwrcwrcwrcwwcwxcwycwzcwAcwBcwCcwDcwEbYHcoLcwFaaqaaqaaqaaqbYHbYIaaicwGcwHcwIcwJcwKcwLcwMcsPcwOcwPcwQcwRcwScwTcwUcwVcvycwWcurcuvcuvcuvcuvcwXcmtcwYcwZcvDcxacxbcxccxdcxecxfcxgcxhcxicxjcxkcxlcxmcuNcxncuQcuQctKaaaaaaaabafjcxocvRcxpaaqcxqcvTckbcvTaaqcxrcxscvWcxtcxucvZcxrcxvabnaaaaaaaaicpCcpDcpCcxwcpFckdckecwccxxcxyckdcxzaaqaaqaaqaaiaaaaaactiaabcxAcxAcxAcxAcxAaaacvlaaacxAcxAcxAcxAcxAaabctiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpcjpcjpcjpcjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacrwcrwcrwcrwcrwcrwcrwcrwcrwaaaaaaaaaafoaaaaabaaaaabaabcxBaabaaaaabcxBaabaaaaabcxBaabaaaaaaaaaaaaaaaaaaaaaaaacvnbAucxCcxDcxEcxFaaicxGbYHcxHcvucvucvucvucvucvucxIaaicxJcxKcxLcxMcxNcxOcxPcxQcxRcxScxTcxUcxVcxWcxXcxYcxZcvzcurcyacybcuvcuvcyccmtcydcyecvDcyfcygcyhaaqcyicyjcykcylcymcyncymcyoaabctKctKctKctKctKaabaabaabcdMcypcvRcxpaaqcyqcyrcyscytcyuabtabXabXcyvabXcywaDKaaqabnaaaaaaaaicpCcpCcpCcwacyxcyycyzcrucyActYctYcyBcyCcyDcyEciVaaaaaactiaaaaabaaaaabaabaabaaacvlaaaaabaaaaabaaaaabaaactiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpcjpcjpcjpcjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacrwcrwcrwcrwcrwcrwcrwcrwcrwaaaaaaaaaafoafoafpaabaabcqKcyFcqMaaacqKcyFcqMaaacqKcyFcqMaabaaaaaaaaaaaaaaaaaaaaaaabaabcyGaaiaaiaaiaaiaaicyHcyIbYHcyJbYHbYHbYHcyKcyLcyMcyNcxLcxLcyOcxNcyPcyQcyRcyScyTcyUcyVcyWcyXcyYcyZczaczbczcczdczeczfczgczhcziczjcxcczkczlczmcznczoczpczqczrczscztczuczvczwcuMcuNczxczyczyctKaaaaaaaabafjczzcvRczAaaqczBczCczCczDaaqczEczFczGczHczIczJczKczLctbaaaaaaczMaoiaoiaoiaoiczNczOczPczOczQczOczOczRcwcckdckdckQaaaaaactiaaacvkcvkcvkcvkcvkaabcvlaabcvkcvkcvkcvkcvkaabctiaaaaaaaaaaaaaaaaaaaaaczSaaaaaaaaaczSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacrwcrwcrwcrwcrwcrwcrwcrwcrwaaaaaaaaaaaaaaaaaaaaaaaacqKcyFcqMaabcqKcyFcqMaaacqKcyFcqMaaaaaaaaaaabaaaaaaaaaaaaaabaabaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiczTckscnpcxLcxLcxMczVczWczXczYczZaaicAacAbcAccAdcAeaaicAfcuvcAgcAhcAicAjcuvcAkcAlcAmcAneiwcAocApcAqcArcAscAtcAucvDcAvcAwcAxcvMaabcvNcAycAzcAActKaaaaaaaaaafjchUcABcACaaqcADcAEcAFcAGcAHcvRcAIcAJczHcAKcALcAMcANcAOaabaaacAPcAQcARcAScATcAUcoBcAVcoBcAWcoBcftcoBcAXcAYckdaaiaabaabctiaabcwjcwkcwkcwkcwkcwlcwmcwncwocwocwocwocAZaabctiaaaaaaaaaaaaaaaaaaaaaczSczSczSczSczSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaOTaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacjpcjpcjpcjpcjpaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacrwcrwcrwcrwcrwcrwcrwcrwcrwaaaaaaaaaaaaaaaaaaaaaaaacqKcyFcqMaabcqKcyFcqMaaacqKcyFcqMaaaaaaaaaaabaaaaaaaaaaaaaabaabaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiaaiczTckscnpcxLcxLcxMczVczWczXczYczZaaicAacAbcAccAdcAeaaicAfcuvcAgcAhcAicAjcuvcAkcAlcAmcAncmucAocApcAqcArcAscAtcAucvDcAvcAwcAxcvMaabcvNcAycAzcAActKaaaaaaaaaafjchUcABcACaaqcADcAEcAFcAGcAHcvRcAIcAJczHcAKcALcAMcANcAOaabaaacAPcAQcARcAScATcAUcoBcAVcoBcAWcoBcftcoBcAXcAYckdaaiaabaabctiaabcwjcwkcwkcwkcwkcwlcwmcwncwocwocwocwocAZaabctiaaaaaaaaaaaaaaaaaaaaaczSczSczSczSczSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaOTaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacrwcrwcrwcrwcrwcrwcrwcrwcrwaaaaaaaaaaaaaaaaaaafpaabcqKcyFcqMaaacqKcyFcqMaaacqKcyFcqMaabaaaaaaaaaaaaaaaaaaaaaaabaabaaicBacBbcBecBdcBecBecBecBfcBgcBhcBecBicBjcBkcBlcBmcBncBmcBocBpcBqcBrcBscxLaaicBtcBucBvcBwcBxcksaoicBycktcBzcBAcBBaaiaaicBCcBDcBEcBFcyfcygcBGcBHcBIcBJcBKcvDcAvcBLcBMcBNcxmcuNcBOczyczyctKaabaaaaaacdMcBPcBQcBRcBScBTcBUcBTcBTcBVcBWcBXcBYcBYcBZcCacCbcCccCdaabaabcCeckdckdckeckdckeckdckeckRcCfcwecCgcwecsbcCgcChaaiaaaaaactiaabcxAcxAcxAcxAcxAaaacvlaabcxAcxAcxAcxAcxAaaactiaaaczSaaaczSaaaaaaczSczSczSczSczSczSczSaaaaaaczSaaaczSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacrwcrwcrwcrwcrwcrwcrwcrwcrwaaaaaaaaaaaaaaaaaaafpaabcqKcyFcqMaaacqKcyFcqMaabcqKcyFcqMaabaabaabaaaaaaaaaaaaaabaabaaaaaicCicCjcCkaaicClcCmcCncCncCncCocCncCncCncCpacCcCqcCrcCscCtcCucCvcCwcCxcCxcCxcCycCzcxLcxLcxLcCAcCBcCCaaqcCDcCEcCFcCGaaiaaiaaiaaicCHcyfcygcCIcCJcCKcCLcCMcvDcCNcCOcCPcCQcCRctKctKctKctKctKaaaaaaaaaafjcCScCTcCUcCVcCWcCXcCWcCWcCYcCZcDacDbcCccDccDdcAMczHcDeaabaabawjcDfcDgcDhcDicDjcDkcDlcDmcDjcDkcDnaaicDocDpabnaaiaaaaaactjaaaaabaaaaabaaaaabaaacDqaaaaabaaaaabaabaabaabctiaaaczSczSczSaaaczSczSczSczSczSczSczSczSczSaaaczSczSczSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacrwcrwcrwcrwcrwcrwcrwcrwcrwaaaaaaaaaaaaaaaaaaafXaabcqKcDrcqMaaacqKcDrcqMaaacqKcDrcqMaabafpaabaabaaaaaaaaaaabaaaaaaaaicDscCjcDtaaiaaicDuaaiaaiaaicDvaaiaaiaaiaaiaaiaaiaaiaaicDwcDxcDycDzcBpcBpcBpcDAcDBcDCcDDcBpcBpcDBcDEcDFcDGcCEcDHcDIaaicDJcDKacCcDLcDMcDNcDOcDPcBKcvDcDQcDRcDScDTcDUczwcDVcuNcDWcDXcDYctKaaaaaaaaaafjcDZcEacEbcEccEdcEecEfczHcEgcEhcEicEjcEkcElcEmcEncEocEpaaaaaaawjcEqcErcEsaaqcEtcEucEvaaqcEwcExcEyaaicEzcEAabnaaaaaaaaactiaaacvkcvkcvkcvkcvkaabctkaabcvkcvkcvkcvkcvkaabctiaaaczSczSczSaaaczSczSczSczSczSczSczSczSczSaaaczSczSczSaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -13248,8 +13250,8 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdEzdFvdFwdFwdFwdFxdEXdEPdFsdEPdFydFxdFzdFzdFzdEBdEFdFAdEPdEPdEPdFBdFndFCaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaabaacaacaacaacaacaacaacaacaacaacaacaacaacdDXdDXdDXdDXdDXdDXdDXdDXdDXdDXdDXdDXdEhdEgdEgdEmdEmdEmdEmdEmdEmdEgdEmdEmdEhdEMdEMdEMdELdFpdFpdEMdENdEMdFpdFpdELdEMdEMdEMdEhdEmdEmdEgdEmdEmdEmdEmdEgdEmdEmdEgdEhdDXdDXaacaacaacaacaacaacaacaacaacaacaacaacaaaaaaaabaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdEzdEAdEAdEAdEAdFDdFEdFFdEXdCadFGdFDdEAdEAdEAdEAdEFdFHdEPdEPdFIdEFdEFdEFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaabaabaabaabaabaacaacaacaacaacaabaabaacaacaabaacaabaacaabaacaabaacaabaacdFJdDXdFJdDXdDXdDXdDXdDXdDXdDXdDXdDXdEhdEmdEmdEmdEmdEmdEmdEmdEgdEmdEmdEmdFKdENdENdENdFLdEMdEMdEMdENdEMdEMdEMdFMdENdENdENdFNdEgdEmdEgdEgdEgdEgdEgdEgdEmdEmdEmdEhdDXdDXaacaacaacaacaacaacaacaacaacaabaacaabaacaabaacaacaacaabaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadEFdEFdEFdEFdEFdEFdEFdEFdEFdFOdEFdEFdEFdEFdEFdEFdEFdEFdFPdEPdEPdEPdFQdFndFRaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaacaacaacaacaacaacaacaabaacaabaacaacaacaacaacdDXdDXdDXdFJdDXdDXdDXdDXdDXdDXdDXdDXdEhdEgdEgdEmdEgdEgdEgdEgdEgdEgdEgdEgdEhdEMdEMdEMdELdFpdFpdEMdENdEMdFpdFpdELdFSdFSdFSdEhdEmdEmdEgdEmdEmdEmdEmdEmdEmdEgdEgdEhdDXdDXaacaacaacaacaacaacaacaacaacaacaacaacaacaacaacaacaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdFTdFUdFXdEWdFWdFXdFTdEKdFZdGadGadGcdEUdFadEPdEPdGddEUdCadEPdGedEPdEFdEFdEFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaqaaqaaqaabaabaabaabaabaaqaaqaaqaaqaaqaaqaaqaaqaaqaaqaaqdDVdDVdDVdDVdDVdDVdDVdDVdDVdDVdDVdDVdEhdEgdEgdEhdGfdEhdEhdEhdGgdEmdGgdEmdEhdELdELdELdELdEMdFpdEMdEMdEMdFpdEMdELdELdELdELdEhdEmdEmdEgdEmdEhdEhdEhdEidEhdEmdEgdEhdDVdDVaaqaaqaaqaaqaaqaabaabaabaabaabaabaabaabaabaabaabaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdGhdFUdFXdFUdFUdFXdGhdGadGadGadGadGadEFdEFdEFdEFdEFdEFdEFdEFdEFdEFaaqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabdDVdFJdGidGjdGjdGidDXdDXdDXdGidGkdGkdGkdGkdGidGiaabaabdELdELdFpdFpdEMdFpdFpdELdELaabaabdGidGidGkdGkdGkdGkdGidDXdDXdDXdGidGjdGjdGkdDXdDVaabaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdFTdFUdFXdEWdFWdFXdFTcmFdFZdGadGadGcdEUdFadEPdEPdGddEUdCadEPdGedEPdEFdEFdEFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaqaaqaaqaabaabaabaabaabaaqaaqaaqaaqaaqaaqaaqaaqaaqaaqaaqdDVdDVdDVdDVdDVdDVdDVdDVdDVdDVdDVdDVdEhdEgdEgdEhdGfdEhdEhdEhdGgdEmdGgdEmdEhdELdELdELdELdEMdFpdEMdEMdEMdFpdEMdELdELdELdELdEhdEmdEmdEgdEmdEhdEhdEhdEidEhdEmdEgdEhdDVdDVaaqaaqaaqaaqaaqaabaabaabaabaabaabaabaabaabaabaabaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdGhdFUdFXdFUdFUdFXdGhdGadGadGadGacnydEFdEFdEFdEFdEFdEFdEFdEFdEFdEFaaqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabaabdDVdFJdGidGjdGjdGidDXdDXdDXdGidGkdGkdGkdGkdGidGiaabaabdELdELdFpdFpdEMdFpdFpdELdELaabaabdGidGidGkdGkdGkdGkdGidDXdDXdDXdGidGjdGjdGkdDXdDVaabaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdGhdGldFXdFUdFUdFXdGhdGmdGndGmdGadGadGodGpdGpdDjdGsdGtaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaabaabdDVdFJdGidGjdGjdGidDVdDVdDXdGidGidGkdGudGkdGudGiaabaabaabdELdELdELdFcdELdELdELaabaabaabdGidGkdGkdGjdGkdGidGidDXdDVdDVdGidGjdGjdGidDXdDVaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdGhdGvdFXdGwdGwdFXdGxdGmdGmdGydGadGadGzdGpdGpdGpdGAdGBaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaabaabdDVdFJdGidGjdGjdGCaabdDVdDXdDXdGidGkdGkdGkdGkdGidGiaabaabaabdELdEMdENdEMdELaabaabaabdGidGidGkdGkdGjdGkdGidDXdDXdDVaabdGCdGjdGjdGidDXdDVaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabdGxdGDdGEdFXdCEdFXdGFdGadGadGadGadGadGtdGpdGpdGpdGGdGBaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaabaabdDVdFJdGidGjdGjdGCaabdDVdDXdDXdGidGkdGudGkdGudGkdGidGiaabaabdELdEMdENdEMdELaabaabdGidGidGkdGkdGkdGkdGkdGidDXdDXdDVaabdGCdGkdGjdGidDXdDVaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -13714,7 +13716,7 @@ aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadynekRekRekRekRdFDdEPdEPeladEPdEPdFDekRekRekRekRdynaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadyndyndyndyndyndEFelbelcelddCaeledEFdyndyndyndyndynaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadEFdEFdEFdFOdEFdEFdEFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaacaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadEFdEFdEFdEFdEFdEFelHelgelgelgelidEFdEFdEFdEFdEFdEFaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaacdEdaacaaiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadEFdEFdEFdEFdEFdEFdEKelgelgelgcpwdEFdEFdEFdEFdEFdEFaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaabaabaacdEdaacaaiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadFTdGlelAdFXelDdGoelgelgelgelgelgdGoehSelkdGrelldGtaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaaaaabaacaacdEddEdaaiaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadGhdGvdFXdFXdFXelmelgelneloelnelgelpdGpdGpdGpdGpdGBaaaaaaaaaaaaaaaaabaabaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaabaabaaaaacaacaacdEddEdaacelqaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaadGxdGDdGEdGwdFXdGoelgelrelseltelgdGodGSeluelvelwdFDaabaaaaaaaaaaaaaabaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaacaacaacaacdEddEddEdaacaacaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa diff --git a/nano/debug.html b/nano/debug.html index a982ba43077..42159c9df17 100644 --- a/nano/debug.html +++ b/nano/debug.html @@ -16,7 +16,7 @@ - +