From b2b623cac83d00d25fb8a08bef7f1176fbae0708 Mon Sep 17 00:00:00 2001 From: ZomgPonies Date: Mon, 4 Nov 2013 07:48:55 -0500 Subject: [PATCH] Programmable cargo machines --- code/WorkInProgress/Sayu/cargoprofile.dm | 797 +++++++++++++++++++++++ code/WorkInProgress/Sayu/programmable.dm | 745 +++++++++++++++++++++ 2 files changed, 1542 insertions(+) create mode 100644 code/WorkInProgress/Sayu/cargoprofile.dm create mode 100644 code/WorkInProgress/Sayu/programmable.dm diff --git a/code/WorkInProgress/Sayu/cargoprofile.dm b/code/WorkInProgress/Sayu/cargoprofile.dm new file mode 100644 index 00000000000..d90b76c8acf --- /dev/null +++ b/code/WorkInProgress/Sayu/cargoprofile.dm @@ -0,0 +1,797 @@ +/datum/cargoprofile + var/name = "All Items" + var/id = "all" // unique ID for the UI + var/enabled = 1 + var/eject_speed = 1 // will change when emagged + var/const/BIG_OBJECT_WORK = 10 + var/const/MOB_WORK = 10 + var/obj/machinery/programmable/master = null + var/universal = 0 // set when both unary and binary machines work + var/mobcheck = 0 + + var/list/whitelist = list(/obj/item,/obj/structure/closet,/obj/structure/bigDelivery,/obj/machinery/portable_atmospherics) + var/list/blacklist = null + var/dedicated_path = null // When constructing a new machine with this as default program, create a machine of the specified type instead. + + + //contains: called to determine if an object/mob will be sorted by this profile + //return 1 for any sortable item + proc/contains(var/atom/A) + if(!istype(A,/obj)) + if(!mobcheck || !istype(A,/mob)) + return 0 + else + var/obj/O = A + if(O.anchored) + return 0 + //If you are using both white and blacklists, blacklists are absoulte, no matter what is whitelisted. + //I understand this has some limitations. You cannot whitelist all items, blacklist weapons, + // and then whitelist a specific weapon. Them's the breaks, kid. + if(blacklist) + for(var/T in blacklist) + if(istype(A,T)) + return 0 + if(whitelist) + for(var/T in whitelist) + if(istype(A,T)) + return 1 + return 0 + return 1 + + + //inlet_reaction: called when a filtered item is chosen by this profile. + //W: Item chosen + //S: input turf location + //remaining: counts down how much more work the unloader wants to do this turn. + //return the amount of work done. + proc/inlet_reaction(var/atom/W,var/turf/S,var/remaining) + if(!W || !S || !master) + return 0 + + if(istype(W,/obj/item)) + var/obj/item/I = W + if(I.w_class > remaining) + return 0 + I.loc = master + master.types[W.type] = src + return I.w_class + + + if(istype(W,/obj/structure) || istype(W,/obj/machinery)) // closets, big deliveries, portable atmospherics, unconnected stuff + if(remaining < BIG_OBJECT_WORK) + return 0 + var/obj/O = W + O.loc = master + master.types[O.type] = src + return BIG_OBJECT_WORK + + //Not item, structure, machinery, or mob + return 0 + + //outlet_reaction: called when a stored object is ejected + //W: the item in question + //D: the destination turf + proc/outlet_reaction(var/atom/W,var/turf/D) + if(!W || !D || !master) + return + + if(master.emagged) + // emagging is not an industry-approved practice. + // some malfunctions may occur. + eject_speed = rand(0,4) + D = get_step(D,master.outdir) + while(prob(20)) + if(master.outdir == NORTH || master.outdir == SOUTH) + D = get_step(D,pick(EAST,WEST,master.outdir)) + else + D = get_step(D,pick(NORTH,SOUTH,master.outdir)) + + if(istype(W,/obj)) + var/obj/O = W + O.loc = master.loc + O.dir = master.outdir + O.throw_at(D,eject_speed,eject_speed) + return + +//---------------------------------------------------------------------------- +// Profiles +//---------------------------------------------------------------------------- + +/datum/cargoprofile/boxes + name = "Move Small Containers" + id = "boxes" + blacklist = null + whitelist = list(/obj/item/weapon/storage, /obj/item/weapon/moneybag, /obj/item/weapon/evidencebag, + /obj/item/weapon/tray, /obj/item/pizzabox, /obj/item/weapon/clipboard, + /obj/item/smallDelivery, /obj/structure/bigDelivery) + +/datum/cargoprofile/cargo + name = "Move Large Containers" + id = "cargo" + blacklist = null + whitelist = list(/obj/structure/closet,/obj/structure/ore_box) + + // Make an honest attempt to move other things out of the way + outlet_reaction(var/atom/W,var/turf/D) + for(var/obj/O in D) + if(O.density && !O.anchored) + step_away(O,src) // move forward first + if(O.loc == D) + step_away(O,D) // move anywhere + ..(W,D) + +/datum/cargoprofile/cargo/empty + name = "Move Empty Large Containers" + id = "cargo-empty" + contains(var/atom/A) + return (..(A) && (A.contents.len == 0)) +/datum/cargoprofile/cargo/full + name = "Move Full Large Containers" + id = "cargo-full" + contains(var/atom/A) + return (..(A) && (A.contents.len > 0)) + +/datum/cargoprofile/supplies + name = "Building Supplies" + id = "supplies" + blacklist = null + whitelist = list(/obj/item/weapon/cable_coil,/obj/item/stack/rods, + /obj/item/stack/sheet/metal,/obj/item/stack/sheet/plasteel, + /obj/item/stack/sheet/glass,/obj/item/stack/sheet/rglass, + /obj/item/stack/tile,/obj/item/weapon/light, + /obj/item/weapon/table_parts) + //todo: maybe stack things while we're here? + +/datum/cargoprofile/exotics + name = "Exotic materials" + id = "exotics" + blacklist = null + whitelist = list(/obj/item/weapon/coin, /obj/item/weapon/spacecash, /obj/item/seeds, + /obj/item/stack/sheet/mineral,/obj/item/stack/sheet/wood,/obj/item/stack/sheet/leather) + +/datum/cargoprofile/organics + name = "Organics, chemicals, and Paraphernalia" + id = "organics" + blacklist = null + whitelist = list(/obj/item/weapon/tank,/obj/item/weapon/reagent_containers, + /obj/item/stack/medical,/obj/item/weapon/storage/pill_bottle,/obj/item/weapon/gun/syringe, + /obj/item/weapon/plastique,/obj/item/weapon/grenade,/obj/item/ammo_box, + /obj/item/weapon/gun/grenadelauncher,/obj/item/weapon/flamethrower, /obj/item/weapon/lighter, + /obj/item/weapon/match,/obj/item/weapon/weldingtool) + +/datum/cargoprofile/food + name = "Food" + id = "food" + blacklist = null // something should probably go here + whitelist = list(/obj/item/weapon/reagent_containers/food) + +/datum/cargoprofile/chemical + name = "Chemicals and Paraphernalia" + id = "chemical" + blacklist = list(/obj/item/weapon/reagent_containers/food) + whitelist = list(/obj/item/weapon/reagent_containers,/obj/item/stack/medical,/obj/item/weapon/storage/pill_bottle, + /obj/item/weapon/gun/syringe,/obj/item/weapon/grenade/chem_grenade,/obj/item/weapon/dnainjector, + /obj/item/weapon/storage/belt/medical,/obj/item/weapon/storage/firstaid,/obj/item/weapon/implanter) + +/datum/cargoprofile/pressure + name = "air tanks" + id = "pressure" + blacklist = null + whitelist = list(/obj/item/weapon/tank,/obj/machinery/portable_atmospherics, + /obj/item/weapon/flamethrower) + //Am I missing any? +/datum/cargoprofile/pressure/empty + name = "empty air tanks" + id = "pressure-low" + var/lowpressure = ONE_ATMOSPHERE + + contains(var/atom/A) + if(..()) + var/pressure = ONE_ATMOSPHERE * 10 // In case of fallthrough, fail test + if(istype(A,/obj/item/weapon/tank)) + var/obj/item/weapon/tank/T = A + pressure = T.air_contents.return_pressure() + if(istype(A,/obj/item/weapon/flamethrower)) + var/obj/item/weapon/flamethrower/T = A + if(!T.ptank) + return 0 + pressure = T.ptank.air_contents.return_pressure() + if(istype(A,/obj/machinery/portable_atmospherics)) + var/obj/machinery/portable_atmospherics/P = A + pressure = P.air_contents.return_pressure() + + if(pressure < lowpressure) + return 1 + + return 0// Not container or failed low pressure check + +/datum/cargoprofile/pressure/full + name = "full air tanks" + id = "pressure-high" + var/highpressure = ONE_ATMOSPHERE * 15 // stolen from canister.dm; Is this right? + + contains(var/atom/A) + if(..()) + var/pressure = 0 // In case of fallthrough, fail test + if(istype(A,/obj/item/weapon/tank)) + var/obj/item/weapon/tank/T = A + pressure = T.air_contents.return_pressure() + if(istype(A,/obj/item/weapon/flamethrower)) + var/obj/item/weapon/flamethrower/T = A + if(!T.ptank) + return 0 + pressure = T.ptank.air_contents.return_pressure() + if(istype(A,/obj/machinery/portable_atmospherics)) + var/obj/machinery/portable_atmospherics/P = A + pressure = P.air_contents.return_pressure() + + if(pressure > highpressure) + return 1 + + return 0// Not container or failed high pressure check + +/datum/cargoprofile/clothing + name = "Crew Kit" + id = "clothing" + blacklist = list(/obj/item/weapon/tank/plasma,/obj/item/weapon/tank/anesthetic, // the rest are air tanks + /obj/item/clothing/mask/facehugger) // NOT CLOTHING AT ALLLLL + whitelist = list(/obj/item/clothing,/obj/item/weapon/storage/belt,/obj/item/weapon/storage/backpack, + /obj/item/device/radio/headset,/obj/item/device/pda,/obj/item/weapon/card/id,/obj/item/weapon/tank, + /obj/item/weapon/handcuffs, /obj/item/weapon/legcuffs) + +/datum/cargoprofile/trash + name = "Trash" + id = "trash" + //Note that this filters out blueprints because they are a paper item. Do NOT throw out the station blueprints unless you be trollin'. + blacklist = null + whitelist = list(/obj/item/trash,/obj/item/toy,/obj/item/weapon/ectoplasm,/obj/item/weapon/bananapeel,/obj/item/weapon/broken_bottle,/obj/item/weapon/bikehorn, + /obj/item/weapon/cigbutt,/obj/item/weapon/contraband,/obj/item/weapon/corncob,/obj/item/weapon/paper,/obj/item/weapon/shard, + /obj/item/weapon/sord,/obj/item/weapon/photo,/obj/item/weapon/folder, + /obj/item/blueprints,/obj/item/weapon/contraband,/obj/item/weapon/kitchen,/obj/item/weapon/book,/obj/item/clothing/mask/facehugger) + +/datum/cargoprofile/weapons + name = "Weapons & Illegals" + id = "weapons" + blacklist = null + //This one is hard since 'weapon contains a lot of things better categorized as devices + whitelist = list(/obj/item/weapon/banhammer,/obj/item/weapon/sord,/obj/item/weapon/butch,/obj/item/weapon/claymore,/obj/item/weapon/holo/esword, + /obj/item/weapon/flamethrower,/obj/item/weapon/grenade,/obj/item/weapon/gun,/obj/item/weapon/hatchet,/obj/item/weapon/katana, + /obj/item/weapon/kitchenknife,/obj/item/weapon/melee,/obj/item/weapon/nullrod,/obj/item/weapon/pickaxe,/obj/item/weapon/twohanded, + /obj/item/weapon/plastique,/obj/item/weapon/scalpel,/obj/item/weapon/shield,/obj/item/weapon/grown/deathnettle) + +/datum/cargoprofile/tools + name = "Devices & Tools" + id = "tools" + blacklist = null + whitelist = list(/obj/item/device,/obj/item/weapon/card,/obj/item/weapon/cartridge,/obj/item/weapon/cautery,/obj/item/weapon/cell,/obj/item/weapon/circuitboard, + /obj/item/weapon/aiModule,/obj/item/weapon/airalarm_electronics,/obj/item/weapon/airlock_electronics,/obj/item/weapon/circular_saw, + /obj/item/weapon/cloaking_device,/obj/item/weapon/crowbar,/obj/item/weapon/disk,/obj/item/weapon/firealarm_electronics,/obj/item/weapon/hand_tele, + /obj/item/weapon/hand_labeler,/obj/item/weapon/hemostat,/obj/item/weapon/mop,/obj/item/weapon/locator,/obj/item/weapon/minihoe, + /obj/item/weapon/packageWrap,/obj/item/weapon/pen,/obj/item/weapon/pickaxe,/obj/item/weapon/pinpointer, + /obj/item/weapon/rcd,/obj/item/weapon/rcd_ammo,/obj/item/weapon/retractor,/obj/item/weapon/rsf,/obj/item/weapon/rsp,/obj/item/weapon/scalpel, + /obj/item/weapon/screwdriver,/obj/item/weapon/shovel,/obj/item/weapon/soap,/obj/item/weapon/stamp,/obj/item/weapon/tray,/obj/item/weapon/weldingtool, + /obj/item/weapon/wirecutters,/obj/item/weapon/wrench,/obj/item/weapon/extinguisher) + +/datum/cargoprofile/finished + name = "Completed Robots" + id = "finished" + blacklist = null + whitelist = list(/obj/mecha,/obj/machinery/bot,/mob/living/silicon/robot) + mobcheck = 1 + //todo: detect and allow finished cyborg endoskeletons with no brain + contains(var/atom/A) + if(..()) + return 1 + if(istype(A,/mob)) + if(blacklist) + for(var/T in blacklist) + if(istype(A,T)) + return 0 + if(whitelist) + for(var/T in whitelist) + if(istype(A,T)) + return 1 + return 0 + return 1 + return 0 + +/datum/cargoprofile/stripping + name = "Auto-Frisker" + id = "frisk" + blacklist = null + whitelist = list(/mob/living/carbon/human) + mobcheck = 1 + + + +//---------------------------------------------------------------------------- +// Overrides (Special Functions) +//---------------------------------------------------------------------------- + +/datum/cargoprofile/cargo/unload + name = "Unload Cargo Boxes" + id = "cargounload" + enabled = 0 + dedicated_path = /obj/machinery/programmable/unloader + + //override the detection to only accept crates with something in it. + //if it doesn't, this object may be handled by another handler. + contains(var/atom/A) + if(..(A)) + if(istype(A,/obj/structure/closet)) + var/obj/structure/closet/C = A + if(!C.can_open() && !C.opened && !master.emagged) // must be able to access the contents + return 0 + if(A.contents.len) + return 1 + return 0 + + //instead of moving the box, strip it of its contents + inlet_reaction(var/obj/W,var/turf/S, var/remaining) + //W should only be crate or ore box, although this will work on anything with contents... + var/I = 0 + if(istype(W,/obj/structure/closet)) + var/obj/structure/closet/C = W + if(!C.can_open() && !C.opened) // must be able to access the contents + if(master.emagged && remaining >= BIG_OBJECT_WORK) + if(prob(10)) + C.welded = 0 + if("broken" in C.vars) + C:broken = 1 + C.open() + C.update_icon() + master.visible_message("\red [master] breaks open [C]!") + else + master.visible_message("\blue [master] is trying to force [C] open!") + + master.sleep += 1 // mechanical strain + return BIG_OBJECT_WORK + master.visible_message("\blue [master] is trying to open [C], but can't!") + master.sleep = 5 + return 0 + + for(var/obj/item/O in W.contents) + if(I > remaining) + return + if(O.w_class > (remaining - I)) + continue + O.loc = master + master.types[O.type] = src + if(O.w_class > 0) + I += O.w_class + else + I++ + if(!W.contents.len && istype(W,/obj/structure/closet)) + var/obj/structure/closet/C = W + C.open() + return I + + +//Inlet stacker: used when the output is a volatile space (conveyor or another unit's input). +//Does not output a stack until it is full. +/datum/cargoprofile/in_stacker + name = "Hold and Stack" + id = "instacker" + universal = 1 + + blacklist = null + whitelist = list(/obj/item/stack,/obj/item/weapon/cable_coil) + + dedicated_path = /obj/machinery/programmable/stacker + + inlet_reaction(var/atom/W,var/turf/S,var/remaining) + if(istype(W,/obj/item/stack)) + var/obj/item/stack/I = W + if(!I.amount) // todo: am I making a bad assumption here? + del I + return + for(var/obj/item/stack/O in master.contents) + if(O.type == I.type && O.amount < O.max_amount) + if(I.amount + O.amount <= O.max_amount) + O.amount += I.amount + del I + return O.w_class + var/leftover = I.amount + O.amount - O.max_amount + O.amount = O.max_amount + I.amount = leftover + continue + //end for + I.loc = master + master.types[I.type] = src + return I.w_class + if(istype(W,/obj/item/weapon/cable_coil)) + var/obj/item/weapon/cable_coil/I = W + if(!I.amount) // todo: am I making a bad assumption here? + del I + return + for(var/obj/item/weapon/cable_coil/O in master.contents) + if(O.type == I.type && O.amount < MAXCOIL) + if(I.amount + O.amount <= MAXCOIL) + O.amount += I.amount + del I + return O.w_class + var/leftover = I.amount + O.amount - MAXCOIL + O.amount = MAXCOIL + I.amount = leftover + continue + //end for + I.loc = master + master.types[I.type] = src + return I.w_class + + //If the stack isn't finished yet, don't eject it + //unless this profile has been disabled. + outlet_reaction(var/atom/W,var/turf/D) + if(istype(W,/obj/item/stack)) + var/obj/item/stack/I = W + if(src.enabled && (I.amount < I.max_amount)) + return // Still needs to be stacked + ..(W,D) + if(istype(W,/obj/item/weapon/cable_coil)) + var/obj/item/weapon/cable_coil/I = W + if(src.enabled && (I.amount < MAXCOIL)) + return // Still needs to be stacked + ..(W,D) + +//Outlet stacker: used when the output square can be trusted. +//Outputs immediately, adding to stacks in the outlet. +/datum/cargoprofile/unary/stacker + name = "Stack Items" + id = "ustacker" + blacklist = null + whitelist = list(/obj/item/stack,/obj/item/weapon/cable_coil) + + dedicated_path = /obj/machinery/programmable/unary/stacker + + inlet_reaction(var/atom/W,var/turf/S,var/remaining) + + //Only pick it up if you are going to stack it + + if(istype(W,/obj/item/stack)) + var/obj/item/stack/I = W + if(I.amount >= I.max_amount) + return 0 + for(var/obj/item/stack/other in S.contents) + if(other.type == I.type && other != I && other.amount < other.max_amount) + return ..(W,S,remaining) + return 0 + + if(istype(W,/obj/item/weapon/cable_coil)) + var/obj/item/weapon/cable_coil/I = W + if(I.amount >= MAXCOIL) + return 0 + for(var/obj/item/weapon/cable_coil/other in S.contents) + if(other != I && other.amount < MAXCOIL) + return ..(W,S,remaining) + return 0 + + outlet_reaction(var/atom/W,var/turf/D) + if(istype(W,/obj/item/stack)) + var/obj/item/stack/I = W + for(var/obj/item/stack/O in D.contents) + if(O.type == I.type && O.amount < O.max_amount) + if(I.amount + O.amount <= O.max_amount) + O.amount += I.amount + del I + return + var/leftover = I.amount + O.amount - O.max_amount + O.amount = O.max_amount + I.amount = leftover + continue + //end for + I.loc = D + return + if(istype(W,/obj/item/weapon/cable_coil)) + var/obj/item/weapon/cable_coil/I = W + for(var/obj/item/weapon/cable_coil/O in D.contents) + if(O.type == I.type && O.amount < MAXCOIL) + if(I.amount + O.amount <= MAXCOIL) // Why did they make it a #define. + O.amount += I.amount + O.update_icon() + del I + return + var/leftover = I.amount + O.amount - MAXCOIL // That wasn't a question + O.amount = MAXCOIL // It was a complaint + I.amount = leftover + continue + //end for + I.loc = D + return + + + +//---------------------------------------------------------------------------- +// Dubious Overrides (For emag use) +//---------------------------------------------------------------------------- + + +//Clogs up the unloader. And, there may be devious uses for it... +/datum/cargoprofile/slow + name = "Slow unloader" + id = "slow" + whitelist = list(/obj/item,/obj/structure/closet,/obj/structure/bigDelivery,/obj/machinery/portable_atmospherics) + blacklist = list() + + inlet_reaction(var/atom/W,var/turf/S,var/remaining) + if(..()) + return remaining + +/datum/cargoprofile/unary/shredder + name = "Paper Shredder" + id = "shredder" + blacklist = null + whitelist = list(/obj/item/weapon/paper,/obj/item/weapon/book,/obj/item/weapon/clipboard,/obj/item/weapon/folder,/obj/item/weapon/photo) + universal = 1 + + dedicated_path = /obj/machinery/programmable/unary/shredder + + + + proc/cliptags(var/Text) + //Removes all html tags + var/index + var/index2 + index = findtextEx(Text,"<") + while(index) + index2 = findtextEx(Text,">",index) + if(!index2) + return copytext(Text,1,index) + Text = "[copytext(Text,1,index)][copytext(Text,index2+1,0)]" + index = findtextEx(Text,"<") + //should have trimmed that text there pretty good + return Text + + + //Recurses through the text, removing large chunks + proc/garbletext(var/Text) + var/l = length(Text) + if(l <= 3) + if(prob(20)) + return pick("#","|","/","*",".","."," ","."," "," ") + return Text + if(prob(50)) + return "[garbletext(copytext(Text,1,l/2))][garbletext(copytext(Text,l/2,0))]" + if(prob(50)) + return "[pick("#","|","/","*",".","."," ","."," "," ")][garbletext(copytext(Text,1,l/2))]" + return "[garbletext(copytext(Text,l/2,0))][pick("#","|","/","*",".","."," ","."," "," ")]" + + proc/garble_keeptags(var/Text) + var/list/L = stringsplit(Text,">") + var/result = "" + for(var/string in L) + var/index = findtextEx(string,"<") + if(index!=1) + result += "[garbletext(copytext(string,1,index))][copytext(string,index)]>" + else + result += "[string]>" + return copytext(result,1,lentext(result)) + + + + + outlet_reaction(var/atom/W,var/turf/D) + if(istype(W,/obj/item/weapon/paper/crumpled)) + del W + return + if(istype(W,/obj/item/weapon/clipboard) || istype(W,/obj/item/weapon/folder)) + // destroy folder, various effects on contents + for(var/obj/item/I in W.contents) + if(prob(25))//JUNK IT + del I + else if(prob(50)) //We've been over this. I can't just take it apart with a crowbar. + var/obj/item/weapon/paper/crumpled/P = new(master.loc) + if(I.name) + P.name = garbletext(I.name) + if(prob(66)) + P.fingerprints = I.fingerprints + P.fingerprintshidden = I.fingerprintshidden + if(istype(I,/obj/item/weapon/paper)) + var/obj/item/weapon/paper/O = I + P.info = garble_keeptags(O.info) + del I + ..(P,D) + else + ..(I,D) // Eject + del W //destroy container + return + if(prob(50)) //JUNK IT NOW! + var/obj/item/weapon/paper/crumpled/P = new(master.loc) + P.name = W.name + var/obj/item/I = W + if(prob(66)) + P.fingerprints = I.fingerprints + P.fingerprintshidden = I.fingerprintshidden + if(istype(I,/obj/item/weapon/paper)) + var/obj/item/weapon/paper/O = I + if(O.info) + P.info = garble_keeptags(O.info) + if(istype(I,/obj/item/weapon/book)) + var/obj/item/weapon/book/B = I + if(B.dat) + P.info = garble_keeptags(B.dat) + if(B.carved && B.store) + ..(B.store,D) + del W + ..(P,D) + else //I want it junked + del W + return + +/datum/cargoprofile/unary/gibber + name = "human shredding" + id = "flesh" + whitelist = list(/mob/living/carbon,/mob/living/simple_animal) + blacklist = null + mobcheck = 1 + contains(var/atom/A) + if(!istype(A,/mob)) + return + if(blacklist) + for(var/T in blacklist) + if(istype(A,T)) + return 0 + if(whitelist) + for(var/T in whitelist) + if(istype(A,T)) + return 1 + return 0 + return 1 + inlet_reaction(var/atom/W,var/turf/S,var/remaining) + var/mob/living/M = W + if(istype(M) && (remaining > MOB_WORK)) + //this is necessarily damaging + var/damage = rand(1,5) + M << "\red The unloading machine grabs you with a hard metallic claw!" + if(M.client) + M.client.eye = master + M.client.perspective = EYE_PERSPECTIVE + M.loc = master + master.types[M.type] = src + M.apply_damage(damage) // todo: ugly + M.visible_message("\red [M.name] gets pulled into the machine!") + return MOB_WORK + outlet_reaction(var/atom/W,var/turf/D) + var/mob/living/M = W + var/bruteloss = M.bruteloss + if(istype(M,/mob/living/carbon/human)) + var/mob/living/carbon/human/C = M + for(var/datum/organ/external/L in C.organs) + bruteloss += L.brute_dam + if(bruteloss < 100) // requires tenderization + M.apply_damage(rand(5,15),BRUTE) + M << "The machine is tearing you apart!" + master.visible_message("\red [master] makes a squishy grinding noise.") + return + M.loc = master.loc + M.gib() + return + + +/datum/cargoprofile/people + name = "Manhandling" + id = "people" + + whitelist = null + blacklist = list(/mob/camera,/mob/new_player,/mob/living/simple_animal/hostile/blobspore,/mob/living/simple_animal/hostile/creature, + /mob/living/simple_animal/space_worm,/mob/living/simple_animal/shade,/mob/living/simple_animal/hostile/faithless,/mob/dead) + universal = 1 + mobcheck = 1 + + + contains(var/atom/A) + if(!istype(A,/mob)) + return + if(blacklist) + for(var/T in blacklist) + if(istype(A,T)) + return 0 + if(whitelist) + for(var/T in whitelist) + if(istype(A,T)) + return 1 + return 0 + return 1 + + inlet_reaction(var/atom/W,var/turf/S,var/remaining) + var/mob/living/M = W + if(remaining > MOB_WORK) + //this is necessarily damaging + var/damage = rand(1,5) + M << "\red The unloading machine grabs you with a hard metallic claw!" + if(M.client) + M.client.eye = master + M.client.perspective = EYE_PERSPECTIVE + M.loc = master + master.types[M.type] = src + M.apply_damage(damage) // todo: ugly + M.visible_message("\red [M.name] gets pulled into the machine!") + return MOB_WORK + + outlet_reaction(var/atom/W,var/turf/D) + var/mob/living/M = W + M.loc = master.loc + M.dir = master.outdir + if(M.client) + M.client.eye = M.client.mob + M.client.perspective = MOB_PERSPECTIVE + + D = get_step(D,master.outdir) // throw attempt + eject_speed = rand(0,4) + + M.visible_message("\blue [M.name] is ejected from the unloader.") + M.throw_at(D,eject_speed,eject_speed) + return + +/datum/cargoprofile/unary/trainer + name = "Boxing Trainer" + id = "trainer" + blacklist = list() + whitelist = list(/mob/living/carbon/human) + mobcheck = 1 + + var/const/PUNCH_WORK = 6 + + dedicated_path = /obj/machinery/programmable/unary/trainer + + contains(var/atom/A) + if(!istype(A,/mob)) + return 0 + if(blacklist) + for(var/T in blacklist) + if(istype(A,T)) + return 0 + if(whitelist) + for(var/T in whitelist) + if(istype(A,T)) + return 1 + return 0 + return 1 + + proc/punch(var/mob/living/carbon/human/M,var/maxpunches) + //stolen from holographic boxing gloves code + //This should probably be done BY the mob, however, the attack code will be expecting a source mob. + + var/damage + if(prob(75)) + damage = rand(0, 6) // pap + else + damage = rand(0, 12) // thwack + + if(!damage) + playsound(master.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + master.visible_message("\red \The [src] punched at [M], but whiffed!") + + if(maxpunches > 1 && prob(50)) // Follow through on a miss, 50% chance + return punch(M,maxpunches - 1) + 1 + return 1 + var/datum/organ/external/affecting = M.get_organ(ran_zone("chest",50)) + var/armor_block = M.run_armor_check(affecting, "melee") + + playsound(master.loc, "punch", 25, 1, -1) + master.visible_message("\red \The [src] has punched [M]!") + if(!master.emagged) + M.apply_damage(damage, HALLOSS, affecting, armor_block) // Clean fight + else + M.apply_damage(damage, BRUTE, affecting, armor_block) // Foul! Foooul! + + if(damage >= 9) + master.visible_message("\red \The [src] has weakened [M]!") + M.apply_effect(4, WEAKEN, armor_block) + if(!master.emagged) + master.sleep = 1 + return maxpunches // The machine is not so sophisticated as to not gloat + else + if(prob(25)) // Follow through on a hit, 25% chance. Pause after. + return punch(M,maxpunches-1) + 1 + return 1 + + inlet_reaction(var/atom/W,var/turf/S,var/remaining) + //stolen from boxing gloves code + var/mob/living/carbon/human/M = W + if((M.lying || (M.health - M.halloss < 25))&& !master.emagged) + M << "\The [src] gives you a break." + master.sleep+=5 + return 0 // Be polite + var/punches = punch(M,remaining / PUNCH_WORK) + if(punches>1)master.sleep++ + return punches * PUNCH_WORK \ No newline at end of file diff --git a/code/WorkInProgress/Sayu/programmable.dm b/code/WorkInProgress/Sayu/programmable.dm new file mode 100644 index 00000000000..4a378811f3b --- /dev/null +++ b/code/WorkInProgress/Sayu/programmable.dm @@ -0,0 +1,745 @@ + +// TODO: Check access +// TODO: Renameable circuit boards +// TODO: Disks? Disassembly? + +/obj/machinery/programmable + name = "Programmable Unloader" + icon = 'icons/obj/machines/mining_machines.dmi' + icon_state = "unloader" + density = 1 + anchored = 1.0 + + var/debug = 0 // When set, this WILL spam people around the machine. + // Identifies profile used and on which item. + + var/on = 1 + var/indir = 8 + var/outdir = 4 + var/turf/input = null + var/turf/output = null + var/typename = "Unloader" + var/ident = "#1" + + var/const/workmax = 20 + var/datum/cargoprofile/default = new() + var/list/profiles = list(new/datum/cargoprofile/cargo(),new/datum/cargoprofile/boxes(),new/datum/cargoprofile/supplies(), + new/datum/cargoprofile/exotics(),new/datum/cargoprofile/tools(),new/datum/cargoprofile/weapons(), + new/datum/cargoprofile/pressure(),new/datum/cargoprofile/chemical(), + new/datum/cargoprofile/food(),new/datum/cargoprofile/clothing(),new/datum/cargoprofile/trash()) + var/list/overrides = list(new/datum/cargoprofile/cargo/unload(),new/datum/cargoprofile/in_stacker()) + var/list/emag_overrides = list(new/datum/cargoprofile/people(),new/datum/cargoprofile/unary/shredder(),new/datum/cargoprofile/unary/trainer()) + var/list/types = list() + + anchored = 1 + var/unwrenched = 0 + use_power = 1 + var/sleep = 0 // When set, the machine will skip the next however-many updates (to avoid spam) + var/open = 0 + var/circuit_removed = 0 + + + +/obj/machinery/programmable/New() + ..() + if(default) + default.master = src + if(!default.enabled) + default.enabled = 1 + for(var/datum/cargoprofile/p in emag_overrides + overrides + profiles) + p.master = src + input = get_step(src.loc,indir) + output = get_step(src.loc,outdir) + var/count = 0 + for(var/obj/machinery/programmable/other in world) + if(other.typename == typename) + count++ + ident = "#[count]" + name = "[typename] [ident]" + +/obj/machinery/programmable/RefreshParts() + //This is called when the machine was constructed. + //Unfortunately, it puts machine parts in our contents list, which we're using. + //Fortunately, we aren't likely to ever need the components list, circuitboard excepted. + + for(var/obj/O in contents) + if(istype(O,/obj/item/weapon/circuitboard/programmable))//retrieve settings here + + var/obj/item/weapon/circuitboard/programmable/C = O + + default = C.default + emagged = C.emagged + profiles = C.profiles + overrides = C.overrides + emag_overrides = C.emag_overrides + + //unary and binary methods (one vs two locales) don't mix + + if(istype(src,/obj/machinery/programmable/unary)) + for(var/datum/cargoprofile/P in profiles) + if(!istype(P,/datum/cargoprofile/unary) && !P.universal) + profiles -= P + for(var/datum/cargoprofile/P in overrides) + if(!istype(P,/datum/cargoprofile/unary) && !P.universal) + overrides -= P + for(var/datum/cargoprofile/P in emag_overrides) + if(!istype(P,/datum/cargoprofile/unary) && !P.universal) + emag_overrides -= P + else + for(var/datum/cargoprofile/P in profiles) + if(istype(P,/datum/cargoprofile/unary) && !P.universal) + profiles -= P + for(var/datum/cargoprofile/P in overrides) + if(istype(P,/datum/cargoprofile/unary) && !P.universal) + overrides -= P + for(var/datum/cargoprofile/P in emag_overrides) + if(istype(P,/datum/cargoprofile/unary) && !P.universal) + emag_overrides -= P + if(default) + default.master = src + for(var/datum/cargoprofile/p in emag_overrides + overrides + profiles) + p.master = src + + del C + else + del O + +/obj/machinery/programmable/attack_hand(mob/user as mob) + if(stat) // moved, or something else + return + usr.set_machine(src) + interact(user) + +/obj/machinery/programmable/proc/printlist(var/list/L) + var/dat + for(var/datum/cargoprofile/p in L) + dat += "[p.name]: [p.enabled?"YES":"NO"]
" + return dat + +/obj/machinery/programmable/proc/buildMenu() + var/dat + dat += "PROGRAMMABLE UNLOADER
" + dat += "POWER: [on ? "ON" : "OFF"]
" + dat += "INLET: [capitalize(dir2text(indir))] " + dat += "OUTLET: [capitalize(dir2text(outdir))]" + dat += " (SWAP)

