diff --git a/code/game/machinery/bots/ed209bot.dm b/code/game/machinery/bots/ed209bot.dm new file mode 100644 index 00000000000..5dbc24f4887 --- /dev/null +++ b/code/game/machinery/bots/ed209bot.dm @@ -0,0 +1,875 @@ +/obj/machinery/bot/ed209 + name = "ED-209" + desc = "A security robot. He looks less than thrilled." + icon = 'aibots.dmi' + icon_state = "ed2090" + layer = 5.0 + density = 1 + anchored = 0 +// weight = 1.0E7 + req_access = list(access_security) + + var/lastfired = 0 + var/shot_delay = 3 //.3 seconds between shots + + var/lasers = 0 + + var/on = 1 + var/locked = 1 //Behavior Controls lock + var/mob/living/carbon/target + var/oldtarget_name + 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/health = 100 + var/idcheck = 1 //If false, all station IDs are authorized for weapons. + var/check_records = 1 //Does it check security records? + var/arrest_type = 0 //If true, don't handcuff + + var/mode = 0 +#define SECBOT_IDLE 0 // idle +#define SECBOT_HUNT 1 // found target, hunting +#define SECBOT_PREP_ARREST 2 // at target, preparing to arrest +#define SECBOT_ARREST 3 // arresting target +#define SECBOT_START_PATROL 4 // start patrol +#define SECBOT_PATROL 5 // patrolling +#define SECBOT_SUMMON 6 // summoned by PDA + + var/auto_patrol = 0 // set to make bot automatically patrol + + var/obj/machinery/camera/cam //Camera for the AI to find them I guess + + 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 + + +/obj/item/weapon/ed209_assembly + name = "ED-209 assembly" + desc = "Some sort of bizarre assembly." + icon = 'aibots.dmi' + icon_state = "ed209_frame" + item_state = "ed209_frame" + var/build_step = 0 + var/created_name = "ED-209 Security Robot" //To preserve the name if it's a unique securitron I guess + + + +/obj/machinery/bot/ed209 + New() + ..() + src.icon_state = "ed209[src.on]" + spawn(3) + src.botcard = new /obj/item/weapon/card/id(src) + src.botcard.access = get_access("Detective") + src.cam = new /obj/machinery/camera(src) + src.cam.c_tag = src.name + src.cam.network = "SS13" + if(radio_controller) + radio_controller.add_object(src, "[control_freq]") + radio_controller.add_object(src, "[beacon_freq]") + + examine() + set src in view() + ..() + + if (src.health < 25) + if (src.health > 15) + usr << text("\red [src]'s parts look loose.") + else + usr << text("\red [src]'s parts look very loose!") + return + + attack_hand(user as mob) + var/dat + + dat += text({" +Automatic Security Unit v2.5

