diff --git a/aurorastation.dme b/aurorastation.dme
index 213fb2d53a9..3de0e2d6bba 100644
--- a/aurorastation.dme
+++ b/aurorastation.dme
@@ -2110,6 +2110,7 @@
#include "code\modules\mob\living\simple_animal\hostile\giant_spider.dm"
#include "code\modules\mob\living\simple_animal\hostile\hivebot.dm"
#include "code\modules\mob\living\simple_animal\hostile\hostile.dm"
+#include "code\modules\mob\living\simple_animal\hostile\icarus_drone.dm"
#include "code\modules\mob\living\simple_animal\hostile\krampus.dm"
#include "code\modules\mob\living\simple_animal\hostile\mimic.dm"
#include "code\modules\mob\living\simple_animal\hostile\moghesfauna.dm"
@@ -2129,7 +2130,6 @@
#include "code\modules\mob\living\simple_animal\hostile\commanded\ives.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\cavern.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\clown.dm"
-#include "code\modules\mob\living\simple_animal\hostile\retaliate\drone.dm"
#include "code\modules\mob\living\simple_animal\hostile\retaliate\retaliate.dm"
#include "code\modules\modular_computers\laptop_vendor.dm"
#include "code\modules\modular_computers\computers\modular_computer\core.dm"
diff --git a/code/_helpers/areas.dm b/code/_helpers/areas.dm
index fb6a515d7fa..20a0648ba06 100644
--- a/code/_helpers/areas.dm
+++ b/code/_helpers/areas.dm
@@ -81,7 +81,9 @@
. = (A.z in z_levels)
/proc/is_station_area(var/area/A)
- . = isStationLevel(A.z)
+ if(A.station_area)
+ return TRUE
+ return FALSE
/proc/is_contact_area(var/area/A)
. = isContactLevel(A.z)
diff --git a/code/controllers/subsystems/mob.dm b/code/controllers/subsystems/mob.dm
index 9f4c44b6600..82eed7035c4 100644
--- a/code/controllers/subsystems/mob.dm
+++ b/code/controllers/subsystems/mob.dm
@@ -46,7 +46,7 @@
/datum/controller/subsystem/mobs/Initialize()
// Some setup work for the eat-types lists.
mtl_synthetic = typecacheof(mtl_synthetic) + list(
- /mob/living/simple_animal/hostile/retaliate/malf_drone = TRUE,
+ /mob/living/simple_animal/hostile/icarus_drone = TRUE,
/mob/living/simple_animal/hostile/viscerator = TRUE,
/mob/living/simple_animal/spiderbot = TRUE
)
diff --git a/code/controllers/subsystems/traumas.dm b/code/controllers/subsystems/traumas.dm
index 164006442d8..bd3743930b7 100644
--- a/code/controllers/subsystems/traumas.dm
+++ b/code/controllers/subsystems/traumas.dm
@@ -41,7 +41,7 @@ var/datum/controller/subsystem/traumas/SStraumas
"security" = typecacheof(list(/mob/living/bot/secbot)),
"lizards" = typecacheof(list(/mob/living/simple_animal/lizard)),
"cats" = typecacheof(list(/mob/living/simple_animal/cat, /mob/living/simple_animal/familiar/pet/cat, /mob/living/simple_animal/cat/kitten)),
- "robots" = typecacheof(list(/mob/living/silicon,/mob/living/bot, /mob/living/simple_animal/hostile/retaliate/malf_drone)),
+ "robots" = typecacheof(list(/mob/living/silicon,/mob/living/bot, /mob/living/simple_animal/hostile/icarus_drone)),
"pests" = typecacheof(list(/mob/living/simple_animal/hostile/carp, /mob/living/simple_animal/rat, /mob/living/carbon/alien/diona)),
"paranormals" = typecacheof(list(/mob/living/simple_animal/hostile/scarybat, /mob/living/simple_animal/hostile/true_changeling,
/mob/living/simple_animal/hostile/mimic, /mob/living/simple_animal/hostile/faithless,
diff --git a/code/datums/outfits/ert/deathsquad.dm b/code/datums/outfits/ert/deathsquad.dm
index e01bcc220a2..01d0c44a1c5 100644
--- a/code/datums/outfits/ert/deathsquad.dm
+++ b/code/datums/outfits/ert/deathsquad.dm
@@ -29,6 +29,7 @@
name = "Asset Protection Lead"
l_pocket = /obj/item/pinpointer
+ r_hand = /obj/item/device/orbital_dropper/icarus_drones
/datum/outfit/admin/deathsquad/get_id_access()
return get_all_accesses()
diff --git a/code/datums/outfits/event/outfit_nanotrasen.dm b/code/datums/outfits/event/outfit_nanotrasen.dm
index a47f0158d75..6719b02e691 100644
--- a/code/datums/outfits/event/outfit_nanotrasen.dm
+++ b/code/datums/outfits/event/outfit_nanotrasen.dm
@@ -25,7 +25,7 @@
uniform = /obj/item/clothing/under/rank/centcom_officer
l_ear = /obj/item/device/radio/headset/heads/captain
head = /obj/item/clothing/head/beret/centcom/officer
-
+ l_pocket = /obj/item/device/orbital_dropper/icarus_drones
/datum/outfit/admin/nt/captain
name = "NanoTrasen Navy Captain"
@@ -33,7 +33,7 @@
uniform = /obj/item/clothing/under/rank/centcom_captain
l_ear = /obj/item/device/radio/headset/heads/captain
head = /obj/item/clothing/head/beret/centcom/captain
-
+ l_pocket = /obj/item/device/orbital_dropper/icarus_drones
/datum/outfit/admin/nt/protection_detail
name = "ERT Protection Detail"
@@ -93,7 +93,8 @@
backpack_contents = list(
/obj/item/storage/box/fancy/cigarettes/cigar = 1,
- /obj/item/flame/lighter/zippo = 1
+ /obj/item/flame/lighter/zippo = 1,
+ /obj/item/device/orbital_dropper/icarus_drones = 1
)
implants = list(
diff --git a/code/game/jobs/access.dm b/code/game/jobs/access.dm
index ddd511b5673..1a55e838f19 100644
--- a/code/game/jobs/access.dm
+++ b/code/game/jobs/access.dm
@@ -239,9 +239,9 @@ var/obj/item/card/id/all_access/ghost_all_access
/mob/living/simple_animal/spiderbot/GetIdCard()
return internal_id
-/mob/living/carbon/human/GetIdCard()
+/mob/living/carbon/human/GetIdCard(var/ignore_hand = FALSE)
var/obj/item/I = get_active_hand()
- if(I)
+ if(I && !ignore_hand)
var/id = I.GetID()
if(id)
return id
diff --git a/code/game/objects/items/devices/drop_targeter/_droptargeter.dm b/code/game/objects/items/devices/drop_targeter/_droptargeter.dm
index fc7b57a073e..82b17b9a612 100644
--- a/code/game/objects/items/devices/drop_targeter/_droptargeter.dm
+++ b/code/game/objects/items/devices/drop_targeter/_droptargeter.dm
@@ -14,6 +14,7 @@
var/paint_distance = 14 // From how far away can you paint a target?
+ var/does_explosion = TRUE
var/emagged = FALSE // If emagged, things can be dropped in on station areas
var/drop_message = "Orbital package inbound, clear the targetted area immediately!"
@@ -77,7 +78,8 @@
announcer.autosay(drop_message_emagged, announcer_name, "Common")
has_dropped++
- addtimer(CALLBACK(GLOBAL_PROC, /proc/explosion, targloc, 1, 2, 4, 6), 100) //YEEHAW
+ if(does_explosion)
+ addtimer(CALLBACK(GLOBAL_PROC, /proc/explosion, targloc, 1, 2, 4, 6), 100) //YEEHAW
addtimer(CALLBACK(src, .proc/orbital_drop, targloc, user), 105)
flick_overlay(I, showto, 20) //2 seconds of the red dot appearing
@@ -86,7 +88,7 @@
/obj/item/device/orbital_dropper/proc/orbital_drop(var/turf/target, var/user)
if(!map)
return
- log_and_message_admins("[key_name_admin(src)] has used a [src] at JMP.")
+ log_and_message_admins("[key_name_admin(user)] has used a [src] at JMP.")
map.load(target, TRUE) //Target must be the center!
diff --git a/code/game/objects/items/devices/drop_targeter/orbital_drops.dm b/code/game/objects/items/devices/drop_targeter/orbital_drops.dm
index 5bc9b9f0d30..e8af8cd020c 100644
--- a/code/game/objects/items/devices/drop_targeter/orbital_drops.dm
+++ b/code/game/objects/items/devices/drop_targeter/orbital_drops.dm
@@ -46,3 +46,23 @@
desc_antag = "This is a stealthy variant of the standard armory orbital drop. It will not report itself dropping on common, unless emagged."
announcer_name = "Syndicate Autodrone"
announcer_channel = "Mercenary"
+
+/obj/item/device/orbital_dropper/icarus_drones
+ name = "icarus painter"
+ desc = "A device used to paint a target, which will then promptly orbitally drop the requested items. This one has been modified to call in Icarus Drones."
+
+ var/num_of_drones = 2
+ drop_amount = 1
+ does_explosion = FALSE
+
+ emagged = TRUE // to let people drop it in the station
+
+ drop_message_emagged = "NanoTrasen combat drones coming your way! Happy hunting!"
+ announcer_name = "NDV Icarus"
+
+ map = null
+
+/obj/item/device/orbital_dropper/icarus_drones/orbital_drop(var/turf/target, var/user)
+ log_and_message_admins("[key_name_admin(user)] has used a [src] at JMP.")
+ for(var/i = 1, i <= num_of_drones, i++)
+ new /mob/living/simple_animal/hostile/icarus_drone(get_random_turf_in_range(target, 4, 2, TRUE))
\ No newline at end of file
diff --git a/code/modules/events/carp_migration.dm b/code/modules/events/carp_migration.dm
index 95c10e679dc..76aaaf6ebe5 100644
--- a/code/modules/events/carp_migration.dm
+++ b/code/modules/events/carp_migration.dm
@@ -15,29 +15,30 @@
/datum/event/carp_migration/setup()
announceWhen = rand(40, 60)
- endWhen = rand(600,1200)
+ endWhen = rand(600, 1200)
despawn_turfs = typecacheof(despawn_turfs)
/datum/event/carp_migration/announce()
var/announcement = ""
+ var/soundfile = 'sound/AI/spacecarp.ogg'
if(severity == EVENT_LEVEL_MAJOR)
- announcement = "Massive migration of unknown biological entities has been detected near [station_name()], please stand-by."
- command_announcement.Announce(announcement, "Lifesign Alert", new_sound = 'sound/AI/massivespacecarp.ogg')
+ announcement = "Massive migration of unknown biological entities has been detected near [station_name()], please stand-by. The NDV Icarus has dispatched combat drones to assist."
+ soundfile = 'sound/AI/massivespacecarp.ogg'
else
- announcement = "Unknown biological [spawned_carp.len == 1 ? "entity has" : "entities have"] been detected near [station_name()], please stand-by."
- command_announcement.Announce(announcement, "Lifesign Alert", new_sound = 'sound/AI/spacecarp.ogg')
+ announcement = "Unknown biological [length(spawned_carp) == 1 ? "entity has" : "entities have"] been detected near [station_name()], please stand-by.[severity == EVENT_LEVEL_MODERATE ? " The NDV Icarus has dispatched combat drones to assist." : ""]"
+ command_announcement.Announce(announcement, "Lifesign Alert", new_sound = soundfile)
/datum/event/carp_migration/start()
if(severity == EVENT_LEVEL_MAJOR)
- spawn_fish(landmarks_list.len)
- spawn_caverndweller(landmarks_list.len)
+ spawn_fish(length(landmarks_list), spawn_drones = TRUE)
+ spawn_caverndweller(length(landmarks_list), spawn_drones = TRUE)
else if(severity == EVENT_LEVEL_MODERATE)
- spawn_fish(rand(4, 6)) //12 to 30 carp, in small groups
- spawn_caverndweller(rand(1, 2)) //less of those, also don't happen in the regular event
+ spawn_fish(rand(4, 6), spawn_drones = TRUE) //12 to 30 carp, in small groups
+ spawn_caverndweller(rand(1, 2), spawn_drones = TRUE) //less of those, also don't happen in the regular event
else
spawn_fish(rand(1, 3), 1, 2) //1 to 6 carp, alone or in pairs
-/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min=3, var/group_size_max=5)
+/datum/event/carp_migration/proc/spawn_fish(var/num_groups, var/group_size_min = 3, var/group_size_max = 5, var/spawn_drones = FALSE)
set waitfor = FALSE
var/list/spawn_locations = list()
@@ -50,7 +51,11 @@
var/i = 1
while (i <= num_groups)
var/group_size = rand(group_size_min, group_size_max)
- for (var/j = 1, j <= group_size, j++)
+ if(spawn_drones && prob(25))
+ var/drone_num = rand(1, 2)
+ for(var/d = 1, d <= drone_num, d++)
+ new /mob/living/simple_animal/hostile/icarus_drone(get_random_turf_in_range(spawn_locations[i], 10, 6, TRUE))
+ for(var/j = 1, j <= group_size, j++)
if(prob(95))
var/mob/living/simple_animal/hostile/carp/carp = new(spawn_locations[i])
spawned_carp += WEAKREF(carp)
@@ -63,7 +68,7 @@
CHECK_TICK
i++
-/datum/event/carp_migration/proc/spawn_caverndweller(var/num_groups, var/group_size_min=2, var/group_size_max=3)
+/datum/event/carp_migration/proc/spawn_caverndweller(var/num_groups, var/group_size_min=2, var/group_size_max=3, var/spawn_drones = FALSE)
set waitfor = FALSE
var/list/spawn_locations = list()
@@ -76,6 +81,10 @@
var/i = 1
while (i <= num_groups)
var/group_size = rand(group_size_min, group_size_max)
+ if(spawn_drones && prob(25))
+ var/drone_num = rand(1, 2)
+ for(var/d = 1, d <= drone_num, d++)
+ new /mob/living/simple_animal/hostile/icarus_drone(get_random_turf_in_range(spawn_locations[i], 10, 6, TRUE))
for (var/j in 1 to group_size)
new /mob/living/simple_animal/hostile/retaliate/cavern_dweller(spawn_locations[i])
CHECK_TICK
diff --git a/code/modules/events/false_alarm.dm b/code/modules/events/false_alarm.dm
index d0951482d82..94e049e808b 100644
--- a/code/modules/events/false_alarm.dm
+++ b/code/modules/events/false_alarm.dm
@@ -47,7 +47,4 @@
E.start()
E.kill()
- E.announce()
-
-
-
+ E.announce()
\ No newline at end of file
diff --git a/code/modules/events/rogue_drones.dm b/code/modules/events/rogue_drones.dm
index 5ba18a589d1..16f4afc9d27 100644
--- a/code/modules/events/rogue_drones.dm
+++ b/code/modules/events/rogue_drones.dm
@@ -12,7 +12,7 @@
var/num = rand(2,10)
for(var/i=0, i drones_list.len * 0.75)
+ if(num_recovered > length(drones_list) * 0.75)
command_announcement.Announce("Icarus drone control reports the malfunctioning wing has been recovered safely.", "Rogue drone alert")
else
command_announcement.Announce("Icarus drone control registers disappointment at the loss of the drones, but the survivors have been recovered.", "Rogue drone alert")
diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/bear_companion.dm b/code/modules/mob/living/simple_animal/hostile/commanded/bear_companion.dm
index 03518651095..50a3706b1eb 100644
--- a/code/modules/mob/living/simple_animal/hostile/commanded/bear_companion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/commanded/bear_companion.dm
@@ -10,7 +10,8 @@
health = 100
maxHealth = 100
- density = 1
+ density = TRUE
+ belongs_to_station = FALSE
attacktext = "swatted"
melee_damage_lower = 25
diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm b/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm
index 14974579ae0..93a7e069b36 100644
--- a/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm
+++ b/code/modules/mob/living/simple_animal/hostile/commanded/commanded.dm
@@ -3,7 +3,8 @@
stance = COMMANDED_STOP
melee_damage_lower = 0
melee_damage_upper = 0
- density = 0
+ density = FALSE
+ belongs_to_station = TRUE
var/short_name = null
var/list/command_buffer = list()
var/list/known_commands = list("stay", "stop", "attack", "follow")
diff --git a/code/modules/mob/living/simple_animal/hostile/commanded/ives.dm b/code/modules/mob/living/simple_animal/hostile/commanded/ives.dm
index 119822afea2..b412bbebb62 100644
--- a/code/modules/mob/living/simple_animal/hostile/commanded/ives.dm
+++ b/code/modules/mob/living/simple_animal/hostile/commanded/ives.dm
@@ -10,6 +10,7 @@
health = 70
maxHealth = 70
+ belongs_to_station = TRUE
stop_automated_movement_when_pulled = TRUE
density = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index ab2e6725dd5..92399a1abdf 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -2,6 +2,7 @@
faction = "hostile"
var/stance = HOSTILE_STANCE_IDLE //Used to determine behavior
var/mob/living/target_mob
+ var/belongs_to_station = FALSE
var/attack_same = 0
var/ranged = 0
var/rapid = 0
diff --git a/code/modules/mob/living/simple_animal/hostile/icarus_drone.dm b/code/modules/mob/living/simple_animal/hostile/icarus_drone.dm
new file mode 100644
index 00000000000..23a965bb31c
--- /dev/null
+++ b/code/modules/mob/living/simple_animal/hostile/icarus_drone.dm
@@ -0,0 +1,358 @@
+/mob/living/simple_animal/hostile/icarus_drone
+ name = "combat drone"
+ desc = "An automated combat drone armed with state of the art weaponry and shielding. This one has the markings of the NDV Icarus on the side."
+ desc_fluff = "Produced by NanoTrasen, these combat drones are often carried and deployed by NDV Drone Carriers to protect local assets."
+ icon_state = "drone3"
+ icon_living = "drone3"
+ icon_dead = "drone_dead"
+ ranged = TRUE
+ rapid = TRUE
+ speak_chance = 5
+ turns_per_move = 3
+ response_help = "pokes"
+ response_disarm = "gently pushes aside"
+ response_harm = "hits"
+ speak = list("ALERT, Icarus Drone active and patrolling!", "Hostile entities in the area!", "Threat parameters nominal.", "Subsystems stable at combat level theta.")
+ emote_see = list("beeps menacingly", "whirrs threateningly", "scans its immediate vicinity")
+ attack_emote = "shifts its targetting vanes towards"
+ a_intent = I_HURT
+ stop_automated_movement_when_pulled = FALSE
+ health = 300
+ maxHealth = 300
+ speed = 8
+ projectiletype = /obj/item/projectile/beam/drone
+ projectilesound = 'sound/weapons/laser3.ogg'
+ destroy_surroundings = 0
+ var/datum/effect_system/ion_trail/ion_trail
+ belongs_to_station = TRUE
+
+ //the drone randomly switches between these states when it's malfunctioning
+ var/hostile_drone = FALSE
+ // FALSE - retaliate, only attack enemies that attack it
+ // TRUE - hostile, attack everything that comes near
+
+ var/turf/patrol_target
+ var/explode_chance = 1
+ var/malfunctioning = FALSE
+ var/disabled = FALSE
+ var/exploding = FALSE
+
+ //Drones aren't affected by atmos.
+ min_oxy = 0
+ max_oxy = 0
+ min_tox = 0
+ max_tox = 0
+ min_co2 = 0
+ max_co2 = 0
+ min_n2 = 0
+ max_n2 = 0
+ minbodytemp = 0
+
+ var/has_loot = TRUE
+ faction = "neutral"
+
+ tameable = FALSE
+
+ flying = TRUE
+
+/mob/living/simple_animal/hostile/icarus_drone/Initialize()
+ . = ..()
+ if(prob(5))
+ projectiletype = /obj/item/projectile/beam/pulse/drone
+ projectilesound = 'sound/weapons/pulse2.ogg'
+ ion_trail = new(src)
+ ion_trail.start()
+ if(!malfunctioning)
+ addtimer(CALLBACK(src, .proc/beam_out), 15 MINUTES)
+
+ // warp in effect
+ var/matrix/M = matrix()
+ M.Scale(0.1, 0.1)
+ animate(src, transform = M, time = 0.1, easing = LINEAR_EASING)
+
+ spark(src, 3, alldirs)
+
+ var/matrix/N = matrix()
+ N.Scale(1, 1)
+ animate(src, transform = N, time = 2, easing = SINE_EASING)
+
+/mob/living/simple_animal/hostile/icarus_drone/proc/beam_out()
+ if(malfunctioning) // if we somehow start malfunctioning after init, don't beam out
+ return
+ visible_message(SPAN_WARNING("\The [src] warps into nothingness!"))
+
+ spark(src, 3, alldirs)
+
+ var/matrix/M = matrix()
+ M.Scale(0.1, 0.1)
+ animate(src, transform = M, time = 2, easing = LINEAR_EASING)
+
+ has_loot = FALSE
+ qdel(src)
+
+/mob/living/simple_animal/hostile/icarus_drone/examine(mob/user)
+ ..()
+ if(malfunctioning)
+ if(hostile_drone)
+ to_chat(user, SPAN_WARNING("It's completely lit up, and its targetting vanes are deployed."))
+ else
+ to_chat(user, SPAN_WARNING("Most of its lights are off, and its targetting vanes are retracted."))
+
+/mob/living/simple_animal/hostile/icarus_drone/do_animate_chat(var/message, var/datum/language/language, var/small, var/list/show_to, var/duration, var/list/message_override)
+ INVOKE_ASYNC(src, /atom/movable/proc/animate_chat, message, language, small, show_to, duration)
+
+/mob/living/simple_animal/hostile/icarus_drone/Allow_Spacemove(var/check_drift = 0)
+ return TRUE
+
+/mob/living/simple_animal/hostile/icarus_drone/validator_living(var/mob/living/L, var/atom/current)
+ if(malfunctioning)
+ return ..()
+ if(L.stat == DEAD)
+ return FALSE
+ if(ishuman(L))
+ var/mob/living/carbon/human/H = L
+ var/obj/item/card/id/ID = H.GetIdCard(TRUE)
+ if(!ID) // can't identify, too risky, don't shoot
+ return FALSE
+ if(!H.handcuffed) // don't shoot cuffed people
+ var/datum/record/general/R = SSrecords.find_record("name", ID.registered_name)
+ if(istype(R) && istype(R.security) && R.security.criminal == "*Arrest*") // blast free criminals
+ return TRUE
+ return FALSE // generally, don't shoot humans
+ if(istype(L, /mob/living/simple_animal/hostile))
+ var/mob/living/simple_animal/hostile/SA = L
+ if(SA.belongs_to_station)
+ return FALSE
+ return TRUE
+ return FALSE
+
+/mob/living/simple_animal/hostile/icarus_drone/validator_bot(var/obj/machinery/bot/B, var/atom/current)
+ if(malfunctioning)
+ return ..()
+ return FALSE
+
+/mob/living/simple_animal/hostile/icarus_drone/validator_turret(var/obj/machinery/porta_turret/T, var/atom/current)
+ if(malfunctioning)
+ return ..()
+ if(is_station_area(get_area(T))) // any turrets not on station turf is probably hostile
+ return FALSE
+ return TRUE
+
+/mob/living/simple_animal/hostile/icarus_drone/validator_e_field(var/obj/effect/energy_field/E, var/atom/current)
+ if(malfunctioning)
+ return ..()
+ return FALSE
+
+/mob/living/simple_animal/hostile/icarus_drone/isSynthetic()
+ return TRUE
+
+//self repair systems have a chance to bring the drone back to life
+/mob/living/simple_animal/hostile/icarus_drone/Life()
+ //emps and lots of damage can temporarily shut us down
+ if(disabled > 0)
+ stat = UNCONSCIOUS
+ icon_state = "drone_dead"
+ disabled--
+ wander = FALSE
+ speak_chance = 0
+ else
+ stat = CONSCIOUS
+ icon_state = "drone0"
+ wander = TRUE
+ speak_chance = 5
+
+ //repair a bit of damage
+ if(prob(1) && health < maxHealth)
+ visible_message(SPAN_NOTICE("\The [src] shudders and shakes as some of its damaged systems come back online."))
+ spark(src, 3, alldirs)
+ health += rand(25, 100)
+
+ //spark for no reason
+ if(malfunctioning && prob(5))
+ spark(src, 3, alldirs)
+
+ //sometimes our targetting sensors malfunction, and we attack anyone nearby
+ if(malfunctioning && prob(disabled ? 0 : 1))
+ if(hostile_drone)
+ src.visible_message(SPAN_NOTICE("\The [src] retracts several targetting vanes, and dulls its running lights."))
+ hostile_drone = FALSE
+ else
+ src.visible_message(SPAN_ALERT("\The [src] suddenly lights up, and additional targetting vanes slide into place."))
+ hostile_drone = TRUE
+
+ if(health / maxHealth > 0.9)
+ icon_state = "drone3"
+ explode_chance = 0
+ else if(health / maxHealth > 0.7)
+ icon_state = "drone2"
+ explode_chance = 0
+ else if(health / maxHealth > 0.5)
+ icon_state = "drone1"
+ explode_chance = 0.5
+ else if(health / maxHealth > 0.3)
+ icon_state = "drone0"
+ explode_chance = 5
+ else if(health > 0)
+ //if health gets too low, shut down
+ icon_state = "drone_dead"
+ exploding = FALSE
+ if(!disabled)
+ if(prob(50))
+ visible_message(SPAN_NOTICE("\The [src] suddenly shuts down!"))
+ else
+ visible_message(SPAN_NOTICE("\The [src] suddenly lies still and quiet."))
+ disabled = rand(150, 600)
+ walk(src, 0)
+
+ if(exploding && prob(20))
+ if(prob(50))
+ visible_message(SPAN_ALERT("\The [src] begins to spark and shake violently!"))
+ else
+ visible_message(SPAN_ALERT("\The [src] sparks and shakes like it's about to explode!"))
+ spark(src, 3, alldirs)
+
+ if(!exploding && !disabled && prob(explode_chance))
+ exploding = TRUE
+ stat = UNCONSCIOUS
+ wander = 1
+ walk(src, 0)
+ spawn(rand(50, 150))
+ if(!disabled && exploding)
+ explosion(get_turf(src), 0, 1, 4, 7)
+ ..()
+
+//ion rifle!
+/mob/living/simple_animal/hostile/icarus_drone/emp_act(severity)
+ health -= rand(3, 15) * (severity + 1)
+ disabled = rand(150, 600)
+ hostile_drone = FALSE
+ walk(src, 0)
+
+/mob/living/simple_animal/hostile/icarus_drone/death()
+ ..(null, "suddenly breaks apart.")
+ qdel(src)
+
+/mob/living/simple_animal/hostile/icarus_drone/Destroy()
+ QDEL_NULL(ion_trail)
+ //some random debris left behind
+ if(has_loot)
+ spark(src, 3, alldirs)
+ var/obj/O
+
+ //shards
+ O = new /obj/item/material/shard(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+ if(prob(75))
+ O = new /obj/item/material/shard(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+ if(prob(50))
+ O = new /obj/item/material/shard(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+ if(prob(25))
+ O = new /obj/item/material/shard(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+
+ //rods
+ O = new /obj/item/stack/rods(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+ if(prob(75))
+ O = new /obj/item/stack/rods(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+ if(prob(50))
+ O = new /obj/item/stack/rods(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+ if(prob(25))
+ O = new /obj/item/stack/rods(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+
+ //plasteel
+ O = new /obj/item/stack/material/plasteel(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+ if(prob(75))
+ O = new /obj/item/stack/material/plasteel(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+ if(prob(50))
+ O = new /obj/item/stack/material/plasteel(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+ if(prob(25))
+ O = new /obj/item/stack/material/plasteel(src.loc)
+ step_to(O, get_turf(pick(view(7, src))))
+
+ //also drop dummy circuit boards deconstructable for research (loot)
+ var/obj/item/circuitboard/C
+
+ //spawn 1-4 boards of a random type
+ var/spawnees = 0
+ var/num_boards = rand(1, 4)
+ var/list/options = list(1, 2, 4, 8, 16, 32, 64, 128, 256)
+ if(malfunctioning)
+ options += 512
+ for(var/i = 0, i < num_boards, i++)
+ var/chosen = pick(options)
+ options.Remove(options.Find(chosen))
+ spawnees |= chosen
+
+ if(spawnees & 1)
+ C = new(src.loc)
+ C.name = "drone CPU motherboard"
+ C.origin_tech = list(TECH_DATA = rand(3, 6))
+
+ if(spawnees & 2)
+ C = new(src.loc)
+ C.name = "drone neural interface"
+ C.origin_tech = list(TECH_BIO = rand(3,6))
+
+ if(spawnees & 4)
+ C = new(src.loc)
+ C.name = "drone suspension processor"
+ C.origin_tech = list(TECH_MAGNET = rand(3,6))
+
+ if(spawnees & 8)
+ C = new(src.loc)
+ C.name = "drone shielding controller"
+ C.origin_tech = list(TECH_BLUESPACE = rand(3,6))
+
+ if(spawnees & 16)
+ C = new(src.loc)
+ C.name = "drone power capacitor"
+ C.origin_tech = list(TECH_POWER = rand(3,6))
+
+ if(spawnees & 32)
+ C = new(src.loc)
+ C.name = "drone hull reinforcer"
+ C.origin_tech = list(TECH_MATERIAL = rand(3,6))
+
+ if(spawnees & 64)
+ C = new(src.loc)
+ C.name = "Drone auto-repair system"
+ C.origin_tech = list(TECH_ENGINEERING = rand(3,6))
+
+ if(spawnees & 128)
+ C = new(src.loc)
+ C.name = "drone phoron overcharge counter"
+ C.origin_tech = list(TECH_PHORON = rand(3,6))
+
+ if(spawnees & 256)
+ C = new(src.loc)
+ C.name = "drone targetting circuitboard"
+ C.origin_tech = list(TECH_COMBAT = rand(3,6))
+
+ if(spawnees & 512)
+ C = new(src.loc)
+ C.name = "corrupted drone morality core"
+ C.origin_tech = list(TECH_ILLEGAL = rand(3,6))
+
+ return ..()
+
+/obj/item/projectile/beam/drone
+ damage = 15
+
+/obj/item/projectile/beam/pulse/drone
+ damage = 10
+
+/mob/living/simple_animal/hostile/icarus_drone/malf
+ speak = list("ALERT.", "Hostile-ile-ile entities dee-twhoooo-wected.", "Threat parameterszzzz- szzet.", "Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.")
+ emote_see = list("beeps menacingly", "whirrs threateningly", "scans its immediate vicinity")
+ belongs_to_station = FALSE
+ malfunctioning = TRUE
+ faction = "malf_drone"
\ No newline at end of file
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
deleted file mode 100644
index 484117b99af..00000000000
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
+++ /dev/null
@@ -1,283 +0,0 @@
-
-//malfunctioning combat drones
-/mob/living/simple_animal/hostile/retaliate/malf_drone
- name = "combat drone"
- desc = "An automated combat drone armed with state of the art weaponry and shielding."
- icon_state = "drone3"
- icon_living = "drone3"
- icon_dead = "drone_dead"
- ranged = 1
- rapid = 1
- speak_chance = 5
- turns_per_move = 3
- response_help = "pokes"
- response_disarm = "gently pushes aside"
- response_harm = "hits"
- speak = list("ALERT.","Hostile-ile-ile entities dee-twhoooo-wected.","Threat parameterszzzz- szzet.","Bring sub-sub-sub-systems uuuup to combat alert alpha-a-a.")
- emote_see = list("beeps menacingly","whirrs threateningly","scans its immediate vicinity")
- a_intent = I_HURT
- stop_automated_movement_when_pulled = 0
- health = 300
- maxHealth = 300
- speed = 8
- projectiletype = /obj/item/projectile/beam/drone
- projectilesound = 'sound/weapons/laser3.ogg'
- destroy_surroundings = 0
- var/datum/effect_system/ion_trail/ion_trail
-
- //the drone randomly switches between these states because it's malfunctioning
- var/hostile_drone = 0
- //0 - retaliate, only attack enemies that attack it
- //1 - hostile, attack everything that comes near
-
- var/turf/patrol_target
- var/explode_chance = 1
- var/disabled = 0
- var/exploding = 0
-
- //Drones aren't affected by atmos.
- min_oxy = 0
- max_oxy = 0
- min_tox = 0
- max_tox = 0
- min_co2 = 0
- max_co2 = 0
- min_n2 = 0
- max_n2 = 0
- minbodytemp = 0
-
- var/has_loot = 1
- faction = "malf_drone"
-
- tameable = FALSE
-
- flying = TRUE
- smart = TRUE
-
-/mob/living/simple_animal/hostile/retaliate/malf_drone/Initialize()
- . = ..()
- if(prob(5))
- projectiletype = /obj/item/projectile/beam/pulse/drone
- projectilesound = 'sound/weapons/pulse2.ogg'
- ion_trail = new(src)
- ion_trail.start()
-
-/mob/living/simple_animal/hostile/retaliate/malf_drone/do_animate_chat(var/message, var/datum/language/language, var/small, var/list/show_to, var/duration, var/list/message_override)
- INVOKE_ASYNC(src, /atom/movable/proc/animate_chat, message, language, small, show_to, duration)
-
-/mob/living/simple_animal/hostile/retaliate/malf_drone/Allow_Spacemove(var/check_drift = 0)
- return 1
-
-/mob/living/simple_animal/hostile/retaliate/malf_drone/ListTargets()
- if(hostile_drone)
- return view(src, 10)
- else
- return ..()
-
-/mob/living/simple_animal/hostile/retaliate/malf_drone/isSynthetic()
- return TRUE
-
-//self repair systems have a chance to bring the drone back to life
-/mob/living/simple_animal/hostile/retaliate/malf_drone/Life()
-
- //emps and lots of damage can temporarily shut us down
- if(disabled > 0)
- stat = UNCONSCIOUS
- icon_state = "drone_dead"
- disabled--
- wander = 0
- speak_chance = 0
- if(disabled <= 0)
- stat = CONSCIOUS
- icon_state = "drone0"
- wander = 1
- speak_chance = 5
-
- //repair a bit of damage
- if(prob(1))
- src.visible_message(SPAN_WARNING("\The [src] shudders and shakes as some of its damaged systems come back online."))
- spark(src, 3, alldirs)
- health += rand(25,100)
-
- //spark for no reason
- if(prob(5))
- spark(src, 3, alldirs)
-
- //sometimes our targetting sensors malfunction, and we attack anyone nearby
- if(prob(disabled ? 0 : 1))
- if(hostile_drone)
- src.visible_message(SPAN_NOTICE("\The [src] retracts several targetting vanes, and dulls its running lights."))
- hostile_drone = 0
- else
- src.visible_message(SPAN_WARNING("\The [src] suddenly lights up, and additional targetting vanes slide into place."))
- hostile_drone = 1
-
- if(health / maxHealth > 0.9)
- icon_state = "drone3"
- explode_chance = 0
- else if(health / maxHealth > 0.7)
- icon_state = "drone2"
- explode_chance = 0
- else if(health / maxHealth > 0.5)
- icon_state = "drone1"
- explode_chance = 0.5
- else if(health / maxHealth > 0.3)
- icon_state = "drone0"
- explode_chance = 5
- else if(health > 0)
- //if health gets too low, shut down
- icon_state = "drone_dead"
- exploding = 0
- if(!disabled)
- if(prob(50))
- src.visible_message(SPAN_NOTICE("\The [src] suddenly shuts down!"))
- else
- src.visible_message(SPAN_NOTICE("\The [src] suddenly lies still and quiet."))
- disabled = rand(150, 600)
- walk(src,0)
-
- if(exploding && prob(20))
- if(prob(50))
- src.visible_message(SPAN_WARNING("\The [src] begins to spark and shake violenty!"))
- else
- src.visible_message(SPAN_WARNING("\The [src] sparks and shakes like it's about to explode!"))
-
- spark(src, 3, alldirs)
-
- if(!exploding && !disabled && prob(explode_chance))
- exploding = 1
- stat = UNCONSCIOUS
- wander = 1
- walk(src,0)
- spawn(rand(50,150))
- if(!disabled && exploding)
- explosion(get_turf(src), 0, 1, 4, 7)
- //proc/explosion(turf/epicenter, devastation_range, heavy_impact_range, light_impact_range, flash_range, adminlog = 1)
- ..()
-
-//ion rifle!
-/mob/living/simple_animal/hostile/retaliate/malf_drone/emp_act(severity)
- health -= rand(3,15) * (severity + 1)
- disabled = rand(150, 600)
- hostile_drone = 0
- walk(src,0)
-
-/mob/living/simple_animal/hostile/retaliate/malf_drone/death()
- ..(null,"suddenly breaks apart.")
- qdel(src)
-
-/mob/living/simple_animal/hostile/retaliate/malf_drone/Destroy()
- QDEL_NULL(ion_trail)
- //some random debris left behind
- if(has_loot)
- spark(src, 3, alldirs)
- var/obj/O
-
- //shards
- O = new /obj/item/material/shard(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
- if(prob(75))
- O = new /obj/item/material/shard(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
- if(prob(50))
- O = new /obj/item/material/shard(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
- if(prob(25))
- O = new /obj/item/material/shard(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
-
- //rods
- O = new /obj/item/stack/rods(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
- if(prob(75))
- O = new /obj/item/stack/rods(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
- if(prob(50))
- O = new /obj/item/stack/rods(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
- if(prob(25))
- O = new /obj/item/stack/rods(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
-
- //plasteel
- O = new /obj/item/stack/material/plasteel(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
- if(prob(75))
- O = new /obj/item/stack/material/plasteel(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
- if(prob(50))
- O = new /obj/item/stack/material/plasteel(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
- if(prob(25))
- O = new /obj/item/stack/material/plasteel(src.loc)
- step_to(O, get_turf(pick(view(7, src))))
-
- //also drop dummy circuit boards deconstructable for research (loot)
- var/obj/item/circuitboard/C
-
- //spawn 1-4 boards of a random type
- var/spawnees = 0
- var/num_boards = rand(1,4)
- var/list/options = list(1,2,4,8,16,32,64,128,256,512)
- for(var/i=0, i