diff --git a/code/datums/supplypacks/engineering.dm b/code/datums/supplypacks/engineering.dm
index 165c5fbfef..28a7431ec1 100644
--- a/code/datums/supplypacks/engineering.dm
+++ b/code/datums/supplypacks/engineering.dm
@@ -77,6 +77,14 @@
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/electrical
name = "Electrical maintenance crate"
contains = list(
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index 0b0b8d014c..a6ceb219c8 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -13,7 +13,7 @@
/obj/effect/meteor/big=3,
/obj/effect/meteor/flaming=1,
/obj/effect/meteor/irradiated=3
- )
+ )
//for threatening meteor event
/var/list/meteors_threatening = list(
@@ -206,6 +206,21 @@
return
..()
+/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
+ return
+
/obj/effect/meteor/proc/make_debris()
for(var/throws = dropamt, throws > 0, throws--)
var/obj/item/O = new meteordrop(get_turf(src))
diff --git a/code/game/machinery/portable_turret.dm b/code/game/machinery/portable_turret.dm
index 01a890bd79..277e1265f9 100644
--- a/code/game/machinery/portable_turret.dm
+++ b/code/game/machinery/portable_turret.dm
@@ -74,6 +74,7 @@
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.
@@ -95,6 +96,8 @@
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
@@ -276,6 +279,31 @@
)
return data
+/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
@@ -322,6 +350,8 @@
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)
@@ -377,9 +407,28 @@
lethal_shot_sound = 'sound/weapons/eluger.ogg'
shot_sound = 'sound/weapons/Taser.ogg'
+<<<<<<< HEAD
/obj/machinery/porta_turret/proc/HasController()
var/area/A = get_area(src)
return A && A.turret_controls.len > 0
+=======
+ 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, "There seems to be a firewall preventing you from accessing this device.")
+ return 1
+
+ if(locked && !issilicon(user))
+ to_chat(user, "Controls locked.")
+ return 1
+>>>>>>> 997b440... Merge pull request #7765 from Mechoid/PointDefense
/obj/machinery/porta_turret/proc/isLocked(mob/user)
if(HasController())
@@ -407,7 +456,47 @@
tgui_interact(user)
/obj/machinery/porta_turret/attack_hand(mob/user)
+<<<<<<< HEAD
tgui_interact(user)
+=======
+ if(isLocked(user))
+ return
+
+ ui_interact(user)
+
+/obj/machinery/porta_turret/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
+ data["access"] = !isLocked(user)
+ data["locked"] = locked
+ data["enabled"] = enabled
+ data["is_lethal"] = 1
+ data["lethal"] = lethal
+
+ if(data["access"])
+ var/settings[0]
+ settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth)
+ settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons)
+ settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records)
+ settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest)
+ 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
+
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
+ if(!ui)
+ ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 300)
+ ui.set_initial_data(data)
+ ui.open()
+ ui.set_auto_update(1)
+
+/obj/machinery/porta_turret/proc/HasController()
+ var/area/A = get_area(src)
+ return A && A.turret_controls.len > 0
+>>>>>>> 997b440... Merge pull request #7765 from Mechoid/PointDefense
/obj/machinery/porta_turret/tgui_interact(mob/user, datum/tgui/ui = null)
if(HasController())
@@ -441,6 +530,7 @@
/obj/machinery/porta_turret/tgui_act(action, list/params, datum/tgui/ui, datum/tgui_state/state)
if(..())
+<<<<<<< HEAD
return TRUE
if(isLocked(usr))
return TRUE
@@ -470,6 +560,38 @@
check_all = !check_all
if("authdown")
check_down = !check_down
+=======
+ return 1
+
+ if(href_list["command"] && href_list["value"])
+ var/value = text2num(href_list["value"])
+ if(href_list["command"] == "enable")
+ enabled = value
+ else if(href_list["command"] == "lethal")
+ lethal = value
+ else if(href_list["command"] == "check_synth")
+ check_synth = value
+ else if(href_list["command"] == "check_weapons")
+ check_weapons = value
+ else if(href_list["command"] == "check_records")
+ check_records = value
+ else if(href_list["command"] == "check_arrest")
+ check_arrest = value
+ else if(href_list["command"] == "check_access")
+ check_access = value
+ else if(href_list["command"] == "check_anomalies")
+ 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
+
+ return 1
+>>>>>>> 997b440... Merge pull request #7765 from Mechoid/PointDefense
/obj/machinery/porta_turret/power_change()
if(powered())
@@ -671,6 +793,10 @@
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
@@ -693,7 +819,7 @@
if(!tryToShootAt(targets))
if(!tryToShootAt(secondarytargets)) // if no valid targets, go for secondary targets
timeout--
- if(timeout <= 0)
+ if(timeout <= 0 && !stay_up)
spawn()
popDown() // no valid targets, close the cover
@@ -701,6 +827,14 @@
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)
@@ -727,10 +861,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, L) > 7) //if it's too far away, why bother?
+ if(get_dist(src, get_turf(L)) > 7) //if it's too far away, why bother?
return TURRET_NOT_TARGET
- if(!(L in check_trajectory(L, src))) //check if we have true line of sight
+ if(!(L in check_trajectory(get_turf(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
@@ -837,8 +971,9 @@
last_target = target
spawn()
popUp() //pop the turret up if it's not already up.
- 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
+ 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
spawn()
shootAt(target)
return 1
@@ -903,6 +1038,11 @@
var/check_anomalies
var/check_all
var/check_down
+<<<<<<< HEAD
+=======
+ var/fire_at_movement
+ var/stay_up
+>>>>>>> 997b440... Merge pull request #7765 from Mechoid/PointDefense
var/ailock
/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC)
@@ -919,6 +1059,11 @@
check_anomalies = TC.check_anomalies
check_all = TC.check_all
check_down = TC.check_down
+<<<<<<< HEAD
+=======
+ fire_at_movement = TC.fire_at_movement
+ stay_up = TC.stay_up
+>>>>>>> 997b440... Merge pull request #7765 from Mechoid/PointDefense
ailock = TC.ailock
power_change()
diff --git a/code/game/machinery/turret_control.dm b/code/game/machinery/turret_control.dm
index ad55fe14a1..02c077cb69 100644
--- a/code/game/machinery/turret_control.dm
+++ b/code/game/machinery/turret_control.dm
@@ -19,6 +19,7 @@
var/locked = TRUE
var/area/control_area //can be area name, path or nothing.
+<<<<<<< HEAD
var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI
var/check_arrest = TRUE //checks if the perp is set to arrest
var/check_records = TRUE //checks if a security record exists at all
@@ -31,6 +32,20 @@
var/ailock = FALSE //Silicons cannot use this
var/syndicate = FALSE
+=======
+ var/check_arrest = 1 //checks if the perp is set to arrest
+ var/check_records = 1 //checks if a security record exists at all
+ var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have
+ var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements
+ 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
+
+>>>>>>> 997b440... Merge pull request #7765 from Mechoid/PointDefense
req_access = list(access_ai_upload)
/obj/machinery/turretid/stun
@@ -121,8 +136,37 @@
/obj/machinery/turretid/attack_hand(mob/user as mob)
tgui_interact(user)
+<<<<<<< HEAD
/obj/machinery/turretid/tgui_interact(mob/user, datum/tgui/ui = null)
ui = SStgui.try_update_ui(user, src, ui)
+=======
+ ui_interact(user)
+
+/obj/machinery/turretid/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
+ var/data[0]
+ data["access"] = !isLocked(user)
+ data["locked"] = locked
+ data["enabled"] = enabled
+ data["is_lethal"] = 1
+ data["lethal"] = lethal
+
+ if(data["access"])
+ var/settings[0]
+ settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth)
+ settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons)
+ settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records)
+ settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest)
+ 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
+
+ ui = SSnanoui.try_update_ui(user, src, ui_key, ui, data, force_open)
+>>>>>>> 997b440... Merge pull request #7765 from Mechoid/PointDefense
if(!ui)
ui = new(user, src, "PortableTurret", name) // 500, 400
ui.open()
@@ -150,9 +194,40 @@
/obj/machinery/turretid/tgui_act(action, params)
if(..())
+<<<<<<< HEAD
return
if(isLocked(usr))
return
+=======
+ return 1
+
+ if(href_list["command"] && href_list["value"])
+ var/value = text2num(href_list["value"])
+ if(href_list["command"] == "enable")
+ enabled = value
+ else if(href_list["command"] == "lethal")
+ lethal = value
+ else if(href_list["command"] == "check_synth")
+ check_synth = value
+ else if(href_list["command"] == "check_weapons")
+ check_weapons = value
+ else if(href_list["command"] == "check_records")
+ check_records = value
+ else if(href_list["command"] == "check_arrest")
+ check_arrest = value
+ else if(href_list["command"] == "check_access")
+ check_access = value
+ else if(href_list["command"] == "check_anomalies")
+ 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
+>>>>>>> 997b440... Merge pull request #7765 from Mechoid/PointDefense
. = TRUE
switch(action)
@@ -194,6 +269,11 @@
TC.check_anomalies = check_anomalies
TC.check_all = check_all
TC.check_down = check_down
+<<<<<<< HEAD
+=======
+ TC.stay_up = stay_up
+ TC.fire_at_movement = fire_at_movement
+>>>>>>> 997b440... Merge pull request #7765 from Mechoid/PointDefense
TC.ailock = ailock
if(istype(control_area))
diff --git a/code/modules/projectiles/guns/energy/laser.dm b/code/modules/projectiles/guns/energy/laser.dm
index 54428d570f..e0159e6333 100644
--- a/code/modules/projectiles/guns/energy/laser.dm
+++ b/code/modules/projectiles/guns/energy/laser.dm
@@ -64,6 +64,28 @@
item_state = "lrifle"
origin_tech = list(TECH_COMBAT = 4, TECH_MAGNET = 3)
+/obj/item/weapon/gun/energy/mininglaser
+ name = "mining-laser rifle"
+ desc = "A Hephaestus Industries G22T rifle, now only produced for its impeccable ability to break stone with its pulsating blasts."
+ description_fluff = "The leading arms producer in the SCG, Hephaestus typically only uses its 'top level' branding for its military-grade equipment used by armed forces across human space."
+ icon = 'icons/obj/gun2.dmi'
+ icon_state = "mininglaser"
+ item_state = "laser"
+ wielded_item_state = "laser-wielded"
+ fire_delay = 8
+ slot_flags = SLOT_BELT|SLOT_BACK
+ w_class = ITEMSIZE_LARGE
+ force = 15
+ origin_tech = list(TECH_COMBAT = 3, TECH_MAGNET = 2)
+ matter = list(DEFAULT_WALL_MATERIAL = 2000)
+ projectile_type = /obj/item/projectile/beam/mininglaser
+ one_handed_penalty = 30
+
+ firemodes = list(
+ list(mode_name="mining", fire_delay=8, projectile_type=/obj/item/projectile/beam/mininglaser, charge_cost = 200),
+ list(mode_name="deter", fire_delay=5, projectile_type=/obj/item/projectile/beam/weaklaser, charge_cost = 80),
+ )
+
/obj/item/weapon/gun/energy/retro
name = "retro laser"
icon_state = "retro"
diff --git a/code/modules/projectiles/projectile/beams.dm b/code/modules/projectiles/projectile/beams.dm
index 41430bea38..d841be9d87 100644
--- a/code/modules/projectiles/projectile/beams.dm
+++ b/code/modules/projectiles/projectile/beams.dm
@@ -17,6 +17,8 @@
hitsound = 'sound/weapons/sear.ogg'
hitsound_wall = 'sound/weapons/effects/searwall.ogg'
+ excavation_amount = 50
+
muzzle_type = /obj/effect/projectile/muzzle/laser
tracer_type = /obj/effect/projectile/tracer/laser
impact_type = /obj/effect/projectile/impact/laser
@@ -46,6 +48,18 @@
damage = 40
armor_penetration = 10
+/obj/item/projectile/beam/mininglaser
+ name = "pulsating laser"
+ damage = 10
+ armor_penetration = 20
+ fire_sound = 'sound/weapons/eluger.ogg'
+
+ excavation_amount = 100
+
+ muzzle_type = /obj/effect/projectile/muzzle/emitter
+ tracer_type = /obj/effect/projectile/tracer/emitter
+ impact_type = /obj/effect/projectile/impact/emitter
+
/obj/item/projectile/beam/heavylaser
name = "heavy laser"
icon_state = "heavylaser"
diff --git a/code/modules/projectiles/projectile/bullets.dm b/code/modules/projectiles/projectile/bullets.dm
index 20769cd520..1483d08274 100644
--- a/code/modules/projectiles/projectile/bullets.dm
+++ b/code/modules/projectiles/projectile/bullets.dm
@@ -10,6 +10,7 @@
sharp = 1
hitsound_wall = "ricochet"
impact_effect_type = /obj/effect/temp_visual/impact_effect
+ excavation_amount = 20
var/mob_passthrough_check = 0
muzzle_type = /obj/effect/projectile/muzzle/bullet
diff --git a/icons/obj/gun2.dmi b/icons/obj/gun2.dmi
index a5c13c95c4..6b81e46b90 100644
Binary files a/icons/obj/gun2.dmi and b/icons/obj/gun2.dmi differ
diff --git a/icons/obj/turrets.dmi b/icons/obj/turrets.dmi
index 32346c5a5e..76b1fab1f3 100644
Binary files a/icons/obj/turrets.dmi and b/icons/obj/turrets.dmi differ