From d86dcf74f86988210700e487bf380302cef25738 Mon Sep 17 00:00:00 2001 From: Geeves Date: Tue, 6 Oct 2020 13:23:48 +0200 Subject: [PATCH] Better Icarus Drones (#10047) Improved Icarus combat drone code across the board, added a non-malfunctioning variant. The Icarus now sends combat drones to substantial space wildlife migrations. Added an Icarus drone target painter to some admin roles. It is an orbital drop device that will warp two Icarus drones at the targetted location. Added examine texts to malfunctioning combat drones that tells you what they're doing. --- aurorastation.dme | 2 +- code/_helpers/areas.dm | 4 +- code/controllers/subsystems/mob.dm | 2 +- code/controllers/subsystems/traumas.dm | 2 +- code/datums/outfits/ert/deathsquad.dm | 1 + .../datums/outfits/event/outfit_nanotrasen.dm | 7 +- code/game/jobs/access.dm | 4 +- .../devices/drop_targeter/_droptargeter.dm | 6 +- .../devices/drop_targeter/orbital_drops.dm | 20 + code/modules/events/carp_migration.dm | 33 +- code/modules/events/false_alarm.dm | 5 +- code/modules/events/rogue_drones.dm | 12 +- .../hostile/commanded/bear_companion.dm | 3 +- .../hostile/commanded/commanded.dm | 3 +- .../simple_animal/hostile/commanded/ives.dm | 1 + .../living/simple_animal/hostile/hostile.dm | 1 + .../simple_animal/hostile/icarus_drone.dm | 358 ++++++++++++++++++ .../simple_animal/hostile/retaliate/drone.dm | 283 -------------- .../geeves-better_icarus_drones.yml | 9 + 19 files changed, 437 insertions(+), 319 deletions(-) create mode 100644 code/modules/mob/living/simple_animal/hostile/icarus_drone.dm delete mode 100644 code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm create mode 100644 html/changelogs/geeves-better_icarus_drones.yml 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