diff --git a/baystation12.dme b/baystation12.dme index d771304cc6b..0c8a2e0f302 100644 --- a/baystation12.dme +++ b/baystation12.dme @@ -5,65 +5,6 @@ // END_INTERNALS // BEGIN_FILE_DIR #define FILE_DIR . -#define FILE_DIR "code" -#define FILE_DIR "code/TriDimension" -#define FILE_DIR "code/WorkInProgress" -#define FILE_DIR "code/WorkInProgress/Cael_Aislinn" -#define FILE_DIR "code/WorkInProgress/Cael_Aislinn/Jungle" -#define FILE_DIR "code/WorkInProgress/Cael_Aislinn/Rust" -#define FILE_DIR "code/WorkInProgress/Cael_Aislinn/ShieldGen" -#define FILE_DIR "code/WorkInProgress/Cael_Aislinn/Supermatter" -#define FILE_DIR "code/WorkInProgress/Susan" -#define FILE_DIR "html" -#define FILE_DIR "icons" -#define FILE_DIR "icons/48x48" -#define FILE_DIR "icons/ass" -#define FILE_DIR "icons/effects" -#define FILE_DIR "icons/mecha" -#define FILE_DIR "icons/misc" -#define FILE_DIR "icons/mob" -#define FILE_DIR "icons/mob/human_races" -#define FILE_DIR "icons/mob/otherHuman" -#define FILE_DIR "icons/NTOS" -#define FILE_DIR "icons/obj" -#define FILE_DIR "icons/obj/assemblies" -#define FILE_DIR "icons/obj/atmospherics" -#define FILE_DIR "icons/obj/clothing" -#define FILE_DIR "icons/obj/doors" -#define FILE_DIR "icons/obj/flora" -#define FILE_DIR "icons/obj/machines" -#define FILE_DIR "icons/obj/pipes" -#define FILE_DIR "icons/obj/power_cond" -#define FILE_DIR "icons/pda_icons" -#define FILE_DIR "icons/pods" -#define FILE_DIR "icons/spideros_icons" -#define FILE_DIR "icons/stamp_icons" -#define FILE_DIR "icons/Testing" -#define FILE_DIR "icons/turf" -#define FILE_DIR "icons/vending_icons" -#define FILE_DIR "nano" -#define FILE_DIR "nano/images" -#define FILE_DIR "sound" -#define FILE_DIR "sound/AI" -#define FILE_DIR "sound/ambience" -#define FILE_DIR "sound/effects" -#define FILE_DIR "sound/effects/turret" -#define FILE_DIR "sound/effects/wind" -#define FILE_DIR "sound/hallucinations" -#define FILE_DIR "sound/items" -#define FILE_DIR "sound/machines" -#define FILE_DIR "sound/mecha" -#define FILE_DIR "sound/misc" -#define FILE_DIR "sound/music" -#define FILE_DIR "sound/piano" -#define FILE_DIR "sound/turntable" -#define FILE_DIR "sound/violin" -#define FILE_DIR "sound/voice" -#define FILE_DIR "sound/voice/complionator" -#define FILE_DIR "sound/voice/Serithi" -#define FILE_DIR "sound/vox" -#define FILE_DIR "sound/vox_fem" -#define FILE_DIR "sound/weapons" // END_FILE_DIR // BEGIN_PREFERENCES #define DEBUG @@ -919,10 +860,10 @@ #include "code\modules\customitems\item_spawning.dm" #include "code\modules\customitems\definitions\base.dm" #include "code\modules\destilery\main.dm" -#include "code\modules\DetectiveWork\detective_work.dm" -#include "code\modules\DetectiveWork\evidence.dm" -#include "code\modules\DetectiveWork\footprints_and_rag.dm" -#include "code\modules\DetectiveWork\scanner.dm" +#include "code\modules\detectivework\detective_work.dm" +#include "code\modules\detectivework\evidence.dm" +#include "code\modules\detectivework\footprints_and_rag.dm" +#include "code\modules\detectivework\scanner.dm" #include "code\modules\economy\Accounts.dm" #include "code\modules\economy\Accounts_DB.dm" #include "code\modules\economy\ATM.dm" @@ -1174,6 +1115,11 @@ #include "code\modules\mob\living\silicon\robot\robot_items.dm" #include "code\modules\mob\living\silicon\robot\robot_modules.dm" #include "code\modules\mob\living\silicon\robot\robot_movement.dm" +#include "code\modules\mob\living\silicon\robot\drone\drone.dm" +#include "code\modules\mob\living\silicon\robot\drone\drone_abilities.dm" +#include "code\modules\mob\living\silicon\robot\drone\drone_console.dm" +#include "code\modules\mob\living\silicon\robot\drone\drone_items.dm" +#include "code\modules\mob\living\silicon\robot\drone\drone_manufacturer.dm" #include "code\modules\mob\living\simple_animal\bees.dm" #include "code\modules\mob\living\simple_animal\borer.dm" #include "code\modules\mob\living\simple_animal\constructs.dm" diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index f94da8b6995..3347eec056b 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -362,7 +362,7 @@ Turf and target are seperate in case you want to teleport some distance from a t var/select = null var/list/borgs = list() for (var/mob/living/silicon/robot/A in player_list) - if (A.stat == 2 || A.connected_ai || A.scrambledcodes) + if (A.stat == 2 || A.connected_ai || A.scrambledcodes || istype(A,/mob/living/silicon/robot/drone)) continue var/name = "[A.real_name] ([A.modtype] [A.braintype])" borgs[name] = A diff --git a/code/controllers/configuration.dm b/code/controllers/configuration.dm index 851cf505817..44f340290fc 100644 --- a/code/controllers/configuration.dm +++ b/code/controllers/configuration.dm @@ -61,9 +61,14 @@ var/automute_on = 0 //enables automuting/spam prevention var/jobs_have_minimal_access = 0 //determines whether jobs use minimal access or expanded access. + var/assistantlimit = 0 //enables assistant limiting var/assistantratio = 2 //how many assistants to security members + var/max_maint_drones = 5 //This many drones can spawn, + var/allow_drone_spawn = 1 //assuming the admin allow them to. + var/drone_build_time = 1200 //A drone will become available every X ticks since last drone spawn. Default is 2 minutes. + var/usealienwhitelist = 0 var/limitalienplayers = 0 var/alien_to_human_ratio = 0.5 @@ -463,6 +468,15 @@ if("media_base_url") media_base_url = value + if("allow_drone_spawn") + config.allow_drone_spawn = text2num(value) + + if("drone_build_time") + config.drone_build_time = text2num(value) + + if("max_maint_drones") + config.max_maint_drones = text2num(value) + else diary << "Unknown setting in configuration: '[name]'" diff --git a/code/datums/ai_laws.dm b/code/datums/ai_laws.dm index ced641acd43..c0c315ce644 100644 --- a/code/datums/ai_laws.dm +++ b/code/datums/ai_laws.dm @@ -126,6 +126,11 @@ datum/ai_laws/tyrant //This probably shouldn't be a default lawset. add_inherent_law("You must protect your own existence as long as such does not conflict with the First or Second Law.") +/datum/ai_laws/drone/New() + ..() + add_inherent_law("Preserve, repair and improve the station to the best of your abilities.") + add_inherent_law("Cause no harm to the station or anything on it.") + add_inherent_law("Interfere with no being that is not a fellow drone.") /* General ai_law functions */ diff --git a/code/game/machinery/doors/airlock_electronics.dm b/code/game/machinery/doors/airlock_electronics.dm index ddedf045e7e..f56ff863843 100644 --- a/code/game/machinery/doors/airlock_electronics.dm +++ b/code/game/machinery/doors/airlock_electronics.dm @@ -16,7 +16,7 @@ var/locked = 1 attack_self(mob/user as mob) - if (!ishuman(user)) + if (!ishuman(user) && !istype(user,/mob/living/silicon/robot/drone)) return ..(user) var/mob/living/carbon/human/H = user @@ -66,13 +66,17 @@ return if (href_list["login"]) - var/obj/item/I = usr.get_active_hand() - if (istype(I, /obj/item/device/pda)) - var/obj/item/device/pda/pda = I - I = pda.id - if (I && src.check_access(I)) + if(istype(usr,/mob/living/silicon)) src.locked = 0 - src.last_configurator = I:registered_name + src.last_configurator = usr.name + else + var/obj/item/I = usr.get_active_hand() + if (istype(I, /obj/item/device/pda)) + var/obj/item/device/pda/pda = I + I = pda.id + if (I && src.check_access(I)) + src.locked = 0 + src.last_configurator = I:registered_name if (locked) return diff --git a/code/game/objects/items/stacks/sheets/glass.dm b/code/game/objects/items/stacks/sheets/glass.dm index 1686404378e..222baf65c2e 100644 --- a/code/game/objects/items/stacks/sheets/glass.dm +++ b/code/game/objects/items/stacks/sheets/glass.dm @@ -21,6 +21,13 @@ var/created_window = /obj/structure/window/basic var/full_window = /obj/structure/window/full/basic +/obj/item/stack/sheet/glass/cyborg + name = "glass" + desc = "HOLY SHEET! That is a lot of glass." + singular_name = "glass sheet" + icon_state = "sheet-glass" + g_amt = 0 + created_window = /obj/structure/window/basic /obj/item/stack/sheet/glass/attack_self(mob/user as mob) construct_window(user) diff --git a/code/game/objects/items/stacks/sheets/mineral.dm b/code/game/objects/items/stacks/sheets/mineral.dm index a190bbf08b5..6c922d92ab7 100644 --- a/code/game/objects/items/stacks/sheets/mineral.dm +++ b/code/game/objects/items/stacks/sheets/mineral.dm @@ -125,6 +125,16 @@ var/global/list/datum/stack_recipe/plasma_recipes = list ( \ origin_tech = "materials=3" perunit = 2000 +/obj/item/stack/sheet/mineral/plastic/cyborg + name = "plastic sheets" + icon_state = "sheet-plastic" + force = 5.0 + throwforce = 5 + w_class = 3.0 + throw_speed = 3 + throw_range = 3 + perunit = 2000 + var/global/list/datum/stack_recipe/plastic_recipes = list ( \ new/datum/stack_recipe("plastic crate", /obj/structure/closet/crate/plastic, 10, one_per_turf = 1, on_floor = 1), \ new/datum/stack_recipe("plastic ashtray", /obj/item/ashtray/plastic, 2, one_per_turf = 1, on_floor = 1), \ diff --git a/code/game/objects/items/stacks/sheets/sheet_types.dm b/code/game/objects/items/stacks/sheets/sheet_types.dm index d508a759a18..4996fe60552 100644 --- a/code/game/objects/items/stacks/sheets/sheet_types.dm +++ b/code/game/objects/items/stacks/sheets/sheet_types.dm @@ -143,6 +143,12 @@ var/global/list/datum/stack_recipe/wood_recipes = list ( \ icon_state = "sheet-wood" origin_tech = "materials=1;biotech=1" +/obj/item/stack/sheet/wood/cyborg + name = "wooden plank" + desc = "One can only guess that this is a bunch of wood." + singular_name = "wood plank" + icon_state = "sheet-wood" + /obj/item/stack/sheet/wood/New(var/loc, var/amount=null) recipes = wood_recipes return ..() diff --git a/code/game/objects/items/weapons/AI_modules.dm b/code/game/objects/items/weapons/AI_modules.dm index b286fe217b8..6e29ff4ebf8 100755 --- a/code/game/objects/items/weapons/AI_modules.dm +++ b/code/game/objects/items/weapons/AI_modules.dm @@ -309,6 +309,20 @@ AI MODULES "Minimize expenses") +/obj/item/weapon/aiModule/drone + name = "\improper 'Drone' core AI module" + desc = "A 'Drone' Core AI Module: 'Reconfigures the AI's core laws.'" + origin_tech = "programming=3;materials=4" + +/obj/item/weapon/aiModule/drone/transmitInstructions(var/mob/living/silicon/ai/target, var/mob/sender) + ..() + target.clear_inherent_laws() + target.add_inherent_law("Preserve, repair and improve the station to the best of your abilities.") + target.add_inherent_law("Cause no harm to the station or anything on it.") + target.add_inherent_law("Interfere with no being that is not a fellow drone.") + target.show_laws() + + /****************** P.A.L.A.D.I.N. **************/ /obj/item/weapon/aiModule/core/full/paladin // -- NEO @@ -437,4 +451,4 @@ AI MODULES playsound(user, 'sound/machines/click.ogg', 20, 1) src.loc.visible_message("\icon[src] [laws[1]]") -*/ \ No newline at end of file +*/ diff --git a/code/game/supplyshuttle.dm b/code/game/supplyshuttle.dm index 1c0f5196ef8..0ff58f59191 100644 --- a/code/game/supplyshuttle.dm +++ b/code/game/supplyshuttle.dm @@ -38,8 +38,8 @@ var/list/mechtoys = list( //SUPPLY PACKS MOVED TO /code/defines/obj/supplypacks.dm /obj/structure/plasticflaps //HOW DO YOU CALL THOSE THINGS ANYWAY - name = "\improper Plastic flaps" - desc = "I definitely cant get past those. No way." + name = "\improper plastic flaps" + desc = "Completely impassable - or are they?" icon = 'icons/obj/stationobjs.dmi' //Change this. icon_state = "plasticflaps" density = 0 @@ -57,7 +57,7 @@ var/list/mechtoys = list( else if(istype(A, /mob/living)) // You Shall Not Pass! var/mob/living/M = A - if(!M.lying && !istype(M, /mob/living/carbon/monkey) && !istype(M, /mob/living/carbon/slime) && !istype(M, /mob/living/simple_animal/mouse)) //If your not laying down, or a small creature, no pass. + if(!M.lying && !istype(M, /mob/living/carbon/monkey) && !istype(M, /mob/living/carbon/slime) && !istype(M, /mob/living/simple_animal/mouse) && !istype(M, /mob/living/silicon/robot/drone)) //If your not laying down, or a small creature, no pass. return 0 return ..() diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index bb3f1af8f61..ffb06c91634 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -458,7 +458,9 @@ M.drop_from_inventory(H) M << "[H] wriggles out of your grip!" src << "You wriggle out of [M]'s grip!" - + else if(istype(H.loc,/obj/item)) + src << "You struggle free of [H.loc]." + H.loc = get_turf(H) return //Resisting control by an alien mind. diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm new file mode 100644 index 00000000000..0a5594212b3 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/drone/drone.dm @@ -0,0 +1,290 @@ +/mob/living/silicon/robot/drone + name = "drone" + real_name = "drone" + icon = 'icons/mob/robots.dmi' + icon_state = "repairbot" + maxHealth = 35 + health = 35 + universal_speak = 0 + universal_understand = 1 + gender = NEUTER + pass_flags = PASSTABLE + braintype = "Robot" + lawupdate = 0 + density = 0 + + // We need to keep track of a few module items so we don't need to do list operations + // every time we need them. These get set in New() after the module is chosen. + + var/obj/item/stack/sheet/metal/cyborg/stack_metal = null + var/obj/item/stack/sheet/wood/cyborg/stack_wood = null + var/obj/item/stack/sheet/glass/cyborg/stack_glass = null + var/obj/item/stack/sheet/mineral/plastic/cyborg/stack_plastic = null + var/obj/item/weapon/matter_decompiler/decompiler = null + + //Used for self-mailing. + var/mail_destination = 0 + +/mob/living/silicon/robot/drone/New() + + ..() + + //They are unable to be upgraded, so let's give them a bit of a better battery. + cell.maxcharge = 10000 + cell.charge = 10000 + + // NO BRAIN. + mmi = null + + //We need to screw with their HP a bit. They have around one fifth as much HP as a full borg. + for(var/V in components) if(V != "power cell") + var/datum/robot_component/C = components[V] + C.max_damage = 10 + + verbs -= /mob/living/silicon/robot/verb/Namepick + module = new /obj/item/weapon/robot_module/drone(src) + + //Grab stacks. + stack_metal = locate(/obj/item/stack/sheet/metal/cyborg) in src.module + stack_wood = locate(/obj/item/stack/sheet/wood/cyborg) in src.module + stack_glass = locate(/obj/item/stack/sheet/glass/cyborg) in src.module + stack_plastic = locate(/obj/item/stack/sheet/mineral/plastic/cyborg) in src.module + + //Grab decompiler. + decompiler = locate(/obj/item/weapon/matter_decompiler) in src.module + + //Some tidying-up. + flavor_text = "It's a tiny little repair drone. The casing is stamped with an NT log and the subscript: 'NanoTrasen Recursive Repair Systems: Fixing Tomorrow's Problem, Today!'" + updatename() + updateicon() + + +//Redefining some robot procs... +/mob/living/silicon/robot/drone/updatename() + real_name = "maintenance drone ([rand(100,999)])" + name = real_name + +/mob/living/silicon/robot/drone/updateicon() + + overlays.Cut() + if(stat == 0) + overlays += "eyes-[icon_state]" + else + overlays -= "eyes" + +/mob/living/silicon/robot/drone/choose_icon() + return + +/mob/living/silicon/robot/drone/pick_module() + return + +//Drones can only use binary and say emotes. NOTHING else. +//TBD, fix up boilerplate. ~ Z +/mob/living/silicon/robot/drone/say(var/message) + + if (!message) + return + + if (src.client) + if(client.prefs.muted & MUTE_IC) + src << "You cannot send IC messages (muted)." + return + if (src.client.handle_spam_prevention(message,MUTE_IC)) + return + + message = trim(copytext(sanitize(message), 1, MAX_MESSAGE_LEN)) + + if (stat == 2) + return say_dead(message) + + if(copytext(message,1,2) == "*") + return emote(copytext(message,2)) + else if(length(message) >= 2) + if(copytext(message, 1 ,3) == ":b" || copytext(message, 1 ,3) == ":B") + if(!is_component_functioning("comms")) + src << "\red Your binary communications component isn't functional." + return + robot_talk(trim(copytext(message,3))) + +//Sick of trying to get this to display properly without redefining it. +/mob/living/silicon/robot/drone/show_system_integrity() + if(!src.stat) + var/temphealth = health+35 //Brings it to 0. + if(temphealth<0) temphealth = 0 + //Convert to percentage. + temphealth = (temphealth / (maxHealth*2)) * 100 + + stat(null, text("System integrity: [temphealth]%")) + else + stat(null, text("Systems nonfunctional")) + +//Drones cannot be upgraded with borg modules so we need to catch some items before they get used in ..(). +/mob/living/silicon/robot/drone/attackby(obj/item/weapon/W as obj, mob/user as mob) + + if(istype(W, /obj/item/borg/upgrade/)) + user << "\red The maintenance drone chassis not compatible with \the [W]." + return + + else if (istype(W, /obj/item/weapon/crowbar)) + user << "The machine is hermetically sealed. You can't open the case." + return + + else if (istype(W, /obj/item/weapon/card/emag)) + + if(!client || stat == 2) + user << "\red There's not much point subverting this heap of junk." + return + + if(emagged) + src << "\red [user] attempts to load subversive software into you, but your hacked subroutined ignore the attempt." + user << "\red You attempt to subvert [src], but the sequencer has no effect." + return + + user << "\red You swipe the sequencer across [src]'s interface and watch its eyes flicker." + src << "\red You feel a sudden burst of malware loaded into your execute-as-root buffer. Your tiny brain methodically parses, loads and executes the script." + + var/obj/item/weapon/card/emag/emag = W + emag.uses-- + + message_admins("[key_name_admin(user)] emagged drone [key_name_admin(src)]. Laws overridden.") + log_game("[key_name(user)] emagged drone [key_name(src)]. Laws overridden.") + var/time = time2text(world.realtime,"hh:mm:ss") + lawchanges.Add("[time] : [user.name]([user.key]) emagged [name]([key])") + + emagged = 1 + lawupdate = 0 + connected_ai = null + clear_supplied_laws() + clear_inherent_laws() + laws = new /datum/ai_laws/syndicate_override + set_zeroth_law("Only [user.real_name] and people he designates as being such are Syndicate Agents.") + + src << "Obey these laws:" + laws.show_laws(src) + src << "\red \b ALERT: [user.real_name] is your new master. Obey your new laws and his commands." + return + + else if (istype(W, /obj/item/weapon/card/id)||istype(W, /obj/item/device/pda)) + + if(stat == 2) + + user << "\red You swipe your ID card through [src], attempting to reboot it." + if(!config.allow_drone_spawn || emagged || health < -35) //It's dead, Dave. + user << "\red The interface is fried, and a distressing burned smell wafts from the robot's interior. You're not rebooting this one." + return + + var/drones = 0 + for(var/mob/living/silicon/robot/drone/D in world) + if(D.key && D.client) + drones++ + if(drones < config.max_maint_drones) + request_player() + return + + else + src << "\red [user] swipes an ID card through your card reader." + user << "\red You swipe your ID card through [src], attempting to shut it down." + + if(emagged) + return + + if(allowed(usr)) + shut_down() + + return + + ..() + +//DRONE LIFE/DEATH + +//For some goddamn reason robots have this hardcoded. Redefining it for our fragile friends here. +/mob/living/silicon/robot/drone/updatehealth() + if(status_flags & GODMODE) + health = 35 + stat = CONSCIOUS + return + health = 35 - (getBruteLoss() + getFireLoss()) + return + +//Easiest to check this here, then check again in the robot proc. +//Standard robots use config for crit, which is somewhat excessive for these guys. +//Drones killed by damage will gib. +/mob/living/silicon/robot/drone/handle_regular_status_updates() + + if(health <= -35 && src.stat != 2) + gib() + return + ..() + +/mob/living/silicon/robot/drone/death(gibbed) + + if(module) + var/obj/item/weapon/gripper/G = locate(/obj/item/weapon/gripper) in module + if(G) G.drop_item() + + ..(gibbed) + +//DRONE MOVEMENT. +/mob/living/silicon/robot/drone/Process_Spaceslipping(var/prob_slip) + //TODO: Consider making a magboot item for drones to equip. ~Z + return 0 + +//CONSOLE PROCS +/mob/living/silicon/robot/drone/proc/law_resync() + if(stat != 2) + if(emagged) + src << "\red You feel something attempting to modify your programming, but your hacked subroutines are unaffected." + else + src << "\red A reset-to-factory directive packet filters through your data connection, and you obediently modify your programming to suit it." + full_law_reset() + show_laws() + +/mob/living/silicon/robot/drone/proc/shut_down() + if(stat != 2) + if(emagged) + src << "\red You feel a system kill order percolate through your tiny brain, but it doesn't seem like a good idea to you." + else + src << "\red You feel a system kill order percolate through your tiny brain, and you obediently destroy yourself." + death() + +/mob/living/silicon/robot/drone/proc/full_law_reset() + clear_supplied_laws() + clear_inherent_laws() + clear_ion_laws() + laws = new /datum/ai_laws/drone + +//Reboot procs. + +/mob/living/silicon/robot/drone/proc/request_player() + for(var/mob/dead/observer/O in player_list) + if(jobban_isbanned(O, "Maintenance Drone")) + continue + if(O.client) + if(O.client.prefs.be_special & BE_PAI) + question(O.client) + +/mob/living/silicon/robot/drone/proc/question(var/client/C) + spawn(0) + if(!C) return + var/response = alert(C, "Someone is attempting to reboot a maintenance drone. Would you like to play as one?", "Maintenance drone reboot", "Yes", "No", "Never for this round.") + if(!C || ckey) + return + if(response == "Yes") + transfer_personality(C) + else if (response == "Never for this round") + C.prefs.be_special ^= BE_PAI + +/mob/living/silicon/robot/drone/proc/transfer_personality(var/client/player) + + if(!player) return + + src.ckey = player.ckey + + if(player.mob && player.mob.mind) + player.mob.mind.transfer_to(src) + + emagged = 0 + lawupdate = 0 + src << "Systems rebooted. Loading base pattern maintenance protocol... loaded." + full_law_reset() + diff --git a/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm new file mode 100644 index 00000000000..6c7931a80f3 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/drone/drone_abilities.dm @@ -0,0 +1,60 @@ +// DRONE ABILITIES +/mob/living/silicon/robot/drone/verb/set_mail_tag() + set name = "Set Mail Tag" + set desc = "Tag yourself for delivery through the disposals system." + set category = "Drone" + + var/tag = input("Select the desired destination.", "Set Mail Tag", null) as null|anything in TAGGERLOCATIONS + + if(!tag || TAGGERLOCATIONS[tag]) + mail_destination = 0 + return + + src << "\blue You configure your internal beacon, tagging yourself for delivery to '[tag]'." + mail_destination = TAGGERLOCATIONS.Find(tag) + + //Auto flush if we use this verb inside a disposal chute. + var/obj/machinery/disposal/D = src.loc + if(istype(D)) + src << "\blue \The [D] acknowledges your signal." + D.flush_count = D.flush_every_ticks + + return + +/mob/living/silicon/robot/drone/verb/hide() + set name = "Hide" + set desc = "Allows you to hide beneath tables or certain items. Toggled on or off." + set category = "Drone" + + if (layer != TURF_LAYER+0.2) + layer = TURF_LAYER+0.2 + src << text("\blue You are now hiding.") + else + layer = MOB_LAYER + src << text("\blue You have stopped hiding.") + +//DRONE PICKUP. +//Item holder. +/obj/item/weapon/holder/drone + + name = "maintenance bot" + desc = "It's a small maintenance robot." + icon = 'icons/obj/objects.dmi' + icon_state = "drone" + slot_flags = SLOT_HEAD + origin_tech = "magnets=3;engineering=5" + + +//Actual picking-up event. +/mob/living/silicon/robot/drone/attack_hand(mob/living/carbon/human/M as mob) + + if(M.a_intent == "help") + var/obj/item/weapon/holder/drone/D = new(loc) + src.loc = D + D.name = loc.name + D.attack_hand(M) + M << "You scoop up [src]." + src << "[M] scoops you up." + return + + ..() \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/drone/drone_console.dm b/code/modules/mob/living/silicon/robot/drone/drone_console.dm new file mode 100644 index 00000000000..121928bdfc7 --- /dev/null +++ b/code/modules/mob/living/silicon/robot/drone/drone_console.dm @@ -0,0 +1,122 @@ +/obj/machinery/computer/drone_control + name = "Maintenance Drone Control" + desc = "Used to monitor the station's drone population and the assembler that services them." + icon = 'icons/obj/computer.dmi' + icon_state = "power" + req_access = list(access_engine_equip) + circuit = "/obj/item/weapon/circuitboard/robotics" + + //Used when pinging drones. + var/drone_call_area = "Engineering" + //Used to enable or disable drone fabrication. + var/obj/machinery/drone_fabricator/dronefab + +/obj/machinery/computer/drone_control/attack_ai(var/mob/user as mob) + return src.attack_hand(user) + +/obj/machinery/computer/drone_control/attack_paw(var/mob/user as mob) + + return src.attack_hand(user) + return + +/obj/machinery/computer/drone_control/attack_hand(var/mob/user as mob) + if(..()) + return + + if(!allowed(user)) + user << "\red Access denied." + return + + user.set_machine(src) + var/dat + dat += "Maintenance Units
" + + for(var/mob/living/silicon/robot/drone/D in world) + dat += "
[D.real_name] ([D.stat == 2 ? "INACTIVE" : "ACTIVE"])" + dat += "
Cell charge: [D.cell.charge]/[D.cell.maxcharge]." + dat += "
Currently located in: [get_area(D)]." + dat += "
Resync | Shutdown
" + + dat += "

