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