+Status: []
+Behaviour controls are [src.locked ? "locked" : "unlocked"]"}, + +"[src.on ? "On" : "Off"]" ) + + if(!src.locked) + dat += text({"
+Check for Weapon Authorization: []
+Check Security Records: []
+Operating Mode: []
+Auto Patrol: []"}, + +"[src.idcheck ? "Yes" : "No"]", +"[src.check_records ? "Yes" : "No"]", +"[src.arrest_type ? "Detain" : "Arrest"]", +"[auto_patrol ? "On" : "Off"]" ) + + + user << browse("Securitron v2.5 controls[dat]", "window=autosec") + onclose(user, "autosec") + return + + Topic(href, href_list) + usr.machine = src + src.add_fingerprint(usr) + if ((href_list["power"]) && (src.allowed(usr))) + src.on = !src.on + src.target = null + src.oldtarget_name = null + src.anchored = 0 + src.mode = SECBOT_IDLE + walk_to(src,0) + src.icon_state = "ed209[src.on]" + src.updateUsrDialog() + + switch(href_list["operation"]) + if ("idcheck") + src.idcheck = !src.idcheck + 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 + updateUsrDialog() + + attack_ai(mob/user as mob) + src.on = !src.on + src.target = null + src.oldtarget_name = null + mode = SECBOT_IDLE + src.anchored = 0 + src.icon_state = "ed209[src.on]" + walk_to(src,0) + + attackby(obj/item/weapon/W as obj, mob/user as mob) + if ((istype(W, /obj/item/weapon/card/emag)) && (!src.emagged)) + 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 + src.oldtarget_name = user.name + src.last_found = world.time + src.anchored = 0 + src.emagged = 1 + src.on = 1 + src.icon_state = "ed209[src.on]" + mode = SECBOT_IDLE + else if (istype(W, /obj/item/weapon/card/id)) + if (src.allowed(user)) + src.locked = !src.locked + user << "Controls are now [src.locked ? "locked." : "unlocked."]" + else + user << "\red Access denied." + + else if (istype(W, /obj/item/weapon/screwdriver)) + if (src.health < 25) + src.health = 25 + for(var/mob/O in viewers(src, null)) + O << "\red [user] repairs [src]!" + else + switch(W.damtype) + if("fire") + src.health -= W.force * 0.75 + if("brute") + src.health -= W.force * 0.5 + else + if (src.health <= 0) + src.explode() + else if ((W.force) && (!src.target)) + src.target = user + src.mode = SECBOT_HUNT + ..() + + + + + process() + set background = 1 + + if (!src.on) + return + var/list/targets = list() + for (var/mob/living/carbon/C in view(12,src)) //Let's find us a target + var/threatlevel = 0 + if ((C.stat) || (C.lying)) + continue + if (istype(C, /mob/living/carbon/human)) + threatlevel = src.assess_perp(C) + else if ((istype(C, /mob/living/carbon/monkey)) && (C.client) && (ticker.mode.name == "monkey")) + threatlevel = 4 + //src.speak(C.real_name + text(": threat: []", threatlevel)) + if (threatlevel < 4 ) + continue + + var/dst = get_dist(src, C) + if ( dst <= 1 || dst > 12) + continue + + targets += C + if (targets.len>0) + var/mob/t = pick(targets) + if (istype(t, /mob/living)) + if (t.stat!=2) + //src.speak("selected target: " + t.real_name) + src.shootAt(t) + switch(mode) + + if(SECBOT_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 + + if(SECBOT_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 + walk_to(src,0) + + if (target) // make sure target exists + if (get_dist(src, src.target) <= 1) // if right next to perp + playsound(src.loc, 'Egloves.ogg', 50, 1, -1) + src.icon_state = "ed209-c" + spawn(2) + src.icon_state = "ed209[src.on]" + var/mob/living/carbon/M = src.target + var/maxstuns = 4 + if (istype(M, /mob/living/carbon/human)) + if (M.weakened < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) + M.weakened = 10 + if (M.stuttering < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) + M.stuttering = 10 + if (M.stunned < 10 && (!(M.mutations & 8)) /*&& (!istype(M:wear_suit, /obj/item/clothing/suit/judgerobe))*/) + M.stunned = 10 + else + M.weakened = 10 + M.stuttering = 10 + M.stunned = 10 + maxstuns-- + if (maxstuns <= 0) + target = null + for(var/mob/O in viewers(src, null)) + O.show_message("\red [src.target] has been stunned by [src]!", 1, "\red You hear someone fall", 2) + + mode = SECBOT_PREP_ARREST + src.anchored = 1 + src.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++ + else + src.frustration = 0 + + if(SECBOT_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 + return + + if (!src.target.handcuffed && !src.arrest_type) + playsound(src.loc, 'handcuffs.ogg', 30, 1, -2) + mode = SECBOT_ARREST + for(var/mob/O in viewers(src, null)) + O.show_message("\red [src] is trying to put handcuffs on [src.target]!", 1) + + spawn(60) + if (get_dist(src, src.target) <= 1) + if (src.target.handcuffed) + return + + if(istype(src.target,/mob/living/carbon)) + src.target.handcuffed = new /obj/item/weapon/handcuffs(src.target) + + mode = SECBOT_IDLE + src.target = null + src.anchored = 0 + src.last_found = world.time + src.frustration = 0 + + // playsound(src.loc, pick('bgod.ogg', 'biamthelaw.ogg', 'bsecureday.ogg', 'bradio.ogg', 'binsult.ogg', '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(SECBOT_ARREST) // arresting + if (src.target.handcuffed) + src.anchored = 0 + mode = SECBOT_IDLE + return + + + if(SECBOT_START_PATROL) // start a patrol + + if(path.len > 0 && patrol_target) // have a valid path, so just resume + mode = SECBOT_PATROL + return + + else if(patrol_target) // has patrol target already + spawn(0) + calc_path() // so just find a route to it + if(path.len == 0) + patrol_target = 0 + return + mode = SECBOT_PATROL + + + else // no patrol target, so need a new one + find_patrol_target() + speak("Engaging patrol mode.") + + + if(SECBOT_PATROL) // patrol mode + patrol_step() + spawn(5) + if(mode == SECBOT_PATROL) + patrol_step() + + if(SECBOT_SUMMON) // summoned to PDA + patrol_step() + spawn(4) + if(mode == SECBOT_SUMMON) + patrol_step() + sleep(4) + patrol_step() + + return + + + // perform a single patrol step + + 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 + 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 + 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() + + + 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 + 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 + + receive_signal(datum/signal/signal) + + if(!on) + return + + /* + world << "rec signal: [signal.source]" + for(var/x in signal.data) + world << "* [x] = [signal.data[x]]" + */ + + var/recv = signal.data["command"] + // process all-bot input + if(recv=="bot_status") + send_status() + + // check to see if we are the commanded bot + if(signal.data["active"] == src) + // process control input + switch(recv) + if("stop") + mode = SECBOT_IDLE + auto_patrol = 0 + return + + if("go") + mode = SECBOT_IDLE + auto_patrol = 1 + return + + if("summon") + patrol_target = signal.data["target"] + next_destination = destination + destination = null + awaiting_beacon = 0 + mode = SECBOT_SUMMON + calc_path() + speak("Responding.") + + return + + + + // receive response from beacon + recv = signal.data["beacon"] + var/valid = signal.data["patrol"] + if(!recv || !valid) + return + + if(recv == new_destination) // if the recvd beacon location matches the set destination + // the we will navigate there + destination = new_destination + patrol_target = signal.source.loc + next_destination = signal.data["next_patrol"] + awaiting_beacon = 0 + + // if looking for nearest beacon + else if(new_destination == "__nearest__") + var/dist = get_dist(src,signal.source.loc) + if(nearest_beacon) + + // note we ignore the beacon we are located at + if(dist>1 && dist 1) + nearest_beacon = recv + nearest_beacon_loc = signal.source.loc + return + + + // send a radio signal with a single data key/value pair + 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 + 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]" + + if (signal.data["findbeacon"]) + frequency.post_signal(src, signal, null) + else if (signal.data["type"] == "secbot") + frequency.post_signal(src, signal, null) + else + frequency.post_signal(src, signal) + + // signals bot status etc. to controller + proc/send_status() + var/list/kv = new() + kv["type"] = "secbot" + kv["name"] = name + kv["loca"] = loc.loc // area + kv["mode"] = mode + post_signal_multiple(control_freq, kv) + + + +// calculates a path to the current destination +// given an optional turf to avoid + 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) + + +// look for a criminal in view of the bot + + proc/look_for_perp() + src.anchored = 0 + src.threatlevel = 0 + for (var/mob/living/carbon/C in view(12,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)) + continue + + if (istype(C, /mob/living/carbon/human)) + src.threatlevel = src.assess_perp(C) + else if ((istype(C, /mob/living/carbon/monkey)) && (C.client) && (ticker.mode.name == "monkey")) + src.threatlevel = 4 + + if (!src.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('ed209_20sec.ogg', 'ed209_15sec.ogg'), 50, 0) + src.visible_message("[src] points at [C.name]!") + mode = SECBOT_HUNT + spawn(0) + process() // ensure bot quickly responds to a perp + break + else + continue + + +//If the security records say to arrest them, arrest them +//Or if they have weapons and aren't security, arrest them. + proc/assess_perp(mob/living/carbon/human/perp as mob) + var/threatcount = 0 + + if(src.emagged) return 10 //Everyone is a criminal! + + if((src.idcheck) || (isnull(perp:wear_id)) || (istype(perp:wear_id, /obj/item/weapon/card/id/syndicate))) + if(src.allowed(perp)) //Corrupt cops cannot exist beep boop + return 0 + + if((istype(perp.l_hand, /obj/item/weapon/gun) && !istype(perp.l_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.l_hand, /obj/item/weapon/baton)) + threatcount += 4 + + if((istype(perp.r_hand, /obj/item/weapon/gun) && !istype(perp.r_hand, /obj/item/weapon/gun/shotgun)) || istype(perp.r_hand, /obj/item/weapon/baton)) + threatcount += 4 + + if(istype(perp:belt, /obj/item/weapon/gun) || istype(perp:belt, /obj/item/weapon/baton)) + threatcount += 2 + + if(istype(perp:wear_suit, /obj/item/clothing/suit/wizrobe)) + threatcount += 2 + + if(perp.mutantrace != "none") + threatcount += 2 + + //Agent cards lower threatlevel when normal idchecking is off. + if((istype(perp:wear_id, /obj/item/weapon/card/id/syndicate)) && src.idcheck) + threatcount -= 2 + + if (src.check_records) + for (var/datum/data/record/E in data_core.general) + var/perpname = perp.name + if (perp:wear_id && perp:wear_id.registered) + perpname = perp.wear_id.registered + if (E.fields["name"] == perpname) + for (var/datum/data/record/R in data_core.security) + if ((R.fields["id"] == E.fields["id"]) && (R.fields["criminal"] == "*Arrest*")) + threatcount = 4 + break + + return threatcount + + Bump(M as mob|obj) //Leave no door unopened! + spawn(0) + if ((istype(M, /obj/machinery/door)) && (!isnull(src.botcard))) + var/obj/machinery/door/D = M + if (D.check_access(src.botcard)) + D.open() + src.frustration = 0 + else if ((istype(M, /mob/living/)) && (!src.anchored)) + src.loc = M:loc + src.frustration = 0 + + return + return + + Bumped(M as mob|obj) + spawn(0) + var/turf/T = get_turf(src) + M:loc = T + + bullet_act(flag, A as obj) + if (flag == PROJECTILE_BULLET) + src.health -= 20 + + // else if (flag == PROJECTILE_WEAKBULLET || PROJECTILE_BEANBAG) //Detective's revolver fires marshmallows + // src.health -= 2 + + else if (flag == PROJECTILE_LASER) + src.health -= 10 + + + if (src.health <= 0) + src.explode() + + proc/speak(var/message) + for(var/mob/O in hearers(src, null)) + O << "[src] beeps, \"[message]\"" + return + + //Generally we want to explode() instead of just deleting the securitron. + ex_act(severity) + switch(severity) + if(1.0) + src.explode() + return + if(2.0) + src.health -= 15 + if (src.health <= 0) + src.explode() + return + return + + meteorhit() + src.explode() + return + + blob_act() + if(prob(50)) + src.explode() + return + + proc/explode() + + walk_to(src,0) + for(var/mob/O in hearers(src, null)) + O.show_message("\red [src] blows apart!", 1) + 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('aibots.dmi', "hs_hole") + Sa.created_name = src.name + new /obj/item/device/prox_sensor(Tsec) + + var/obj/item/weapon/baton/B = new /obj/item/weapon/baton(Tsec) + B.charges = 0 + + if (prob(50)) + new /obj/item/robot_parts/l_arm(Tsec) + + var/datum/effects/system/spark_spread/s = new /datum/effects/system/spark_spread + s.set_up(3, 1, src) + s.start() + del(src) + + + + + + + + +/obj/item/weapon/ed209_assembly/attackby(obj/item/weapon/W as obj, mob/user as mob) + + + if (((istype(W, /obj/item/robot_parts/l_leg)) || (istype(W, /obj/item/robot_parts/r_leg))) && (src.build_step == 0 || src.build_step == 1)) + src.build_step++ + user << "You add the robot leg to [src]!" + src.name = "legs/frame assembly" + if (src.build_step == 1) + src.item_state = "ed209_leg" + src.icon_state = "ed209_leg" + if (src.build_step == 2) + src.item_state = "ed209_legs" + src.icon_state = "ed209_legs" + + del(W) + + else if (istype(W, /obj/item/clothing/suit/armor/vest) && (src.build_step == 2)) + src.build_step++ + user << "You add the armor to [src]!" + src.name = "vest/legs/frame assembly" + src.item_state = "ed209_shell" + src.icon_state = "ed209_shell" + del(W) + else if ((istype(W, /obj/item/weapon/weldingtool)) && (src.build_step == 3)) + if ((W:welding) && (W:get_fuel() >= 1)) + W:use_fuel(1) + src.build_step++ + src.name = "shielded frame assembly" + user << "You welded the vest to [src]!" + else if (istype(W, /obj/item/clothing/head/helmet) && (src.build_step == 4)) + src.build_step++ + user << "You add the helmet to [src]!" + src.name = "covered and shielded frame assembly" + src.item_state = "ed209_hat" + src.icon_state = "ed209_hat" + del(W) + else if (istype(W, /obj/item/device/prox_sensor) && (src.build_step == 5)) + src.build_step++ + user << "You add the prox sensor to [src]!" + src.name = "prox/covered and armed frame assembly" + src.item_state = "ed209_prox" + src.icon_state = "ed209_prox" + del(W) + else if(istype(W, /obj/item/weapon/cable_coil) && (src.build_step == 6) ) + var/obj/item/weapon/cable_coil/coil = W + var/turf/T = get_turf(user) + user.visible_message("[user] wires the airlock assembly.", "You start to wire the airlock assembly.") + sleep(40) + if(get_turf(user) == T) + coil.use(1) + src.build_step++ + user << "\blue You wire the ED-209 assembly!" + src.name = "Wired ED-209 Assembly" + else if (istype(W, /obj/item/weapon/gun/energy/taser_gun) && (src.build_step == 7)) + src.build_step++ + user << "You add the taser gun to [src]!" + src.name = "Taser/Wired ED-209 Assembly" + src.item_state = "ed209_taser" + src.icon_state = "ed209_taser" + del(W) + else if(istype(W, /obj/item/weapon/screwdriver) && (src.build_step == 8) ) + playsound(src.loc, 'Screwdriver.ogg', 100, 1) + var/turf/T = get_turf(user) + user << "\blue Now attaching the gun to the frame." + sleep(40) + if(get_turf(user) == T) + src.build_step++ + src.name = "Armed ED-209 Assembly" + user << "\blue Taser gun attached!" + else if ((istype(W, /obj/item/weapon/cell)) && (src.build_step >= 9)) + src.build_step++ + user << "You complete the ED-209!" + var/obj/machinery/bot/ed209/S = new /obj/machinery/bot/ed209 + S.loc = get_turf(src) + S.name = src.created_name + del(W) + del(src) + + else if (istype(W, /obj/item/weapon/pen)) + var/t = input(user, "Enter new robot name", src.name, src.created_name) as text + t = copytext(sanitize(t), 1, MAX_MESSAGE_LEN) + if (!t) + return + if (!in_range(src, usr) && src.loc != usr) + return + + src.created_name = t + +/obj/machinery/bot/ed209/proc/shootAt(var/mob/target) + if(lastfired && world.time - lastfired < shot_delay) + return + lastfired = world.time + var/turf/T = loc + var/atom/U = (istype(target, /atom/movable) ? target.loc : target) + if ((!( U ) || !( T ))) + return + while(!( istype(U, /turf) )) + U = U.loc + if (!( istype(T, /turf) )) + return + + //if(lastfired && world.time - lastfired < 100) + // playsound(src.loc, 'ed209_shoot.ogg', 50, 0) + + var/obj/beam/a_laser/A + if (src.emagged) + A = new /obj/beam/a_laser( loc ) + use_power(50) + else + A = new /obj/bullet/electrode( loc ) + use_power(100) + + if (!( istype(U, /turf) )) + //A = null + del(A) + return + A.current = U + A.yo = U.y - T.y + A.xo = U.x - T.x + spawn( 0 ) + A.process() + return + return \ No newline at end of file diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm new file mode 100644 index 00000000000..48b3b23bfed --- /dev/null +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -0,0 +1,100 @@ +/obj/item/weapon/airlock_electronics + name = "Airlock Electronics" + icon = 'door_assembly.dmi' + icon_state = "door_electronics" + + m_amt = 50 + g_amt = 50 + + req_access = list(access_engine) + + var + list/conf_access = null + last_configurator = null + locked = 1 + + attack_self(mob/user as mob) + if (!ishuman(user)) + return ..(user) + + var/mob/living/carbon/human/H = user + if(H.brainloss >= 60) + return + + var/t1 = text("Настройка доступа
\n") + + + if (last_configurator) + t1 += "Настройщик: [last_configurator]
" + + if (locked) + t1 += "Вставьте ID
" + else + t1 += "Заблокировать
" + + + t1 += conf_access == null ? "Все
" : "Все
" + + t1 += "
" + + var/list/accesses = get_all_accesses() + for (var/acc in accesses) + var/aname = get_access_desc(acc) + + if (!conf_access || !conf_access.len || !(acc in conf_access)) + t1 += "[aname]
" + else + t1 += "[aname]
" + + t1 += text("