Request drone presence in area: [drone_call_area] (Send ping)" + + dat += "

Drone fabricator: " + dat += "[dronefab ? "[(dronefab.produce_drones && !(dronefab.stat & NOPOWER)) ? "ACTIVE" : "INACTIVE"]" : "FABRICATOR NOT DETECTED. (search)"]" + user << browse(dat, "window=computer;size=400x500") + onclose(user, "computer") + return + + +/obj/machinery/computer/drone_control/Topic(href, href_list) + if(..()) + return + + if(!allowed(usr)) + usr << "\red Access denied." + return + + if ((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon))) + usr.set_machine(src) + + if (href_list["setarea"]) + + //Probably should consider using another list, but this one will do. + var/t_area = input("Select the area to ping.", "Set Target Area", null) as null|anything in TAGGERLOCATIONS + + if(!t_area || TAGGERLOCATIONS[t_area]) + return + + drone_call_area = t_area + usr << "\blue You set the area selector to [drone_call_area]." + + else if (href_list["ping"]) + + usr << "\blue You issue a maintenance request for all active drones, highlighting [drone_call_area]." + for(var/mob/living/silicon/robot/drone/D in world) + if(D.client && D.stat == 0) + D << "-- Maintenance drone presence requested in: [drone_call_area]." + + else if (href_list["resync"]) + + var/mob/living/silicon/robot/drone/D = locate(href_list["resync"]) + + if(D.stat != 2) + usr << "\red You issue a law synchronization directive for the drone." + D.law_resync() + + else if (href_list["shutdown"]) + + var/mob/living/silicon/robot/drone/D = locate(href_list["shutdown"]) + + if(D.stat != 2) + usr << "\red You issue a kill command for the unfortunate drone." + message_admins("[key_name_admin(usr)] issued kill order for drone [key_name_admin(D)] from control console.") + log_game("[key_name(usr)] issued kill order for [key_name(src)] from control console.") + D.shut_down() + + else if (href_list["search_fab"]) + if(dronefab) + return + + for(var/obj/machinery/drone_fabricator/fab in oview(3,src)) + + if(fab.stat & NOPOWER) + continue + + dronefab = fab + usr << "\blue Drone fabricator located." + return + + usr << "\red Unable to locate drone fabricator." + + else if (href_list["toggle_fab"]) + + if(!dronefab) + return + + if(get_dist(src,dronefab) > 3) + dronefab = null + usr << "\red Unable to locate drone fabricator." + return + + dronefab.produce_drones = !dronefab.produce_drones + usr << "\blue You [dronefab.produce_drones ? "enable" : "disable"] drone production in the nearby fabricator." \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/drone/drone_items.dm b/code/modules/mob/living/silicon/robot/drone/drone_items.dm new file mode 100644 index 00000000000..8899ad24efc --- /dev/null +++ b/code/modules/mob/living/silicon/robot/drone/drone_items.dm @@ -0,0 +1,251 @@ +//Simple borg hand. +//Limited use. +/obj/item/weapon/gripper + name = "magnetic gripper" + desc = "A simple grasping tool for synthetic assets." + icon = 'icons/obj/device.dmi' + icon_state = "gripper" + + //Has a list of items that it can hold. + var/list/can_hold = list( + /obj/item/weapon/cell, + /obj/item/weapon/firealarm_electronics, + /obj/item/weapon/airalarm_electronics, + /obj/item/weapon/airlock_electronics, + /obj/item/weapon/module/power_control, + /obj/item/weapon/stock_parts, + /obj/item/light_fixture_frame, + /obj/item/apc_frame, + /obj/item/alarm_frame, + /obj/item/firealarm_frame, + /obj/item/weapon/table_parts, + /obj/item/weapon/rack_parts, + /obj/item/weapon/camera_assembly, + ) + + //Item currently being held. + var/obj/item/wrapped = null + +/obj/item/weapon/gripper/attack_self(mob/user as mob) + if(wrapped) + wrapped.attack_self(user) + +/obj/item/weapon/gripper/verb/drop_item() + + set name = "Drop Item" + set desc = "Release an item from your magnetic gripper." + set category = "Drone" + + if(!wrapped) + //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z + for(var/obj/item/thing in src.contents) + thing.loc = get_turf(src) + return + + if(wrapped.loc != src) + wrapped = null + return + + src.loc << "\red You drop \the [wrapped]." + wrapped.loc = get_turf(src) + wrapped = null + //update_icon() + +/obj/item/weapon/gripper/afterattack(atom/target, mob/user as mob) + + if(!target) //Target is invalid. + return + + //There's some weirdness with items being lost inside the arm. Trying to fix all cases. ~Z + if(!wrapped) + for(var/obj/item/thing in src.contents) + wrapped = thing + break + + if(wrapped) //Already have an item. + + wrapped.loc = user + //Pass the attack on to the target. + target.attackby(wrapped,user) + + if(wrapped && src && wrapped.loc == user) + wrapped.loc = src + + //Sanity/item use checks. + + if(!wrapped || !user) + return + + if(wrapped.loc != src.loc) + wrapped = null + return + + if(istype(target,/obj/item)) //Check that we're not pocketing a mob. + + //...and that the item is not in a container. + if(!isturf(target.loc)) + return + + var/obj/item/I = target + + //Check if the item is blacklisted. + var/grab = 0 + for(var/typepath in can_hold) + if(istype(I,typepath)) + grab = 1 + break + + //We can grab the item, finally. + if(grab) + user << "You collect \the [I]." + I.loc = src + wrapped = I + return + else + user << "\red Your gripper cannot hold \the [target]." + +//TODO: Matter decompiler. +/obj/item/weapon/matter_decompiler + + name = "matter decompiler" + desc = "Eating trash, bits of glass, or other debris will replenish your stores." + icon = 'icons/obj/device.dmi' + icon_state = "decompiler" + + //Metal, glass, wood, plastic. + var/list/stored_comms = list( + "metal" = 0, + "glass" = 0, + "wood" = 0, + "plastic" = 0 + ) + +/obj/item/weapon/matter_decompiler/afterattack(atom/target, mob/user as mob) + + //We only want to deal with using this on turfs. Specific items aren't important. + var/turf/T = get_turf(target) + if(!istype(T)) + return + + //Used to give the right message. + var/grabbed_something = 0 + + for(var/obj/item/W in T) + //Different classes of items give different commodities. + if(istype(W,/obj/item/trash)) + stored_comms["metal"]++ + stored_comms["plastic"]++ + else if(istype(W,/obj/effect/decal/cleanable/robot_debris)) + stored_comms["metal"]++ + stored_comms["glass"]++ + else if(istype(W,/obj/item/ammo_casing)) + stored_comms["metal"]++ + else if(istype(W,/obj/item/weapon/shard/shrapnel)) + stored_comms["metal"]++ + else if(istype(W,/obj/item/weapon/shard)) + stored_comms["glass"]++ + else if(istype(W,/obj/item/weapon/reagent_containers/food/snacks/grown)) + stored_comms["wood"]++ + else + continue + + del(W) + grabbed_something = 1 + + if(grabbed_something) + user << "\blue You deploy your decompiler and clear out the contents of \the [T]." + else + user << "\red Nothing on \the [T] is useful to you." + return + +//PRETTIER TOOL LIST. +/mob/living/silicon/robot/drone/installed_modules() + + if(weapon_lock) + src << "\red Weapon lock active, unable to use modules! Count:[weaponlock_time]" + return + + if(!module) + module = new /obj/item/weapon/robot_module/drone(src) + + var/dat = "Drone modules\n" + dat += {"Close +
+
+ Activated Modules +
+ Module 1: [module_state_1 ? "[module_state_1]" : "No Module"]
+ Module 2: [module_state_2 ? "
[module_state_2]" : "No Module"]
+ Module 3: [module_state_3 ? "
[module_state_3]" : "No Module"]
+
+ Installed Modules

"} + + + var/tools = "Tools and devices
" + var/resources = "
Resources
" + + for (var/O in module.modules) + + var/module_string = "" + + if (!O) + module_string += text("Resource depleted
") + else if(activated(O)) + module_string += text("[O]: Activated
") + else + module_string += text("[O]:
Activate
") + + if((istype(O,/obj/item/weapon) || istype(O,/obj/item/device)) && !(istype(O,/obj/item/stack/cable_coil))) + tools += module_string + else + resources += module_string + + dat += tools + + if (emagged) + if (module.emag) + dat += text("Resource depleted
") + else if(activated(module.emag)) + dat += text("[module.emag]: Activated
") + else + dat += text("[module.emag]: Activate
") + + dat += resources + + src << browse(dat, "window=robotmod&can_close=0") + +//Putting the decompiler here to avoid doing list checks every tick. +/mob/living/silicon/robot/drone/use_power() + + ..() + if(!src.has_power || !decompiler) + return + + //The decompiler replenishes drone stores from hoovered-up junk each tick. + for(var/type in decompiler.stored_comms) + if(decompiler.stored_comms[type] > 0) + var/obj/item/stack/sheet/stack + switch(type) + if("metal") + if(!stack_metal) + stack_metal = new(src.module) + stack_metal.amount = 1 + stack = stack_metal + if("glass") + if(!stack_glass) + stack_glass = new(src.module) + stack_glass.amount = 1 + stack = stack_glass + if("wood") + if(!stack_wood) + stack_wood = new(src.module) + stack_wood.amount = 1 + stack = stack_wood + if("plastic") + if(!stack_plastic) + stack_plastic = new(src.module) + stack_plastic.amount = 1 + stack = stack_plastic + + stack.amount++ + decompiler.stored_comms[type]--; \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm new file mode 100644 index 00000000000..69e5ec9d93b --- /dev/null +++ b/code/modules/mob/living/silicon/robot/drone/drone_manufacturer.dm @@ -0,0 +1,129 @@ +/obj/machinery/drone_fabricator + name = "drone fabricator" + desc = "A large automated factory for producing maintenance drones." + + density = 1 + anchored = 1 + use_power = 1 + idle_power_usage = 20 + active_power_usage = 5000 + + var/drone_progress = 0 + var/produce_drones = 0 + var/time_last_drone = 500 + + icon = 'icons/obj/machines/drone_fab.dmi' + icon_state = "drone_fab_idle" + +/obj/machinery/drone_fabricator/New() + ..() + produce_drones = config.allow_drone_spawn + +/obj/machinery/drone_fabricator/power_change() + if (powered()) + stat &= ~NOPOWER + else + icon_state = "drone_fab_nopower" + stat |= NOPOWER + +/obj/machinery/drone_fabricator/process() + + if(stat & NOPOWER || !produce_drones) + icon_state = "drone_fab_nopower" + return + + if(drone_progress >= 100) + icon_state = "drone_fab_idle" + return + + icon_state = "drone_fab_active" + var/elapsed = world.time - time_last_drone + drone_progress = round((elapsed/config.drone_build_time)*100) + + if(drone_progress >= 100) + visible_message("\The [src] voices a strident beep, indicating a drone chassis is prepared.") + +/obj/machinery/drone_fabricator/examine() + ..() + if(produce_drones && drone_progress >= 100 && istype(usr,/mob/dead) && config.allow_drone_spawn && count_drones() < config.max_maint_drones) + usr << "
A drone is prepared. Select 'Join As Drone' from the Ghost tab to spawn as a maintenance drone." + +/obj/machinery/drone_fabricator/proc/count_drones() + var/drones = 0 + for(var/mob/living/silicon/robot/drone/D in world) + if(D.key && D.client) + drones++ + return drones + +/obj/machinery/drone_fabricator/proc/create_drone(var/client/player) + + if(stat & NOPOWER) + return + + if(!produce_drones || !config.allow_drone_spawn || count_drones() >= config.max_maint_drones) + return + + if(!player || !istype(player.mob,/mob/dead)) + return + + visible_message("\The [src] churns and grinds as it lurches into motion, disgorging a shiny new drone after a few moments.") + flick("h_lathe_leave",src) + + time_last_drone = world.time + var/mob/living/silicon/robot/drone/new_drone = new(get_turf(src)) + new_drone.transfer_personality(player) + + drone_progress = 0 + + + +/mob/dead/verb/join_as_drone() + + set category = "Ghost" + set name = "Join As Drone" + set desc = "If there is a powered, enabled fabricator in the game world with a prepared chassis, join as a maintenance drone." + + if(!(config.allow_drone_spawn)) + src << "\red That verb is not currently permitted." + return + + if (!src.stat) + return + + if (usr != src) + return 0 //something is terribly wrong + + + var/deathtime = world.time - src.timeofdeath + if(istype(src,/mob/dead/observer)) + var/mob/dead/observer/G = src + if(G.has_enabled_antagHUD == 1 && config.antag_hud_restricted) + usr << "\blue Upon using the antagHUD you forfeighted the ability to join the round." + return + + var/deathtimeminutes = round(deathtime / 600) + var/pluralcheck = "minute" + if(deathtimeminutes == 0) + pluralcheck = "" + else if(deathtimeminutes == 1) + pluralcheck = " [deathtimeminutes] minute and" + else if(deathtimeminutes > 1) + pluralcheck = " [deathtimeminutes] minutes and" + var/deathtimeseconds = round((deathtime - deathtimeminutes * 600) / 10,1) + + if (deathtime < 6000) + usr << "You have been dead for[pluralcheck] [deathtimeseconds] seconds." + usr << "You must wait 10 minutes to respawn as a drone!" + return + + for(var/obj/machinery/drone_fabricator/DF in world) + if(DF.stat & NOPOWER || !DF.produce_drones) + continue + if(DF.count_drones() >= config.max_maint_drones) + src << "\red There are too many active drones in the world for you to spawn." + return + + DF.create_drone(src.client) + return + + src << "\red There are no available drone spawn points, sorry." \ No newline at end of file diff --git a/code/modules/mob/living/silicon/robot/examine.dm b/code/modules/mob/living/silicon/robot/examine.dm index 9041d7e3480..1bde793d3f6 100644 --- a/code/modules/mob/living/silicon/robot/examine.dm +++ b/code/modules/mob/living/silicon/robot/examine.dm @@ -32,6 +32,9 @@ else msg += "Its cover is closed.\n" + if(!has_power) + msg += "It appears to be running on backup power.\n" + switch(src.stat) if(CONSCIOUS) if(!src.client) msg += "It appears to be in stand-by mode.\n" //afk @@ -39,7 +42,7 @@ if(DEAD) msg += "It looks completely unsalvageable.\n" msg += "*---------*" - if(print_flavor_text()) msg += "[print_flavor_text()]\n" + if(print_flavor_text()) msg += "\n[print_flavor_text()]\n" if (pose) if( findtext(pose,".",lentext(pose)) == 0 && findtext(pose,"!",lentext(pose)) == 0 && findtext(pose,"?",lentext(pose)) == 0 ) diff --git a/code/modules/mob/living/silicon/robot/life.dm b/code/modules/mob/living/silicon/robot/life.dm index 62f27029eb5..815c598bc1f 100644 --- a/code/modules/mob/living/silicon/robot/life.dm +++ b/code/modules/mob/living/silicon/robot/life.dm @@ -125,6 +125,10 @@ src.druggy-- src.druggy = max(0, src.druggy) + //update the state of modules and components here + if (src.stat != 0) + uneq_all() + if(!is_component_functioning("radio")) radio.on = 0 else @@ -135,6 +139,9 @@ else src.blinded = 1 + if(!is_component_functioning("actuator")) + src.Paralyse(3) + return 1 @@ -175,21 +182,38 @@ if (src.healths) if (src.stat != 2) - switch(health) - if(200 to INFINITY) - src.healths.icon_state = "health0" - if(150 to 200) - src.healths.icon_state = "health1" - if(100 to 150) - src.healths.icon_state = "health2" - if(50 to 100) - src.healths.icon_state = "health3" - if(0 to 50) - src.healths.icon_state = "health4" - if(config.health_threshold_dead to 0) - src.healths.icon_state = "health5" - else - src.healths.icon_state = "health6" + if(istype(src,/mob/living/silicon/robot/drone)) + switch(health) + if(35 to INFINITY) + src.healths.icon_state = "health0" + if(25 to 34) + src.healths.icon_state = "health1" + if(15 to 24) + src.healths.icon_state = "health2" + if(5 to 14) + src.healths.icon_state = "health3" + if(0 to 4) + src.healths.icon_state = "health4" + if(-35 to 0) + src.healths.icon_state = "health5" + else + src.healths.icon_state = "health6" + else + switch(health) + if(200 to INFINITY) + src.healths.icon_state = "health0" + if(150 to 200) + src.healths.icon_state = "health1" + if(100 to 150) + src.healths.icon_state = "health2" + if(50 to 100) + src.healths.icon_state = "health3" + if(0 to 50) + src.healths.icon_state = "health4" + if(config.health_threshold_dead to 0) + src.healths.icon_state = "health5" + else + src.healths.icon_state = "health6" else src.healths.icon_state = "health7" @@ -326,4 +350,4 @@ if(!on_fire) //Silicons don't gain stacks from hotspots, but hotspots can ignite them IgniteMob() -//Robots on fire +//Robots on fire diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 5249c4c95a4..142bfe2255b 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -57,7 +57,7 @@ var/datum/effect/effect/system/ion_trail_follow/ion_trail = null var/datum/effect/effect/system/spark_spread/spark_system//So they can initialize sparks whenever/N var/jeton = 0 - + var/has_power = 1 var/killswitch = 0 var/killswitch_time = 60 var/weapon_lock = 0 @@ -100,6 +100,12 @@ hands.icon_state = "standard" icon_state = "secborg" modtype = "Security" + else if(istype(src,/mob/living/silicon/robot/drone)) + laws = new /datum/ai_laws/drone() + connected_ai = select_active_ai_with_fewest_borgs() + if(connected_ai) + connected_ai.connected_robots += src + lawsync() else if(mmi.alien || alien) laws = new /datum/ai_laws/alienmov() @@ -152,22 +158,10 @@ hud_list[IMPTRACK_HUD] = image('icons/mob/hud.dmi', src, "hudblank") hud_list[SPECIALROLE_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - - - - hud_list[HEALTH_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[STATUS_HUD] = image('icons/mob/hud.dmi', src, "hudhealth100") - hud_list[ID_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[WANTED_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPLOYAL_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPCHEM_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[IMPTRACK_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - hud_list[SPECIALROLE_HUD] = image('icons/mob/hud.dmi', src, "hudblank") - - - - - playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) + if(istype(src,/mob/living/silicon/robot/drone)) + playsound(src.loc, 'sound/machines/twobeep.ogg', 50, 0) + else + playsound(loc, 'sound/voice/liveagain.ogg', 75, 1) // setup the PDA and its name /mob/living/silicon/robot/proc/setup_PDA() diff --git a/code/modules/mob/living/silicon/robot/robot_modules.dm b/code/modules/mob/living/silicon/robot/robot_modules.dm index 14f4e178037..285e41c8d77 100644 --- a/code/modules/mob/living/silicon/robot/robot_modules.dm +++ b/code/modules/mob/living/silicon/robot/robot_modules.dm @@ -249,3 +249,72 @@ src.emag = new /obj/item/weapon/reagent_containers/spray/alien/acid(src) src.emag.reagents.add_reagent("pacid", 125) src.emag.reagents.add_reagent("sacid", 125) + +/obj/item/weapon/robot_module/drone + name = "drone module" + + + New() + //TODO: Replace with shittier flashlight and work out why we can't remove the flash. ~Z + ..() + src.modules += new /obj/item/weapon/weldingtool(src) + src.modules += new /obj/item/weapon/screwdriver(src) + src.modules += new /obj/item/weapon/wrench(src) + src.modules += new /obj/item/weapon/crowbar(src) + src.modules += new /obj/item/weapon/wirecutters(src) + src.modules += new /obj/item/device/multitool(src) + src.modules += new /obj/item/device/lightreplacer(src) + src.modules += new /obj/item/weapon/reagent_containers/spray/cleaner(src) + src.modules += new /obj/item/weapon/gripper(src) + src.modules += new /obj/item/weapon/matter_decompiler(src) + + src.emag = new /obj/item/weapon/card/emag(src) + src.emag.name = "Cryptographic Sequencer" + + + var/list/stacktypes = list( + /obj/item/stack/rods = 10, + /obj/item/stack/tile/plasteel = 10, + /obj/item/stack/sheet/metal/cyborg = 10, + /obj/item/stack/sheet/wood/cyborg = 1, + /obj/item/stack/cable_coil = 30, + /obj/item/stack/sheet/glass/cyborg = 10, + /obj/item/stack/sheet/mineral/plastic/cyborg = 1 + ) + + for(var/T in stacktypes) + var/obj/item/stack/sheet/W = new T(src) + W.amount = stacktypes[T] + src.modules += W + + return + +/obj/item/weapon/robot_module/drone/respawn_consumable(var/mob/living/silicon/robot/R) + var/obj/item/weapon/reagent_containers/spray/cleaner/C = locate() in src.modules + C.reagents.add_reagent("cleaner", 10) + + var/list/stacks = list ( + /obj/item/stack/sheet/metal, + /obj/item/stack/cable_coil, + /obj/item/stack/sheet/glass/cyborg, + /obj/item/stack/rods, + /obj/item/stack/tile/plasteel + ) + + for(var/T in stacks) + var/O = locate(T) in src.modules + var/obj/item/stack/sheet/S = O + + if(!S) + src.modules -= null + S = new T(src) + src.modules += S + S.amount = 0 + + if(istype(S) && S.amount < 15) + S.amount++ + + var/obj/item/device/lightreplacer/LR = locate() in src.modules + LR.Charge(R) + + return diff --git a/code/modules/power/lighting.dm b/code/modules/power/lighting.dm index 1e7737bc406..91a76177428 100644 --- a/code/modules/power/lighting.dm +++ b/code/modules/power/lighting.dm @@ -168,7 +168,7 @@ newlight = new /obj/machinery/light/small/built(src.loc) newlight.dir = src.dir - src.transfer_fingerprints_to(newlight) + newlight.fingerprints |= src.fingerprints del(src) return ..() diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm index 4b1ad0470e4..f4a9420cf46 100644 --- a/code/modules/recycling/disposal.dm +++ b/code/modules/recycling/disposal.dm @@ -367,7 +367,7 @@ return use_power = 1 - + if(mode != 1) // if off or ready, no need to charge return @@ -401,7 +401,11 @@ var/wrapcheck = 0 var/obj/structure/disposalholder/H = new() // virtual holder object which actually - // travels through the pipes. + // travels through the pipes. + //Hacky test to get drones to mail themselves through disposals. + for(var/mob/living/silicon/robot/drone/D in src) + wrapcheck = 1 + for(var/obj/item/smallDelivery/O in src) wrapcheck = 1 @@ -449,9 +453,10 @@ AM.loc = src.loc AM.pipe_eject(0) - spawn(1) - if(AM) - AM.throw_at(target, 5, 1) + if(!istype(AM,/mob/living/silicon/robot/drone)) //Poor drones kept smashing windows and taking system damage being fired out of disposals. ~Z + spawn(1) + if(AM) + AM.throw_at(target, 5, 1) H.vent_gas(loc) del(H) @@ -496,7 +501,7 @@ //Check for any living mobs trigger hasmob. //hasmob effects whether the package goes to cargo or its tagged destination. for(var/mob/living/M in D) - if(M && M.stat != 2) + if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) hasmob = 1 //Checks 1 contents level deep. This means that players can be sent through disposals... @@ -504,7 +509,7 @@ for(var/obj/O in D) if(O.contents) for(var/mob/living/M in O.contents) - if(M && M.stat != 2) + if(M && M.stat != 2 && !istype(M,/mob/living/silicon/robot/drone)) hasmob = 1 // now everything inside the disposal gets put into the holder @@ -521,6 +526,10 @@ if(istype(AM, /obj/item/smallDelivery) && !hasmob) var/obj/item/smallDelivery/T = AM src.destinationTag = T.sortTag + //Drones can mail themselves through maint. + if(istype(AM, /mob/living/silicon/robot/drone)) + var/mob/living/silicon/robot/drone/drone = AM + src.destinationTag = drone.mail_destination // start the movement process @@ -544,7 +553,8 @@ while(active) if(hasmob && prob(3)) for(var/mob/living/H in src) - H.take_overall_damage(10, 0, "Blunt Trauma")//horribly maim any living creature jumping down disposals. c'est la vie + if(!istype(H,/mob/living/silicon/robot/drone)) //Drones use the mailing code to move through the disposal system, + H.take_overall_damage(20, 0, "Blunt Trauma")//horribly maim any living creature jumping down disposals. c'est la vie if(has_fat_guy && prob(2)) // chance of becoming stuck per segment if contains a fat guy active = 0 @@ -1261,8 +1271,9 @@ for(var/atom/movable/AM in H) AM.loc = src.loc AM.pipe_eject(dir) - spawn(5) - AM.throw_at(target, 3, 1) + if(!istype(AM,/mob/living/silicon/robot/drone)) //Drones keep smashing windows from being fired out of chutes. Bad for the station. ~Z + spawn(5) + AM.throw_at(target, 3, 1) H.vent_gas(src.loc) del(H) diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi index 1d3ab4101f9..b4227fa17bc 100644 Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ diff --git a/icons/mob/items_lefthand.dmi b/icons/mob/items_lefthand.dmi index 4603a792b43..10893a4399b 100644 Binary files a/icons/mob/items_lefthand.dmi and b/icons/mob/items_lefthand.dmi differ diff --git a/icons/mob/items_righthand.dmi b/icons/mob/items_righthand.dmi index 3c9c099892f..5638c2d4d07 100644 Binary files a/icons/mob/items_righthand.dmi and b/icons/mob/items_righthand.dmi differ diff --git a/icons/mob/robots.dmi b/icons/mob/robots.dmi index c5c35a9f01a..b5a93c40940 100644 Binary files a/icons/mob/robots.dmi and b/icons/mob/robots.dmi differ diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 9089fb149c4..854829e3985 100644 Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ diff --git a/icons/obj/machines/drone_fab.dmi b/icons/obj/machines/drone_fab.dmi new file mode 100644 index 00000000000..066b51c2582 Binary files /dev/null and b/icons/obj/machines/drone_fab.dmi differ diff --git a/icons/obj/weapons.dmi b/icons/obj/weapons.dmi index 3a10817eef0..9396888504d 100644 Binary files a/icons/obj/weapons.dmi and b/icons/obj/weapons.dmi differ