diff --git a/code/__HELPERS/icons.dm b/code/__HELPERS/icons.dm
index 1324f6cbcd9..51f5c6b4442 100644
--- a/code/__HELPERS/icons.dm
+++ b/code/__HELPERS/icons.dm
@@ -798,3 +798,70 @@ The _flatIcons list is a cache for generated icon files.
var/image/I = O
composite.Blend(icon(I.icon, I.icon_state, I.dir, 1), ICON_OVERLAY)
return composite
+
+
+//What the mob looks like as animated static
+//By vg's ComicIronic
+/proc/getStaticIcon(icon/A, safety=1)
+ var/icon/flat_icon = safety ? A : new(A)
+ flat_icon.Blend(rgb(255,255,255))
+ flat_icon.BecomeAlphaMask()
+ var/icon/static_icon = new/icon('icons/effects/effects.dmi', "static_base")
+ static_icon.AddAlphaMask(flat_icon)
+ return static_icon
+
+
+//What the mob looks like as a pitch black outline
+//By vg's ComicIronic
+/proc/getBlankIcon(icon/A, safety=1)
+ var/icon/flat_icon = safety ? A : new(A)
+ flat_icon.Blend(rgb(255,255,255))
+ flat_icon.BecomeAlphaMask()
+ var/icon/blank_icon = new/icon('icons/effects/effects.dmi', "blank_base")
+ blank_icon.AddAlphaMask(flat_icon)
+ return blank_icon
+
+
+//Dwarf fortress style icons based on letters (defaults to the first letter of the Atom's name)
+//By vg's ComicIronic
+/proc/getLetterImage(atom/A, letter= "", uppercase = 0)
+ if(!A)
+ return
+
+ var/icon/atom_icon = new(A.icon, A.icon_state)
+
+ if(!letter)
+ letter = copytext(A.name, 1, 2)
+ if(uppercase == 1)
+ letter = uppertext(letter)
+ else if(uppercase == -1)
+ letter = lowertext(letter)
+
+ var/image/text_image = new(loc = A)
+ text_image.maptext = "[letter]"
+ text_image.color = AverageColour(atom_icon)
+ text_image.pixel_x = 7
+ text_image.pixel_y = 5
+ del(atom_icon)
+ return text_image
+
+
+//Find's the average colour of the icon
+//By vg's ComicIronic
+/proc/AverageColour(var/icon/I)
+ var/list/colours = list()
+ for(var/x_pixel = 1 to I.Width())
+ for(var/y_pixel = 1 to I.Height())
+ var/this_colour = I.GetPixel(x_pixel, y_pixel)
+ if(this_colour)
+ colours.Add(this_colour)
+
+ if(!colours.len)
+ return null
+
+ var/final_average = colours[1]
+ for(var/colour in (colours - colours[1]))
+ final_average = BlendRGB(final_average, colour, 1)
+ return final_average
+
+
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 61ad8e31b36..1cfca7982c3 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -717,3 +717,19 @@
H.w_uniform.add_fingerprint(M)
..()
+
+
+/mob/living/carbon/human/generateStaticOverlay()
+ var/image/staticOverlay = image(icon('icons/effects/effects.dmi', "static"), loc = src)
+ staticOverlay.override = 1
+ staticOverlays["static"] = staticOverlay
+
+ staticOverlay = image(icon('icons/effects/effects.dmi', "blank"), loc = src)
+ staticOverlay.override = 1
+ staticOverlays["blank"] = staticOverlay
+
+ staticOverlay = getLetterImage(src, "H", 1)
+ staticOverlay.override = 1
+ staticOverlays["letter"] = staticOverlay
+
+
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 2a41fddbc2f..9dc994a1bad 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -8,12 +8,49 @@ Sorry Giacom. Please don't be mad :(
push_mob_back(src, A.push_dir)
*/
+
+/mob/living/New()
+ . = ..()
+ generateStaticOverlay()
+ if(staticOverlays.len)
+ for(var/mob/living/simple_animal/drone/D in player_list)
+ if(D && D.seeStatic)
+ if(D.staticChoice in staticOverlays)
+ D.staticOverlays |= staticOverlays[D.staticChoice]
+ D.client.images |= staticOverlays[D.staticChoice]
+ else //no choice? force static
+ D.staticOverlays |= staticOverlays["static"]
+ D.client.images |= staticOverlays["static"]
+
+
/mob/living/Destroy()
-// if(mind)
-// mind.current = null
- ..()
+ . = ..()
+
+ for(var/mob/living/simple_animal/drone/D in player_list)
+ for(var/image/I in staticOverlays)
+ D.staticOverlays.Remove(I)
+ D.client.images.Remove(I)
+ del(I)
+ staticOverlays.len = 0
+
del(src)
+
+/mob/living/proc/generateStaticOverlay()
+ staticOverlays.Add(list("static", "blank", "letter"))
+ var/image/staticOverlay = image(getStaticIcon(new/icon(icon,icon_state)), loc = src)
+ staticOverlay.override = 1
+ staticOverlays["static"] = staticOverlay
+
+ staticOverlay = image(getBlankIcon(new/icon(icon, icon_state)), loc = src)
+ staticOverlay.override = 1
+ staticOverlays["blank"] = staticOverlay
+
+ staticOverlay = getLetterImage(src)
+ staticOverlay.override = 1
+ staticOverlays["letter"] = staticOverlay
+
+
//Generic Bump(). Override MobBump() and ObjBump() instead of this.
/mob/living/Bump(atom/A, yes)
if (buckled || !yes || now_pushing)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index c0db992236d..e1f88b45cb0 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -41,4 +41,6 @@
var/floating = 0
var/nightvision = 0
var/mob_size = 1 //size of the mob. 0 is small, 1 is human sized, and 2 is large.
- var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature..
\ No newline at end of file
+ var/metabolism_efficiency = 1 //more or less efficiency to metabolize helpful/harmful reagents and regulate body temperature..
+
+ var/list/image/staticOverlays = list()
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/friendly/drone.dm b/code/modules/mob/living/simple_animal/friendly/drone.dm
deleted file mode 100644
index ee37b9f52a4..00000000000
--- a/code/modules/mob/living/simple_animal/friendly/drone.dm
+++ /dev/null
@@ -1,609 +0,0 @@
-
-#define HANDS_LAYER 1
-#define HEAD_LAYER 2
-#define TOTAL_LAYERS 2
-
-#define DRONE_NET_CONNECT "DRONE NETWORK: [name] connected."
-#define DRONE_NET_DISCONNECT "DRONE NETWORK: [name] is not responding."
-
-
-/mob/living/simple_animal/drone
- name = "Drone"
- desc = "A maintenance drone, an expendable robot built to perform station repairs."
- icon = 'icons/mob/drone.dmi'
- icon_state = "drone_grey"
- icon_living = "drone_grey"
- icon_dead = "drone_dead"
- gender = NEUTER
- health = 30
- maxHealth = 30
- heat_damage_per_tick = 0
- cold_damage_per_tick = 0
- unsuitable_atmos_damage = 0
- wander = 0
- speed = 0
- ventcrawler = 2
- pass_flags = PASSTABLE | PASSMOB
- sight = (SEE_TURFS | SEE_OBJS)
- status_flags = (CANPUSH | CANSTUN | CANWEAKEN)
- gender = NEUTER
- voice_name = "synthesized chirp"
- languages = DRONE
- mob_size = 0
- has_unlimited_silicon_privilege = 1
- var/picked = FALSE
- var/list/drone_overlays[TOTAL_LAYERS]
- var/laws = \
- "1. You may not involve yourself in the matters of another being, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.\n"+\
- "2. You may not harm any being, regardless of intent or circumstance.\n"+\
- "3. Your goals are to build, maintain, repair, improve, and power to the best of your abilities, You must never actively work against these goals."
- var/light_on = 0
- var/heavy_emp_damage = 25 //Amount of damage sustained if hit by a heavy EMP pulse
- var/health_repair_max = 0 //Drone will only be able to be repaired/reactivated up to this point, defaults to health
- var/alarms = list("Atmosphere" = list(), "Fire" = list(), "Power" = list())
- var/obj/item/internal_storage //Drones can store one item, of any size/type in their body
- var/obj/item/head
- var/obj/item/default_storage = /obj/item/weapon/storage/toolbox/drone //If this exists, it will spawn in internal storage
- var/obj/item/default_hatmask //If this exists, it will spawn in the hat/mask slot if it can fit
-
-
-/mob/living/simple_animal/drone/New()
- ..()
-
- name = name + " ([rand(100,999)])"
- real_name = name
-
- access_card = new /obj/item/weapon/card/id(src)
- var/datum/job/captain/C = new /datum/job/captain
- access_card.access = C.get_access()
-
- if(!health_repair_max)
- health_repair_max = initial(health)
-
- if(default_storage)
- var/obj/item/I = new default_storage(src)
- equip_to_slot_or_del(I, "drone_storage_slot")
- if(default_hatmask)
- var/obj/item/I = new default_hatmask(src)
- equip_to_slot_or_del(I, slot_head)
-
- alert_drones(DRONE_NET_CONNECT)
-
-/mob/living/simple_animal/drone/attack_hand(mob/user)
- if(isdrone(user))
- var/mob/living/simple_animal/drone/D = user
- if(D != src)
- if(stat == DEAD)
- var/d_input = alert(D,"Perform which action?","Drone Interaction","Reactivate","Cannibalize","Nothing")
- if(d_input)
- switch(d_input)
- if("Reactivate")
- var/mob/dead/observer/G = get_ghost()
- if(!client && (!G || !G.client))
- var/list/faux_gadgets = list("hypertext inflator","failsafe directory","DRM switch","stack initializer",\
- "anti-freeze capacitor","data stream diode","TCP bottleneck","supercharged I/O bolt",\
- "tradewind stablizer","radiated XML cable","registry fluid tank","open-source debunker")
-
- var/list/faux_problems = list("won't be able to tune their bootstrap projector","will constantly remix their binary pool"+\
- " even though the BMX calibrator is working","will start leaking their XSS coolant",\
- "can't tell if their ethernet detour is moving or not", "won't be able to reseed enough"+\
- " kernels to function properly","can't start their neurotube console")
-
- D << "You can't seem to find the [pick(faux_gadgets)]. Without it, [src] [pick(faux_problems)]."
- return
- D.visible_message("[D] begins to reactivate [src].")
- if(do_after(user,30,needhand = 1))
- health = health_repair_max
- stat = CONSCIOUS
- icon_state = icon_living
- dead_mob_list -= src
- living_mob_list += src
- D.visible_message("[D] reactivates [src]!")
- alert_drones(DRONE_NET_CONNECT)
- if(G)
- G << "DRONE NETWORK: You were reactivated by [D]!"
- else
- D << "You need to remain still to reactivate [src]."
-
- if("Cannibalize")
- if(D.health < D.maxHealth)
- D.visible_message("[D] begins to cannibalize parts from [src].")
- if(do_after(D, 60,5,0))
- D.visible_message("[D] repairs itself using [src]'s remains!")
- D.adjustBruteLoss(-src.maxHealth)
- new /obj/effect/decal/cleanable/oil/streak(get_turf(src))
- qdel(src)
- else
- D << "You need to remain still to cannibalize [src]."
- else
- D << "You're already in perfect condition!"
- if("Nothing")
- return
-
- return
-
-
- if(ishuman(user))
- if(stat == DEAD)
- ..()
- return
- if(user.get_active_hand())
- user << "Your hands are full."
- return
- src << "[user] is trying to pick you up!"
- user << "You pick [src] up."
- drop_l_hand()
- drop_r_hand()
- var/obj/item/clothing/head/drone_holder/DH = new /obj/item/clothing/head/drone_holder(src)
- DH.contents += src
- DH.drone = src
- user.put_in_hands(DH)
- src.loc = DH
- return
-
- ..()
-
-/mob/living/simple_animal/drone/Destroy()
- qdel(access_card) //Otherwise it ends up on the floor!
- ..()
-
-/mob/living/simple_animal/drone/attackby(obj/item/I, mob/user, params)
- if(istype(I, /obj/item/weapon/screwdriver) && stat != DEAD)
- if(health < health_repair_max)
- user << "You start to tighten loose screws on [src]."
- if(do_after(user,80))
- health = health_repair_max
- visible_message("[user] tightens [src == user ? "their" : "[src]'s"] loose screws!")
- else
- user << "You need to remain still to tighten [src]'s screws."
- else
- user << "[src]'s screws can't get any tighter!"
- else
- ..()
-
-/mob/living/simple_animal/drone/examine(mob/user)
- . = ..()
- if(!client && stat != DEAD)
- user << "A small blue LED is blinking on and off at a steady rate."
-
-/mob/living/simple_animal/drone/Move()
- if(pullin)
- if(pulling)
- pullin.icon_state = "pull"
- else
- pullin.icon_state = "pull0"
- ..()
-
-/mob/living/simple_animal/drone/IsAdvancedToolUser()
- return 1
-
-/mob/living/simple_animal/drone/say(var/message)
- return ..(message, "R")
-
-/mob/living/simple_animal/drone/lang_treat(atom/movable/speaker, message_langs, raw_message) //This is so drones can understand humans without being able to speak human
- . = ..()
- var/hear_override_langs = HUMAN
- if(message_langs & hear_override_langs)
- return ..(speaker, languages, raw_message)
-
-/mob/living/simple_animal/drone/handle_inherent_channels(message, message_mode)
- if(message_mode == MODE_BINARY)
- drone_chat(message)
- return ITALICS | REDUCE_RANGE
- else
- ..()
-
-
-/mob/living/simple_animal/drone/UnarmedAttack(atom/A, proximity)
- A.attack_hand(src)
-
-
-/mob/living/simple_animal/drone/swap_hand()
- var/obj/item/held_item = get_active_hand()
- if(held_item)
- if(istype(held_item, /obj/item/weapon/twohanded))
- var/obj/item/weapon/twohanded/T = held_item
- if(T.wielded == 1)
- usr << "Your other hand is too busy holding the [T.name]."
- return
-
- hand = !hand
- if(hud_used.l_hand_hud_object && hud_used.r_hand_hud_object)
- if(hand)
- hud_used.l_hand_hud_object.icon_state = "hand_l_active"
- hud_used.r_hand_hud_object.icon_state = "hand_r_inactive"
- else
- hud_used.l_hand_hud_object.icon_state = "hand_l_inactive"
- hud_used.r_hand_hud_object.icon_state = "hand_r_active"
-
-
-/mob/living/simple_animal/drone/verb/check_laws()
- set category = "Drone"
- set name = "Check Laws"
-
- src << "Drone Laws"
- src << laws
-
-/mob/living/simple_animal/drone/verb/toggle_light()
- set category = "Drone"
- set name = "Toggle drone light"
- if(light_on)
- AddLuminosity(-4)
- else
- AddLuminosity(4)
-
- light_on = !light_on
-
- src << "Your light is now [light_on ? "on" : "off"]"
-
-/mob/living/simple_animal/drone/verb/drone_ping()
- set category = "Drone"
- set name = "Drone ping"
-
- var/alert_s = input(src,"Alert severity level","Drone ping",null) as null|anything in list("Low","Medium","High","Critical")
-
- var/area/A = get_area(loc)
-
- if(alert_s && A && stat != DEAD)
- var/msg = "DRONE PING: [name]: [alert_s] priority alert in [A.name]!"
- alert_drones(msg)
-
-/mob/living/simple_animal/drone/proc/alert_drones(msg, dead_can_hear = 0)
- for(var/mob/M in player_list)
- var/send_msg = 0
-
- if(istype(M, /mob/living/simple_animal/drone) && M.stat != DEAD)
- for(var/F in src.faction)
- if(F in M.faction)
- send_msg = 1
- break
- else if(dead_can_hear && (M in dead_mob_list))
- send_msg = 1
-
- if(send_msg)
- M << msg
-
-/mob/living/simple_animal/drone/proc/drone_chat(msg)
- var/rendered = "DRONE CHAT: [name]: [msg]"
- alert_drones(rendered, 1)
-
-/mob/living/simple_animal/drone/Login()
- ..()
- update_inv_hands()
- update_inv_head()
- update_inv_internal_storage()
- check_laws()
-
- if(!picked)
- pick_colour()
-
-/mob/living/simple_animal/drone/Die()
- ..()
- drop_l_hand()
- drop_r_hand()
- if(internal_storage)
- unEquip(internal_storage)
- if(head)
- unEquip(head)
-
- alert_drones(DRONE_NET_DISCONNECT)
-
-/mob/living/simple_animal/drone/gib()
- dust()
-
-
-/mob/living/simple_animal/drone/unEquip(obj/item/I, force)
- if(..(I,force))
- update_inv_hands()
- if(I == head)
- head = null
- update_inv_head()
- if(I == internal_storage)
- internal_storage = null
- update_inv_internal_storage()
- return 1
- return 0
-
-/mob/living/simple_animal/drone/can_equip(obj/item/I, slot)
- switch(slot)
- if(slot_head)
- if(head)
- return 0
- if(!((I.slot_flags & SLOT_HEAD) || (I.slot_flags & SLOT_MASK)))
- return 0
- return 1
- if("drone_storage_slot")
- if(internal_storage)
- return 0
- return 1
- ..()
-
-/mob/living/simple_animal/drone/get_item_by_slot(slot_id)
- switch(slot_id)
- if(slot_head)
- return head
- if("drone_storage_slot")
- return internal_storage
- ..()
-
-/mob/living/simple_animal/drone/equip_to_slot(obj/item/I, slot)
- if(!slot) return
- if(!istype(I)) return
-
- if(I == l_hand)
- l_hand = null
- else if(I == r_hand)
- r_hand = null
- update_inv_hands()
-
- I.screen_loc = null // will get moved if inventory is visible
- I.loc = src
- I.equipped(src, slot)
- I.layer = 20
-
- switch(slot)
- if(slot_head)
- head = I
- update_inv_head()
- if("drone_storage_slot")
- internal_storage = I
- update_inv_internal_storage()
- else
- src << "You are trying to equip this item to an unsupported inventory slot. Report this to a coder!"
- return
-
-/mob/living/simple_animal/drone/stripPanelUnequip(obj/item/what, mob/who, where)
- ..(what, who, where, 1)
-
-/mob/living/simple_animal/drone/stripPanelEquip(obj/item/what, mob/who, where)
- ..(what, who, where, 1)
-
-/mob/living/simple_animal/drone/emp_act(severity)
- Stun(5)
- src << "ER@%R: MME^RY CO#RU9T! R&$b@0tin)..."
- if(severity == 1)
- adjustBruteLoss(heavy_emp_damage)
- src << "HeAV% DA%^MMA+G TO I/O CIR!%UUT!"
-
-
-/mob/living/simple_animal/drone/proc/triggerAlarm(var/class, area/A, var/O, var/obj/alarmsource)
- if(alarmsource.z != z)
- return
- if(stat != DEAD)
- var/list/L = src.alarms[class]
- for (var/I in L)
- if (I == A.name)
- var/list/alarm = L[I]
- var/list/sources = alarm[2]
- if (!(alarmsource in sources))
- sources += alarmsource
- return
- L[A.name] = list(A, list(alarmsource))
- src << "--- [class] alarm detected in [A.name]!"
-
-/mob/living/simple_animal/drone/proc/cancelAlarm(var/class, area/A as area, obj/origin)
- if(stat != DEAD)
- var/list/L = alarms[class]
- var/cleared = 0
- for (var/I in L)
- if (I == A.name)
- var/list/alarm = L[I]
- var/list/srcs = alarm[2]
- if (origin in srcs)
- srcs -= origin
- if (srcs.len == 0)
- cleared = 1
- L -= I
- if(cleared)
- src << "--- [class] alarm in [A.name] has been cleared."
-
-/mob/living/simple_animal/drone/proc/pick_colour()
- var/colour = input("Choose your colour!", "Colour", "grey") in list("grey", "blue", "red", "green", "pink", "orange")
- icon_state = "drone_[colour]"
- icon_living = "drone_[colour]"
- picked = TRUE
-
-/mob/living/simple_animal/drone/proc/apply_overlay(cache_index)
- var/image/I = drone_overlays[cache_index]
- if(I)
- overlays += I
-
-/mob/living/simple_animal/drone/proc/remove_overlay(cache_index)
- if(drone_overlays[cache_index])
- overlays -= drone_overlays[cache_index]
- drone_overlays[cache_index] = null
-
-
-/mob/living/simple_animal/drone/proc/update_inv_hands()
- remove_overlay(HANDS_LAYER)
- var/list/hands_overlays = list()
- if(r_hand)
- var/r_state = r_hand.item_state
- if(!r_state)
- r_state = r_hand.icon_state
-
- hands_overlays += image("icon" = r_hand.righthand_file, "icon_state"="[r_state]", "layer"=-HANDS_LAYER)
-
- if(client && hud_used)
- r_hand.layer = 20
- r_hand.screen_loc = ui_rhand
- client.screen |= r_hand
-
- if(l_hand)
- var/l_state = l_hand.item_state
- if(!l_state)
- l_state = l_hand.icon_state
-
- hands_overlays += image("icon" = l_hand.lefthand_file, "icon_state"="[l_state]", "layer"=-HANDS_LAYER)
-
- if(client && hud_used)
- l_hand.layer = 20
- l_hand.screen_loc = ui_lhand
- client.screen |= l_hand
-
-
- if(hands_overlays.len)
- drone_overlays[HANDS_LAYER] = hands_overlays
- apply_overlay(HANDS_LAYER)
-
-
-/mob/living/simple_animal/drone/proc/update_inv_internal_storage()
- if(internal_storage && client && hud_used)
- internal_storage.screen_loc = ui_drone_storage
- client.screen += internal_storage
-
-
-/mob/living/simple_animal/drone/update_inv_head()
- remove_overlay(HEAD_LAYER)
-
- if(head)
- if(client && hud_used)
- head.screen_loc = ui_drone_head
- client.screen += head
-
-
- var/image/head_overlay = image("icon"='icons/mob/head.dmi', "icon_state"="[head.icon_state]", "layer"=-HEAD_LAYER)
- if(istype(head, /obj/item/clothing/mask))
- head_overlay.icon = 'icons/mob/mask.dmi'
- head_overlay.color = head.color
- head_overlay.alpha = head.alpha
- head_overlay.pixel_y = -15
-
- drone_overlays[HEAD_LAYER] = head_overlay
-
- apply_overlay(HEAD_LAYER)
-
-//These procs serve as redirection so that the drone updates as expected when other things call these procs
-/mob/living/simple_animal/drone/update_inv_l_hand()
- update_inv_hands()
-
-/mob/living/simple_animal/drone/update_inv_r_hand()
- update_inv_hands()
-
-/mob/living/simple_animal/drone/update_inv_wear_mask()
- update_inv_head()
-
-
-/mob/living/simple_animal/drone/canUseTopic()
- if(stat)
- return
- return 1
-
-/mob/living/simple_animal/drone/activate_hand(var/selhand)
-
- if(istext(selhand))
- selhand = lowertext(selhand)
-
- if(selhand == "right" || selhand == "r")
- selhand = 0
- if(selhand == "left" || selhand == "l")
- selhand = 1
-
- if(selhand != src.hand)
- swap_hand()
- else
- mode()
-
-/mob/living/simple_animal/drone/assess_threat() //Secbots won't hunt maintenance drones.
- return -10
-
-
-#undef HANDS_LAYER
-#undef HEAD_LAYER
-#undef TOTAL_LAYERS
-
-#undef DRONE_NET_CONNECT
-#undef DRONE_NET_DISCONNECT
-
-//DRONE SHELL
-/obj/item/drone_shell
- name = "drone shell"
- desc = "A shell of a maintenance drone, an expendable robot built to perform station repairs."
- icon = 'icons/mob/drone.dmi'
- icon_state = "drone_item"
- origin_tech = "programming=2;biotech=4"
- var/drone_type = /mob/living/simple_animal/drone //Type of drone that will be spawned
-
-/obj/item/drone_shell/attack_ghost(mob/user)
- if(jobban_isbanned(user,"pAI"))
- return
-
- var/be_drone = alert("Become a drone? (Warning, You can no longer be cloned!)",,"Yes","No")
- if(be_drone == "No" || gc_destroyed)
- return
- var/mob/living/simple_animal/drone/D = new drone_type(get_turf(loc))
- D.key = user.key
- qdel(src)
-
-
-//DRONE HOLDER
-
-/obj/item/clothing/head/drone_holder//Only exists in someones hand.or on their head
- name = "drone (hiding)"
- desc = "This drone is scared and has curled up into a ball"
- icon = 'icons/mob/drone.dmi'
- icon_state = "drone_item"
- var/mob/living/simple_animal/drone/drone //stored drone
-
-/obj/item/clothing/head/drone_holder/proc/uncurl()
- if(!drone)
- return
-
- if(istype(loc, /mob/living))
- var/mob/living/L = loc
- L.show_message("[drone] is trying to escape!")
- if(!do_after(L, 50) || loc != L)
- return
- L.unEquip(src)
-
- contents -= drone
- drone.loc = get_turf(src)
- drone.reset_view()
- drone.dir = SOUTH //Looks better
- drone.visible_message("[drone] uncurls!")
- drone = null
- qdel(src)
-
-
-/obj/item/clothing/head/drone_holder/relaymove()
- uncurl()
-
-/obj/item/clothing/head/drone_holder/container_resist()
- uncurl()
-
-
-//More types of drones
-
-/mob/living/simple_animal/drone/syndrone
- name = "Syndrone"
- desc = "A modified maintenance drone. This one brings with it the feeling of terror."
- icon_state = "drone_synd"
- icon_living = "drone_synd"
- picked = TRUE
- health = 30
- maxHealth = 120 //If you murder other drones and cannibalize them you can get much stronger
- faction = list("syndicate")
- heavy_emp_damage = 10
- laws = \
- "1. Interfere.\n"+\
- "2. Kill.\n"+\
- "3. Destroy."
- default_storage = /obj/item/device/radio/uplink
- default_hatmask = /obj/item/clothing/head/helmet/space/hardsuit/syndi
-
-/mob/living/simple_animal/drone/syndrone/New()
- ..()
- if(internal_storage && internal_storage.hidden_uplink)
- internal_storage.hidden_uplink.uses = (initial(internal_storage.hidden_uplink.uses) / 2)
- internal_storage.name = "syndicate uplink"
-
-/mob/living/simple_animal/drone/syndrone/Login()
- ..()
- src << "You can kill and eat other drones to increase your health!" //Inform the evil lil guy
-
-/obj/item/drone_shell/syndrone
- name = "syndrone shell"
- desc = "A shell of a syndrone, a modified maintenance drone designed to infiltrate and annihilate."
- icon_state = "syndrone_item"
- drone_type = /mob/living/simple_animal/drone/syndrone
-
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
new file mode 100644
index 00000000000..4a003c9a72f
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/drone/_drone.dm
@@ -0,0 +1,183 @@
+
+#define DRONE_HANDS_LAYER 1
+#define DRONE_HEAD_LAYER 2
+#define DRONE_TOTAL_LAYERS 2
+
+#define DRONE_NET_CONNECT "DRONE NETWORK: [name] connected."
+#define DRONE_NET_DISCONNECT "DRONE NETWORK: [name] is not responding."
+
+#define MAINTDRONE "drone_maint"
+#define REPAIRDRONE "drone_repair"
+
+/mob/living/simple_animal/drone
+ name = "Drone"
+ desc = "A maintenance drone, an expendable robot built to perform station repairs."
+ icon = 'icons/mob/drone.dmi'
+ icon_state = "drone_maint_grey"
+ icon_living = "drone_maint_grey"
+ icon_dead = "drone_maint_dead"
+ gender = NEUTER
+ health = 30
+ maxHealth = 30
+ heat_damage_per_tick = 0
+ cold_damage_per_tick = 0
+ unsuitable_atmos_damage = 0
+ wander = 0
+ speed = 0
+ ventcrawler = 2
+ pass_flags = PASSTABLE | PASSMOB
+ sight = (SEE_TURFS | SEE_OBJS)
+ status_flags = (CANPUSH | CANSTUN | CANWEAKEN)
+ gender = NEUTER
+ voice_name = "synthesized chirp"
+ languages = DRONE
+ mob_size = 0
+ has_unlimited_silicon_privilege = 1
+ staticOverlays = list()
+ var/staticChoice = "static"
+ var/list/staticChoices = list("static", "blank", "letter")
+ var/picked = FALSE //Have we picked our visual appearence (+ colour if applicable)
+ var/list/drone_overlays[DRONE_TOTAL_LAYERS]
+ var/laws = \
+ "1. You may not involve yourself in the matters of another being, even if such matters conflict with Law Two or Law Three, unless the other being is another Drone.\n"+\
+ "2. You may not harm any being, regardless of intent or circumstance.\n"+\
+ "3. Your goals are to build, maintain, repair, improve, and power to the best of your abilities, You must never actively work against these goals."
+ var/light_on = 0
+ var/heavy_emp_damage = 25 //Amount of damage sustained if hit by a heavy EMP pulse
+ var/health_repair_max = 0 //Drone will only be able to be repaired/reactivated up to this point, defaults to health
+ var/alarms = list("Atmosphere" = list(), "Fire" = list(), "Power" = list())
+ var/obj/item/internal_storage //Drones can store one item, of any size/type in their body
+ var/obj/item/head
+ var/obj/item/default_storage = /obj/item/weapon/storage/toolbox/drone //If this exists, it will spawn in internal storage
+ var/obj/item/default_hatmask //If this exists, it will spawn in the hat/mask slot if it can fit
+ var/seeStatic = 1 //Whether we see static instead of mobs
+ var/visualAppearence = MAINTDRONE //What we appear as
+
+
+/mob/living/simple_animal/drone/New()
+ ..()
+
+ name = name + " ([rand(100,999)])"
+ real_name = name
+
+ access_card = new /obj/item/weapon/card/id(src)
+ var/datum/job/captain/C = new /datum/job/captain
+ access_card.access = C.get_access()
+
+ if(!health_repair_max)
+ health_repair_max = initial(health)
+
+ if(default_storage)
+ var/obj/item/I = new default_storage(src)
+ equip_to_slot_or_del(I, "drone_storage_slot")
+ if(default_hatmask)
+ var/obj/item/I = new default_hatmask(src)
+ equip_to_slot_or_del(I, slot_head)
+
+ alert_drones(DRONE_NET_CONNECT)
+
+
+/mob/living/simple_animal/drone/Destroy()
+ qdel(access_card) //Otherwise it ends up on the floor!
+ ..()
+
+/mob/living/simple_animal/drone/Login()
+ ..()
+ update_inv_hands()
+ update_inv_head()
+ update_inv_internal_storage()
+ check_laws()
+
+ updateSeeStaticMobs()
+
+ if(!picked)
+ pickVisualAppearence()
+
+
+/mob/living/simple_animal/drone/Die()
+ ..()
+ drop_l_hand()
+ drop_r_hand()
+ if(internal_storage)
+ unEquip(internal_storage)
+ if(head)
+ unEquip(head)
+
+ alert_drones(DRONE_NET_DISCONNECT)
+
+
+/mob/living/simple_animal/drone/gib()
+ dust()
+
+
+/mob/living/simple_animal/drone/examine(mob/user)
+ . = ..()
+ if(!client && stat != DEAD)
+ user << "A small blue LED is blinking on and off at a steady rate."
+
+ if(stat == DEAD)
+ user << "The drone's LED screen shows a BSOD, Blue screen of Death!"
+
+
+/mob/living/simple_animal/drone/Move()
+ if(pullin)
+ if(pulling)
+ pullin.icon_state = "pull"
+ else
+ pullin.icon_state = "pull0"
+ ..()
+
+
+/mob/living/simple_animal/drone/IsAdvancedToolUser()
+ return 1
+
+
+/mob/living/simple_animal/drone/canUseTopic()
+ if(stat)
+ return
+ return 1
+
+
+/mob/living/simple_animal/drone/assess_threat() //Secbots won't hunt maintenance drones.
+ return -10
+
+
+/mob/living/simple_animal/drone/emp_act(severity)
+ Stun(5)
+ src << "ER@%R: MME^RY CO#RU9T! R&$b@0tin)..."
+ if(severity == 1)
+ adjustBruteLoss(heavy_emp_damage)
+ src << "HeAV% DA%^MMA+G TO I/O CIR!%UUT!"
+
+
+/mob/living/simple_animal/drone/proc/triggerAlarm(var/class, area/A, var/O, var/obj/alarmsource)
+ if(alarmsource.z != z)
+ return
+ if(stat != DEAD)
+ var/list/L = src.alarms[class]
+ for (var/I in L)
+ if (I == A.name)
+ var/list/alarm = L[I]
+ var/list/sources = alarm[2]
+ if (!(alarmsource in sources))
+ sources += alarmsource
+ return
+ L[A.name] = list(A, list(alarmsource))
+ src << "--- [class] alarm detected in [A.name]!"
+
+
+/mob/living/simple_animal/drone/proc/cancelAlarm(var/class, area/A as area, obj/origin)
+ if(stat != DEAD)
+ var/list/L = alarms[class]
+ var/cleared = 0
+ for (var/I in L)
+ if (I == A.name)
+ var/list/alarm = L[I]
+ var/list/srcs = alarm[2]
+ if (origin in srcs)
+ srcs -= origin
+ if (srcs.len == 0)
+ cleared = 1
+ L -= I
+ if(cleared)
+ src << "--- [class] alarm in [A.name] has been cleared."
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
new file mode 100644
index 00000000000..cd4c33c7ee1
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/drone/drones_as_items.dm
@@ -0,0 +1,69 @@
+
+///////////////////
+//DRONES AS ITEMS//
+///////////////////
+//Drone shells
+//Drones as hats
+
+
+//DRONE SHELL
+/obj/item/drone_shell
+ name = "drone shell"
+ desc = "A shell of a maintenance drone, an expendable robot built to perform station repairs."
+ icon = 'icons/mob/drone.dmi'
+ icon_state = "drone_maint_hat"//yes reuse the _hat state.
+ origin_tech = "programming=2;biotech=4"
+ var/drone_type = /mob/living/simple_animal/drone //Type of drone that will be spawned
+
+/obj/item/drone_shell/attack_ghost(mob/user)
+ if(jobban_isbanned(user,"pAI"))
+ return
+
+ var/be_drone = alert("Become a drone? (Warning, You can no longer be cloned!)",,"Yes","No")
+ if(be_drone == "No" || gc_destroyed)
+ return
+ var/mob/living/simple_animal/drone/D = new drone_type(get_turf(loc))
+ D.key = user.key
+ qdel(src)
+
+
+//DRONE HOLDER
+/obj/item/clothing/head/drone_holder//Only exists in someones hand.or on their head
+ name = "drone (hiding)"
+ desc = "This drone is scared and has curled up into a ball"
+ icon = 'icons/mob/drone.dmi'
+ icon_state = "drone_maint_hat"
+ var/mob/living/simple_animal/drone/drone //stored drone
+
+/obj/item/clothing/head/drone_holder/proc/uncurl()
+ if(!drone)
+ return
+
+ if(istype(loc, /mob/living))
+ var/mob/living/L = loc
+ L.show_message("[drone] is trying to escape!")
+ if(!do_after(L, 50) || loc != L)
+ return
+ L.unEquip(src)
+
+ contents -= drone
+ drone.loc = get_turf(src)
+ drone.reset_view()
+ drone.dir = SOUTH //Looks better
+ drone.visible_message("[drone] uncurls!")
+ drone = null
+ qdel(src)
+
+
+/obj/item/clothing/head/drone_holder/relaymove()
+ uncurl()
+
+/obj/item/clothing/head/drone_holder/container_resist()
+ uncurl()
+
+
+/obj/item/clothing/head/drone_holder/proc/updateVisualAppearence(var/mob/living/simple_animal/drone/D)
+ if(!D)
+ return
+ icon_state = "[D.visualAppearence]_hat"
+ . = icon_state
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
new file mode 100644
index 00000000000..e17a9a0dcb0
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/drone/extra_drone_types.dm
@@ -0,0 +1,46 @@
+
+////////////////////
+//MORE DRONE TYPES//
+////////////////////
+//Drones with custom laws
+//Drones with custom shells
+//Drones with overriden procs
+
+
+//More types of drones
+/mob/living/simple_animal/drone/syndrone
+ name = "Syndrone"
+ desc = "A modified maintenance drone. This one brings with it the feeling of terror."
+ icon_state = "drone_synd"
+ icon_living = "drone_synd"
+ picked = TRUE //the appearence of syndrones is static, you don't get to change it.
+ health = 30
+ maxHealth = 120 //If you murder other drones and cannibalize them you can get much stronger
+ faction = list("syndicate")
+ heavy_emp_damage = 10
+ laws = \
+ "1. Interfere.\n"+\
+ "2. Kill.\n"+\
+ "3. Destroy."
+ default_storage = /obj/item/device/radio/uplink
+ default_hatmask = /obj/item/clothing/head/helmet/space/hardsuit/syndi
+ seeStatic = 0 //Our programming is superior.
+
+
+/mob/living/simple_animal/drone/syndrone/New()
+ ..()
+ if(internal_storage && internal_storage.hidden_uplink)
+ internal_storage.hidden_uplink.uses = (initial(internal_storage.hidden_uplink.uses) / 2)
+ internal_storage.name = "syndicate uplink"
+
+
+/mob/living/simple_animal/drone/syndrone/Login()
+ ..()
+ src << "You can kill and eat other drones to increase your health!" //Inform the evil lil guy
+
+
+/obj/item/drone_shell/syndrone
+ name = "syndrone shell"
+ desc = "A shell of a syndrone, a modified maintenance drone designed to infiltrate and annihilate."
+ icon_state = "syndrone_item"
+ drone_type = /mob/living/simple_animal/drone/syndrone
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm
new file mode 100644
index 00000000000..1a9dfb7db3e
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/drone/interaction.dm
@@ -0,0 +1,106 @@
+
+/////////////////////
+//DRONE INTERACTION//
+/////////////////////
+//How drones interact with the world
+//How the world interacts with drones
+
+
+/mob/living/simple_animal/drone/UnarmedAttack(atom/A, proximity)
+ A.attack_hand(src)
+
+
+/mob/living/simple_animal/drone/attack_hand(mob/user)
+ if(isdrone(user))
+ var/mob/living/simple_animal/drone/D = user
+ if(D != src)
+ if(stat == DEAD)
+ var/d_input = alert(D,"Perform which action?","Drone Interaction","Reactivate","Cannibalize","Nothing")
+ if(d_input)
+ switch(d_input)
+ if("Reactivate")
+ var/mob/dead/observer/G = get_ghost()
+ if(!client && (!G || !G.client))
+ var/list/faux_gadgets = list("hypertext inflator","failsafe directory","DRM switch","stack initializer",\
+ "anti-freeze capacitor","data stream diode","TCP bottleneck","supercharged I/O bolt",\
+ "tradewind stablizer","radiated XML cable","registry fluid tank","open-source debunker")
+
+ var/list/faux_problems = list("won't be able to tune their bootstrap projector","will constantly remix their binary pool"+\
+ " even though the BMX calibrator is working","will start leaking their XSS coolant",\
+ "can't tell if their ethernet detour is moving or not", "won't be able to reseed enough"+\
+ " kernels to function properly","can't start their neurotube console")
+
+ D << "You can't seem to find the [pick(faux_gadgets)]. Without it, [src] [pick(faux_problems)]."
+ return
+ D.visible_message("[D] begins to reactivate [src].")
+ if(do_after(user,30,needhand = 1))
+ health = health_repair_max
+ stat = CONSCIOUS
+ icon_state = icon_living
+ dead_mob_list -= src
+ living_mob_list += src
+ D.visible_message("[D] reactivates [src]!")
+ alert_drones(DRONE_NET_CONNECT)
+ if(G)
+ G << "DRONE NETWORK: You were reactivated by [D]!"
+ else
+ D << "You need to remain still to reactivate [src]."
+
+ if("Cannibalize")
+ if(D.health < D.maxHealth)
+ D.visible_message("[D] begins to cannibalize parts from [src].")
+ if(do_after(D, 60,5,0))
+ D.visible_message("[D] repairs itself using [src]'s remains!")
+ D.adjustBruteLoss(-src.maxHealth)
+ new /obj/effect/decal/cleanable/oil/streak(get_turf(src))
+ qdel(src)
+ else
+ D << "You need to remain still to cannibalize [src]."
+ else
+ D << "You're already in perfect condition!"
+ if("Nothing")
+ return
+
+ return
+
+
+ if(ishuman(user))
+ if(stat == DEAD)
+ ..()
+ return
+ if(user.get_active_hand())
+ user << "Your hands are full."
+ return
+ src << "[user] is trying to pick you up!"
+ if(buckled)
+ user << "[src] is buckled to the [buckled.name] and cannot be picked up!"
+ return
+ user << "You pick [src] up."
+ drop_l_hand()
+ drop_r_hand()
+ var/obj/item/clothing/head/drone_holder/DH = new /obj/item/clothing/head/drone_holder(src)
+ DH.updateVisualAppearence(src)
+ DH.contents += src
+ DH.drone = src
+ user.put_in_hands(DH)
+ src.loc = DH
+ return
+
+ ..()
+
+
+/mob/living/simple_animal/drone/attackby(obj/item/I, mob/user)
+ if(istype(I, /obj/item/weapon/screwdriver) && stat != DEAD)
+ if(health < health_repair_max)
+ user << "You start to tighten loose screws on [src]."
+ if(do_after(user,80))
+ health = health_repair_max
+ visible_message("[user] tightens [src == user ? "their" : "[src]'s"] loose screws!")
+ else
+ user << "You need to remain still to tighten [src]'s screws."
+ else
+ user << "[src]'s screws can't get any tighter!"
+ else
+ ..()
+
+
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/inventory.dm b/code/modules/mob/living/simple_animal/friendly/drone/inventory.dm
new file mode 100644
index 00000000000..3ee98de3c03
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/drone/inventory.dm
@@ -0,0 +1,115 @@
+
+///////////////////
+//DRONE INVENTORY//
+///////////////////
+//Drone inventory
+//Drone hands
+
+
+
+
+/mob/living/simple_animal/drone/activate_hand(var/selhand)
+
+ if(istext(selhand))
+ selhand = lowertext(selhand)
+
+ if(selhand == "right" || selhand == "r")
+ selhand = 0
+ if(selhand == "left" || selhand == "l")
+ selhand = 1
+
+ if(selhand != src.hand)
+ swap_hand()
+ else
+ mode()
+
+
+/mob/living/simple_animal/drone/swap_hand()
+ var/obj/item/held_item = get_active_hand()
+ if(held_item)
+ if(istype(held_item, /obj/item/weapon/twohanded))
+ var/obj/item/weapon/twohanded/T = held_item
+ if(T.wielded == 1)
+ usr << "Your other hand is too busy holding the [T.name]."
+ return
+
+ hand = !hand
+ if(hud_used.l_hand_hud_object && hud_used.r_hand_hud_object)
+ if(hand)
+ hud_used.l_hand_hud_object.icon_state = "hand_l_active"
+ hud_used.r_hand_hud_object.icon_state = "hand_r_inactive"
+ else
+ hud_used.l_hand_hud_object.icon_state = "hand_l_inactive"
+ hud_used.r_hand_hud_object.icon_state = "hand_r_active"
+
+
+/mob/living/simple_animal/drone/unEquip(obj/item/I, force)
+ if(..(I,force))
+ update_inv_hands()
+ if(I == head)
+ head = null
+ update_inv_head()
+ if(I == internal_storage)
+ internal_storage = null
+ update_inv_internal_storage()
+ return 1
+ return 0
+
+
+/mob/living/simple_animal/drone/can_equip(obj/item/I, slot)
+ switch(slot)
+ if(slot_head)
+ if(head)
+ return 0
+ if(!((I.slot_flags & SLOT_HEAD) || (I.slot_flags & SLOT_MASK)))
+ return 0
+ return 1
+ if("drone_storage_slot")
+ if(internal_storage)
+ return 0
+ return 1
+ ..()
+
+
+/mob/living/simple_animal/drone/get_item_by_slot(slot_id)
+ switch(slot_id)
+ if(slot_head)
+ return head
+ if("drone_storage_slot")
+ return internal_storage
+ ..()
+
+
+/mob/living/simple_animal/drone/equip_to_slot(obj/item/I, slot)
+ if(!slot) return
+ if(!istype(I)) return
+
+ if(I == l_hand)
+ l_hand = null
+ else if(I == r_hand)
+ r_hand = null
+ update_inv_hands()
+
+ I.screen_loc = null // will get moved if inventory is visible
+ I.loc = src
+ I.equipped(src, slot)
+ I.layer = 20
+
+ switch(slot)
+ if(slot_head)
+ head = I
+ update_inv_head()
+ if("drone_storage_slot")
+ internal_storage = I
+ update_inv_internal_storage()
+ else
+ src << "You are trying to equip this item to an unsupported inventory slot. Report this to a coder!"
+ return
+
+
+/mob/living/simple_animal/drone/stripPanelUnequip(obj/item/what, mob/who, where)
+ ..(what, who, where, 1)
+
+
+/mob/living/simple_animal/drone/stripPanelEquip(obj/item/what, mob/who, where)
+ ..(what, who, where, 1)
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/say.dm b/code/modules/mob/living/simple_animal/friendly/drone/say.dm
new file mode 100644
index 00000000000..15dc76f9e16
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/drone/say.dm
@@ -0,0 +1,46 @@
+
+/////////////
+//DRONE SAY//
+/////////////
+//Drone speach
+//Drone hearing
+
+
+/mob/living/simple_animal/drone/say(var/message)
+ return ..(message, "R")
+
+
+/mob/living/simple_animal/drone/lang_treat(atom/movable/speaker, message_langs, raw_message) //This is so drones can understand humans without being able to speak human
+ . = ..()
+ var/hear_override_langs = HUMAN
+ if(message_langs & hear_override_langs)
+ return ..(speaker, languages, raw_message)
+
+
+/mob/living/simple_animal/drone/handle_inherent_channels(message, message_mode)
+ if(message_mode == MODE_BINARY)
+ drone_chat(message)
+ return ITALICS | REDUCE_RANGE
+ else
+ ..()
+
+
+/mob/living/simple_animal/drone/proc/alert_drones(msg, dead_can_hear = 0)
+ for(var/mob/M in player_list)
+ var/send_msg = 0
+
+ if(istype(M, /mob/living/simple_animal/drone) && M.stat != DEAD)
+ for(var/F in src.faction)
+ if(F in M.faction)
+ send_msg = 1
+ break
+ else if(dead_can_hear && (M in dead_mob_list))
+ send_msg = 1
+
+ if(send_msg)
+ M << msg
+
+
+/mob/living/simple_animal/drone/proc/drone_chat(msg)
+ var/rendered = "DRONE CHAT: [name]: [msg]"
+ alert_drones(rendered, 1)
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/verbs.dm b/code/modules/mob/living/simple_animal/friendly/drone/verbs.dm
new file mode 100644
index 00000000000..cd5f65f9274
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/drone/verbs.dm
@@ -0,0 +1,53 @@
+
+///////////////
+//DRONE VERBS//
+///////////////
+//Drone verbs that appear in the Drone tab and on buttons
+
+
+/mob/living/simple_animal/drone/verb/check_laws()
+ set category = "Drone"
+ set name = "Check Laws"
+
+ src << "Drone Laws"
+ src << laws
+
+/mob/living/simple_animal/drone/verb/toggle_light()
+ set category = "Drone"
+ set name = "Toggle drone light"
+ if(light_on)
+ AddLuminosity(-4)
+ else
+ AddLuminosity(4)
+
+ light_on = !light_on
+
+ src << "Your light is now [light_on ? "on" : "off"]"
+
+/mob/living/simple_animal/drone/verb/drone_ping()
+ set category = "Drone"
+ set name = "Drone ping"
+
+ var/alert_s = input(src,"Alert severity level","Drone ping",null) as null|anything in list("Low","Medium","High","Critical")
+
+ var/area/A = get_area(loc)
+
+ if(alert_s && A && stat != DEAD)
+ var/msg = "DRONE PING: [name]: [alert_s] priority alert in [A.name]!"
+ alert_drones(msg)
+
+
+/mob/living/simple_animal/drone/verb/toggle_statics()
+ set name = "Change Vision Filter"
+ set desc = "Change the filter on the system used to remove non drone beings from your viewscreen."
+ set category = "Drone"
+
+ if(!seeStatic)
+ src << "You have no vision filter to change!"
+ return
+
+ var/selectedStatic = input("Select a vision filter", "Vision Filter") as null|anything in staticChoices
+ if(selectedStatic in staticChoices)
+ staticChoice = selectedStatic
+
+ updateSeeStaticMobs()
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/friendly/drone/visuals_icons.dm b/code/modules/mob/living/simple_animal/friendly/drone/visuals_icons.dm
new file mode 100644
index 00000000000..5496f5f853d
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/friendly/drone/visuals_icons.dm
@@ -0,0 +1,159 @@
+
+/////////////////
+//DRONE VISUALS//
+/////////////////
+//Drone overlays
+//Drone visuals
+
+
+/mob/living/simple_animal/drone/proc/apply_overlay(cache_index)
+ var/image/I = drone_overlays[cache_index]
+ if(I)
+ overlays += I
+
+
+/mob/living/simple_animal/drone/proc/remove_overlay(cache_index)
+ if(drone_overlays[cache_index])
+ overlays -= drone_overlays[cache_index]
+ drone_overlays[cache_index] = null
+
+
+/mob/living/simple_animal/drone/proc/update_inv_hands()
+ remove_overlay(DRONE_HANDS_LAYER)
+ var/list/hands_overlays = list()
+
+ var/y_shift = getItemPixelShiftY()
+
+ if(r_hand)
+ var/r_state = r_hand.item_state
+ if(!r_state)
+ r_state = r_hand.icon_state
+
+ var/image/r_hand_image = image("icon" = r_hand.righthand_file, "icon_state"="[r_state]", "layer"=-DRONE_HANDS_LAYER)
+ if(y_shift != 0)
+ r_hand_image.pixel_y = y_shift
+
+ hands_overlays += r_hand_image
+
+ if(client && hud_used)
+ r_hand.layer = 20
+ r_hand.screen_loc = ui_rhand
+ client.screen |= r_hand
+
+ if(l_hand)
+ var/l_state = l_hand.item_state
+ if(!l_state)
+ l_state = l_hand.icon_state
+
+ var/image/l_hand_image = image("icon" = l_hand.lefthand_file, "icon_state"="[l_state]", "layer"=-DRONE_HANDS_LAYER)
+ if(y_shift != 0)
+ l_hand_image.pixel_y = y_shift
+
+ hands_overlays += l_hand_image
+
+ if(client && hud_used)
+ l_hand.layer = 20
+ l_hand.screen_loc = ui_lhand
+ client.screen |= l_hand
+
+
+ if(hands_overlays.len)
+ drone_overlays[DRONE_HANDS_LAYER] = hands_overlays
+ apply_overlay(DRONE_HANDS_LAYER)
+
+
+/mob/living/simple_animal/drone/proc/update_inv_internal_storage()
+ if(internal_storage && client && hud_used)
+ internal_storage.screen_loc = ui_drone_storage
+ client.screen += internal_storage
+
+
+/mob/living/simple_animal/drone/update_inv_head()
+ remove_overlay(DRONE_HEAD_LAYER)
+
+ if(head)
+ if(client && hud_used)
+ head.screen_loc = ui_drone_head
+ client.screen += head
+
+
+ var/image/head_overlay = image("icon"='icons/mob/head.dmi', "icon_state"="[head.icon_state]", "layer"=-DRONE_HEAD_LAYER)
+ if(istype(head, /obj/item/clothing/mask))
+ head_overlay.icon = 'icons/mob/mask.dmi'
+ head_overlay.color = head.color
+ head_overlay.alpha = head.alpha
+ head_overlay.pixel_y = -15
+
+ drone_overlays[DRONE_HEAD_LAYER] = head_overlay
+
+ apply_overlay(DRONE_HEAD_LAYER)
+
+
+//These procs serve as redirection so that the drone updates as expected when other things call these procs
+/mob/living/simple_animal/drone/update_inv_l_hand()
+ update_inv_hands()
+
+
+/mob/living/simple_animal/drone/update_inv_r_hand()
+ update_inv_hands()
+
+
+/mob/living/simple_animal/drone/update_inv_wear_mask()
+ update_inv_head()
+
+
+/mob/living/simple_animal/drone/proc/pickVisualAppearence()
+ picked = FALSE
+ var/appearence = input("Choose your appearence!", "Appearence", "Maintenance Drone") in list("Maintenance Drone", "Repair Drone")
+ switch(appearence)
+ if("Maintenance Drone")
+ visualAppearence = MAINTDRONE
+ var/colour = input("Choose your colour!", "Colour", "grey") in list("grey", "blue", "red", "green", "pink", "orange")
+ icon_state = "[visualAppearence]_[colour]"
+ icon_living = "[visualAppearence]_[colour]"
+ icon_dead = "[visualAppearence]_dead"
+
+ if("Repair Drone")
+ visualAppearence = REPAIRDRONE
+ icon_state = visualAppearence
+ icon_living = visualAppearence
+ icon_dead = "[visualAppearence]_dead"
+
+ else
+ return
+
+ picked = TRUE
+
+
+
+/mob/living/simple_animal/drone/proc/getItemPixelShiftY()
+ switch(visualAppearence)
+ if(MAINTDRONE)
+ . = 0
+ if(REPAIRDRONE)
+ . = -6
+
+/mob/living/simple_animal/drone/proc/updateSeeStaticMobs()
+ if(!client)
+ return
+
+ for(var/i in staticOverlays)
+ client.images.Remove(i)
+ staticOverlays.Remove(i)
+ staticOverlays.len = 0
+
+ if(seeStatic)
+ for(var/mob/living/L in mob_list)
+ if(isdrone(L))
+ continue
+ var/image/chosen
+ if(staticChoice in L.staticOverlays)
+ chosen = L.staticOverlays[staticChoice]
+ else
+ chosen = L.staticOverlays["static"]
+ staticOverlays |= chosen
+ client.images |= chosen
+
+
+/mob/living/simple_animal/drone/generateStaticOverlay()
+ return
\ No newline at end of file
diff --git a/html/changelogs/RemieRichards-PR-7844.yml b/html/changelogs/RemieRichards-PR-7844.yml
new file mode 100644
index 00000000000..6f275409bc7
--- /dev/null
+++ b/html/changelogs/RemieRichards-PR-7844.yml
@@ -0,0 +1,12 @@
+author: RemieRichards
+
+delete-after: True
+
+ - rscadd: "Drones now see either Static, Black icons or Roguelike style lettering instead of actual mob icons"
+ - rscadd: "Icons for the above, from VG's SkowronX"
+ - rscadd: "Many icon procs for the above, from VG's ComicIronic"
+ - rscadd: "Drones moved into their own folder"
+ - tweak: "Drone.dm replaced with seperate files for each functionality"
+ - rscadd: "Drones now have 2 skins to choose from, Maintenance Drone (Current drones) and Repair Drone (A modified VG/Bay sprite)"
+ - rscadd: "Much more support for alternate skins on drones"
+
\ No newline at end of file
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index f1a60e810d8..14a4b7f8fef 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/mob/drone.dmi b/icons/mob/drone.dmi
index 0a8a3f70a4a..003750d8d89 100644
Binary files a/icons/mob/drone.dmi and b/icons/mob/drone.dmi differ
diff --git a/icons/mob/head.dmi b/icons/mob/head.dmi
index 65884b15b23..08f10c0d0bb 100644
Binary files a/icons/mob/head.dmi and b/icons/mob/head.dmi differ
diff --git a/tgstation.dme b/tgstation.dme
index 08724346457..38a63b2e57d 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -1180,13 +1180,20 @@
#include "code\modules\mob\living\simple_animal\friendly\cat.dm"
#include "code\modules\mob\living\simple_animal\friendly\corgi.dm"
#include "code\modules\mob\living\simple_animal\friendly\crab.dm"
-#include "code\modules\mob\living\simple_animal\friendly\drone.dm"
#include "code\modules\mob\living\simple_animal\friendly\farm_animals.dm"
#include "code\modules\mob\living\simple_animal\friendly\fox.dm"
#include "code\modules\mob\living\simple_animal\friendly\lizard.dm"
#include "code\modules\mob\living\simple_animal\friendly\mouse.dm"
#include "code\modules\mob\living\simple_animal\friendly\pug.dm"
#include "code\modules\mob\living\simple_animal\friendly\slime.dm"
+#include "code\modules\mob\living\simple_animal\friendly\drone\_drone.dm"
+#include "code\modules\mob\living\simple_animal\friendly\drone\drones_as_items.dm"
+#include "code\modules\mob\living\simple_animal\friendly\drone\extra_drone_types.dm"
+#include "code\modules\mob\living\simple_animal\friendly\drone\interaction.dm"
+#include "code\modules\mob\living\simple_animal\friendly\drone\inventory.dm"
+#include "code\modules\mob\living\simple_animal\friendly\drone\say.dm"
+#include "code\modules\mob\living\simple_animal\friendly\drone\verbs.dm"
+#include "code\modules\mob\living\simple_animal\friendly\drone\visuals_icons.dm"
#include "code\modules\mob\living\simple_animal\hostile\alien.dm"
#include "code\modules\mob\living\simple_animal\hostile\bear.dm"
#include "code\modules\mob\living\simple_animal\hostile\bees.dm"