Закрыть

\n", src) + + user << browse(t1, "window=airlock_electronics") + onclose(user, "airlock") + + Topic(href, href_list) + ..() + if (usr.stat || usr.restrained()) + return + if (href_list["close"]) + usr << browse(null, "window=airlock") + return + + if (!ishuman(usr)) + return + + if (href_list["login"]) + var/obj/item/I + if (istype(usr.l_hand, /obj/item/weapon/card/id)) I = usr.l_hand + if (istype(usr.r_hand, /obj/item/weapon/card/id)) I = usr.r_hand + if (I && src.check_access(I)) + src.locked = 0 + src.last_configurator = I:registered + + if (locked) + return + + if (href_list["logout"]) + locked = 1 + + if (href_list["access"]) + toggle_access(href_list["access"]) + + attack_self(usr) + + proc + toggle_access(var/acc) + if (acc == "all") + conf_access = null + else + var/req = text2num(acc) + + conf_access = list(req) + /* + if (conf_access == null) + conf_access = list() + + if (!(req in conf_access)) + conf_access += req + else + conf_access -= req + */ diff --git a/code/game/objects/closets/firecloset.dm b/code/game/objects/closets/firecloset.dm new file mode 100644 index 00000000000..465518d4339 --- /dev/null +++ b/code/game/objects/closets/firecloset.dm @@ -0,0 +1,21 @@ +/obj/closet/firecloset/New() + ..() + + if (prob (1)) + del(src) + return + + switch (pickweight(list("extinguisher" = 50, "toolbox" = 30, "nothing" = 20))) + if ("extinguisher") + new /obj/item/weapon/extinguisher(src) + if ("toolbox") + new /obj/item/weapon/storage/toolbox/emergency(src) + + if (prob (30)) + new /obj/item/clothing/mask/breath(src) + + if (prob (30)) + new /obj/item/weapon/tank/emergency_oxygen(src) + + if (prob (10)) + new /obj/item/clothing/suit/fire(src) \ No newline at end of file diff --git a/icons/mob/fullness.dmi b/icons/mob/fullness.dmi new file mode 100644 index 00000000000..7093184b301 Binary files /dev/null and b/icons/mob/fullness.dmi differ