" + if(default) + dat += "MAIN PROGRAM: " + dat += "[default.name]: [default.enabled ? "YES" : "NO"]
" + if(profiles.len) + if(!default || !default.enabled) + dat += printlist(profiles) + dat += "
" + if(overrides.len) + dat += "
OVERRIDES:
" + dat += printlist(overrides) + dat += "" + return dat + +/obj/machinery/programmable/interact(mob/user as mob) + var/dat = buildMenu() + user << browse("Unloader[dat]", "window=progreload") + onclose(user, "progreload") + return + +/obj/machinery/programmable/Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + add_fingerprint(usr) + switch(href_list["operation"]) + if("start") + on = (on ? 0 : 1) + if(on) use_power = 1 + else use_power = 0 + updateUsrDialog() + return + if("inlet") + indir *= 2 // N S E W + if(indir > 8) + indir = 1 // W N + if(indir == src.outdir) + indir *= 2 + if(indir > 8) + indir = 1 + input = get_step(src,indir) // todo: check for glasswalls / no path to target? + updateUsrDialog() + return + if("outlet") + outdir *= 2 + if(outdir > 8) + outdir = 1 + if(outdir == indir) + outdir *= 2 + if(outdir > 8) + outdir = 1 + output = get_step(src,outdir) // todo: check for walls / glasswalls / invalid output locations + updateUsrDialog() + return + if("swapdir") + var/temp = outdir + outdir = indir + indir = temp + input = get_step(src,indir) + output = get_step(src,outdir) + updateUsrDialog() + return + if("default") + default.enabled = (default.enabled ? 0 : 1) + updateUsrDialog() + return + var/which = href_list["operation"] + for(var/datum/cargoprofile/p in overrides + profiles) + if(which == p.id) + p.enabled = (p.enabled? 0 : 1) + updateUsrDialog() + return + +/obj/machinery/programmable/attackby(obj/item/I as obj, mob/user as mob) + if(istype(I,/obj/item/weapon/card/emag)) + if(emagged) + return + user << "You swipe the unloader with your card. After a moment's grinding, it beeps in a sinister fashion." + playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0) + emagged = 1 + overrides += emag_overrides + + var/datum/effect/effect/system/spark_spread/s = new /datum/effect/effect/system/spark_spread + s.set_up(2, 1, src) + s.start() + + return + if(istype(I,/obj/item/weapon/wrench)) // code borrowed from pipe dispenser + if (unwrenched==0) + playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) + user << "\blue You begin to unfasten \the [src] from the floor..." + if (do_after(user, 40)) + user.visible_message( \ + "[user] unfastens \the [src].", \ + "\blue You have unfastened \the [src]. Now it can be pulled somewhere else.", \ + "You hear ratchet.") + src.anchored = 0 + src.stat |= MAINT + src.unwrenched = 1 + if (usr.machine==src) + usr << browse(null, "window=pipedispenser") + else /* unwrenched */ + playsound(src.loc, 'sound/items/Ratchet.ogg', 50, 1) + user << "\blue You begin to fasten \the [src] to the floor..." + if (do_after(user, 20)) + user.visible_message( \ + "[user] fastens \the [src].", \ + "\blue You fastened \the [src] into place.", \ + "You hear ratchet.") + src.anchored = 1 + src.input = get_step(src.loc,src.indir) + src.output = get_step(src.loc,src.outdir) + if(!open && !circuit_removed) + src.stat &= ~MAINT + src.unwrenched = 0 + power_change() + if(istype(I,/obj/item/weapon/screwdriver)) + if(open) + open = 0 + if(!unwrenched && !circuit_removed) + src.stat &= ~MAINT + user << "You open the [src]'s maintenance panel." + else + open = 1 + src.stat |= MAINT + user << "You close the [src]'s maintenance panel." + if(istype(I,/obj/item/weapon/crowbar)) + if(open) + user << "\blue You begin to pry out the [src]'s circuits." + if(do_after(user,40)) + user << "\blue You remove the circuitboard." + circuit_removed = 1 + use_power = 0 + on = 0 + + var/obj/item/weapon/circuitboard/programmable/P = new(src.loc) + P.emagged = src.emagged + P.default = src.default + src.default = null + P.profiles = src.profiles + src.profiles = null + P.overrides = src.overrides + src.overrides = null + P.emag_overrides = src.emag_overrides + src.emag_overrides = null + return + else + ..(I,user) + if(istype(I,/obj/item/weapon/circuitboard/programmable)) + if(!open) + user << "You have to open the machine first!" + return + if(!circuit_removed) + user << "There is already a circuitboard present!" + return + circuit_removed = 0 + I.loc = src + RefreshParts() + + + +/obj/machinery/programmable/process() + if (!output || !input) + return + + if(!on || stat || sleep) + if(sleep > 0) // prevent input or output errors from happening every tick + sleep-- + use_power = 0 + + //Do not let things get stuck inside. That's broken behavior. + for(var/obj/O in contents) + O.loc = loc + for(var/mob/M in contents) + M.loc = loc + if(M.client) + M.client.eye = M.client.mob + M.client.perspective = MOB_PERSPECTIVE + M << "\blue The machine turns off, and you fall out." + + return + + //Normal output reaction + if(contents.len) + for(var/atom/movable/A in contents) + var/datum/cargoprofile/p = types[A.type] + if(p) + p.outlet_reaction(A,output) + else + A.loc = output // may have been dropped by a mob, etc + + if(types.len > 50) + types = list() // good luck mr. garbage collector + + + var/work = 0 + for(var/mob/M in input.contents) + for(var/datum/cargoprofile/p in overrides + default) + if(p.enabled && p.mobcheck && p.contains(M)) + var/done = p.inlet_reaction(M,input,workmax - work) + if(done) + work += done + if(src.debug) + visible_message("[p.name]:[M.name] ([done])") + break + + if(sleep) + return // something stopped the machine + + for (var/obj/A in input.contents)// I fully expect this to cause unreasonable lag + if(!A) + break + if(work > workmax) + playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0) // Beep if the machine is full - testing only probably + break + + var/done = 0 // work done - testing only + var/aname // target item name - testing only + + for(var/datum/cargoprofile/p in overrides) + if(p.enabled && p.contains(A)) + aname = A.name // in case of deletion + done = p.inlet_reaction(A,input,workmax - work) + if(done) + work += done + if(src.debug) + visible_message("[p.name]:[aname] ([done])") + + else + break + if(sleep) + break // Something stopped the machine + if(!A || A.loc != input || done) + continue // next item + + if(default && default.enabled) + if(default.contains(A)) + aname = A.name + done = default.inlet_reaction(A,input,workmax - work) + if(done) + work += done + if(src.debug) + visible_message("[default.name]: [aname] ([done])") + continue + for(var/datum/cargoprofile/p in profiles) + if(p.enabled && p.contains(A)) + aname = A.name + done = p.inlet_reaction(A,input,workmax - work) + if(done) + work += done + if(src.debug) + visible_message("[p.name]:[aname] ([done])") + else + break + if(work) + use_power = 2 + else + use_power = 1 +//---------------------------------------------------------------------------- +// Specialty machines +//---------------------------------------------------------------------------- + +//Uses the inlet stacking profile. Ejects only full stacks. +/obj/machinery/programmable/stacker + name = "Stacking & Spooling Machine" + default = new/datum/cargoprofile/in_stacker() + profiles = list() + overrides = list() + emag_overrides = list() + typename = "Stacking and Spooling Machine" + + +/obj/machinery/programmable/unloader + name = "Cargo Unloader" + default = new/datum/cargoprofile/cargo/unload() + profiles = list() + overrides = list() + emag_overrides = list() + typename = "Cargo Unloader" + +/obj/machinery/programmable/delivery + name = "Finished robot delivery" + default = new/datum/cargoprofile/finished() + profiles = list() + overrides = list() + emag_overrides = list() + typename = "Robot Delivery" + +/obj/machinery/programmable/crate_handler + name = "Crate Handler" + default = null + profiles = list(new/datum/cargoprofile/cargo/unload(),new/datum/cargoprofile/cargo(),new/datum/cargoprofile/cargo/empty(),new/datum/cargoprofile/cargo/full()) + overrides = list() + emag_overrides = list() + typename = "Crate Handler" + +//---------------------------------------------------------------------------- +// Unary machine: Input and output in the same location +// Be careful with this, it could easily be running the same +// computations every round needlessly +//---------------------------------------------------------------------------- +/obj/machinery/programmable/unary + name = "Programmable Processor" + default = null + profiles = list() + overrides = list(new/datum/cargoprofile/unary/stacker(),new/datum/cargoprofile/unary/trainer()) + emag_overrides = list(new/datum/cargoprofile/unary/shredder()) + indir = 1 + outdir = 1 + typename = "Processor" + + New() + ..() + outdir = indir + output = input + buildMenu() + var/dat + dat += "PROGRAMMABLE PROCESSOR
" + dat += "POWER: [on ? "ON" : "OFF"]
" + dat += "INLET: [capitalize(dir2text(indir))]
" + if(default) + dat += "MAIN PROGRAM: " + dat += "[default.name]: [default.enabled ? "YES" : "NO"]
" + if(profiles.len) + if(!default || !default.enabled) + dat += printlist(profiles) + dat += "
" + if(overrides.len) + dat += "
OVERRIDES:
" + dat += printlist(overrides) + dat += "" + return dat + Topic(href, href_list) + switch(href_list["operation"]) + if("inlet") + indir *= 2 // N S E W + if(indir > 8) + indir = 1 // W N + outdir = indir + input = get_step(src,indir) // todo: check for glasswalls / no path to target? + output = input + updateUsrDialog() + return + return ..() + +/obj/machinery/programmable/unary/stacker + name = "Stacking Machine" + default = new/datum/cargoprofile/unary/stacker() + profiles = list() + overrides = list() + emag_overrides = list() + typename = "Stacking Machine" + +/obj/machinery/programmable/unary/shredder + name = "Paper Shredder" + default = new/datum/cargoprofile/unary/shredder() + profiles = list() + overrides = list() + emag_overrides = list(new/datum/cargoprofile/unary/gibber()) + typename = "Paper Shredder" + +/obj/machinery/programmable/unary/trainer + name = "\improper Boxing Trainer" + default = new/datum/cargoprofile/unary/trainer() + profiles = list() + overrides = list() + emag_overrides = list() + typename = "Boxing Trainer" + + attack_hand(mob/user as mob) //How did I type this with boxing gloves on? + if(!istype(user,/mob/living/carbon/human)) + return ..() + + var/mob/living/carbon/human/H = user + if(H.gloves && istype(H.gloves, /obj/item/clothing/gloves/boxing)) + var/newsleep = 0 + if(H.loc != input) + H << "The boxing machine refuses to acknowledge you unless you face it head on!" + return + var/damage = 0 + if(H.a_intent != "hurt") + damage += rand(0,5) + else + damage += rand(0,10) + if(!damage) + playsound(H.loc, 'sound/weapons/punchmiss.ogg', 25, 1, -1) + visible_message("[H] tries to punch \the [src], but whiffs") + return + + playsound(loc, "punch", 25, 1, -1) + if(HULK in H.mutations) damage += 5 + + if(damage < 5) + visible_message("[H] gives \the [src] a weak punch.") + if(prob(10)) + visible_message("\blue \The [src] feints at [H], as though mocking \him.") + else if(damage < 10) + visible_message("[H] hits \the [src] with a solid [pick("punch","jab","smack")].") + else if(damage < 15) + visible_message("[pick("Whoa!","Nice!","Gasp!")] [H] hits [src] with a powerful [pick("punch","jab","uppercut","left hook", "right hook")].") + else if(damage < 20) + visible_message("[pick("WHOA!","ACK!","Jeez!")] [H] hits [src] so hard, the whole machine rocks band and forth for a moment.") + else + visible_message("Holy moly! [H] hits \the [src] so hard it stops working.") + stat |= BROKEN + return + while(damage >= 5) + if(prob(50)) + newsleep++ + damage -= 5 + if(newsleep) + if(emagged) + visible_message("\red \The [src]'s lights glow a bloodthirsty red. It refuses to stop!") + sleep = 0 + else + sleep += newsleep + visible_message("\blue \The [src]'s lights dim for a moment and it beeps, signifying a valid hit.") + playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0) + return + + else + return ..() + +//---------------------------------------------------------------------------- +// For construction +//---------------------------------------------------------------------------- +/obj/item/weapon/circuitboard/programmable + name = "Circuit board (Programmable Unloader)" + build_path = "/obj/machinery/programmable" + board_type = "machine" + origin_tech = "engineering=3;programming=6" + frame_desc = "Requires 2 Manipulators, 1 Scanning Module, 1 Cable." + req_components = list( + "/obj/item/weapon/stock_parts/scanning_module" = 1, + "/obj/item/weapon/stock_parts/manipulator" = 2, + "/obj/item/weapon/cable_coil" = 1) + + //Customization of the machine + var/datum/cargoprofile/default = new/datum/cargoprofile() + var/list/profiles = list() + var/list/overrides = list() + var/list/emag_overrides = list() + + var/emagged = 0 + var/hacking = 0 + + proc/resetlists() + profiles = list(new/datum/cargoprofile/cargo(),new/datum/cargoprofile/boxes(),new/datum/cargoprofile/supplies(), + new/datum/cargoprofile/exotics(),new/datum/cargoprofile/tools(),new/datum/cargoprofile/weapons(),new/datum/cargoprofile/finished(), + new/datum/cargoprofile/pressure(),new/datum/cargoprofile/pressure/full(),new/datum/cargoprofile/pressure/empty(), + new/datum/cargoprofile/chemical(),new/datum/cargoprofile/organics(),new/datum/cargoprofile/food(), + new/datum/cargoprofile/clothing(),new/datum/cargoprofile/trash()) + overrides = list(new/datum/cargoprofile/cargo/unload(),new/datum/cargoprofile/in_stacker(), + new/datum/cargoprofile/unary/stacker(),new/datum/cargoprofile/unary/trainer()) + emag_overrides = list(new/datum/cargoprofile/people(),new/datum/cargoprofile/unary/shredder()) + + New() + ..() + resetlists() + + attackby(obj/item/I as obj, mob/user as mob) + if(istype(I,/obj/item/device/multitool)) + hacking = (hacking?0:1) + if(hacking) + user << "You unlock the data port on the board. You can now use a PDA to alter its data." + else + user << "You relock the data port." + if(istype(I,/obj/item/device/pda)) + if(!hacking) + user << "It looks like you can't access the board's data port. You'll have to open it with a multitool." + else + user.set_machine(src) + interact(user) + if(istype(I,/obj/item/weapon/card/emag) && !emagged) + if(!hacking) + user << "There seems to be a data port on the card, but it's locked. A multitool could open it." + else + emagged = 1 + overrides += emag_overrides + user << "You swipe the card in the card's data port. The lights flicker, then flash once." + + proc/format(var/datum/cargoprofile/P,var/level) + // PROFILE=0 OVERRIDE=1 MAIN=2 + if(P == null) + return "NONE
" + var/dat = "[P.name]" + if(level == 0 || (level == 1 && !default)) + dat += " PROMOTE" + if(level > 0) + dat += " DEMOTE" + dat += " REMOVE" + dat += "
" + return dat + + interact(mob/user as mob) + var/dat + dat = "MAIN FUNCTION
" + dat += format(default,2) + dat += "- CAUTION -
\[DELETE NON-MAIN ALGORITHMS\]
" + dat += "\[MASTER RESET\]
" + + dat += "OVERRIDES:
" + for(var/datum/cargoprofile/P in overrides) + dat += format(P,1) + dat += "
" + dat += "- CAUTION -
\[DELETE ALL OVERRIDES\]
" + + dat += "
TERTIARY PROFILES:
" + for(var/datum/cargoprofile/P in profiles) + dat += format(P,0) + dat += "
" + dat += "- CAUTION -
\[DELETE TERTIARY PROFILES\]" + + user << browse("Circuit Reprogramming[dat]", "window=progcircuit") + onclose(user, "progcircuit") + + Topic(href, href_list) + if(..()) + return + usr.set_machine(src) + var/id = href_list["id"] + var/level = text2num(href_list["level"]) + switch(href_list["operation"]) + if("promote") + if(level == 0) + for(var/datum/cargoprofile/T in profiles) + if(T.id == id) + overrides += T + profiles -= T + //updateUsrDialog() + interact(usr) + return + + if(level == 1) + if(default) return + for(var/datum/cargoprofile/T in overrides) + if(T.id == id) + default = T + overrides -= T + + if(default.dedicated_path) + build_path = "[default.dedicated_path]" + else + if(istype(default,/datum/cargoprofile/unary)) + build_path = "/obj/machinery/programmable/unary" + else + build_path = "/obj/machinery/programmable" + + //updateUsrDialog() + interact(usr) + return + + if("demote") + if(level == 2) + overrides += default + default = null + //updateUsrDialog() + interact(usr) + return + if(level == 1) + for(var/datum/cargoprofile/T in overrides) + if(T.id == id) + profiles += T + overrides -= T + //updateUsrDialog() + interact(usr) + return + if("delete") + if(level == 2) + default = null + //updateUsrDialog() + interact(usr) + return + if(level == 1) + for(var/datum/cargoprofile/T in overrides) + if(T.id == id) + overrides -= T + //updateUsrDialog() + interact(usr) + return + if(level == 0) + for(var/datum/cargoprofile/T in profiles) + if(T.id == id) + profiles -= T + //updateUsrDialog() + interact(usr) + return + if("deleteall") + for(var/datum/cargoprofile/T in profiles) + profiles -= T + for(var/datum/cargoprofile/T in overrides) + overrides -= T + //updateUsrDialog() + interact(usr) + return + if("deleteoverrides") + for(var/datum/cargoprofile/T in overrides) + overrides -= T + interact(usr) + return + if("deleteprofiles") + for(var/datum/cargoprofile/T in profiles) + profiles -= T + interact(usr) + return + if("reset") + resetlists() + interact(usr) + return + + //End switch + + +datum/design/programmable + name = "Circuit Design (Programmable Unloader)" + desc = "Allows for the construction of circuit boards used to build a Programmable Unloader." + id = "selunload" + req_tech = list("programming" = 5) + build_type = IMPRINTER + materials = list("$glass" = 2000, "sacid" = 20) + build_path = "/obj/item/weapon/circuitboard/programmable"