Merge pull request #7919 from Atermonera/point_defense_targeting

Hybridizes Point Defense Turrets with Bay's System
This commit is contained in:
Schnayy
2021-02-26 09:04:43 -06:00
committed by GitHub
23 changed files with 494 additions and 116 deletions
+1
View File
@@ -164,3 +164,4 @@
#define COLOR_ASSEMBLY_HOT_PINK "#FF69B4"
#define COLOR_ASTEROID_ROCK "#735555"
#define COLOR_GOLD "#ffcc33"
+7
View File
@@ -99,6 +99,13 @@
//Area flags, possibly more to come
#define RAD_SHIELDED 1 //shielded from radiation, clearly
// OnTopic return values
#define TOPIC_NOACTION 0
#define TOPIC_HANDLED 1
#define TOPIC_REFRESH 2
#define TOPIC_UPDATE_PREVIEW 4
#define TOPIC_REFRESH_UPDATE_PREVIEW (TOPIC_REFRESH|TOPIC_UPDATE_PREVIEW)
// Convoluted setup so defines can be supplied by Bay12 main server compile script.
// Should still work fine for people jamming the icons into their repo.
#ifndef CUSTOM_ITEM_OBJ
+1
View File
@@ -1,4 +1,5 @@
GLOBAL_LIST_INIT(speech_toppings, list("|" = "i", "+" = "b", "_" = "u"))
GLOBAL_LIST_EMPTY(meteor_list)
/// List of wire colors for each object type of that round. One for airlocks, one for vendors, etc.
GLOBAL_LIST_EMPTY(wire_color_directory) // This is an associative list with the `holder_type` as the key, and a list of colors as the value.
+13 -7
View File
@@ -77,13 +77,19 @@
containertype = /obj/structure/closet/crate/focalpoint
containername = "advanced hull shield generator crate"
/datum/supply_pack/eng/pointdefense
name = "Point Defense Turret"
contains = list(/obj/machinery/porta_turret/pointdefense/orderable)
cost = 70
containertype = /obj/structure/closet/crate/large/secure/heph
containername = "point defense crate"
access = access_security
/datum/supply_pack/eng/point_defense_cannon_circuit
name = "Point Defense Turret Circuit"
contains = list(/obj/item/weapon/circuitboard/pointdefense = 2)
cost = 20
containertype = /obj/structure/closet/crate/heph
containername = "point defense turret circuit crate"
/datum/supply_pack/eng/point_defense_control_circuit
name = "Point Defense Controller Circuit"
contains = list(/obj/item/weapon/circuitboard/pointdefense_control = 1)
cost = 30
containertype = /obj/structure/closet/crate/heph
containername = "point defense mainframe circuit crate"
/datum/supply_pack/eng/electrical
name = "Electrical maintenance crate"
+18 -20
View File
@@ -109,13 +109,13 @@
desc = "You should probably run instead of gawking at this."
icon = 'icons/obj/meteor.dmi'
icon_state = "small"
density = 1
anchored = 1
density = TRUE
anchored = TRUE
var/hits = 4
var/hitpwr = 2 //Level of ex_act to be called on hit.
var/dest
pass_flags = PASSTABLE
var/heavy = 0
var/heavy = FALSE
var/z_original
var/meteordrop = /obj/item/weapon/ore/iron
@@ -126,10 +126,10 @@
// Multiply this and the hits var to get a rough idea of how penetrating a meteor is.
var/wall_power = 100
/obj/effect/meteor/New()
..()
/obj/effect/meteor/Initialize()
. = ..()
z_original = z
GLOB.meteor_list += src
/obj/effect/meteor/Move()
if(z != z_original || loc == dest)
@@ -147,7 +147,8 @@
get_hit()
/obj/effect/meteor/Destroy()
walk(src,0) //this cancels the walk_towards() proc
walk(src,FALSE) //this cancels the walk_towards() proc
GLOB.meteor_list -= src
return ..()
/obj/effect/meteor/New()
@@ -160,10 +161,10 @@
ram_turf(get_turf(A))
get_hit()
else
die(0)
die(FALSE)
/obj/effect/meteor/CanPass(atom/movable/mover, turf/target)
return istype(mover, /obj/effect/meteor) ? 1 : ..()
return istype(mover, /obj/effect/meteor) ? TRUE : ..()
/obj/effect/meteor/proc/ram_turf(var/turf/T)
//first bust whatever is in the turf
@@ -186,9 +187,9 @@
/obj/effect/meteor/proc/get_hit()
hits--
if(hits <= 0)
die(1)
die(TRUE)
/obj/effect/meteor/proc/die(var/explode = 1)
/obj/effect/meteor/proc/die(var/explode = TRUE)
make_debris()
meteor_effect(explode)
qdel(src)
@@ -205,16 +206,13 @@
/obj/effect/meteor/bullet_act(var/obj/item/projectile/Proj)
if(Proj.excavation_amount)
get_hit()
if(!QDELETED(src))
wall_power -= Proj.excavation_amount + Proj.damage + (Proj.hitscan * 25) // Instant-impact projectiles are inherently better at dealing with meteors.
wall_power = max(1, wall_power)
if(wall_power < Proj.excavation_amount)
if(prob(min(90, 100 - Proj.damage)))
die(TRUE)
else
die(FALSE)
return
if(!QDELETED(src))
wall_power -= Proj.excavation_amount + Proj.damage + (Proj.hitscan * 25) // Instant-impact projectiles are inherently better at dealing with meteors.
if(wall_power <= 0)
die(FALSE) // If you kill the meteor, then it dies.
return
return
/obj/effect/meteor/proc/make_debris()
+329
View File
@@ -0,0 +1,329 @@
//
// Control computer for point defense batteries.
// Handles control UI, but also coordinates their fire to avoid overkill.
//
GLOBAL_LIST_BOILERPLATE(pointdefense_controllers, /obj/machinery/pointdefense_control)
GLOBAL_LIST_BOILERPLATE(pointdefense_turrets, /obj/machinery/pointdefense)
/obj/machinery/pointdefense_control
name = "fire assist mainframe"
desc = "A specialized computer designed to synchronize a variety of weapon systems and a vessel's astronav data."
description_info = "To connect the mainframe to turrets, use a multitool to set the ident tag to that of the turrets."
icon = 'icons/obj/pointdefense.dmi'
icon_state = "control"
density = TRUE
anchored = TRUE
circuit = /obj/item/weapon/circuitboard/pointdefense_control
var/list/targets = list() // Targets being engaged by associated batteries
var/ui_template = "pointdefense_control.tmpl"
var/id_tag = null
/obj/machinery/pointdefense_control/Initialize(mapload)
. = ..()
if(id_tag)
//No more than 1 controller please.
for(var/thing in pointdefense_controllers)
var/obj/machinery/pointdefense_control/PC = thing
if(PC != src && PC.id_tag == id_tag)
warning("Two [src] with the same id_tag of [id_tag]")
id_tag = null
// TODO - Remove this bit once machines are converted to Initialize
if(ispath(circuit))
circuit = new circuit(src)
default_apply_parts()
/obj/machinery/pointdefense_control/get_description_interaction()
. = ..()
if(!id_tag)
. += "[desc_panel_image("multitool")]to set ident tag"
/obj/machinery/pointdefense_control/ui_interact(var/mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(ui_template)
var/list/data = build_ui_data()
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
if (!ui)
ui = new(user, src, ui_key, ui_template, name, 400, 600)
ui.set_initial_data(data)
ui.open()
ui.set_auto_update(1)
/obj/machinery/pointdefense_control/attack_hand(mob/user)
if((. = ..()))
return
if(CanUseTopic(user, global.default_state) > STATUS_CLOSE)
ui_interact(user)
return TRUE
/obj/machinery/pointdefense_control/Topic(var/mob/user, var/href_list)
if((. = ..()))
return
if(href_list["toggle_active"])
var/obj/machinery/pointdefense/PD = locate(href_list["toggle_active"])
if(!istype(PD))
return TOPIC_NOACTION
//if(!lan || !lan.is_connected(PD))
if(PD.id_tag != id_tag)
return TOPIC_NOACTION
if(!(get_z(PD) in GetConnectedZlevels(get_z(src))))
to_chat(user, "<span class='warning'>[PD] is not within control range.</span>")
return TOPIC_NOACTION
if(!PD.Activate()) //Activate() whilst the device is active will return false.
PD.Deactivate()
return TOPIC_REFRESH
/obj/machinery/pointdefense_control/proc/build_ui_data()
var/list/data = list()
data["id"] = id_tag
data["name"] = name
var/list/turrets = list()
if(id_tag)
var/list/connected_z_levels = GetConnectedZlevels(get_z(src))
for(var/i = 1 to LAZYLEN(pointdefense_turrets))
var/obj/machinery/pointdefense/PD = pointdefense_turrets[i]
if(!(PD.id_tag == id_tag && get_z(PD) in connected_z_levels))
continue
var/list/turret = list()
turret["id"] = "#[i]"
turret["ref"] = "\ref[PD]"
turret["active"] = PD.active
turret["effective_range"] = PD.active ? "[PD.kill_range] meter\s" : "OFFLINE."
turret["reaction_wheel_delay"] = PD.active ? "[(PD.rotation_speed / (1 SECONDS))] second\s" : "OFFLINE."
turret["recharge_time"] = PD.active ? "[(PD.charge_cooldown / (1 SECONDS))] second\s" : "OFFLINE."
turrets += list(turret)
data["turrets"] = turrets
return data
/obj/machinery/pointdefense_control/attackby(var/obj/item/W, var/mob/user)
if(W?.is_multitool())
var/new_ident = input(user, "Enter a new ident tag.", "[src]", id_tag) as null|text
if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, physical_state))
// Check for duplicate controllers with this ID
for(var/thing in pointdefense_controllers)
var/obj/machinery/pointdefense_control/PC = thing
if(PC != src && PC.id_tag == id_tag)
to_chat(user, "<span class='warning'>The [new_ident] network already has a controller.</span>")
return
to_chat(user, "<span class='notice'>You register [src] with the [new_ident] network.</span>")
id_tag = new_ident
return
if(default_deconstruction_screwdriver(user, W))
return
if(default_deconstruction_crowbar(user, W))
return
if(default_part_replacement(user, W))
return
return ..()
//
// The acutal point defense battery
//
/obj/machinery/pointdefense
name = "\improper point defense battery"
icon = 'icons/obj/pointdefense.dmi'
icon_state = "pointdefense"
desc = "A Kuiper pattern anti-meteor battery. Capable of destroying most threats in a single salvo."
description_info = "Must have the same ident tag as a fire assist mainframe on the same facility. Use a multitool to set the ident tag."
density = TRUE
anchored = TRUE
circuit = /obj/item/weapon/circuitboard/pointdefense
idle_power_usage = 0.1 KILOWATTS
appearance_flags = PIXEL_SCALE
var/active = TRUE
var/charge_cooldown = 1 SECOND //time between it can fire at different targets
var/last_shot = 0
var/kill_range = 18
var/rotation_speed = 0.25 SECONDS //How quickly we turn to face threats
var/weakref/engaging = null // The meteor we're shooting at
var/id_tag = null
/obj/machinery/pointdefense/Initialize()
. = ..()
// TODO - Remove this bit once machines are converted to Initialize
if(ispath(circuit))
circuit = new circuit(src)
default_apply_parts()
update_icon()
/obj/machinery/pointdefense/get_description_interaction()
. = ..()
if(!id_tag)
. += "[desc_panel_image("multitool")]to set ident tag and connect to a mainframe."
/obj/machinery/pointdefense/update_icon()
if(!active || !id_tag || inoperable())
icon_state = "[initial(icon_state)]_off"
else
icon_state = initial(icon_state)
/obj/machinery/pointdefense/power_change()
var/old_stat = stat
..()
if(old_stat != stat)
update_icon()
// Find controller with the same tag on connected z levels (if any)
/obj/machinery/pointdefense/proc/get_controller()
if(!id_tag)
return null
var/list/connected_z_levels = GetConnectedZlevels(get_z(src))
for(var/thing in pointdefense_controllers)
var/obj/machinery/pointdefense_control/PDC = thing
if(PDC.id_tag == id_tag && (get_z(PDC) in connected_z_levels))
return PDC
/obj/machinery/pointdefense/attackby(var/obj/item/W, var/mob/user)
if(W?.is_multitool())
var/new_ident = input(user, "Enter a new ident tag.", "[src]", id_tag) as null|text
if(new_ident && new_ident != id_tag && user.Adjacent(src) && CanInteract(user, physical_state))
to_chat(user, "<span class='notice'>You register [src] with the [new_ident] network.</span>")
id_tag = new_ident
return
if(default_deconstruction_screwdriver(user, W))
return
if(default_deconstruction_crowbar(user, W))
return
if(default_part_replacement(user, W))
return
return ..()
//Guns cannot shoot through hull or generally dense turfs.
/obj/machinery/pointdefense/proc/space_los(meteor)
for(var/turf/T in getline(src,meteor))
if(T.density)
return FALSE
return TRUE
/obj/machinery/pointdefense/proc/Shoot(var/weakref/target)
var/obj/effect/meteor/M = target.resolve()
if(!istype(M))
engaging = null
return
engaging = target
var/Angle = round(Get_Angle(src,M))
var/matrix/rot_matrix = matrix()
rot_matrix.Turn(Angle)
addtimer(CALLBACK(src, .proc/finish_shot, target), rotation_speed)
animate(src, transform = rot_matrix, rotation_speed, easing = SINE_EASING)
set_dir(ATAN2(transform.b, transform.a) > 0 ? NORTH : SOUTH)
/obj/machinery/pointdefense/proc/finish_shot(var/weakref/target)
var/obj/machinery/pointdefense_control/PC = get_controller()
engaging = null
PC.targets -= target
last_shot = world.time
var/obj/effect/meteor/M = target.resolve()
if(!istype(M))
return
//We throw a laser but it doesnt have to hit for meteor to explode
var/obj/item/projectile/beam/pointdefense/beam = new(get_turf(src))
playsound(src, 'sound/weapons/mandalorian.ogg', 75, 1)
use_power_oneoff(idle_power_usage * 10)
beam.launch_projectile(target = M.loc, user = src)
/obj/machinery/pointdefense/process()
..()
if(stat & (NOPOWER|BROKEN))
return
if(!active)
return
var/desiredir = ATAN2(transform.b, transform.a) > 0 ? NORTH : SOUTH
if(dir != desiredir)
set_dir(desiredir)
if(LAZYLEN(GLOB.meteor_list) > 0)
find_and_shoot()
/obj/machinery/pointdefense/proc/find_and_shoot()
// There ARE meteors to shoot
if(LAZYLEN(GLOB.meteor_list) == 0)
return
// We can shoot
if(engaging || ((world.time - last_shot) < charge_cooldown))
return
var/obj/machinery/pointdefense_control/PC = get_controller()
if(!istype(PC))
return
// Compile list of known targets
var/list/existing_targets = list()
for(var/weakref/WR in PC.targets)
var/obj/effect/meteor/M = WR.resolve()
existing_targets += M
// First, try and acquire new targets
var/list/potential_targets = GLOB.meteor_list.Copy() - existing_targets
for(var/obj/effect/meteor/M in potential_targets)
if(targeting_check(M))
var/weakref/target = weakref(M)
PC.targets += target
engaging = target
Shoot(target)
return
// Then, focus fire on existing targets
for(var/obj/effect/meteor/M in existing_targets)
if(targeting_check(M))
var/weakref/target = weakref(M)
engaging = target
Shoot(target)
return
/obj/machinery/pointdefense/proc/targeting_check(var/obj/effect/meteor/M)
// Target in range
var/list/connected_z_levels = GetConnectedZlevels(get_z(src))
if(!(M.z in connected_z_levels))
return FALSE
if(get_dist(M, src) > kill_range)
return FALSE
// If we can shoot it, then shoot
if(emagged || !space_los(M))
return FALSE
return TRUE
/obj/machinery/pointdefense/RefreshParts()
. = ..()
// Calculates an average rating of components that affect shooting rate
var/shootrate_divisor = total_component_rating_of_type(/obj/item/weapon/stock_parts/capacitor)
charge_cooldown = 2 SECONDS / (shootrate_divisor ? shootrate_divisor : 1)
//Calculate max shooting range
var/killrange_multiplier = total_component_rating_of_type(/obj/item/weapon/stock_parts/capacitor)
killrange_multiplier += 1.5 * total_component_rating_of_type(/obj/item/weapon/stock_parts/scanning_module)
kill_range = 10 + 4 * killrange_multiplier
var/rotation_divisor = total_component_rating_of_type(/obj/item/weapon/stock_parts/manipulator)
rotation_speed = 0.5 SECONDS / (rotation_divisor ? rotation_divisor : 1)
/obj/machinery/pointdefense/proc/Activate()
if(active)
return FALSE
playsound(src, 'sound/weapons/flash.ogg', 100, 0)
update_use_power(USE_POWER_IDLE)
active = TRUE
update_icon()
return TRUE
/obj/machinery/pointdefense/proc/Deactivate()
if(!active)
return FALSE
playsound(src, 'sound/machines/apc_nopower.ogg', 50, 0)
update_use_power(USE_POWER_OFF)
active = FALSE
update_icon()
return TRUE
+5 -68
View File
@@ -73,7 +73,6 @@
var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos)
var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg
var/check_all = FALSE //If active, will fire on anything, including synthetics.
var/fire_at_movement = FALSE // If active, will fire on the most recent thing to move in range, as soon as possible.
var/ailock = FALSE // AI cannot use this
var/check_down = FALSE //If active, will shoot to kill when lethals are also on
var/faction = null //if set, will not fire at people in the same faction for any reason.
@@ -94,8 +93,6 @@
var/timeout = 10 // When a turret pops up, then finds nothing to shoot at, this number decrements until 0, when it pops down.
var/can_salvage = TRUE // If false, salvaging doesn't give you anything.
var/stay_up = FALSE // If true, the turret will remain open while it is powered.
/obj/machinery/porta_turret/crescent
req_one_access = list(access_cent_specops)
enabled = FALSE
@@ -303,31 +300,6 @@
return 1
/obj/machinery/porta_turret/pointdefense
name = "point-defense turret"
turret_type = "core"
desc = "A basic ship-mounted rock-breaker."
description_info = "This turret is capable of blasting incoming meteors into gravel, but it is very limited in range."
installation = /obj/item/weapon/gun/energy/mininglaser
check_arrest = FALSE
check_records = FALSE
check_weapons = FALSE
check_access = FALSE
check_anomalies = FALSE
check_synth = FALSE
check_all = FALSE
fire_at_movement = TRUE
stay_up = TRUE
ailock = FALSE
check_down = FALSE
/obj/machinery/porta_turret/pointdefense/orderable
enabled = FALSE
anchored = FALSE
locked = FALSE
/obj/machinery/porta_turret/Initialize()
//Sets up a spark system
spark_system = new /datum/effect/effect/system/spark_spread
@@ -374,8 +346,6 @@
var/obj/item/projectile/P = initial(E.projectile_type)
//var/obj/item/ammo_casing/shottype = E.projectile_type
GLOB.moved_event.register_global(src, /obj/machinery/porta_turret/proc/point_defense)
projectile = P
lethal_projectile = projectile
shot_sound = initial(P.fire_sound)
@@ -431,14 +401,6 @@
lethal_shot_sound = 'sound/weapons/eluger.ogg'
shot_sound = 'sound/weapons/Taser.ogg'
if(/obj/item/weapon/gun/energy/mininglaser)
lethal_icon_color = "green"
lethal_projectile = /obj/item/projectile/beam/mininglaser
lethal_shot_sound = 'sound/weapons/eluger.ogg'
icon_color = "red"
projectile = /obj/item/projectile/beam/weaklaser
shot_sound = 'sound/weapons/Laser.ogg'
/obj/machinery/porta_turret/proc/isLocked(mob/user)
if(ailock && issilicon(user))
to_chat(user, "<span class='notice'>There seems to be a firewall preventing you from accessing this device.</span>")
@@ -480,8 +442,6 @@
settings[++settings.len] = list("category" = "Check misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies)
settings[++settings.len] = list("category" = "Neutralize All Entities", "setting" = "check_all", "value" = check_all)
settings[++settings.len] = list("category" = "Neutralize Downed Entities", "setting" = "check_down", "value" = check_down)
settings[++settings.len] = list("category" = "Remain Deployed", "setting" = "stay_up", "value" = stay_up)
settings[++settings.len] = list("category" = "Fire At Movement", "setting" = "fire_at_movement", "value" = fire_at_movement)
data["settings"] = settings
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
@@ -535,10 +495,6 @@
check_all = value
else if(href_list["command"] == "check_down")
check_down = value
else if(href_list["command"] == "stay_up")
stay_up = value
else if(href_list["command"] == "fire_at_movement")
fire_at_movement = value
return 1
@@ -742,10 +698,6 @@
popDown()
return
if(stay_up)
timeout = 10
popUp()
var/list/targets = list() //list of primary targets
var/list/secondarytargets = list() //targets that are least important
@@ -768,7 +720,7 @@
if(!tryToShootAt(targets))
if(!tryToShootAt(secondarytargets)) // if no valid targets, go for secondary targets
timeout--
if(timeout <= 0 && !stay_up)
if(timeout <= 0)
spawn()
popDown() // no valid targets, close the cover
@@ -776,14 +728,6 @@
use_power(20000)
health = min(health+1, maxhealth) // 1HP for 20kJ
// We're expecting the first arg to be a target, for this we don't care about previous and next turfs.
/obj/machinery/porta_turret/proc/point_defense(var/atom/movable/Targ)
if((stat & (NOPOWER|BROKEN)) || !fire_at_movement) // Are we even able or supposed to fire at non-moving targets?
return
if((isliving(Targ) && assess_living(Targ)) || ((istype(Targ, /obj/item) || istype(Targ, /obj/effect/meteor)) && Targ.invisibility < INVISIBILITY_LEVEL_ONE && Targ in view(7,src))) // Is the target a living thing, if so, is it a valid target? Or if it's not living, is it in sight?
target(Targ) // Yes? Blast it.
/obj/machinery/porta_turret/proc/assess_and_assign(var/mob/living/L, var/list/targets, var/list/secondarytargets)
switch(assess_living(L))
if(TURRET_PRIORITY_TARGET)
@@ -810,10 +754,10 @@
if(L.stat == DEAD && !emagged) //if the perp is dead, no need to bother really
return TURRET_NOT_TARGET //move onto next potential victim!
if(get_dist(src, get_turf(L)) > 7) //if it's too far away, why bother?
if(get_dist(src, L) > 7) //if it's too far away, why bother?
return TURRET_NOT_TARGET
if(!(L in check_trajectory(get_turf(L), src))) //check if we have true line of sight
if(!(L in check_trajectory(L, src))) //check if we have true line of sight
return TURRET_NOT_TARGET
if(emagged) // If emagged not even the dead get a rest
@@ -920,9 +864,8 @@
last_target = target
spawn()
popUp() //pop the turret up if it's not already up.
if(dir != get_dir(src, target))
set_dir(get_dir(src, target)) //even if you can't shoot, follow the target
playsound(src, 'sound/machines/turrets/turret_rotate.ogg', 100, 1) // Play rotating sound
set_dir(get_dir(src, target)) //even if you can't shoot, follow the target
playsound(src, 'sound/machines/turrets/turret_rotate.ogg', 100, 1) // Play rotating sound
spawn()
shootAt(target)
return 1
@@ -986,9 +929,6 @@
var/check_weapons
var/check_anomalies
var/check_all
var/check_down
var/fire_at_movement
var/stay_up
var/ailock
/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC)
@@ -1004,9 +944,6 @@
check_weapons = TC.check_weapons
check_anomalies = TC.check_anomalies
check_all = TC.check_all
check_down = TC.check_down
fire_at_movement = TC.fire_at_movement
stay_up = TC.stay_up
ailock = TC.ailock
power_change()
-15
View File
@@ -25,9 +25,6 @@
var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos)
var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg
var/check_all = 0 //If active, will shoot at anything.
var/check_down = 0 //If active, will shoot at prone individuals.
var/stay_up = 0 //If active, the turret will not pop-down unless it loses power or is disabled.
var/fire_at_movement = 0 //If active, the turret will prioritize objects or creatures that move in its range.
var/ailock = 0 //Silicons cannot use this
req_access = list(access_ai_upload)
@@ -137,9 +134,6 @@
settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access)
settings[++settings.len] = list("category" = "Check misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies)
settings[++settings.len] = list("category" = "Neutralize All Entities", "setting" = "check_all", "value" = check_all)
settings[++settings.len] = list("category" = "Neutralize Downed Entities", "setting" = "check_down", "value" = check_down)
settings[++settings.len] = list("category" = "Remain Deployed", "setting" = "stay_up", "value" = stay_up)
settings[++settings.len] = list("category" = "Fire At Movement", "setting" = "fire_at_movement", "value" = fire_at_movement)
data["settings"] = settings
@@ -174,12 +168,6 @@
check_anomalies = value
else if(href_list["command"] == "check_all")
check_all = value
else if(href_list["command"] == "check_down")
check_down = value
else if(href_list["command"] == "stay_up")
stay_up = value
else if(href_list["command"] == "fire_at_movement")
fire_at_movement = value
updateTurrets()
return 1
@@ -195,9 +183,6 @@
TC.check_weapons = check_weapons
TC.check_anomalies = check_anomalies
TC.check_all = check_all
TC.check_down = check_down
TC.stay_up = stay_up
TC.fire_at_movement = fire_at_movement
TC.ailock = ailock
if(istype(control_area))
@@ -79,3 +79,6 @@
light_range = 4
light_power = 3
light_color = "#3300ff"
/obj/effect/projectile/impact/pointdefense
icon_state = "impact_pointdef"
@@ -91,3 +91,6 @@
light_range = 4
light_power = 3
light_color = "#3300ff"
/obj/effect/projectile/muzzle/pointdefense
icon_state = "muzzle_pointdef"
@@ -120,3 +120,6 @@
light_range = 1
light_power = 0.5
light_color = "#f6f2b6"
/obj/effect/projectile/tracer/pointdefense
icon_state = "beam_pointdef"
@@ -0,0 +1,23 @@
#ifndef T_BOARD
#error T_BOARD macro is not defined but we need it!
#endif
/obj/item/weapon/circuitboard/pointdefense
name = T_BOARD("point defense battery")
board_type = new /datum/frame/frame_types/machine
desc = "Control systems for a Kuiper pattern point defense battery. Aim away from vessel."
build_path = /obj/machinery/pointdefense
origin_tech = list(TECH_ENGINEERING = 3, TECH_COMBAT = 2)
req_components = list(
/obj/item/mecha_parts/mecha_equipment/weapon/energy/laser = 1,
/obj/item/weapon/stock_parts/manipulator = 2,
/obj/item/weapon/stock_parts/capacitor = 2,
)
/obj/item/weapon/circuitboard/pointdefense_control
name = T_BOARD("fire assist mainframe")
board_type = new /datum/frame/frame_types/machine
desc = "A control computer to synchronize point defense batteries."
build_path = /obj/machinery/pointdefense_control
origin_tech = list(TECH_ENGINEERING = 3, TECH_COMBAT = 2)
req_components = list()
@@ -1,9 +1,3 @@
#define TOPIC_NOACTION 0
#define TOPIC_HANDLED 1
#define TOPIC_REFRESH 2
#define TOPIC_UPDATE_PREVIEW 4
#define TOPIC_REFRESH_UPDATE_PREVIEW (TOPIC_REFRESH|TOPIC_UPDATE_PREVIEW)
#define PREF_FBP_CYBORG "cyborg"
#define PREF_FBP_POSI "posi"
#define PREF_FBP_SOFTWARE "software"
@@ -279,3 +279,22 @@
/obj/item/projectile/beam/shock/weak
damage = 5
agony = 10
//
// Projectile Beam Definitions
//
/obj/item/projectile/beam/pointdefense
name = "point defense salvo"
icon_state = "laser"
damage = 15
damage_type = ELECTROCUTE //You should be safe inside a voidsuit
sharp = FALSE //"Wide" spectrum beam
light_color = COLOR_GOLD
excavation_amount = 200 // Good at shooting rocks
muzzle_type = /obj/effect/projectile/muzzle/pointdefense
tracer_type = /obj/effect/projectile/tracer/pointdefense
impact_type = /obj/effect/projectile/impact/pointdefense
@@ -662,6 +662,19 @@ CIRCUITS BELOW
build_path = /obj/item/weapon/circuitboard/microwave/advanced
sort_string = "HACAA"
/datum/design/circuit/pointdefense
name = "point defense battery"
id = "pointdefense"
req_tech = list(TECH_DATA = 2, TECH_ENGINEERING = 3, TECH_COMBAT = 4)
build_path = /obj/item/weapon/circuitboard/pointdefense
sort_string = "OAABA"
/datum/design/circuit/pointdefense_control
name = "point defense control"
id = "pointdefense_control"
req_tech = list(TECH_DATA = 4, TECH_ENGINEERING = 3, TECH_COMBAT = 2)
build_path = /obj/item/weapon/circuitboard/pointdefense_control
sort_string = "OAABB"
/* I have no idea how this was even running before, but it doesn't seem to be necessary.
///////////////////////////////////