diff --git a/_maps/RandomRuins/SandRuins/whitesands_cave_base.dmm b/_maps/RandomRuins/SandRuins/whitesands_cave_base.dmm
index 187b24b1d267..22683628b02e 100644
--- a/_maps/RandomRuins/SandRuins/whitesands_cave_base.dmm
+++ b/_maps/RandomRuins/SandRuins/whitesands_cave_base.dmm
@@ -864,7 +864,7 @@
/area/ruin/whitesands/cave_base)
"tB" = (
/obj/machinery/porta_turret/cave_base{
- mode = 1
+ lethal = 1
},
/obj/effect/turf_decal/box/red,
/obj/structure/cable,
@@ -1103,7 +1103,7 @@
/area/ruin/whitesands/cave_base)
"yD" = (
/obj/machinery/porta_turret/cave_base{
- mode = 1
+ lethal = 1
},
/obj/structure/cable,
/obj/effect/turf_decal/box/red,
@@ -1507,7 +1507,7 @@
},
/obj/machinery/light/floor,
/obj/machinery/porta_turret/cave_base{
- mode = 1
+ lethal = 1
},
/turf/open/floor/concrete/slab_1/whitesands,
/area/ruin/whitesands/cave_base)
@@ -1536,7 +1536,7 @@
/area/overmap_encounter/planetoid/cave/explored)
"HF" = (
/obj/machinery/porta_turret/cave_base{
- mode = 1
+ lethal = 1
},
/obj/structure/cable{
icon_state = "0-4"
@@ -2016,7 +2016,7 @@
/obj/effect/turf_decal/box/red,
/obj/machinery/light/floor,
/obj/machinery/porta_turret/cave_base{
- mode = 1
+ lethal = 1
},
/turf/open/floor/concrete/slab_1/whitesands,
/area/ruin/whitesands/cave_base)
@@ -2182,7 +2182,7 @@
/turf/open/floor/plating/asteroid/whitesands/grass,
/area/overmap_encounter/planetoid/cave/explored)
"Xe" = (
-/obj/machinery/porta_turret,
+/obj/machinery/porta_turret/cave_base,
/turf/closed/mineral/random/whitesands,
/area/overmap_encounter/planetoid/cave/explored)
"XF" = (
diff --git a/_maps/shuttles/pgf/pgf_crying_sun.dmm b/_maps/shuttles/pgf/pgf_crying_sun.dmm
index 4ebc29275cd9..97e965f541d3 100644
--- a/_maps/shuttles/pgf/pgf_crying_sun.dmm
+++ b/_maps/shuttles/pgf/pgf_crying_sun.dmm
@@ -1190,8 +1190,8 @@
},
/obj/machinery/porta_turret/ship/pgf/light{
dir = 5;
- mode = 1;
- id = "crying_sun_grid"
+ id = "crying_sun_grid";
+ lethal = 1
},
/turf/open/floor/engine/hull,
/area/ship/external/dark)
@@ -1484,8 +1484,8 @@
},
/obj/machinery/porta_turret/ship/pgf/light{
dir = 8;
- mode = 1;
- id = "crying_sun_grid"
+ id = "crying_sun_grid";
+ lethal = 1
},
/turf/open/floor/engine/hull/reinforced,
/area/ship/external/dark)
@@ -3177,8 +3177,8 @@
/obj/structure/catwalk/over/plated_catwalk/dark,
/obj/machinery/porta_turret/ship/pgf/light{
dir = 4;
- mode = 1;
- id = "crying_sun_grid"
+ id = "crying_sun_grid";
+ lethal = 1
},
/turf/open/floor/plating/airless,
/area/ship/external/dark)
@@ -4241,8 +4241,8 @@
},
/obj/machinery/porta_turret/ship/pgf/light{
dir = 4;
- mode = 1;
- id = "crying_sun_grid"
+ id = "crying_sun_grid";
+ lethal = 1
},
/turf/open/floor/plating/airless,
/area/ship/external/dark)
@@ -5846,8 +5846,8 @@
},
/obj/machinery/porta_turret/ship/pgf/light{
dir = 10;
- mode = 1;
- id = "crying_sun_grid"
+ id = "crying_sun_grid";
+ lethal = 1
},
/turf/open/floor/engine/hull,
/area/ship/external/dark)
@@ -6312,8 +6312,8 @@
},
/obj/machinery/porta_turret/ship/pgf/light{
dir = 6;
- mode = 1;
- id = "crying_sun_grid"
+ id = "crying_sun_grid";
+ lethal = 1
},
/turf/open/floor/engine/hull,
/area/ship/external/dark)
@@ -6398,8 +6398,8 @@
},
/obj/machinery/porta_turret/ship/pgf/light{
dir = 5;
- mode = 1;
- id = "crying_sun_grid"
+ id = "crying_sun_grid";
+ lethal = 1
},
/turf/open/floor/engine/hull,
/area/ship/external/dark)
@@ -6726,8 +6726,8 @@
/obj/structure/cable,
/obj/machinery/porta_turret/ship/pgf/light{
dir = 5;
- mode = 1;
- id = "crying_sun_grid"
+ id = "crying_sun_grid";
+ lethal = 1
},
/turf/open/floor/engine/hull,
/area/ship/external/dark)
diff --git a/_maps/shuttles/subshuttles/pgf_nail.dmm b/_maps/shuttles/subshuttles/pgf_nail.dmm
index 6b0c12bc7f53..7414776c1e9f 100644
--- a/_maps/shuttles/subshuttles/pgf_nail.dmm
+++ b/_maps/shuttles/subshuttles/pgf_nail.dmm
@@ -397,8 +397,8 @@
},
/obj/machinery/porta_turret/ship/pgf/light{
dir = 8;
- mode = 1;
- id = "nail_grid"
+ id = "nail_grid";
+ lethal = 1
},
/turf/open/floor/engine/hull/reinforced/interior,
/area/ship/external/dark)
diff --git a/code/__DEFINES/machines.dm b/code/__DEFINES/machines.dm
index 02e6853338d7..7c04242f9976 100644
--- a/code/__DEFINES/machines.dm
+++ b/code/__DEFINES/machines.dm
@@ -143,3 +143,27 @@
#define CLONING_DELETE_RECORD (1<<1)
#define CLICKSOUND_INTERVAL (0.1 SECONDS) //! clicky noises, how much time needed in between clicks on the machine for the sound to play on click again.
+
+/// ONLY shoots at mobs who match the rest of the flags and have weaponry/are otherwise dangerous
+#define TURRET_FLAG_SHOOT_DANGEROUS_ONLY (1<<0)
+/// Will shoot at things that shoot at it
+#define TURRET_FLAG_SHOOT_RETALIATE (1<<1)
+
+/// Will shoot at things that aren't human
+#define TURRET_FLAG_SHOOT_FAUNA (1<<2)
+/// Will shoot at humans
+#define TURRET_FLAG_SHOOT_HUMANS (1<<3)
+/// Will shoot at silicons
+#define TURRET_FLAG_SHOOT_SILICONS (1<<4)
+/// Will shoot at any kind of mob
+#define TURRET_FLAG_SHOOT_ALLMOBS TURRET_FLAG_SHOOT_FAUNA|TURRET_FLAG_SHOOT_HUMANS|TURRET_FLAG_SHOOT_SILICONS
+
+/// Will only shoot at things that AREN'T in the turret's set faction
+#define TURRET_FLAG_SHOOT_NONFACTION (1<<5)
+/// Will only shoot at things that ARE in the turret's set faction
+#define TURRET_FLAG_SHOOT_SPECIFIC_FACTION (1<<6)
+/// Will totally ignore targets' factions - the same as not setting the above two flags
+//#define TURRET_FLAG_SHOOT_ALLFACTION (1<<8)
+
+#define TURRET_FLAG_DEFAULT TURRET_FLAG_SHOOT_DANGEROUS_ONLY|TURRET_FLAG_SHOOT_RETALIATE|TURRET_FLAG_SHOOT_FAUNA|TURRET_FLAG_SHOOT_NONFACTION
+#define TURRET_FLAG_HOSTILE TURRET_FLAG_SHOOT_ALLMOBS|TURRET_FLAG_SHOOT_RETALIATE|TURRET_FLAG_SHOOT_NONFACTION
diff --git a/code/_globalvars/bitfields.dm b/code/_globalvars/bitfields.dm
index df6f39cd0bbe..61fa476fe7b1 100644
--- a/code/_globalvars/bitfields.dm
+++ b/code/_globalvars/bitfields.dm
@@ -277,3 +277,13 @@ DEFINE_BITFIELD(bodytype, list(
"BODYTYPE_KEPORI" = BODYTYPE_KEPORI,
"BODYTYPE_VOX" = BODYTYPE_VOX
))
+
+DEFINE_BITFIELD(turret_flags, list(
+ "TURRET_FLAG_SHOOT_DANGEROUS_ONLY" = TURRET_FLAG_SHOOT_DANGEROUS_ONLY,
+ "TURRET_FLAG_SHOOT_RETALIATE" = TURRET_FLAG_SHOOT_RETALIATE,
+ "TURRET_FLAG_SHOOT_FAUNA" = TURRET_FLAG_SHOOT_FAUNA,
+ "TURRET_FLAG_SHOOT_HUMANS" = TURRET_FLAG_SHOOT_HUMANS,
+ "TURRET_FLAG_SHOOT_SILICONS" = TURRET_FLAG_SHOOT_SILICONS,
+ "TURRET_FLAG_SHOOT_NONFACTION" = TURRET_FLAG_SHOOT_NONFACTION,
+ "TURRET_FLAG_SHOOT_SPECIFIC_FACTION" = TURRET_FLAG_SHOOT_SPECIFIC_FACTION,
+))
diff --git a/code/controllers/subsystem/turrets.dm b/code/controllers/subsystem/turrets.dm
index 634f6327e458..7c99cc33a4c8 100644
--- a/code/controllers/subsystem/turrets.dm
+++ b/code/controllers/subsystem/turrets.dm
@@ -1,41 +1,4 @@
-SUBSYSTEM_DEF(turrets)
+PROCESSING_SUBSYSTEM_DEF(turrets)
name = "Turrets"
wait = 5
- init_order = INIT_ORDER_MACHINES
- flags = SS_KEEP_TIMING
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
- var/list/processing = list()
- var/list/currentrun = list()
-
-/datum/controller/subsystem/turrets/get_metrics()
- . = ..()
- var/list/cust = list()
- cust["processing"] = length(processing)
- .["custom"] = cust
-
-/datum/controller/subsystem/turrets/stat_entry(msg)
- msg = "M:[length(processing)]]"
- return ..()
-
-
-/datum/controller/subsystem/turrets/fire(resumed = 0)
- if (!resumed)
- src.currentrun = processing.Copy()
-
- //cache for sanic speed (lists are references anyways)
- var/list/currentrun = src.currentrun
-
- var/seconds = wait * 0.1
- while(currentrun.len)
- var/obj/machinery/thing = currentrun[currentrun.len]
- currentrun.len--
- if(QDELETED(thing) || thing.process(seconds) == PROCESS_KILL)
- processing -= thing
- if (!QDELETED(thing))
- thing.datum_flags &= ~DF_ISPROCESSING
- if (MC_TICK_CHECK)
- return
-
-/datum/controller/subsystem/turrets/Recover()
- if (istype(SSturrets.processing))
- processing = SSmachines.processing
diff --git a/code/datums/simple_beam.dm b/code/datums/simple_beam.dm
new file mode 100644
index 000000000000..ec6538239754
--- /dev/null
+++ b/code/datums/simple_beam.dm
@@ -0,0 +1,71 @@
+/datum/simple_beam
+ ///The source of the beam, which must be visible for the beam to be seen. Can NOT be null.
+ VAR_PRIVATE/atom/movable/origin
+ ///The target of the beam. Can be null.
+ VAR_PRIVATE/atom/movable/target
+ ///The visual representation of the beam.
+ VAR_PRIVATE/obj/effect/simple_beam/its_beam
+
+/datum/simple_beam/New(_origin, _target, icon = 'icons/effects/beam.dmi', icon_state = "1-full", icon_color = null, icon_alpha = 255)
+ origin = _origin
+ target = _target
+
+ its_beam = new /obj/effect/simple_beam(origin, icon, icon_state, icon_color, icon_alpha)
+ origin.vis_contents += its_beam
+
+ set_target(target)
+
+/datum/simple_beam/Destroy(force)
+ origin.vis_contents -= its_beam
+ QDEL_NULL(its_beam)
+
+ if(target)
+ UnregisterSignal(origin, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
+
+ return ..()
+
+/datum/simple_beam/proc/draw()
+ if(origin.z != target.z)
+ set_target(null)
+ return
+
+ var/f_dx = ((target.pixel_x - origin.pixel_x + 16) / world.icon_size) + (target.x - origin.x)
+ var/f_dy = ((target.pixel_y - origin.pixel_y) / world.icon_size) + (target.y - origin.y)
+ var/dist = sqrt(f_dx * f_dx + f_dy * f_dy)
+ var/s_dx = f_dy/dist
+ var/s_dy = -f_dx/dist
+ var/matrix/translation = matrix()
+ translation.Translate(0, 16)
+ translation.Multiply(new /matrix(s_dx, f_dx, 0, s_dy, f_dy, 0))
+
+ its_beam.transform = translation
+
+/datum/simple_beam/proc/set_target(new_target)
+ if(target)
+ UnregisterSignal(target, COMSIG_MOVABLE_MOVED)
+ UnregisterSignal(origin, COMSIG_MOVABLE_MOVED)
+
+ target = new_target
+
+ if(target)
+ its_beam.vis_flags &= ~VIS_HIDE
+
+ RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(draw))
+ RegisterSignal(origin, COMSIG_MOVABLE_MOVED, PROC_REF(draw))
+
+ draw()
+ else
+ its_beam.vis_flags |= VIS_HIDE
+
+/obj/effect/simple_beam
+ layer = ABOVE_LIGHTING_LAYER
+ plane = ABOVE_LIGHTING_PLANE
+
+/obj/effect/simple_beam/New(loc, icon, icon_state, icon_color, icon_alpha)
+ src.icon = icon
+ src.icon_state = icon_state
+ src.color = icon_color
+ src.alpha = icon_alpha
+
+ return ..()
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index acc2797b360a..6f1a0069e5c9 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -1320,9 +1320,6 @@
/atom/proc/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
return
-/atom/proc/disconnect_from_shuttle(obj/docking_port/mobile/port)
- return
-
/// Generic logging helper
/atom/proc/log_message(message, message_type, color=null, log_globally=TRUE)
if(!log_globally)
diff --git a/code/game/machinery/porta_turret/portable_turret.dm b/code/game/machinery/porta_turret/portable_turret.dm
index a9e69edc00e9..dc0c2d47f963 100644
--- a/code/game/machinery/porta_turret/portable_turret.dm
+++ b/code/game/machinery/porta_turret/portable_turret.dm
@@ -1,188 +1,138 @@
-#define TURRET_STUN 0
-#define TURRET_LETHAL 1
-
-#define POPUP_ANIM_TIME 5
-#define POPDOWN_ANIM_TIME 5 //Be sure to change the icon animation at the same time or it'll look bad
-
-#define TURRET_FLAG_SHOOT_ALL_REACT (1<<0) // The turret gets pissed off and shoots at people nearby (unless they have sec access!)
-#define TURRET_FLAG_AUTH_WEAPONS (1<<1) // Checks if it can shoot people that have a weapon they aren't authorized to have
-#define TURRET_FLAG_SHOOT_CRIMINALS (1<<2) // Checks if it can shoot people that are wanted
-#define TURRET_FLAG_SHOOT_ALL (1<<3) // The turret gets pissed off and shoots at people nearby (unless they have sec access!)
-#define TURRET_FLAG_SHOOT_ANOMALOUS (1<<4) // Checks if it can shoot at unidentified lifeforms (ie xenos)
-#define TURRET_FLAG_SHOOT_UNSHIELDED (1<<5) // Checks if it can shoot people that aren't mindshielded and who arent heads
-#define TURRET_FLAG_SHOOT_BORGS (1<<6) // checks if it can shoot cyborgs
-#define TURRET_FLAG_SHOOT_HEADS (1<<7) // checks if it can shoot at heads of staff
-
-DEFINE_BITFIELD(turret_flags, list(
- "TURRET_FLAG_SHOOT_ALL_REACT" = TURRET_FLAG_SHOOT_ALL_REACT,
- "TURRET_FLAG_AUTH_WEAPONS" = TURRET_FLAG_AUTH_WEAPONS,
- "TURRET_FLAG_SHOOT_CRIMINALS" = TURRET_FLAG_SHOOT_CRIMINALS,
- "TURRET_FLAG_SHOOT_ALL" = TURRET_FLAG_SHOOT_ALL,
- "TURRET_FLAG_SHOOT_ANOMALOUS" = TURRET_FLAG_SHOOT_ANOMALOUS,
- "TURRET_FLAG_SHOOT_UNSHIELDED" = TURRET_FLAG_SHOOT_UNSHIELDED,
- "TURRET_FLAG_SHOOT_BORGS" = TURRET_FLAG_SHOOT_BORGS,
- "TURRET_FLAG_SHOOT_HEADS" = TURRET_FLAG_SHOOT_HEADS,
-))
-
/obj/machinery/porta_turret
name = "turret"
icon = 'icons/obj/turrets.dmi'
- icon_state = "turretCover"
- layer = OBJ_LAYER
- invisibility = INVISIBILITY_OBSERVER //the turret is invisible if it's inside its cover
+ icon_state = "standard_stun"
density = TRUE
- desc = "A covered turret that shoots at its enemies."
- use_power = IDLE_POWER_USE //this turret uses and requires power
- idle_power_usage = IDLE_DRAW_MINIMAL //when inactive, this turret takes up constant 50 Equipment power
- active_power_usage = ACTIVE_DRAW_LOW //when active, this turret takes up constant 300 Equipment power
- req_access = list(ACCESS_SECURITY) /// Only people with Security access
- power_channel = AREA_USAGE_EQUIP //drains power from the EQUIPMENT channel
- max_integrity = 160 //the turret's health
+ desc = "A turret that shoots at its enemies."
+ use_power = IDLE_POWER_USE
+ idle_power_usage = IDLE_DRAW_LOW
+ active_power_usage = ACTIVE_DRAW_HIGH
+ req_access = list(ACCESS_SECURITY)
+ power_channel = AREA_USAGE_EQUIP
+ max_integrity = 200
integrity_failure = 0.5
armor = list("melee" = 50, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90)
base_icon_state = "standard"
- subsystem_type = /datum/controller/subsystem/turrets
+ subsystem_type = /datum/controller/subsystem/processing/turrets
+ circuit = /obj/item/circuitboard/machine/turret
+
/// Scan range of the turret for locating targets
var/scan_range = 7
- /// For turrets inside other objects
- var/atom/base = null
- /// If the turret cover is "open" and the turret is raised
- var/raised = FALSE
- /// If the turret is currently opening or closing its cover
- var/raising = FALSE
+ /// List of ALL targets in range, even if they are not visible
+ var/list/mob/living/targets = list()
+ /// The current target of the turret, if any
+ var/mob/living/current_target
+
+ /// The beam showing which target we're acquiring
+ var/datum/simple_beam/target_beam
+
/// If the turret's behaviour control access is locked
var/locked = TRUE
- /// If the turret responds to control panels
- var/controllock = FALSE
- /// The type of weapon installed by default
- var/installation = /obj/item/gun/energy/e_gun/turret
- /// What stored gun is in the turret
- var/obj/item/gun/stored_gun = null
- /// The charge of the gun when retrieved from wreckage
- var/gun_charge = 0
+
/// In which mode is turret in, stun or lethal
- var/mode = TURRET_STUN
+ var/lethal = FALSE
+
/// Stun mode projectile type
- var/stun_projectile = null
+ var/stun_projectile = /obj/projectile/beam/disabler
/// Sound of stun projectile
- var/stun_projectile_sound
+ var/stun_projectile_sound = 'sound/weapons/plasma_cutter.ogg'
/// Lethal mode projectile type
- var/lethal_projectile = null
+ var/lethal_projectile = /obj/projectile/beam/laser
/// Sound of lethal projectile
- var/lethal_projectile_sound
+ var/lethal_projectile_sound = 'sound/weapons/plasma_cutter.ogg'
+
/// Power needed per shot
var/reqpower = 500
- /// Will stay active
- var/always_up = FALSE
- /// Hides the cover
- var/has_cover = TRUE
- /// The cover that is covering this turret
- var/obj/machinery/porta_turret_cover/cover = null
- /// Ticks until next shot (1.5 ?) If this needs to go below 5, use SSFastProcess
- var/shot_delay = 15
- /// Turret flags about who is turret allowed to shoot
- var/turret_flags = TURRET_FLAG_SHOOT_CRIMINALS | TURRET_FLAG_SHOOT_ANOMALOUS
+
+ /// If the turret is currently manually controlled
+ var/manual_control = FALSE
+
+ /// Ticks until next shot If this needs to go below 5, use SSFastProcess
+ var/shot_delay = 1.5 SECONDS
+ /// Cooldown until we can shoot again
+ COOLDOWN_DECLARE(fire_cooldown)
+
+ /// Reaction time of the turret, how long it takes after acquiring a target to begin firing
+ var/reaction_time
+ /// Cooldown until we can start firing
+ COOLDOWN_DECLARE(reaction_cooldown)
+
/// Determines if the turret is on
var/on = TRUE
+ /// Turret flags about who is turret allowed to shoot
+ var/turret_flags = TURRET_FLAG_DEFAULT
+
+ /// If the turret is currently retaliating. Turrets will ignore all other settings to shoot at the attacker until they're dead or out of range
+ var/retaliating = FALSE
+
/// Same faction mobs will never be shot at, no matter the other settings
- var/list/faction = list("turret")
+ var/list/faction = list("neutral", "turret")
+
+ var/list/target_faction = list("hostile")
+
/// The spark system, used for generating... sparks?
var/datum/effect_system/spark_spread/spark_system
- /// Linked turret control panel of the turret
- var/obj/machinery/turretid/cp = null
+
/// The turret will try to shoot from a turf in that direction when in a wall
var/wall_turret_direction
- /// If the turret is manually controlled
- var/manual_control = FALSE
- /// Action button holder for quitting manual control
- var/datum/action/turret_quit/quit_action
- /// Action button holder for switching between turret modes when manually controlling
- var/datum/action/turret_toggle/toggle_action
- /// Mob that is remotely controlling the turret
- var/mob/remote_controller
- //our cooldowns
- COOLDOWN_DECLARE(fire_cooldown)
+
/// For connecting to additional turrets
var/id = ""
+ var/static/list/loc_connections = list(
+ COMSIG_ATOM_ENTERED = PROC_REF(on_entered),
+ COMSIG_ATOM_EXITED = PROC_REF(on_uncrossed),
+ )
/obj/machinery/porta_turret/Initialize()
. = ..()
- if(!base)
- base = src
+ if(!reaction_time)
+ reaction_time = shot_delay
+
+ target_beam = new(src, null, 'icons/effects/beam.dmi', "1-full", COLOR_RED, 127)
update_appearance()
//Sets up a spark system
spark_system = new /datum/effect_system/spark_spread
spark_system.set_up(5, 0, src)
spark_system.attach(src)
- setup()
- if(has_cover)
- cover = new /obj/machinery/porta_turret_cover(loc)
- cover.parent_turret = src
- var/mutable_appearance/base = mutable_appearance('icons/obj/turrets.dmi', "basedark")
- base.layer = NOT_HIGH_OBJ_LAYER
- underlays += base
- if(!has_cover)
- INVOKE_ASYNC(src, PROC_REF(popUp))
+/obj/machinery/porta_turret/Destroy()
+ targets.Cut()
+ targets = null
-/obj/machinery/porta_turret/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
- id = "[REF(port)][id]"
- port.turret_list |= WEAKREF(src)
+ set_target(null)
-/obj/machinery/porta_turret/disconnect_from_shuttle(obj/docking_port/mobile/port)
- port.turret_list -= WEAKREF(src)
+ QDEL_NULL(spark_system)
+ QDEL_NULL(target_beam)
+ remove_control()
+ return ..()
-/obj/machinery/porta_turret/proc/toggle_on(set_to)
- var/current = on
- if (!isnull(set_to))
- on = set_to
- else
- on = !on
- if (current != on)
- check_should_process()
- if (!on)
- popDown()
+/obj/machinery/porta_turret/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/connect_range, src, loc_connections, scan_range, TRUE)
-/obj/machinery/porta_turret/proc/check_should_process()
- if (datum_flags & DF_ISPROCESSING)
- if (!on || !anchored || (machine_stat & BROKEN) || !powered())
- end_processing()
- else
- if (on && anchored && !(machine_stat & BROKEN) && powered())
- begin_processing()
+/obj/machinery/porta_turret/proc/on_entered(atom/old_loc, atom/movable/new_target)
+ var/static/list/typecache_of_targets = typecacheof(list(
+ /mob/living/carbon,
+ /mob/living/silicon,
+ /mob/living/simple_animal,
+ /obj/mecha,
+ ))
-/obj/machinery/porta_turret/update_icon_state()
- if(!anchored)
- icon_state = "turretCover"
- return ..()
- if(machine_stat & BROKEN)
- icon_state = "[base_icon_state]_broken"
- return ..()
- if(!powered())
- icon_state = "[base_icon_state]_unpowered"
- return ..()
- if(!on || !raised)
- icon_state = "[base_icon_state]_off"
- return ..()
- switch(mode)
- if(TURRET_STUN)
- icon_state = "[base_icon_state]_stun"
- if(TURRET_LETHAL)
- icon_state = "[base_icon_state]_lethal"
- return ..()
+ if(is_type_in_typecache(new_target, typecache_of_targets))
+ targets |= new_target
-/obj/machinery/porta_turret/proc/setup(obj/item/gun/turret_gun)
- if(stored_gun)
- qdel(stored_gun)
- stored_gun = null
+/obj/machinery/porta_turret/proc/on_uncrossed(atom/old_loc, atom/movable/target)
+ //Should also get any deleted targets, since they're moved to nullspace
+ targets -= target
- if(installation && !turret_gun)
- stored_gun = new installation(src)
- else if (turret_gun)
- stored_gun = turret_gun
+/obj/machinery/porta_turret/RefreshParts()
+ var/obj/item/gun/turret_gun = locate() in component_parts
+
+ if(!turret_gun)
+ return
- var/list/gun_properties = stored_gun.get_turret_properties()
+ var/list/gun_properties = turret_gun.get_turret_properties()
//required properties
stun_projectile = gun_properties["stun_projectile"]
@@ -197,191 +147,249 @@ DEFINE_BITFIELD(turret_flags, list(
if(gun_properties["reqpower"])
reqpower = gun_properties["reqpower"]
- update_appearance()
+ update_appearance(UPDATE_ICON_STATE)
return gun_properties
-/obj/machinery/porta_turret/Destroy()
- //deletes its own cover with it
- QDEL_NULL(cover)
- base = null
- if(cp)
- cp.turrets -= src
- cp = null
- QDEL_NULL(stored_gun)
- QDEL_NULL(spark_system)
- remove_control()
+/obj/machinery/porta_turret/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
+ id = "[text_ref(port)][initial(id)]"
+ port.turret_list |= WEAKREF(src)
+
+/obj/machinery/porta_turret/proc/toggle_on(set_to)
+ var/current = on
+ if (!isnull(set_to))
+ on = set_to
+ else
+ on = !on
+ if (current != on)
+ check_should_process()
+
+/obj/machinery/porta_turret/proc/check_should_process()
+ var/functional = (on && anchored && !(machine_stat & BROKEN) && powered())
+ var/processing = (datum_flags & DF_ISPROCESSING)
+
+ if(processing && !functional)
+ end_processing()
+
+ var/datum/component/connect_range/prox = GetComponent(/datum/component/connect_range)
+ prox?.set_tracked(null)
+ set_target(null)
+
+ else if(!processing && functional)
+ begin_processing()
+
+ var/datum/component/connect_range/prox = GetComponent(/datum/component/connect_range)
+ prox?.set_tracked(src)
+
+/obj/machinery/porta_turret/update_icon_state()
+ if(machine_stat & BROKEN)
+ icon_state = "[base_icon_state]_broken"
+ return ..()
+ if(!powered())
+ icon_state = "[base_icon_state]_unpowered"
+ return ..()
+ if(!on)
+ icon_state = "[base_icon_state]_off"
+ return ..()
+ if(lethal)
+ icon_state = "[base_icon_state]_lethal"
+ else
+ icon_state = "[base_icon_state]_stun"
return ..()
/obj/machinery/porta_turret/ui_interact(mob/user, datum/tgui/ui)
ui = SStgui.try_update_ui(user, src, ui)
if(!ui)
- ui = new(user, src, "PortableTurret", name)
+ ui = new(user, src, "TurretControl", name)
ui.open()
/obj/machinery/porta_turret/ui_data(mob/user)
- var/list/data = list(
+ var/allow_manual_control = FALSE
+ if(issilicon(user))
+ var/mob/living/silicon/silicon_user = user
+ allow_manual_control = silicon_user.hack_software
+
+ return list(
"locked" = locked,
- "on" = on,
- "check_weapons" = turret_flags & TURRET_FLAG_AUTH_WEAPONS,
- "neutralize_criminals" = turret_flags & TURRET_FLAG_SHOOT_CRIMINALS,
- "neutralize_all" = turret_flags & TURRET_FLAG_SHOOT_ALL,
- "neutralize_unidentified" = turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS,
- "neutralize_nonmindshielded" = turret_flags & TURRET_FLAG_SHOOT_UNSHIELDED,
- "neutralize_cyborgs" = turret_flags & TURRET_FLAG_SHOOT_BORGS,
- "ignore_heads" = turret_flags & TURRET_FLAG_SHOOT_HEADS,
+ "enabled" = on,
+ "lethal" = lethal,
+ "siliconUser" = user.has_unlimited_silicon_privilege && check_ship_ai_access(user),
"manual_control" = manual_control,
- "silicon_user" = FALSE,
- "allow_manual_control" = FALSE,
+ "dangerous_only" = turret_flags & TURRET_FLAG_SHOOT_DANGEROUS_ONLY,
+ "retaliate" = turret_flags & TURRET_FLAG_SHOOT_RETALIATE,
+ "shoot_fauna" = turret_flags & TURRET_FLAG_SHOOT_FAUNA,
+ "shoot_humans" = turret_flags & TURRET_FLAG_SHOOT_HUMANS,
+ "shoot_silicons" = turret_flags & TURRET_FLAG_SHOOT_SILICONS,
+ "only_nonfaction" = turret_flags & TURRET_FLAG_SHOOT_NONFACTION,
+ "only_specificfaction" = turret_flags & TURRET_FLAG_SHOOT_SPECIFIC_FACTION,
+ "allow_manual_control" = allow_manual_control,
)
- if(issilicon(user))
- data["silicon_user"] = TRUE
- if(!manual_control)
- var/mob/living/silicon/S = user
- if(S.hack_software)
- data["allow_manual_control"] = TRUE
- return data
/obj/machinery/porta_turret/ui_act(action, list/params)
. = ..()
if(.)
return
+ if(locked)
+ to_chat(usr, span_warning("[src]'s controls are locked."))
+ return
+
switch(action)
+ if("lock")
+ if(!usr.has_unlimited_silicon_privilege)
+ return
+ toggle_lock(usr)
+ return TRUE
if("power")
if(anchored)
toggle_on()
return TRUE
else
to_chat(usr, "It has to be secured first!")
- if("authweapon")
- turret_flags ^= TURRET_FLAG_AUTH_WEAPONS
- return TRUE
- if("shootcriminals")
- turret_flags ^= TURRET_FLAG_SHOOT_CRIMINALS
+ if("manual")
+ if(!issilicon(usr))
+ return
+ var/mob/living/silicon/user = usr
+ if(!user.hack_software)
+ return
+ give_control(usr)
return TRUE
- if("shootall")
- turret_flags ^= TURRET_FLAG_SHOOT_ALL
+ if("mode")
+ lethal = !lethal
return TRUE
- if("checkxenos")
- turret_flags ^= TURRET_FLAG_SHOOT_ANOMALOUS
+
+ if("toggle_dangerous")
+ turret_flags ^= TURRET_FLAG_SHOOT_DANGEROUS_ONLY
return TRUE
- if("checkloyal")
- turret_flags ^= TURRET_FLAG_SHOOT_UNSHIELDED
+ if("toggle_retaliate")
+ turret_flags ^= TURRET_FLAG_SHOOT_RETALIATE
return TRUE
- if("shootborgs")
- turret_flags ^= TURRET_FLAG_SHOOT_BORGS
+
+ if("toggle_fauna")
+ turret_flags ^= TURRET_FLAG_SHOOT_FAUNA
return TRUE
- if("shootheads")
- turret_flags ^= TURRET_FLAG_SHOOT_HEADS
+ if("toggle_humans")
+ turret_flags ^= TURRET_FLAG_SHOOT_HUMANS
return TRUE
- if("manual")
- if(!issilicon(usr))
- return
- give_control(usr)
+ if("toggle_silicons")
+ turret_flags ^= TURRET_FLAG_SHOOT_SILICONS
return TRUE
-/obj/machinery/porta_turret/ui_host(mob/user)
- if(has_cover && cover)
- return cover
- if(base)
- return base
- return src
+ if("toggle_nonfaction")
+ turret_flags ^= TURRET_FLAG_SHOOT_NONFACTION
+ return TRUE
+ if("toggle_specificfaction")
+ turret_flags ^= TURRET_FLAG_SHOOT_SPECIFIC_FACTION
+ return TRUE
/obj/machinery/porta_turret/power_change()
. = ..()
+ if(!(flags_1 & INITIALIZED_1))
+ return
if(!anchored || (machine_stat & BROKEN) || !powered())
- update_appearance()
+ update_appearance(UPDATE_ICON_STATE)
remove_control()
+ set_target(null)
check_should_process()
/obj/machinery/porta_turret/attackby(obj/item/I, mob/user, params)
- if(machine_stat & BROKEN)
- if(I.tool_behaviour == TOOL_CROWBAR)
- //If the turret is destroyed, you can remove it with a crowbar to
- //try and salvage its components
- to_chat(user, "You begin prying the metal coverings off...")
- if(I.use_tool(src, user, 20))
- if(prob(70))
- if(stored_gun)
- stored_gun.forceMove(loc)
- stored_gun = null
- to_chat(user, "You remove the turret and salvage some components.")
- if(prob(50))
- new /obj/item/stack/sheet/metal(loc, rand(1,4))
- if(prob(50))
- new /obj/item/assembly/prox_sensor(loc)
- else
- to_chat(user, "You remove the turret but did not manage to salvage anything.")
- qdel(src)
- if(I.tool_behaviour == TOOL_WELDER && user.a_intent == INTENT_HELP)
- if(obj_integrity < max_integrity)
- if(!I.tool_start_check(user, amount=0))
- return
+ if(machine_stat & BROKEN && I.tool_behaviour == TOOL_CROWBAR)
+ //If the turret is destroyed, you can remove it with a crowbar to
+ //try and salvage its components
+ to_chat(user, "You begin prying the metal coverings off...")
+ if(I.use_tool(src, user, 20))
+ if(prob(70))
+ var/obj/item/gun/stored_gun = locate() in component_parts
+ if(stored_gun)
+ stored_gun.forceMove(loc)
+ to_chat(user, "You remove the turret and salvage some components.")
+ if(prob(50))
+ new /obj/item/stack/sheet/metal(loc, rand(1,4))
+ if(prob(50))
+ new /obj/item/assembly/prox_sensor(loc)
+ else
+ to_chat(user, "You remove the turret but did not manage to salvage anything.")
+ qdel(src)
+ return
- to_chat(user, "You begin repairing [src]...")
- if(I.use_tool(src, user, 40, volume=50))
- obj_integrity = obj_integrity + 20
- to_chat(user, "You repair [src].")
- if(obj_integrity > (max_integrity * integrity_failure) && BROKEN)
- obj_integrity = max_integrity
- set_machine_stat(machine_stat & ~BROKEN)
- update_appearance()
- check_should_process()
- else
+ if(I.tool_behaviour == TOOL_WELDER && user.a_intent == INTENT_HELP)
+ if(obj_integrity >= max_integrity)
to_chat(user, "[src] is already in good condition!")
- return
+ return
+ to_chat(user, "You begin repairing [src]...")
+ while(obj_integrity < max_integrity)
+ if(!I.use_tool(src, user, 4 SECONDS, 2, 50))
+ break
+ obj_integrity = max(obj_integrity + 20, max_integrity)
+ to_chat(user, "You repair [src].")
- else if((I.tool_behaviour == TOOL_WRENCH) && (!on))
- if(raised)
- return
+ if(obj_integrity > (max_integrity * integrity_failure) && (machine_stat & BROKEN))
+ obj_integrity = max_integrity
+ set_machine_stat(machine_stat & ~BROKEN)
+ update_appearance(UPDATE_ICON_STATE)
+ check_should_process()
+
+ return
+
+ if((I.tool_behaviour == TOOL_WRENCH) && !on)
//This code handles moving the turret around. After all, it's a portable turret!
if(!anchored && !isinspace())
set_anchored(TRUE)
- invisibility = INVISIBILITY_MAXIMUM
- update_appearance()
+ update_appearance(UPDATE_ICON_STATE)
to_chat(user, "You secure the exterior bolts on the turret.")
- if(has_cover)
- cover = new /obj/machinery/porta_turret_cover(loc) //create a new turret. While this is handled in process(), this is to workaround a bug where the turret becomes invisible for a split second
- cover.parent_turret = src //make the cover's parent src
else if(anchored)
set_anchored(FALSE)
to_chat(user, "You unsecure the exterior bolts on the turret.")
power_change()
- invisibility = 0
- qdel(cover) //deletes the cover, and the turret instance itself becomes its own cover.
-
- if(I.GetID())
- //Behavior lock/unlock mangement
- if(allowed(user))
- locked = !locked
- to_chat(user, "Controls are now [locked ? "locked" : "unlocked"].")
- else
- to_chat(user, "Access denied.")
return
- if(I.tool_behaviour == TOOL_MULTITOOL && !locked)
+ if(I.tool_behaviour == TOOL_MULTITOOL)
+ if(locked)
+ to_chat(user, span_warning("The controls are locked."))
+ return
if(!multitool_check_buffer(user, I))
return
var/obj/item/multitool/M = I
M.buffer = src
to_chat(user, "You add [src] to multitool buffer.")
return
+
+ if(istype(I, /obj/item/card/id))
+ toggle_lock(user)
+ return
+
return ..()
+/obj/machinery/porta_turret/AltClick(mob/user)
+ . = ..()
+ toggle_lock(user)
+
+/obj/machinery/porta_turret/proc/toggle_lock(mob/user)
+ if(!user.canUseTopic(src, !issilicon(user)))
+ return
+ if(!allowed(user))
+ to_chat(user, span_alert("Access denied."))
+ return
+ if(obj_flags & EMAGGED || (machine_stat & (BROKEN|MAINT)))
+ to_chat(user, span_warning("The turret is unresponsive!"))
+ return
+
+ to_chat(user, span_notice("You [locked ? "unlock" : "lock"] [src]."))
+ locked = !locked
+ update_appearance()
+
/obj/machinery/porta_turret/emag_act(mob/user)
if(obj_flags & EMAGGED)
return
to_chat(user, "You short out [src]'s threat assessment circuits.")
audible_message("[src] hums oddly...")
obj_flags |= EMAGGED
- controllock = TRUE
+ locked = TRUE
toggle_on(FALSE) //turns off the turret temporarily
- update_appearance()
+ update_appearance(UPDATE_ICON_STATE)
//6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit
addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), 6 SECONDS)
- //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
/obj/machinery/porta_turret/emp_act(severity)
. = ..()
@@ -390,900 +398,231 @@ DEFINE_BITFIELD(turret_flags, list(
if(on)
//if the turret is on, the EMP no matter how severe disables the turret for a while
//and scrambles its settings, with a slight chance of having an emag effect
- if(prob(50))
- turret_flags |= TURRET_FLAG_SHOOT_CRIMINALS
- if(prob(50))
- turret_flags |= TURRET_FLAG_AUTH_WEAPONS
- if(prob(20))
- turret_flags |= TURRET_FLAG_SHOOT_ALL // Shooting everyone is a pretty big deal, so it's least likely to get turned on
+ if(prob(5))
+ turret_flags ^= TURRET_FLAG_SHOOT_HUMANS
+ if(prob(5))
+ turret_flags ^= TURRET_FLAG_SHOOT_FAUNA
+ if(prob(1))
+ turret_flags ^= TURRET_FLAG_SHOOT_NONFACTION
+ if(prob(1))
+ turret_flags ^= TURRET_FLAG_SHOOT_SPECIFIC_FACTION
toggle_on(FALSE)
remove_control()
- addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), rand(60,600))
+ addtimer(CALLBACK(src, PROC_REF(toggle_on), TRUE), rand(6 SECONDS, 60 SECONDS))
/obj/machinery/porta_turret/take_damage(damage, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
. = ..()
- if(. && obj_integrity > 0) //damage received
- if(prob(30))
- spark_system.start()
- if(on && !(turret_flags & TURRET_FLAG_SHOOT_ALL_REACT) && !(obj_flags & EMAGGED))
- turret_flags |= TURRET_FLAG_SHOOT_ALL_REACT
- addtimer(CALLBACK(src, PROC_REF(reset_attacked)), 60)
+ if(!. || obj_integrity <= 0)
+ return
+ //damage received
+ if(prob(30))
+ spark_system.start()
-/obj/machinery/porta_turret/proc/reset_attacked()
- turret_flags &= ~TURRET_FLAG_SHOOT_ALL_REACT
+/obj/machinery/porta_turret/proc/retaliate(mob/living/target)
+ if(!(turret_flags & TURRET_FLAG_SHOOT_RETALIATE) || current_target || !on || (req_ship_access && allowed(target)) || (machine_stat & BROKEN|NOPOWER|MAINT))
+ return
-/obj/machinery/porta_turret/deconstruct(disassembled = TRUE)
- qdel(src)
+ set_target(target)
+ target(target)
+ retaliating = TRUE
+
+/obj/machinery/porta_turret/bullet_act(obj/projectile/hitting_projectile)
+ . = ..()
+ if(ismob(hitting_projectile.firer))
+ retaliate(hitting_projectile.firer)
+
+/obj/machinery/porta_turret/attacked_by(obj/item/I, mob/living/user)
+ . = ..()
+ if(!I.force || I.damtype == STAMINA)
+ return
+ retaliate(user)
/obj/machinery/porta_turret/obj_break(damage_flag)
. = ..()
if(.)
power_change()
- invisibility = 0
spark_system.start() //creates some sparks because they look cool
- qdel(cover) //deletes the cover - no need on keeping it there!
/obj/machinery/porta_turret/process()
- //the main machinery process
- if(cover == null && anchored) //if it has no cover and is anchored
- if(machine_stat & BROKEN) //if the turret is borked
- qdel(cover) //delete its cover, assuming it has one. Workaround for a pesky little bug
- else
- if(has_cover)
- cover = new /obj/machinery/porta_turret_cover(loc) //if the turret has no cover and is anchored, give it a cover
- cover.parent_turret = src //assign the cover its parent_turret, which would be this (src)
-
if(!on || (machine_stat & (NOPOWER|BROKEN)) || manual_control)
return PROCESS_KILL
- var/list/targets = list()
- for(var/mob/A in view(scan_range, base))
- if(A.invisibility > SEE_INVISIBLE_LIVING)
- continue
-
- if(turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS)//if it's set to check for simple animals
- if(isanimal(A))
- var/mob/living/simple_animal/SA = A
- if(SA.stat || in_faction(SA)) //don't target if dead or in faction
- continue
- targets += SA
- continue
-
- if(issilicon(A))
- var/mob/living/silicon/sillycone = A
-
- if(ispAI(A))
- continue
-
- if((turret_flags & TURRET_FLAG_SHOOT_BORGS) && sillycone.stat != DEAD && iscyborg(sillycone))
- targets += sillycone
- continue
-
- if(sillycone.stat || in_faction(sillycone))
- continue
-
- if(iscyborg(sillycone))
- var/mob/living/silicon/robot/sillyconerobot = A
- if(LAZYLEN(faction) && (ROLE_SYNDICATE in faction) && sillyconerobot.emagged == TRUE)
- continue
-
- else if(iscarbon(A))
- var/mob/living/carbon/C = A
- //If not emagged, only target carbons that can use items
- if(mode != TURRET_LETHAL && (C.stat || C.handcuffed || !(C.mobility_flags & MOBILITY_USE)))
- continue
-
- //If emagged, target all but dead carbons
- if(mode == TURRET_LETHAL && C.stat == DEAD)
- continue
-
- //if the target is a human and not in our faction, analyze threat level
- if(ishuman(C) && !in_faction(C))
-
- if(assess_perp(C) >= 4)
- targets += C
- else if(turret_flags & TURRET_FLAG_SHOOT_ANOMALOUS) //non humans who are not simple animals (xenos etc)
- if(!in_faction(C))
- targets += C
-
- for(var/A in GLOB.mechas_list)
- if((get_dist(A, base) < scan_range) && can_see(base, A, scan_range))
- var/obj/mecha/Mech = A
- if(Mech.occupant && !in_faction(Mech.occupant)) //If there is a user and they're not in our faction
- if(assess_perp(Mech.occupant) >= 4)
- targets += Mech
-
- if(targets.len)
- tryToShootAt(targets)
- else if(!always_up)
- popDown() // no valid targets, close the cover
-
-/obj/machinery/porta_turret/proc/tryToShootAt(list/atom/movable/targets)
- while(targets.len > 0)
- var/atom/movable/M = pick(targets)
- targets -= M
- if(target(M))
- return 1
-
-/obj/machinery/porta_turret/proc/popUp() //pops the turret up
- if(!anchored)
- return
- if(raising || raised)
- return
- if(machine_stat & BROKEN)
- return
- invisibility = 0
- raising = 1
- if(cover)
- flick("popup", cover)
- sleep(POPUP_ANIM_TIME)
- raising = 0
- if(cover)
- cover.icon_state = "openTurretCover"
- raised = 1
- layer = MOB_LAYER
-
-/obj/machinery/porta_turret/proc/popDown() //pops the turret down
- if(raising || !raised)
- return
- if(machine_stat & BROKEN)
- return
- layer = OBJ_LAYER
- raising = 1
- if(cover)
- flick("popdown", cover)
- sleep(POPDOWN_ANIM_TIME)
- raising = 0
- if(cover)
- cover.icon_state = "turretCover"
- raised = 0
- invisibility = 2
- update_appearance()
-
-/obj/machinery/porta_turret/proc/assess_perp(mob/living/carbon/human/perp)
- var/threatcount = 0 //the integer returned
-
- if(obj_flags & EMAGGED)
- return 10 //if emagged, always return 10.
-
- if((turret_flags & (TURRET_FLAG_SHOOT_ALL | TURRET_FLAG_SHOOT_ALL_REACT)) && !allowed(perp))
- //if the turret has been attacked or is angry, target all non-sec people
- if(!allowed(perp))
- return 10
-
- if(turret_flags & TURRET_FLAG_AUTH_WEAPONS) //check for weapon authorization
- if(isnull(perp.wear_id) || istype(perp.wear_id.GetID(), /obj/item/card/id/syndicate))
-
- if(allowed(perp)) //if the perp has security access, return 0
- return 0
- if(perp.is_holding_item_of_type(/obj/item/gun) || perp.is_holding_item_of_type(/obj/item/melee/baton))
- threatcount += 4
-
- if(istype(perp.belt, /obj/item/gun) || istype(perp.belt, /obj/item/melee/baton))
- threatcount += 2
-
- if(turret_flags & TURRET_FLAG_SHOOT_CRIMINALS) //if the turret can check the records, check if they are set to *Arrest* on records
- var/perpname = perp.get_face_name(perp.get_id_name())
- var/datum/data/record/R = find_record("name", perpname, GLOB.data_core.security)
- if(!R || (R.fields["criminal"] == "*Arrest*"))
- threatcount += 4
-
- if((turret_flags & TURRET_FLAG_SHOOT_UNSHIELDED) && (!HAS_TRAIT(perp, TRAIT_MINDSHIELD)))
- threatcount += 4
-
- // If we aren't shooting heads then return a threatcount of 0
- if (!(turret_flags & TURRET_FLAG_SHOOT_HEADS) && (perp.get_assignment() in GLOB.command_positions))
- return 0
-
- return threatcount
-
-/obj/machinery/porta_turret/proc/in_faction(mob/target)
- for(var/faction1 in faction)
- if(faction1 in target.faction)
- return TRUE
- if(ismouse(target))
- return TRUE
- return FALSE
-
-/obj/machinery/porta_turret/proc/target(atom/movable/target)
- if(target)
- popUp() //pop the turret up if it's not already up.
- setDir(get_dir(base, target))//even if you can't shoot, follow the target
- shootAt(target)
- return 1
- return
-
-/obj/machinery/porta_turret/proc/shootAt(atom/movable/target)
- if(!raised) //the turret has to be raised in order to fire - makes sense, right?
+ if(!COOLDOWN_FINISHED(src, fire_cooldown))
return
- if(!(obj_flags & EMAGGED)) //if it hasn't been emagged, cooldown before shooting again
- if(!COOLDOWN_FINISHED(src, fire_cooldown))
+ if(current_target)
+ //Try to fire at the current target first
+ if(check_target(current_target) && target(current_target))
return
- COOLDOWN_START(src, fire_cooldown, shot_delay)
-
- var/turf/T = get_turf(src)
- var/turf/U = get_turf(target)
- if(!istype(T) || !istype(U))
- return
- //Wall turrets will try to find adjacent empty turf to shoot from to cover full arc
- if(T.density)
- if(wall_turret_direction)
- var/turf/closer = get_step(T,wall_turret_direction)
- if(istype(closer) && !closer.is_blocked_turf() && T.Adjacent(closer))
- T = closer
- else
- var/target_dir = get_dir(T,target)
- for(var/d in list(0,-45,45))
- var/turf/closer = get_step(T,turn(target_dir,d))
- if(istype(closer) && !closer.is_blocked_turf() && T.Adjacent(closer))
- T = closer
- break
-
- update_appearance()
- var/obj/projectile/A
- //any emagged turrets drains 2x power and uses a different projectile?
- if(mode == TURRET_STUN)
- use_power(reqpower)
- A = new stun_projectile(T)
- playsound(loc, stun_projectile_sound, 75, TRUE)
- else
- use_power(reqpower * 2)
- A = new lethal_projectile(T)
- playsound(loc, lethal_projectile_sound, 75, TRUE)
+ //Current target is invalid, so we need to find a new one
+ set_target(null)
+ for(var/atom/movable/target as anything in targets)
+ //TODO: Remove this if it never happens, because it shouldn't
+ if(QDELETED(target))
+ targets -= target
+ stack_trace("Qdeleted target in turret list")
+ return FALSE
- //Shooting Code:
- A.preparePixelProjectile(target, T)
- A.firer = src
- A.fired_from = src
- A.fire()
- return A
-
-/obj/machinery/porta_turret/proc/setState(on, mode, shoot_cyborgs)
- if(controllock)
- return
-
- shoot_cyborgs ? (turret_flags |= TURRET_FLAG_SHOOT_BORGS) : (turret_flags &= ~TURRET_FLAG_SHOOT_BORGS)
- toggle_on(on)
- src.mode = mode
- power_change()
+ if(isnull(target))
+ targets -= target
+ stack_trace("Null target in turret list")
+ continue
-/datum/action/turret_toggle
- name = "Toggle Mode"
- icon_icon = 'icons/mob/actions/actions_mecha.dmi'
- button_icon_state = "mech_cycle_equip_off"
+ if(check_target(target))
+ break
-/datum/action/turret_toggle/Trigger()
- var/obj/machinery/porta_turret/P = target
- if(!istype(P))
- return
- P.setState(P.on,!P.mode)
+/obj/machinery/porta_turret/proc/check_target(atom/movable/target, check_flags = turret_flags)
+ // mecha|carbon|silicon|simple_animal
+ if(ismecha(target))
+ var/obj/mecha/mech = target
+ if(!mech.occupant)
+ targets -= target
+ return FALSE
+ target = mech.occupant
-/datum/action/turret_quit
- name = "Release Control"
- icon_icon = 'icons/mob/actions/actions_mecha.dmi'
- button_icon_state = "mech_eject"
+ // We know the target must be a mob now
+ var/mob/target_mob = target
-/datum/action/turret_quit/Trigger()
- var/obj/machinery/porta_turret/P = target
- if(!istype(P))
- return
- P.remove_control(FALSE)
-
-/obj/machinery/porta_turret/proc/give_control(mob/A)
- if(manual_control || !can_interact(A))
+ if(target_mob.stat == DEAD)
+ //They probably won't need to be re-checked
+ targets -= target
return FALSE
- remote_controller = A
- if(!quit_action)
- quit_action = new(src)
- quit_action.Grant(remote_controller)
- if(!toggle_action)
- toggle_action = new(src)
- toggle_action.Grant(remote_controller)
- remote_controller.reset_perspective(src)
- remote_controller.click_intercept = src
- manual_control = TRUE
- always_up = TRUE
- popUp()
- return TRUE
-/obj/machinery/porta_turret/proc/remove_control(warning_message = TRUE)
- if(!manual_control)
+ if((check_flags & TURRET_FLAG_SHOOT_NONFACTION) && faction_check(src.faction, target_mob.faction))
return FALSE
- if(remote_controller)
- if(warning_message)
- to_chat(remote_controller, "Your uplink to [src] has been severed!")
- quit_action.Remove(remote_controller)
- toggle_action.Remove(remote_controller)
- remote_controller.click_intercept = null
- remote_controller.reset_perspective()
- always_up = initial(always_up)
- manual_control = FALSE
- remote_controller = null
- return TRUE
-/obj/machinery/porta_turret/proc/InterceptClickOn(mob/living/caller, params, atom/A)
- if(!manual_control)
- return FALSE
- if(!can_interact(caller))
- remove_control()
+ if((check_flags & TURRET_FLAG_SHOOT_SPECIFIC_FACTION) && !faction_check(src.faction, target_mob.faction))
return FALSE
- log_combat(caller,A,"fired with manual turret control at")
- target(A)
- return TRUE
-
-/obj/machinery/porta_turret/syndicate
- installation = null
- always_up = 1
- use_power = NO_POWER_USE
- has_cover = 0
- scan_range = 9
- req_access = list(ACCESS_SYNDICATE)
- mode = TURRET_LETHAL
- stun_projectile = /obj/projectile/bullet
- lethal_projectile = /obj/projectile/bullet
- lethal_projectile_sound = 'sound/weapons/gun/pistol/shot.ogg'
- stun_projectile_sound = 'sound/weapons/gun/pistol/shot.ogg'
- icon_state = "syndie_off"
- base_icon_state = "syndie"
- faction = list(ROLE_SYNDICATE)
- desc = "A ballistic machine gun auto-turret."
-
-/obj/machinery/porta_turret/syndicate/ComponentInitialize()
- . = ..()
- AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
-
-/obj/machinery/porta_turret/syndicate/setup()
- return
-
-/obj/machinery/porta_turret/syndicate/assess_perp(mob/living/carbon/human/perp)
- return 10 //Syndicate turrets shoot everything not in their faction
-
-/obj/machinery/porta_turret/syndicate/energy
- icon_state = "standard_lethal"
- base_icon_state = "standard"
- stun_projectile = /obj/projectile/energy/electrode
- stun_projectile_sound = 'sound/weapons/taser.ogg'
- lethal_projectile = /obj/projectile/beam/laser
- lethal_projectile_sound = 'sound/weapons/laser.ogg'
- desc = "An energy blaster auto-turret."
-
-/obj/machinery/porta_turret/syndicate/energy/heavy
- icon_state = "standard_lethal"
- base_icon_state = "standard"
- stun_projectile = /obj/projectile/energy/electrode
- stun_projectile_sound = 'sound/weapons/taser.ogg'
- lethal_projectile = /obj/projectile/beam/laser/heavylaser
- lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
- desc = "An energy blaster auto-turret."
-
-/obj/machinery/porta_turret/syndicate/energy/raven
- stun_projectile = /obj/projectile/beam/laser
- stun_projectile_sound = 'sound/weapons/laser.ogg'
- faction = list("neutral","silicon","turret")
-
-/obj/machinery/porta_turret/syndicate/pod
- integrity_failure = 0.5
- max_integrity = 40
- stun_projectile = /obj/projectile/bullet/syndicate_turret
- lethal_projectile = /obj/projectile/bullet/syndicate_turret
-
-/obj/machinery/porta_turret/ai
- faction = list("silicon")
- turret_flags = TURRET_FLAG_SHOOT_CRIMINALS | TURRET_FLAG_SHOOT_ANOMALOUS | TURRET_FLAG_SHOOT_HEADS
-
-/obj/machinery/porta_turret/ai/assess_perp(mob/living/carbon/human/perp)
- return 10 //AI turrets shoot at everything not in their faction
-
-/obj/machinery/porta_turret/ship
- installation = null
- max_integrity = 200
- always_up = 1
- use_power = ACTIVE_POWER_USE
- active_power_usage = ACTIVE_DRAW_MINIMAL
- has_cover = 0
- scan_range = 9
- req_ship_access = TRUE
- stun_projectile = /obj/projectile/beam/disabler
- lethal_projectile = /obj/projectile/beam/laser
- lethal_projectile_sound = 'sound/weapons/plasma_cutter.ogg'
- stun_projectile_sound = 'sound/weapons/plasma_cutter.ogg'
- icon_state = "syndie_off"
- base_icon_state = "syndie"
- faction = list("neutral", "turret")
- mode = TURRET_STUN
-
-/obj/machinery/porta_turret/ship/ComponentInitialize()
- . = ..()
- AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
-
-/obj/machinery/porta_turret/ship/setup()
- return
-
-/obj/machinery/porta_turret/ship/examine(mob/user)
- . = ..()
- if(in_range(user, src) || isobserver(user))
- if(!(machine_stat & BROKEN))
- . += "[src] reports its integrity is currently [round((obj_integrity / max_integrity) * 100)] percent."
-
-/obj/machinery/porta_turret/ship/weak
- max_integrity = 120
- integrity_failure = 0.5
- name = "Old Laser Turret"
- desc = "A turret built with substandard parts and run down further with age. Still capable of delivering lethal lasers to the odd space carp, but not much else."
- stun_projectile = /obj/projectile/beam/disabler/weak
- lethal_projectile = /obj/projectile/beam/weak/penetrator
- faction = list("neutral", "turret")
-
-/obj/machinery/porta_turret/ship/ballistic
- stun_projectile = /obj/projectile/bullet/turret/rubber
- lethal_projectile = /obj/projectile/bullet/turret
- lethal_projectile_sound = 'sound/weapons/gun/smg/shot.ogg'
- stun_projectile_sound = 'sound/weapons/gun/smg/shot.ogg'
- desc = "A ballistic machine gun auto-turret."
-
-//high rof, range, faster projectile speed
-/* 'Nanotrasen' turrets */
-
-/obj/machinery/porta_turret/ship/nt
- name = "Sharplite Defense Turret"
- desc = "A cheap and effective turret designed by Sharplite and purchased and installed on most Nanotrasen Vessels."
- faction = list(FACTION_PLAYER_NANOTRASEN, "turret")
- max_integrity = 160
- integrity_failure = 0.6
- icon_state = "standard_lethal"
- base_icon_state = "standard"
- stun_projectile = /obj/projectile/beam/disabler/sharplite
- lethal_projectile = /obj/projectile/beam/laser/sharplite
- lethal_projectile_sound = 'sound/weapons/gun/laser/nt-fire.ogg'
- stun_projectile_sound = 'sound/weapons/taser2.ogg'
- shot_delay = 10
- scan_range = 10
-
-/obj/machinery/porta_turret/ship/nt/light
- name = "Sharplite LDS"
- desc = "A cheap and effective 'defensive system' designed by Sharplite for installation on Nanotrasen vessels."
- stun_projectile = /obj/projectile/beam/disabler/weak/sharplite
- lethal_projectile = /obj/projectile/beam/laser/light/sharplite
- lethal_projectile_sound = 'sound/weapons/gun/laser/nt-fire.ogg'
- stun_projectile_sound = 'sound/weapons/taser2.ogg'
-
-/obj/machinery/porta_turret/ship/nt/heavy
- name = "Sharplite Defense Cannon"
- desc = "A heavy laser mounting designed by Sharplite for usage on Nanotrasen vessels."
- lethal_projectile = /obj/projectile/beam/laser/heavylaser/sharplite
- lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
- max_integrity = 250
-
-/obj/machinery/porta_turret/ship/nt/pulse
- name = "Sharplite Pulse Cannon"
- desc = "A pulse cannon mounting designed by Sharplite. Not sold to any purchasers and exclusively used on Nanotrasen Vessels."
- lethal_projectile = /obj/projectile/beam/pulse/sharplite_turret
- lethal_projectile_sound = 'sound/weapons/gun/laser/heavy_laser.ogg'
- max_integrity = 250
-
-/* Syndicate Turrets */
-
-/obj/machinery/porta_turret/ship/syndicate
- faction = list(FACTION_PLAYER_SYNDICATE, "turret")
- icon_state = "standard_lethal"
- base_icon_state = "standard"
-
-/obj/machinery/porta_turret/ship/syndicate/weak
- name = "Light Laser Turret"
- desc = "A low powered turret designed by the Gorlex Maurauders during the ICW. Effectively weaponizes mining equipment."
- stun_projectile = /obj/projectile/beam/disabler/weak
- lethal_projectile = /obj/projectile/beam/weak/penetrator
- icon_state = "syndie_off"
- base_icon_state = "syndie"
-
-/obj/machinery/porta_turret/ship/syndicate/heavy
- name = "Heavy Laser Turret"
- desc = "Produced by Cybersun, this turret is a duel mount of a propietary heavy laser, and crowd control taser system."
- stun_projectile = /obj/projectile/energy/electrode
- stun_projectile_sound = 'sound/weapons/taser.ogg'
- lethal_projectile = /obj/projectile/beam/laser/heavylaser
- lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
- max_integrity = 300
-
-/* New Gorlex Republic Turrets */
-// Midline ballistic turrets
-
-/obj/machinery/porta_turret/ship/ngr
- name = "Oasis Turret"
- desc = "A turret manufactured by the New Gorlex Republic for its ships and installations. Proudly manufactured within the nation!"
- stun_projectile = /obj/projectile/bullet/c57x39mm/rubber
- stun_projectile_sound = 'sound/weapons/gun/smg/sidewinder.ogg'
- lethal_projectile = /obj/projectile/bullet/c57x39mm
- lethal_projectile_sound = 'sound/weapons/gun/smg/sidewinder.ogg'
- faction = list(FACTION_NGR, FACTION_PLAYER_SYNDICATE, "turret") //player_syndicate is just to be safe
-
-/obj/machinery/porta_turret/ship/ngr/light
- name = "Sonoran Turret"
- desc = "A light turret manufactured by the New Gorlex Republic for its ships and installations. Proudly manufactured within the Nation, using locally produced munitions!"
- stun_projectile = /obj/projectile/bullet/c45/rubber
- stun_projectile_sound = 'sound/weapons/gun/smg/cobra.ogg'
- lethal_projectile = /obj/projectile/bullet/c45
- lethal_projectile_sound = 'sound/weapons/gun/smg/cobra.ogg'
- scan_range = 7
- shot_delay = 10
-
-/obj/machinery/porta_turret/ship/ngr/heavy
- name = "Cliff Turret"
- desc = "A heavy turret manufactured by the New Gorlex Republic for its ships and installations. Has a reputation of being extremely dangerous."
- stun_projectile = /obj/projectile/bullet/a65clip/rubber
- stun_projectile_sound = 'sound/weapons/gun/sniper/cmf90.ogg'
- lethal_projectile = /obj/projectile/bullet/a65clip
- lethal_projectile_sound = 'sound/weapons/gun/sniper/cmf90.ogg'
- scan_range = 14
- shot_delay = 30
-
-
-/* Inteq Turrets */
-//slower rof, higher damage + range
-
-/obj/machinery/porta_turret/ship/inteq
- name = "Vanguard Turret"
- desc = "A turret designed by IRMG engineers for defending ships from hostile flora, fauna, and people (and Elzousa, which count as flora and people)."
- stun_projectile = /obj/projectile/bullet/a762_40/rubber
- stun_projectile_sound = 'sound/weapons/gun/rifle/skm.ogg'
- lethal_projectile = /obj/projectile/bullet/a762_40
- lethal_projectile_sound = 'sound/weapons/gun/rifle/skm.ogg'
- scan_range = 9
- shot_delay = 20
- integrity_failure = 0.4
- faction = list(FACTION_PLAYER_INTEQ, "turret")
-
-/obj/machinery/porta_turret/ship/inteq/light
- name = "Close-In Vanguard Turret"
- desc = "A light turret designed by IRMG engineers for the the task of defending from close-in encounters. Low power, high speed."
- stun_projectile = /obj/projectile/bullet/c10mm/rubber
- stun_projectile_sound = 'sound/weapons/gun/smg/vector_fire.ogg'
- lethal_projectile = /obj/projectile/bullet/c10mm
- lethal_projectile_sound = 'sound/weapons/gun/smg/vector_fire.ogg'
- subsystem_type = /datum/controller/subsystem/processing/fastprocess //turns out if you have a shot delay below what SSmachines fires at you need to use a different subsystem
- scan_range = 5
- shot_delay = 5
-
-/obj/machinery/porta_turret/ship/inteq/heavy
- name = "Vanguard Overwatch Turret"
- desc = "A turret designed by IRMG engineers to provide long range defensive fire on their installations. Has a habit of leaving big holes."
- stun_projectile = /obj/projectile/bullet/a308/rubber
- stun_projectile_sound = 'sound/weapons/gun/rifle/f4.ogg'
- lethal_projectile = /obj/projectile/bullet/a308
- lethal_projectile_sound = 'sound/weapons/gun/rifle/f4.ogg'
- scan_range = 12
- shot_delay = 20
-
-/* Solcon Turrets */
-
-/obj/machinery/porta_turret/ship/solgov
- faction = list(FACTION_PLAYER_SOLCON, "turret")
-
-/* Pan Gezena Federation Turrets */
-//midline but hitscan
-
-/obj/machinery/porta_turret/ship/pgf
- name = "Etherbor Defensive Mount"
- desc = "A less portable Etherbor offering, the EDM is a self-directed linkage of energy weapons, designed to keep intruders away from Gezenan vessels."
- faction = list(FACTION_PLAYER_GEZENA, "Turret")
- stun_projectile = /obj/projectile/beam/hitscan/disabler
- stun_projectile_sound = 'sound/weapons/gun/energy/kalixpistol.ogg'
- lethal_projectile = /obj/projectile/beam/hitscan/kalix/pgf/assault
- lethal_projectile_sound = 'sound/weapons/gun/energy/kalixsmg.ogg'
- icon_state = "standard_lethal"
- base_icon_state = "standard"
- max_integrity = 250
- integrity_failure = 0.4
-
-/obj/machinery/porta_turret/ship/pgf/light
- name = "Etherbor Deterrent System"
- desc = "A light turret manufactured by Etherbor. It offers a lightweight assembly of energy weapons to accost nearby foes."
- lethal_projectile = /obj/projectile/beam/hitscan/kalix/pgf
- lethal_projectile_sound = 'sound/weapons/gun/energy/kalixsmg.ogg'
-
-/obj/machinery/porta_turret/ship/pgf/heavy
- name = "Etherbor Point-Defense System"
- desc = "A high-powered defensive turret manufactured by Etherbor. The EPDS contains heavy energy weapons linked in tandem."
- scan_range = 12
- stun_projectile = /obj/projectile/beam/hitscan/disabler/heavy
- stun_projectile_sound = 'sound/weapons/gun/energy/kalixpistol.ogg'
- lethal_projectile = /obj/projectile/beam/hitscan/kalix/pgf/sniper //fwoom
- lethal_projectile_sound = 'sound/weapons/gun/laser/heavy_laser.ogg'
-
-///CLIP Turrets
-
-//high damage low range
-
-/obj/machinery/porta_turret/ship/clip
- name = "Clover Mintaka"
- desc = "Clover Photonic's offering for the Confederated League's 476FS \"Defense System\" competition, the Mintaka (and its sister systems, the Alnitak and Ori) handily beat out the Lunatex \"Vigil\" line during the final round of testing, and earned a prestigous contract."
- faction = list(FACTION_PLAYER_MINUTEMAN, "Turret")
- stun_projectile = /obj/projectile/beam/disabler
- stun_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
- lethal_projectile = /obj/projectile/beam/laser/assault
- lethal_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
- icon_state = "standard_lethal"
- base_icon_state = "standard"
-
- scan_range = 8
- shot_delay = 10
- max_integrity = 200
- integrity_failure = 0.3
-
-/obj/machinery/porta_turret/ship/clip/light
- name = "Clover Alnitak"
- desc = "Clover Photonic's light turret system, unveiled as part of Clover's defense line-up in the early 470s. While lacking the punch of its sister systems, it still presents a hassle to circumvent."
- stun_projectile = /obj/projectile/beam/disabler
- stun_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
- lethal_projectile = /obj/projectile/beam/laser/light
- lethal_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
-
- scan_range = 6
- shot_delay = 10
- max_integrity = 200
- integrity_failure = 0.4
-/obj/machinery/porta_turret/ship/clip/heavy
- name = "Clover Ori"
- desc = "Clover Photonic's heaviest entry in the Confederated League's 476FS \"Defense System\" competition, the Ori's results demolished the handily beat out the Lunatex \"Vigil Sword\" during testing, earning better marks on durability, effectiveness, and reaction rate."
- stun_projectile = /obj/projectile/beam/disabler
- stun_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
- lethal_projectile = /obj/projectile/beam/laser/heavylaser/assault
- lethal_projectile_sound = 'sound/weapons/gun/laser/e40_las.ogg'
+ if(iscyborg(target_mob))
+ return (check_flags & TURRET_FLAG_SHOOT_SILICONS) && target(target_mob)
- scan_range = 10
- shot_delay = 20
- max_integrity = 300
- integrity_failure = 0.3
+ if(!ishuman(target_mob))
+ if(!(check_flags & TURRET_FLAG_SHOOT_FAUNA))
+ return FALSE
+ if(!(check_flags & TURRET_FLAG_SHOOT_DANGEROUS_ONLY))
+ return target(target_mob)
-/// Frontiersmen Turrets
+ //this is gross
+ var/static/list/dangerous_fauna = typecacheof(list(/mob/living/simple_animal/hostile, /mob/living/carbon/alien, /mob/living/carbon/monkey))
+ if(!is_type_in_typecache(target_mob, dangerous_fauna))
+ return FALSE
-// fast and spitty
+ if(ismonkey(target_mob))
+ var/mob/living/carbon/monkey/monke = target_mob
+ return monke.mode == MONKEY_HUNT && target(target_mob)
+ if(istype(target_mob, /mob/living/simple_animal/hostile/retaliate))
+ var/mob/living/simple_animal/hostile/retaliate/target_animal = target_mob
+ return length(target_animal.enemies) && target(target_mob)
-/obj/machinery/porta_turret/ship/frontiersmen
- name = "Spitter Turret"
- desc = "A juryrigged mishmash of a 9mm SMG and targetting system. Stand clear!"
- faction = list(FACTION_FRONTIER, "Turret")
- subsystem_type = /datum/controller/subsystem/processing/fastprocess
- integrity_failure = 0.6
- max_integrity = 180
+ return target(target_mob)
- icon_state = "standard_lethal"
- base_icon_state = "standard"
-
- stun_projectile = /obj/projectile/bullet/c9mm
- stun_projectile_sound = 'sound/weapons/gun/smg/spitter.ogg'
- lethal_projectile = /obj/projectile/bullet/c9mm
- lethal_projectile_sound = 'sound/weapons/gun/smg/spitter.ogg'
- shot_delay = 2
- scan_range = 6
-
-/obj/machinery/porta_turret/ship/frontiersmen/assess_perp(mob/living/carbon/human/perp)
- return 10 //Syndicate turrets shoot everything not in their faction //this needs to be default behavior and im gonna refactor it soon
-
-/obj/machinery/porta_turret/ship/frontiersmen/light
- name = "Pounder Turret"
- desc = "A low caliber SMG with an atrociously high cycle rate, frankensteined together with a targetting assembly."
- stun_projectile = /obj/projectile/bullet/c22lr
- stun_projectile_sound = 'sound/weapons/gun/smg/pounder.ogg'
- lethal_projectile = /obj/projectile/bullet/c22lr
- lethal_projectile_sound = 'sound/weapons/gun/smg/pounder.ogg'
- shot_delay = 1
-
-/obj/machinery/porta_turret/ship/frontiersmen/heavy
- name = "Mulcher Turret"
- desc = "An abombination made out of the components of a Shredder and an automatic targetting system. Careful now."
- stun_projectile = /obj/projectile/bullet/slug/beanbag
- stun_projectile_sound = 'sound/weapons/gun/hmg/shredder.ogg'
- lethal_projectile = /obj/projectile/bullet/slug
- lethal_projectile_sound = 'sound/weapons/gun/hmg/shredder.ogg'
- shot_delay = 3
- scan_range = 8
-
-////////////////////////
-//Turret Control Panel//
-////////////////////////
-
-/obj/machinery/turretid
- name = "turret control panel"
- desc = "Used to control a room's automated defenses."
- icon = 'icons/obj/machines/turret_control.dmi'
- icon_state = "control_standby"
- base_icon_state = "control"
- density = FALSE
- req_access = list(ACCESS_AI_UPLOAD)
- resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
- /// Variable dictating if linked turrets are active and will shoot targets
- var/enabled = TRUE
- /// Variable dictating if linked turrets will shoot lethal projectiles
- var/lethal = FALSE
- /// Variable dictating if the panel is locked, preventing changes to turret settings
- var/locked = TRUE
- /// An area in which linked turrets are located, it can be an area name, path or nothing
- var/control_area = null
- /// AI is unable to use this machine if set to TRUE
- var/ailock = FALSE
- /// Variable dictating if linked turrets will shoot cyborgs
- var/shoot_cyborgs = FALSE
- /// List of all linked turrets
- var/list/turrets = list()
- ///id for connecting to additional turrets
- var/id = ""
+ //We know the target must be a human now
+ var/mob/living/carbon/human/target_carbon = target_mob
-/obj/machinery/turretid/Initialize(mapload, ndir = 0, built = 0)
- . = ..()
- if(built)
- setDir(ndir)
- locked = FALSE
- pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
- pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0
- power_change() //Checks power and initial settings
-
-/obj/machinery/turretid/Destroy()
- turrets.Cut()
- return ..()
+ if(req_ship_access && (check_access(target_carbon.get_active_held_item()) || check_access(target_carbon.wear_id)))
+ return FALSE
-/obj/machinery/turretid/Initialize(mapload) //map-placed turrets autolink turrets
- . = ..()
- if(!mapload)
- return
+ if(!(check_flags & TURRET_FLAG_SHOOT_DANGEROUS_ONLY))
+ return target(target_carbon)
-/obj/machinery/turretid/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
- id = "[REF(port)][id]"
- RegisterSignal(port, COMSIG_SHIP_DONE_CONNECTING, PROC_REF(late_connect_to_shuttle))
+ //Not dangerous if you can't hold anything
+ if(target_carbon.handcuffed || !(target_carbon.mobility_flags & MOBILITY_USE))
+ return FALSE
-/obj/machinery/turretid/disconnect_from_shuttle(obj/docking_port/mobile/port)
- UnregisterSignal(port, COMSIG_SHIP_DONE_CONNECTING)
+ if(target_carbon.is_holding_item_of_type(/obj/item/gun) || target_carbon.is_holding_item_of_type(/obj/item/melee))
+ return target(target_carbon)
-/obj/machinery/turretid/proc/late_connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
- SIGNAL_HANDLER
+//Returns whether or not we should stop searching for targets
+/obj/machinery/porta_turret/proc/target(mob/living/target)
+ if(!COOLDOWN_FINISHED(src, fire_cooldown))
+ return TRUE
- for(var/datum/weakref/ship_guns in port.turret_list)
- var/obj/machinery/porta_turret/turret_gun = ship_guns.resolve()
- if(turret_gun.id == id)
- turrets |= turret_gun
- turret_gun.cp = src
+ var/turf/our_turf = get_turf(src)
+ if(!istype(our_turf))
+ return TRUE
-/obj/machinery/turretid/examine(mob/user)
- . += ..()
- if(issilicon(user) && !(machine_stat & BROKEN))
- . += {"Ctrl-click [src] to [ enabled ? "disable" : "enable"] turrets.
- Alt-click [src] to set turrets to [ lethal ? "stun" : "kill"]."}
+ //Wall turrets will try to find adjacent empty turf to shoot from to cover full arc
+ if(our_turf.density)
+ if(wall_turret_direction)
+ var/turf/closer = get_step(our_turf, wall_turret_direction)
+ if(istype(closer) && !closer.is_blocked_turf() && our_turf.Adjacent(closer))
+ our_turf = closer
+ else
+ var/target_dir = get_dir(our_turf, target)
+ for(var/d in list(0, -45, 45))
+ var/turf/closer = get_step(our_turf, turn(target_dir, d))
+ if(istype(closer) && !closer.is_blocked_turf() && our_turf.Adjacent(closer))
+ our_turf = closer
+ break
+ if(!can_see(our_turf, target, scan_range))
+ return FALSE
-/obj/machinery/turretid/attackby(obj/item/I, mob/user, params)
- if(machine_stat & BROKEN)
- return
+ setDir(get_dir(our_turf, target))
- if(I.tool_behaviour == TOOL_MULTITOOL)
- if(!multitool_check_buffer(user, I))
- return
- var/obj/item/multitool/M = I
- if(M.buffer && istype(M.buffer, /obj/machinery/porta_turret))
- turrets |= M.buffer
- to_chat(user, "You link \the [M.buffer] with \the [src].")
- return
+ if(!manual_control)
+ if(current_target != target)
+ set_target(target)
+ COOLDOWN_START(src, reaction_cooldown, reaction_time)
- if (issilicon(user))
- return attack_hand(user)
+ if(ishuman(target) || target.client)
+ target.do_alert_animation(target)
- // trying to unlock the interface
- if (in_range(src, user))
- if (allowed(usr))
- if(obj_flags & EMAGGED)
- to_chat(user, "The turret control is unresponsive!")
- return
+ return TRUE
- locked = !locked
- to_chat(user, "You [ locked ? "lock" : "unlock"] the panel.")
- else
- to_chat(user, "Access denied.")
+ if(!COOLDOWN_FINISHED(src, reaction_cooldown))
+ return TRUE
-/obj/machinery/turretid/emag_act(mob/user)
- if(obj_flags & EMAGGED)
- return
- to_chat(user, "You short out the turret controls' access analysis module.")
- obj_flags |= EMAGGED
- locked = FALSE
+ target_beam.set_target(null)
+ COOLDOWN_START(src, fire_cooldown, shot_delay)
-/obj/machinery/turretid/attack_ai(mob/user)
- if(!ailock || isAdminGhostAI(user))
- return attack_hand(user)
+ update_appearance(UPDATE_ICON_STATE)
+ var/obj/projectile/shot
+ //any lethaling turrets drain 2x the power and use a different projectile
+ if(lethal)
+ use_power(reqpower * 2)
+ shot = new lethal_projectile(our_turf)
+ playsound(loc, lethal_projectile_sound, 75, TRUE)
else
- to_chat(user, "There seems to be a firewall preventing you from accessing this device!")
-
-/obj/machinery/turretid/ui_interact(mob/user, datum/tgui/ui)
- ui = SStgui.try_update_ui(user, src, ui)
- if(!ui)
- ui = new(user, src, "TurretControl", name)
- ui.open()
+ use_power(reqpower)
+ shot = new stun_projectile(our_turf)
+ playsound(loc, stun_projectile_sound, 75, TRUE)
-/obj/machinery/turretid/ui_data(mob/user)
- var/list/data = list()
- data["locked"] = locked
- data["siliconUser"] = user.has_unlimited_silicon_privilege && check_ship_ai_access(user)
- data["enabled"] = enabled
- data["lethal"] = lethal
- data["shootCyborgs"] = shoot_cyborgs
- return data
-/obj/machinery/turretid/ui_act(action, list/params)
- . = ..()
- if(.)
- return
-
- switch(action)
- if("lock")
- if(!usr.has_unlimited_silicon_privilege)
- return
- if((obj_flags & EMAGGED) || (machine_stat & BROKEN))
- to_chat(usr, "The turret control is unresponsive!")
- return
- locked = !locked
- return TRUE
- if("power")
- toggle_on(usr)
- return TRUE
- if("mode")
- toggle_lethal(usr)
- return TRUE
- if("shoot_silicons")
- shoot_silicons(usr)
- return TRUE
+ //Shooting Code:
+ shot.preparePixelProjectile(target, our_turf)
+ shot.firer = src
+ shot.fired_from = src
+ shot.fire()
+ return TRUE
-/obj/machinery/turretid/proc/toggle_lethal(mob/user)
- lethal = !lethal
- add_hiddenprint(user)
- log_combat(user, src, "[lethal ? "enabled" : "disabled"] lethals on")
- updateTurrets()
-
-/obj/machinery/turretid/proc/toggle_on(mob/user)
- enabled = !enabled
- add_hiddenprint(user)
- log_combat(user, src, "[enabled ? "enabled" : "disabled"]")
- updateTurrets()
-
-/obj/machinery/turretid/proc/shoot_silicons(mob/user)
- shoot_cyborgs = !shoot_cyborgs
- add_hiddenprint(user)
- log_combat(user, src, "[shoot_cyborgs ? "Shooting Borgs" : "Not Shooting Borgs"]")
- updateTurrets()
-
-/obj/machinery/turretid/proc/updateTurrets()
- for (var/obj/machinery/porta_turret/aTurret in turrets)
- aTurret.setState(enabled, lethal, shoot_cyborgs)
- update_appearance()
+/obj/machinery/porta_turret/proc/set_target(atom/movable/target = null)
+ if(current_target)
+ UnregisterSignal(current_target, COMSIG_PARENT_QDELETING)
-/obj/machinery/turretid/update_icon_state()
- if(machine_stat & NOPOWER)
- icon_state = "[base_icon_state]_off"
- return ..()
- if (enabled)
- icon_state = "[base_icon_state]_[lethal ? "kill" : "stun"]"
- return ..()
- icon_state = "[base_icon_state]_standby"
- return ..()
+ retaliating = FALSE
+ current_target = target
+ target_beam.set_target(target)
-/obj/machinery/turretid/lethal
- lethal = TRUE
+ if(current_target)
+ RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(set_target))
-/obj/machinery/turretid/ship
- req_ship_access = TRUE
+/obj/machinery/porta_turret/proc/set_state(on, new_mode, new_flags)
+ if(locked)
+ return
+ if(!isnull(new_flags))
+ turret_flags = new_flags
-/obj/item/wallframe/turret_control
- name = "turret control frame"
- desc = "Used for building turret control panels."
- icon_state = "apc"
- result_path = /obj/machinery/turretid
- custom_materials = list(/datum/material/iron=MINERAL_MATERIAL_AMOUNT)
- inverse_pixel_shift = TRUE
+ lethal = new_mode
+ toggle_on(on)
+ power_change()
/obj/item/gun/proc/get_turret_properties()
. = list()
@@ -1319,7 +658,3 @@ DEFINE_BITFIELD(turret_flags, list(
.["stun_projectile_sound"] = initial(primary_ammo.fire_sound)
.["lethal_projectile"] = .["stun_projectile"]
.["lethal_projectile_sound"] = .["stun_projectile_sound"]
-
-/obj/item/gun/energy/e_gun/turret/get_turret_properties()
- . = ..()
-
diff --git a/code/game/machinery/porta_turret/portable_turret_construct.dm b/code/game/machinery/porta_turret/portable_turret_construct.dm
deleted file mode 100644
index ad2f82eace29..000000000000
--- a/code/game/machinery/porta_turret/portable_turret_construct.dm
+++ /dev/null
@@ -1,192 +0,0 @@
-#define PTURRET_UNSECURED 0
-#define PTURRET_BOLTED 1
-#define PTURRET_START_INTERNAL_ARMOUR 2
-#define PTURRET_INTERNAL_ARMOUR_ON 3
-#define PTURRET_GUN_EQUIPPED 4
-#define PTURRET_SENSORS_ON 5
-#define PTURRET_CLOSED 6
-#define PTURRET_START_EXTERNAL_ARMOUR 7
-#define PTURRET_EXTERNAL_ARMOUR_ON 8
-
-/obj/machinery/porta_turret_construct
- name = "turret frame"
- icon = 'icons/obj/turrets.dmi'
- icon_state = "turret_frame"
- desc = "An unfinished covered turret frame."
- anchored = FALSE
- density = TRUE
- var/build_step = PTURRET_UNSECURED //the current step in the building process
- var/finish_name = "turret" //the name applied to the product turret
- var/obj/item/gun/installed_gun = null
-
-/obj/machinery/porta_turret_construct/attackby(obj/item/I, mob/user, params)
- //this is a bit unwieldy but self-explanatory
- switch(build_step)
- if(PTURRET_UNSECURED) //first step
- if(I.tool_behaviour == TOOL_WRENCH && !anchored)
- I.play_tool_sound(src, 100)
- to_chat(user, "You secure the external bolts.")
- set_anchored(TRUE)
- build_step = PTURRET_BOLTED
- return
-
- else if(I.tool_behaviour == TOOL_CROWBAR && !anchored)
- I.play_tool_sound(src, 75)
- to_chat(user, "You dismantle the turret construction.")
- new /obj/item/stack/sheet/metal(loc, 5)
- qdel(src)
- return
-
- if(PTURRET_BOLTED)
- if(istype(I, /obj/item/stack/sheet/metal))
- var/obj/item/stack/sheet/metal/M = I
- if(M.use(2))
- to_chat(user, "You add some metal armor to the interior frame.")
- build_step = PTURRET_START_INTERNAL_ARMOUR
- icon_state = "turret_frame2"
- else
- to_chat(user, "You need two sheets of metal to continue construction!")
- return
-
- else if(I.tool_behaviour == TOOL_WRENCH)
- I.play_tool_sound(src, 75)
- to_chat(user, "You unfasten the external bolts.")
- set_anchored(FALSE)
- build_step = PTURRET_UNSECURED
- return
-
-
- if(PTURRET_START_INTERNAL_ARMOUR)
- if(I.tool_behaviour == TOOL_WRENCH)
- I.play_tool_sound(src, 100)
- to_chat(user, "You bolt the metal armor into place.")
- build_step = PTURRET_INTERNAL_ARMOUR_ON
- return
-
- else if(I.tool_behaviour == TOOL_WELDER)
- if(!I.tool_start_check(user, amount=5)) //uses up 5 fuel
- return
-
- to_chat(user, "You start to remove the turret's interior metal armor...")
-
- if(I.use_tool(src, user, 20, volume=50, amount=5)) //uses up 5 fuel
- build_step = PTURRET_BOLTED
- to_chat(user, "You remove the turret's interior metal armor.")
- new /obj/item/stack/sheet/metal(drop_location(), 2)
- return
-
-
- if(PTURRET_INTERNAL_ARMOUR_ON)
- if(istype(I, /obj/item/gun/energy)) //the gun installation part
- var/obj/item/gun/energy/E = I
- if(!user.transferItemToLoc(E, src))
- return
- installed_gun = E
- to_chat(user, "You add [I] to the turret.")
- build_step = PTURRET_GUN_EQUIPPED
- return
-
- else if(I.tool_behaviour == TOOL_WRENCH)
- I.play_tool_sound(src, 100)
- to_chat(user, "You remove the turret's metal armor bolts.")
- build_step = PTURRET_START_INTERNAL_ARMOUR
- return
-
- if(PTURRET_GUN_EQUIPPED)
- if(isprox(I))
- build_step = PTURRET_SENSORS_ON
- if(!user.temporarilyRemoveItemFromInventory(I))
- return
- to_chat(user, "You add the proximity sensor to the turret.")
- qdel(I)
- return
-
-
- if(PTURRET_SENSORS_ON)
- if(I.tool_behaviour == TOOL_SCREWDRIVER)
- I.play_tool_sound(src, 100)
- build_step = PTURRET_CLOSED
- to_chat(user, "You close the internal access hatch.")
- return
-
-
- if(PTURRET_CLOSED)
- if(istype(I, /obj/item/stack/sheet/metal))
- var/obj/item/stack/sheet/metal/M = I
- if(M.use(2))
- to_chat(user, "You add some metal armor to the exterior frame.")
- build_step = PTURRET_START_EXTERNAL_ARMOUR
- else
- to_chat(user, "You need two sheets of metal to continue construction!")
- return
-
- else if(I.tool_behaviour == TOOL_SCREWDRIVER)
- I.play_tool_sound(src, 100)
- build_step = PTURRET_SENSORS_ON
- to_chat(user, "You open the internal access hatch.")
- return
-
- if(PTURRET_START_EXTERNAL_ARMOUR)
- if(I.tool_behaviour == TOOL_WELDER)
- if(!I.tool_start_check(user, amount=5))
- return
-
- to_chat(user, "You begin to weld the turret's armor down...")
- if(I.use_tool(src, user, 30, volume=50, amount=5))
- build_step = PTURRET_EXTERNAL_ARMOUR_ON
- to_chat(user, "You weld the turret's armor down.")
-
- //The final step: create a full turret
-
- var/obj/machinery/porta_turret/turret
- turret = new/obj/machinery/porta_turret(loc)
- turret.name = finish_name
- turret.installation = installed_gun.type
- turret.setup(installed_gun)
- qdel(src)
- return
-
- else if(I.tool_behaviour == TOOL_CROWBAR)
- I.play_tool_sound(src, 75)
- to_chat(user, "You pry off the turret's exterior armor.")
- new /obj/item/stack/sheet/metal(loc, 2)
- build_step = PTURRET_CLOSED
- return
-
- if(istype(I, /obj/item/pen)) //you can rename turrets like bots!
- var/t = stripped_input(user, "Enter new turret name", name, finish_name)
- if(!t)
- return
- if(!Adjacent(user))
- return
-
- finish_name = t
- return
- return ..()
-
-
-/obj/machinery/porta_turret_construct/attack_hand(mob/user)
- . = ..()
- if(.)
- return
- switch(build_step)
- if(PTURRET_GUN_EQUIPPED)
- build_step = PTURRET_INTERNAL_ARMOUR_ON
-
- installed_gun.forceMove(loc)
- to_chat(user, "You remove [installed_gun] from the turret frame.")
- installed_gun = null
-
- if(PTURRET_SENSORS_ON)
- to_chat(user, "You remove the prox sensor from the turret frame.")
- new /obj/item/assembly/prox_sensor(loc)
- build_step = PTURRET_GUN_EQUIPPED
-
-/obj/machinery/porta_turret_construct/attack_ai()
- return
-
-/obj/machinery/porta_turret_construct/Destroy()
- if(installed_gun)
- qdel(installed_gun)
- installed_gun = null
- . = ..()
diff --git a/code/game/machinery/porta_turret/portable_turret_control.dm b/code/game/machinery/porta_turret/portable_turret_control.dm
new file mode 100644
index 000000000000..ba70fe3a44b1
--- /dev/null
+++ b/code/game/machinery/porta_turret/portable_turret_control.dm
@@ -0,0 +1,241 @@
+
+////////////////////////
+//Turret Control Panel//
+////////////////////////
+
+/obj/machinery/turretid
+ name = "turret control panel"
+ desc = "Used to control a room's automated defenses."
+ icon = 'icons/obj/machines/turret_control.dmi'
+ icon_state = "control_standby"
+ base_icon_state = "control"
+ density = FALSE
+ req_access = list(ACCESS_AI_UPLOAD)
+ resistance_flags = LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
+ /// Variable dictating if linked turrets are active and will shoot targets
+ var/enabled = TRUE
+ /// Variable dictating if linked turrets will shoot lethal projectiles
+ var/lethal = FALSE
+ /// Variable dictating if the panel is locked, preventing changes to turret settings
+ var/locked = TRUE
+ /// AI is unable to use this machine if set to TRUE
+ var/ailock = FALSE
+ /// Flags to apply to all linked turrets
+ var/turret_flags = TURRET_FLAG_DEFAULT
+ /// List of all linked turrets
+ var/list/datum/weakref/turret_refs = list()
+ ///id for connecting to additional turrets
+ var/id = ""
+
+/obj/machinery/turretid/Initialize(mapload, ndir = 0, built = 0)
+ . = ..()
+ if(built)
+ setDir(ndir)
+ locked = FALSE
+ pixel_x = (dir & 3)? 0 : (dir == 4 ? -24 : 24)
+ pixel_y = (dir & 3)? (dir ==1 ? -24 : 24) : 0
+ power_change()
+
+/obj/machinery/turretid/Destroy()
+ turret_refs.Cut()
+ return ..()
+
+/obj/machinery/turretid/connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
+ id = "[text_ref(port)][id]"
+ RegisterSignal(port, COMSIG_SHIP_DONE_CONNECTING, PROC_REF(late_connect_to_shuttle))
+
+/obj/machinery/turretid/proc/late_connect_to_shuttle(obj/docking_port/mobile/port, obj/docking_port/stationary/dock)
+ SIGNAL_HANDLER
+
+ for(var/datum/weakref/ship_gun in port.turret_list)
+ var/obj/machinery/porta_turret/turret_gun = ship_gun.resolve()
+ //skip if it doesn't exist or if the id doesn't match
+ if(turret_gun?.id != id)
+ continue
+
+ turret_refs |= ship_gun
+
+ update_turrets()
+ UnregisterSignal(port, COMSIG_SHIP_DONE_CONNECTING)
+
+/obj/machinery/turretid/examine(mob/user)
+ . += ..()
+ if((machine_stat & (BROKEN|MAINT)))
+ return
+
+ . += span_notice("Alt-click [src] to [locked ? "unlock" : "lock"] it.")
+ . += span_notice("Ctrl-click [src] to [enabled ? "disable" : "enable"] turrets.")
+ . += span_notice("Ctrl-shift-click [src] to set turrets to [lethal ? "stun" : "kill"] mode.")
+
+/obj/machinery/turretid/attackby(obj/item/I, mob/user, params)
+ if(machine_stat & BROKEN)
+ return
+
+ if(I.tool_behaviour == TOOL_MULTITOOL)
+ if(!multitool_check_buffer(user, I))
+ return
+ var/obj/item/multitool/M = I
+ if(M.buffer && istype(M.buffer, /obj/machinery/porta_turret))
+ turret_refs |= WEAKREF(M.buffer)
+ to_chat(user, "You link \the [M.buffer] with \the [src].")
+ return
+
+ if(issilicon(user))
+ return attack_hand(user)
+
+ if(istype(I, /obj/item/card/id))
+ toggle_lock(user)
+
+/obj/machinery/turretid/AltClick(mob/user)
+ . = ..()
+ toggle_lock(user)
+
+/obj/machinery/turretid/CtrlClick(mob/user)
+ . = ..()
+ toggle_on(user)
+
+/obj/machinery/turretid/CtrlShiftClick(mob/user)
+ . = ..()
+ toggle_lethal(user)
+
+/obj/machinery/turretid/emag_act(mob/user)
+ if(obj_flags & EMAGGED)
+ return
+ to_chat(user, span_notice("You short out the turret controls' access analysis module."))
+ obj_flags |= EMAGGED
+ locked = FALSE
+
+/obj/machinery/turretid/attack_ai(mob/user)
+ if(!ailock || isAdminGhostAI(user))
+ return attack_hand(user)
+ else
+ to_chat(user, span_warning("There seems to be a firewall preventing you from accessing this device!"))
+
+/obj/machinery/turretid/ui_interact(mob/user, datum/tgui/ui)
+ ui = SStgui.try_update_ui(user, src, ui)
+ if(!ui)
+ ui = new(user, src, "TurretControl", name)
+ ui.open()
+
+/obj/machinery/turretid/ui_data(mob/user)
+ return list(
+ "locked" = locked,
+ "enabled" = enabled,
+ "lethal" = lethal,
+ "siliconUser" = user.has_unlimited_silicon_privilege && check_ship_ai_access(user),
+ "dangerous_only" = turret_flags & TURRET_FLAG_SHOOT_DANGEROUS_ONLY,
+ "retaliate" = turret_flags & TURRET_FLAG_SHOOT_RETALIATE,
+ "shoot_fauna" = turret_flags & TURRET_FLAG_SHOOT_FAUNA,
+ "shoot_humans" = turret_flags & TURRET_FLAG_SHOOT_HUMANS,
+ "shoot_silicons" = turret_flags & TURRET_FLAG_SHOOT_SILICONS,
+ "only_nonfaction" = turret_flags & TURRET_FLAG_SHOOT_NONFACTION,
+ "only_specificfaction" = turret_flags & TURRET_FLAG_SHOOT_SPECIFIC_FACTION,
+ )
+
+/obj/machinery/turretid/ui_act(action, list/params)
+ . = ..()
+ if(.)
+ return
+
+ switch(action)
+ if("lock")
+ if(!usr.has_unlimited_silicon_privilege)
+ return
+ toggle_lock(usr)
+ return TRUE
+ if("power")
+ toggle_on(usr)
+ if("mode")
+ toggle_lethal(usr)
+ if("toggle_dangerous")
+ turret_flags ^= TURRET_FLAG_SHOOT_DANGEROUS_ONLY
+ if("toggle_retaliate")
+ turret_flags ^= TURRET_FLAG_SHOOT_RETALIATE
+
+
+ if("toggle_fauna")
+ turret_flags ^= TURRET_FLAG_SHOOT_FAUNA
+ if("toggle_humans")
+ turret_flags ^= TURRET_FLAG_SHOOT_HUMANS
+ if("toggle_silicons")
+ turret_flags ^= TURRET_FLAG_SHOOT_SILICONS
+ if("toggle_nonfaction")
+ turret_flags ^= TURRET_FLAG_SHOOT_NONFACTION
+ if("toggle_specificfaction")
+ turret_flags ^= TURRET_FLAG_SHOOT_SPECIFIC_FACTION
+
+ else
+ return
+
+ update_turrets()
+
+/obj/machinery/turretid/proc/toggle_lock(mob/user)
+ if(!user.canUseTopic(src, !issilicon(user)))
+ return
+ if(!allowed(user))
+ to_chat(user, span_alert("Access denied."))
+ return
+ if(obj_flags & EMAGGED || (machine_stat & (BROKEN|MAINT)))
+ to_chat(user, span_warning("The turret control is unresponsive!"))
+ return
+
+ to_chat(user, span_notice("You [locked ? "unlock" : "lock"] the turret control."))
+ locked = !locked
+ update_appearance()
+
+/obj/machinery/turretid/proc/toggle_lethal(mob/user)
+ if(!user.canUseTopic(src, !issilicon(user)))
+ return
+ if(obj_flags & EMAGGED || (machine_stat & (BROKEN|MAINT)))
+ to_chat(user, span_warning("The turret control is unresponsive!"))
+ return
+
+ lethal = !lethal
+ add_hiddenprint(user)
+ log_combat(user, src, "[lethal ? "enabled" : "disabled"] lethals on")
+
+/obj/machinery/turretid/proc/toggle_on(mob/user)
+ if(!user.canUseTopic(src, !issilicon(user)))
+ return
+ if(obj_flags & EMAGGED || (machine_stat & (BROKEN|MAINT)))
+ to_chat(user, span_warning("The turret control is unresponsive!"))
+ return
+
+ enabled = !enabled
+ add_hiddenprint(user)
+ log_combat(user, src, "[enabled ? "enabled" : "disabled"]")
+
+/obj/machinery/turretid/proc/update_turrets()
+ for(var/datum/weakref/turret_ref in turret_refs)
+ var/obj/machinery/porta_turret/turret = turret_ref.resolve()
+ if(!turret)
+ turret_refs -= turret_ref
+ continue
+ turret.set_state(enabled, lethal, turret_flags)
+ update_appearance()
+
+/obj/machinery/turretid/update_icon_state()
+ if(machine_stat & NOPOWER)
+ icon_state = "[base_icon_state]_off"
+ return ..()
+ if (enabled)
+ icon_state = "[base_icon_state]_[lethal ? "kill" : "stun"]"
+ return ..()
+ icon_state = "[base_icon_state]_standby"
+ return ..()
+
+/obj/machinery/turretid/lethal
+ lethal = TRUE
+ turret_flags = TURRET_FLAG_HOSTILE
+
+/obj/machinery/turretid/ship
+ req_ship_access = TRUE
+
+/obj/item/wallframe/turret_control
+ name = "turret control frame"
+ desc = "Used for building turret control panels."
+ icon = 'icons/obj/machines/turret_control.dmi'
+ icon_state = "control_off"
+ result_path = /obj/machinery/turretid
+ custom_materials = list(/datum/material/iron=MINERAL_MATERIAL_AMOUNT)
+ inverse_pixel_shift = TRUE
diff --git a/code/game/machinery/porta_turret/portable_turret_cover.dm b/code/game/machinery/porta_turret/portable_turret_cover.dm
deleted file mode 100644
index a1da4cbf6463..000000000000
--- a/code/game/machinery/porta_turret/portable_turret_cover.dm
+++ /dev/null
@@ -1,93 +0,0 @@
-
-/************************
-* PORTABLE TURRET COVER *
-************************/
-
-/obj/machinery/porta_turret_cover
- name = "turret"
- icon = 'icons/obj/turrets.dmi'
- icon_state = "turretCover"
- layer = HIGH_OBJ_LAYER
- density = FALSE
- max_integrity = 80
- var/obj/machinery/porta_turret/parent_turret = null
-
-/obj/machinery/porta_turret_cover/Destroy()
- if(parent_turret)
- parent_turret.cover = null
- parent_turret.invisibility = 0
- parent_turret = null
- return ..()
-
-//The below code is pretty much just recoded from the initial turret object. It's necessary but uncommented because it's exactly the same!
-//>necessary
-//I'm not fixing it because i'm fucking bored of this code already, but someone should just reroute these to the parent turret's procs.
-
-/obj/machinery/porta_turret_cover/attack_ai(mob/user)
- return ..() || parent_turret.attack_ai(user)
-
-/obj/machinery/porta_turret_cover/attack_robot(mob/user)
- return ..() || parent_turret.attack_robot(user)
-
-/obj/machinery/porta_turret_cover/attack_hand(mob/user)
- return ..() || parent_turret.attack_hand(user)
-
-/obj/machinery/porta_turret_cover/attack_ghost(mob/user)
- return ..() || parent_turret.attack_ghost(user)
-
-/obj/machinery/porta_turret_cover/attackby(obj/item/I, mob/user, params)
- if(I.tool_behaviour == TOOL_WRENCH && !parent_turret.on)
- if(parent_turret.raised)
- return
-
- if(!parent_turret.anchored)
- parent_turret.set_anchored(TRUE)
- to_chat(user, "You secure the exterior bolts on the turret.")
- parent_turret.invisibility = 0
- parent_turret.update_appearance()
- else
- parent_turret.set_anchored(FALSE)
- to_chat(user, "You unsecure the exterior bolts on the turret.")
- parent_turret.invisibility = INVISIBILITY_MAXIMUM
- parent_turret.update_appearance()
- qdel(src)
- return
- if(I.GetID())
- if(parent_turret.allowed(user))
- parent_turret.locked = !parent_turret.locked
- to_chat(user, "Controls are now [parent_turret.locked ? "locked" : "unlocked"].")
- else
- to_chat(user, "Access denied.")
- return
-
- if(I.tool_behaviour == TOOL_MULTITOOL && !parent_turret.locked)
- if(!multitool_check_buffer(user, I))
- return
- var/obj/item/multitool/M = I
- M.buffer = parent_turret
- to_chat(user, "You add [parent_turret] to multitool buffer.")
- return
- return ..()
-
-/obj/machinery/porta_turret_cover/attacked_by(obj/item/I, mob/user)
- parent_turret.attacked_by(I, user)
-
-/obj/machinery/porta_turret_cover/attack_alien(mob/living/carbon/alien/humanoid/user)
- parent_turret.attack_alien(user)
-
-/obj/machinery/porta_turret_cover/attack_animal(mob/living/simple_animal/user)
- parent_turret.attack_animal(user)
-
-/obj/machinery/porta_turret_cover/attack_hulk(mob/living/carbon/human/user)
- return parent_turret.attack_hulk(user)
-
-/obj/machinery/porta_turret_cover/can_be_overridden()
- . = 0
-
-/obj/machinery/porta_turret_cover/emag_act(mob/user)
- if(!(parent_turret.obj_flags & EMAGGED))
- to_chat(user, "You short out [parent_turret]'s threat assessment circuits.")
- visible_message("[parent_turret] hums oddly...")
- parent_turret.obj_flags |= EMAGGED
- parent_turret.on = FALSE
- addtimer(VARSET_CALLBACK(parent_turret, on, TRUE), 4 SECONDS)
diff --git a/code/game/machinery/porta_turret/portable_turret_manual_control.dm b/code/game/machinery/porta_turret/portable_turret_manual_control.dm
new file mode 100644
index 000000000000..7fac7309bfed
--- /dev/null
+++ b/code/game/machinery/porta_turret/portable_turret_manual_control.dm
@@ -0,0 +1,68 @@
+/obj/machinery/porta_turret
+ /// Action button holder for quitting manual control
+ var/datum/action/turret_quit/quit_action
+ /// Action button holder for switching between turret modes when manually controlling
+ var/datum/action/turret_toggle/toggle_action
+ /// Mob that is remotely controlling the turret
+ var/mob/remote_controller
+
+/datum/action/turret_toggle
+ name = "Toggle Mode"
+ icon_icon = 'icons/mob/actions/actions_mecha.dmi'
+ button_icon_state = "mech_cycle_equip_off"
+
+/datum/action/turret_toggle/Trigger()
+ var/obj/machinery/porta_turret/P = target
+ if(!istype(P))
+ return
+ P.set_state(P.on, !P.lethal)
+
+/datum/action/turret_quit
+ name = "Release Control"
+ icon_icon = 'icons/mob/actions/actions_mecha.dmi'
+ button_icon_state = "mech_eject"
+
+/datum/action/turret_quit/Trigger()
+ var/obj/machinery/porta_turret/P = target
+ if(!istype(P))
+ return
+ P.remove_control(FALSE)
+
+/obj/machinery/porta_turret/proc/give_control(mob/A)
+ if(manual_control || !can_interact(A))
+ return FALSE
+ remote_controller = A
+ if(!quit_action)
+ quit_action = new(src)
+ quit_action.Grant(remote_controller)
+ if(!toggle_action)
+ toggle_action = new(src)
+ toggle_action.Grant(remote_controller)
+ remote_controller.reset_perspective(src)
+ remote_controller.click_intercept = src
+ manual_control = TRUE
+ return TRUE
+
+/obj/machinery/porta_turret/proc/remove_control(warning_message = TRUE)
+ if(!manual_control)
+ return FALSE
+ if(remote_controller)
+ if(warning_message)
+ to_chat(remote_controller, "Your uplink to [src] has been severed!")
+ quit_action.Remove(remote_controller)
+ toggle_action.Remove(remote_controller)
+ remote_controller.click_intercept = null
+ remote_controller.reset_perspective()
+ manual_control = FALSE
+ remote_controller = null
+ return TRUE
+
+/obj/machinery/porta_turret/proc/InterceptClickOn(mob/living/caller, params, atom/A)
+ if(!manual_control)
+ return FALSE
+ if(!can_interact(caller))
+ remove_control()
+ return FALSE
+ log_combat(caller,A,"fired with manual turret control at")
+ target(A)
+ return TRUE
diff --git a/code/game/machinery/porta_turret/portable_turret_types.dm b/code/game/machinery/porta_turret/portable_turret_types.dm
new file mode 100644
index 000000000000..53a3fbd3fa12
--- /dev/null
+++ b/code/game/machinery/porta_turret/portable_turret_types.dm
@@ -0,0 +1,340 @@
+
+/obj/machinery/porta_turret/syndicate
+ circuit = /obj/item/circuitboard/machine/turret/ship
+ use_power = NO_POWER_USE
+ scan_range = 9
+ req_access = list(ACCESS_SYNDICATE)
+ lethal = TRUE
+ stun_projectile = /obj/projectile/bullet
+ lethal_projectile = /obj/projectile/bullet
+ lethal_projectile_sound = 'sound/weapons/gun/pistol/shot.ogg'
+ stun_projectile_sound = 'sound/weapons/gun/pistol/shot.ogg'
+ icon_state = "syndie_off"
+ base_icon_state = "syndie"
+ faction = list(ROLE_SYNDICATE)
+ turret_flags = TURRET_FLAG_HOSTILE
+ desc = "A ballistic machine gun auto-turret."
+
+/obj/machinery/porta_turret/syndicate/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
+
+/obj/machinery/porta_turret/syndicate/energy
+ icon_state = "standard_lethal"
+ base_icon_state = "standard"
+ stun_projectile = /obj/projectile/energy/electrode
+ stun_projectile_sound = 'sound/weapons/taser.ogg'
+ lethal_projectile = /obj/projectile/beam/laser
+ lethal_projectile_sound = 'sound/weapons/laser.ogg'
+ desc = "An energy blaster auto-turret."
+
+/obj/machinery/porta_turret/syndicate/energy/heavy
+ icon_state = "standard_lethal"
+ base_icon_state = "standard"
+ stun_projectile = /obj/projectile/energy/electrode
+ stun_projectile_sound = 'sound/weapons/taser.ogg'
+ lethal_projectile = /obj/projectile/beam/laser/heavylaser
+ lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
+ desc = "An energy blaster auto-turret."
+
+/obj/machinery/porta_turret/syndicate/energy/raven
+ stun_projectile = /obj/projectile/beam/laser
+ stun_projectile_sound = 'sound/weapons/laser.ogg'
+ faction = list("neutral","silicon","turret")
+
+/obj/machinery/porta_turret/syndicate/pod
+ integrity_failure = 0.5
+ max_integrity = 40
+ stun_projectile = /obj/projectile/bullet/syndicate_turret
+ lethal_projectile = /obj/projectile/bullet/syndicate_turret
+
+/obj/machinery/porta_turret/ship
+ circuit = /obj/item/circuitboard/machine/turret/ship
+ scan_range = 9
+ req_ship_access = TRUE
+ icon_state = "syndie_off"
+ base_icon_state = "syndie"
+
+/obj/machinery/porta_turret/ship/ComponentInitialize()
+ . = ..()
+ AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
+
+/obj/machinery/porta_turret/ship/examine(mob/user)
+ . = ..()
+ if(in_range(user, src) || isobserver(user))
+ if(!(machine_stat & BROKEN))
+ . += "[src] reports its integrity is currently [round((obj_integrity / max_integrity) * 100)] percent."
+
+/obj/machinery/porta_turret/ship/weak
+ max_integrity = 120
+ integrity_failure = 0.5
+ name = "Old Laser Turret"
+ desc = "A turret built with substandard parts and run down further with age. Still capable of delivering lethal lasers to the odd space carp, but not much else."
+ stun_projectile = /obj/projectile/beam/disabler/weak
+ lethal_projectile = /obj/projectile/beam/weak/penetrator
+ faction = list("neutral", "turret")
+
+/obj/machinery/porta_turret/ship/ballistic
+ stun_projectile = /obj/projectile/bullet/turret/rubber
+ lethal_projectile = /obj/projectile/bullet/turret
+ lethal_projectile_sound = 'sound/weapons/gun/smg/shot.ogg'
+ stun_projectile_sound = 'sound/weapons/gun/smg/shot.ogg'
+ desc = "A ballistic machine gun auto-turret."
+
+//high rof, range, faster projectile speed
+/* 'Nanotrasen' turrets */
+
+/obj/machinery/porta_turret/ship/nt
+ name = "Sharplite Defense Turret"
+ desc = "A cheap and effective turret designed by Sharplite and purchased and installed on most Nanotrasen Vessels."
+ faction = list(FACTION_PLAYER_NANOTRASEN, "turret")
+ max_integrity = 160
+ integrity_failure = 0.6
+ icon_state = "standard_lethal"
+ base_icon_state = "standard"
+ stun_projectile = /obj/projectile/beam/disabler/sharplite
+ lethal_projectile = /obj/projectile/beam/laser/sharplite
+ lethal_projectile_sound = 'sound/weapons/gun/laser/nt-fire.ogg'
+ stun_projectile_sound = 'sound/weapons/taser2.ogg'
+ shot_delay = 10
+ scan_range = 10
+
+/obj/machinery/porta_turret/ship/nt/light
+ name = "Sharplite LDS"
+ desc = "A cheap and effective 'defensive system' designed by Sharplite for installation on Nanotrasen vessels."
+ stun_projectile = /obj/projectile/beam/disabler/weak/sharplite
+ lethal_projectile = /obj/projectile/beam/laser/light/sharplite
+ lethal_projectile_sound = 'sound/weapons/gun/laser/nt-fire.ogg'
+ stun_projectile_sound = 'sound/weapons/taser2.ogg'
+
+/obj/machinery/porta_turret/ship/nt/heavy
+ name = "Sharplite Defense Cannon"
+ desc = "A heavy laser mounting designed by Sharplite for usage on Nanotrasen vessels."
+ lethal_projectile = /obj/projectile/beam/laser/heavylaser/sharplite
+ lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
+ max_integrity = 250
+
+/obj/machinery/porta_turret/ship/nt/pulse
+ name = "Sharplite Pulse Cannon"
+ desc = "A pulse cannon mounting designed by Sharplite. Not sold to any purchasers and exclusively used on Nanotrasen Vessels."
+ lethal_projectile = /obj/projectile/beam/pulse/sharplite_turret
+ lethal_projectile_sound = 'sound/weapons/gun/laser/heavy_laser.ogg'
+ max_integrity = 250
+
+/* Syndicate Turrets */
+
+/obj/machinery/porta_turret/ship/syndicate
+ faction = list(FACTION_PLAYER_SYNDICATE, "turret")
+ icon_state = "standard_lethal"
+ base_icon_state = "standard"
+
+/obj/machinery/porta_turret/ship/syndicate/weak
+ name = "Light Laser Turret"
+ desc = "A low powered turret designed by the Gorlex Maurauders during the ICW. Effectively weaponizes mining equipment."
+ stun_projectile = /obj/projectile/beam/disabler/weak
+ lethal_projectile = /obj/projectile/beam/weak/penetrator
+ icon_state = "syndie_off"
+ base_icon_state = "syndie"
+
+/obj/machinery/porta_turret/ship/syndicate/heavy
+ name = "Heavy Laser Turret"
+ desc = "Produced by Cybersun, this turret is a duel mount of a propietary heavy laser, and crowd control taser system."
+ stun_projectile = /obj/projectile/energy/electrode
+ stun_projectile_sound = 'sound/weapons/taser.ogg'
+ lethal_projectile = /obj/projectile/beam/laser/heavylaser
+ lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
+ max_integrity = 300
+
+/* New Gorlex Republic Turrets */
+// Midline ballistic turrets
+
+/obj/machinery/porta_turret/ship/ngr
+ name = "Oasis Turret"
+ desc = "A turret manufactured by the New Gorlex Republic for its ships and installations. Proudly manufactured within the nation!"
+ stun_projectile = /obj/projectile/bullet/c57x39mm/rubber
+ stun_projectile_sound = 'sound/weapons/gun/smg/sidewinder.ogg'
+ lethal_projectile = /obj/projectile/bullet/c57x39mm
+ lethal_projectile_sound = 'sound/weapons/gun/smg/sidewinder.ogg'
+ faction = list(FACTION_NGR, FACTION_PLAYER_SYNDICATE, "turret") //player_syndicate is just to be safe
+
+/obj/machinery/porta_turret/ship/ngr/light
+ name = "Sonoran Turret"
+ desc = "A light turret manufactured by the New Gorlex Republic for its ships and installations. Proudly manufactured within the Nation, using locally produced munitions!"
+ stun_projectile = /obj/projectile/bullet/c45/rubber
+ stun_projectile_sound = 'sound/weapons/gun/smg/cobra.ogg'
+ lethal_projectile = /obj/projectile/bullet/c45
+ lethal_projectile_sound = 'sound/weapons/gun/smg/cobra.ogg'
+ scan_range = 7
+ shot_delay = 10
+
+/obj/machinery/porta_turret/ship/ngr/heavy
+ name = "Cliff Turret"
+ desc = "A heavy turret manufactured by the New Gorlex Republic for its ships and installations. Has a reputation of being extremely dangerous."
+ stun_projectile = /obj/projectile/bullet/a65clip/rubber
+ stun_projectile_sound = 'sound/weapons/gun/sniper/cmf90.ogg'
+ lethal_projectile = /obj/projectile/bullet/a65clip
+ lethal_projectile_sound = 'sound/weapons/gun/sniper/cmf90.ogg'
+ scan_range = 14
+ shot_delay = 30
+
+
+/* Inteq Turrets */
+//slower rof, higher damage + range
+
+/obj/machinery/porta_turret/ship/inteq
+ name = "Vanguard Turret"
+ desc = "A turret designed by IRMG engineers for defending ships from hostile flora, fauna, and people (and Elzousa, which count as flora and people)."
+ stun_projectile = /obj/projectile/bullet/a762_40/rubber
+ stun_projectile_sound = 'sound/weapons/gun/rifle/skm.ogg'
+ lethal_projectile = /obj/projectile/bullet/a762_40
+ lethal_projectile_sound = 'sound/weapons/gun/rifle/skm.ogg'
+ scan_range = 9
+ shot_delay = 20
+ integrity_failure = 0.4
+ faction = list(FACTION_PLAYER_INTEQ, "turret")
+
+/obj/machinery/porta_turret/ship/inteq/light
+ name = "Close-In Vanguard Turret"
+ desc = "A light turret designed by IRMG engineers for the the task of defending from close-in encounters. Low power, high speed."
+ stun_projectile = /obj/projectile/bullet/c10mm/rubber
+ stun_projectile_sound = 'sound/weapons/gun/smg/vector_fire.ogg'
+ lethal_projectile = /obj/projectile/bullet/c10mm
+ lethal_projectile_sound = 'sound/weapons/gun/smg/vector_fire.ogg'
+ scan_range = 5
+ shot_delay = 5
+
+/obj/machinery/porta_turret/ship/inteq/heavy
+ name = "Vanguard Overwatch Turret"
+ desc = "A turret designed by IRMG engineers to provide long range defensive fire on their installations. Has a habit of leaving big holes."
+ stun_projectile = /obj/projectile/bullet/a308/rubber
+ stun_projectile_sound = 'sound/weapons/gun/rifle/f4.ogg'
+ lethal_projectile = /obj/projectile/bullet/a308
+ lethal_projectile_sound = 'sound/weapons/gun/rifle/f4.ogg'
+ scan_range = 12
+ shot_delay = 20
+
+/* Solcon Turrets */
+
+/obj/machinery/porta_turret/ship/solgov
+ faction = list(FACTION_PLAYER_SOLCON, "turret")
+
+/* Pan Gezena Federation Turrets */
+//midline but hitscan
+
+/obj/machinery/porta_turret/ship/pgf
+ name = "Etherbor Defensive Mount"
+ desc = "A less portable Etherbor offering, the EDM is a self-directed linkage of energy weapons, designed to keep intruders away from Gezenan vessels."
+ faction = list(FACTION_PLAYER_GEZENA, "Turret")
+ stun_projectile = /obj/projectile/beam/hitscan/disabler
+ stun_projectile_sound = 'sound/weapons/gun/energy/kalixpistol.ogg'
+ lethal_projectile = /obj/projectile/beam/hitscan/kalix/pgf/assault
+ lethal_projectile_sound = 'sound/weapons/gun/energy/kalixsmg.ogg'
+ icon_state = "standard_lethal"
+ base_icon_state = "standard"
+ max_integrity = 250
+ integrity_failure = 0.4
+
+/obj/machinery/porta_turret/ship/pgf/light
+ name = "Etherbor Deterrent System"
+ desc = "A light turret manufactured by Etherbor. It offers a lightweight assembly of energy weapons to accost nearby foes."
+ lethal_projectile = /obj/projectile/beam/hitscan/kalix/pgf
+ lethal_projectile_sound = 'sound/weapons/gun/energy/kalixsmg.ogg'
+
+/obj/machinery/porta_turret/ship/pgf/heavy
+ name = "Etherbor Point-Defense System"
+ desc = "A high-powered defensive turret manufactured by Etherbor. The EPDS contains heavy energy weapons linked in tandem."
+ scan_range = 12
+ stun_projectile = /obj/projectile/beam/hitscan/disabler/heavy
+ stun_projectile_sound = 'sound/weapons/gun/energy/kalixpistol.ogg'
+ lethal_projectile = /obj/projectile/beam/hitscan/kalix/pgf/sniper //fwoom
+ lethal_projectile_sound = 'sound/weapons/gun/laser/heavy_laser.ogg'
+
+///CLIP Turrets
+
+//high damage low range
+
+/obj/machinery/porta_turret/ship/clip
+ name = "Clover Mintaka"
+ desc = "Clover Photonic's offering for the Confederated League's 476FS \"Defense System\" competition, the Mintaka (and its sister systems, the Alnitak and Ori) handily beat out the Lunatex \"Vigil\" line during the final round of testing, and earned a prestigous contract."
+ faction = list(FACTION_PLAYER_MINUTEMAN, "Turret")
+ stun_projectile = /obj/projectile/beam/disabler
+ stun_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
+ lethal_projectile = /obj/projectile/beam/laser/assault
+ lethal_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
+ icon_state = "standard_lethal"
+ base_icon_state = "standard"
+
+ scan_range = 8
+ shot_delay = 10
+ max_integrity = 200
+ integrity_failure = 0.3
+
+/obj/machinery/porta_turret/ship/clip/light
+ name = "Clover Alnitak"
+ desc = "Clover Photonic's light turret system, unveiled as part of Clover's defense line-up in the early 470s. While lacking the punch of its sister systems, it still presents a hassle to circumvent."
+ stun_projectile = /obj/projectile/beam/disabler
+ stun_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
+ lethal_projectile = /obj/projectile/beam/laser/light
+ lethal_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
+
+ scan_range = 6
+ shot_delay = 10
+ max_integrity = 200
+ integrity_failure = 0.4
+
+/obj/machinery/porta_turret/ship/clip/heavy
+ name = "Clover Ori"
+ desc = "Clover Photonic's heaviest entry in the Confederated League's 476FS \"Defense System\" competition, the Ori's results demolished the handily beat out the Lunatex \"Vigil Sword\" during testing, earning better marks on durability, effectiveness, and reaction rate."
+ stun_projectile = /obj/projectile/beam/disabler
+ stun_projectile_sound = 'sound/weapons/gun/laser/e-fire.ogg'
+ lethal_projectile = /obj/projectile/beam/laser/heavylaser/assault
+ lethal_projectile_sound = 'sound/weapons/gun/laser/e40_las.ogg'
+
+ scan_range = 10
+ shot_delay = 20
+ max_integrity = 300
+ integrity_failure = 0.3
+
+
+/// Frontiersmen Turrets
+
+// fast and spitty
+
+/obj/machinery/porta_turret/ship/frontiersmen
+ name = "Spitter Turret"
+ desc = "A juryrigged mishmash of a 9mm SMG and targetting system. Stand clear!"
+ faction = list(FACTION_FRONTIER, "Turret")
+ subsystem_type = /datum/controller/subsystem/processing/fastprocess
+ integrity_failure = 0.6
+ max_integrity = 180
+
+ icon_state = "standard_lethal"
+ base_icon_state = "standard"
+
+ stun_projectile = /obj/projectile/bullet/c9mm
+ stun_projectile_sound = 'sound/weapons/gun/smg/spitter.ogg'
+ lethal_projectile = /obj/projectile/bullet/c9mm
+ lethal_projectile_sound = 'sound/weapons/gun/smg/spitter.ogg'
+ shot_delay = 2
+ scan_range = 6
+
+ turret_flags = TURRET_FLAG_HOSTILE
+
+/obj/machinery/porta_turret/ship/frontiersmen/light
+ name = "Pounder Turret"
+ desc = "A low caliber SMG with an atrociously high cycle rate, frankensteined together with a targetting assembly."
+ stun_projectile = /obj/projectile/bullet/c22lr
+ stun_projectile_sound = 'sound/weapons/gun/smg/pounder.ogg'
+ lethal_projectile = /obj/projectile/bullet/c22lr
+ lethal_projectile_sound = 'sound/weapons/gun/smg/pounder.ogg'
+ shot_delay = 1
+
+/obj/machinery/porta_turret/ship/frontiersmen/heavy
+ name = "Mulcher Turret"
+ desc = "An abombination made out of the components of a Shredder and an automatic targetting system. Careful now."
+ stun_projectile = /obj/projectile/bullet/slug/beanbag
+ stun_projectile_sound = 'sound/weapons/gun/hmg/shredder.ogg'
+ lethal_projectile = /obj/projectile/bullet/slug
+ lethal_projectile_sound = 'sound/weapons/gun/hmg/shredder.ogg'
+ shot_delay = 3
+ scan_range = 8
diff --git a/code/game/objects/items/circuitboards/machine_circuitboards.dm b/code/game/objects/items/circuitboards/machine_circuitboards.dm
index 896fa3a3e357..a1d01e6d777d 100644
--- a/code/game/objects/items/circuitboards/machine_circuitboards.dm
+++ b/code/game/objects/items/circuitboards/machine_circuitboards.dm
@@ -1510,3 +1510,15 @@
icon_state = "engineering"
build_path = /obj/machinery/suit_storage_unit
req_components = list(/obj/item/stock_parts/micro_laser = 4)
+
+/obj/item/circuitboard/machine/turret
+ name = "Turret"
+ icon_state = "security"
+ build_path = /obj/machinery/porta_turret
+ req_components = list(/obj/item/stock_parts/capacitor = 2, /obj/item/stock_parts/scanning_module = 1, /obj/item/assembly/prox_sensor = 1, /obj/item/gun/energy = 1)
+ def_components = list(/obj/item/gun/energy = /obj/item/gun/energy/e_gun/turret)
+
+/obj/item/circuitboard/machine/turret/ship
+ name = "Ship-mounted Turret"
+ //We don't want to let people take the gun out of the turret
+ def_components = list(/obj/item/gun/energy = /obj/item/stack/sheet/metal)
diff --git a/code/game/objects/items/devices/mines.dm b/code/game/objects/items/devices/mines.dm
index 6a7887c57601..4e1ec520e55c 100644
--- a/code/game/objects/items/devices/mines.dm
+++ b/code/game/objects/items/devices/mines.dm
@@ -322,7 +322,7 @@
if(!iscarbon(triggerer))
return
//Quick and dirty solution for preventing activations behind walls.
- if(!(triggerer in view(proximity_range, src)))
+ if(!can_see(src, triggerer))
return
if(!can_trigger(triggerer))
return
diff --git a/code/game/objects/items/stacks/sheets/recipes/recipes_metal.dm b/code/game/objects/items/stacks/sheets/recipes/recipes_metal.dm
index bd2a13a1ac88..44b7d7f26983 100644
--- a/code/game/objects/items/stacks/sheets/recipes/recipes_metal.dm
+++ b/code/game/objects/items/stacks/sheets/recipes/recipes_metal.dm
@@ -225,7 +225,6 @@ GLOBAL_LIST_INIT(metal_recipes, list ( \
null, \
new/datum/stack_recipe("firelock frame (fulltile)", /obj/structure/firelock_frame, 3, time = 50, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("firelock frame (directional)", /obj/structure/firelock_frame/border, 1, time = 25, on_floor = TRUE), \
- new/datum/stack_recipe("turret frame", /obj/machinery/porta_turret_construct, 5, time = 25, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("meatspike frame", /obj/structure/kitchenspike_frame, 5, time = 25, one_per_turf = TRUE, on_floor = TRUE), \
new/datum/stack_recipe("reflector frame", /obj/structure/reflector, 5, time = 25, one_per_turf = TRUE, on_floor = TRUE), \
null, \
diff --git a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
index ff7ddace1d4b..4f5e7bc161c9 100644
--- a/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
+++ b/code/modules/antagonists/traitor/equipment/Malf_Modules.dm
@@ -741,21 +741,6 @@ GLOBAL_LIST_INIT(malf_modules, subtypesof(/datum/AI_Module))
unlock_text = replacetext(unlock_text, "CAMSUPGRADED", "[upgraded_cameras]") //This works, since unlock text is called after upgrade()
-/// AI Turret Upgrade: Increases the health and damage of all turrets.
-/datum/AI_Module/upgrade/upgrade_turrets
- name = "AI Turret Upgrade"
- description = "Improves the power and health of all AI turrets. This effect is permanent. Upgrade is done immediately upon purchase."
- cost = 30
- upgrade = TRUE
- unlock_text = "You establish a power diversion to your turrets, upgrading their health and damage."
- unlock_sound = 'sound/items/rped.ogg'
-
-/datum/AI_Module/upgrade/upgrade_turrets/upgrade(mob/living/silicon/ai/AI)
- for(var/obj/machinery/porta_turret/ai/turret in GLOB.machines)
- turret.obj_integrity += 30
- turret.lethal_projectile = /obj/projectile/beam/laser/heavylaser //Once you see it, you will know what it means to FEAR.
- turret.lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
-
/// Enhanced Surveillance: Enables AI to hear conversations going on near its active vision.
/datum/AI_Module/upgrade/eavesdrop
name = "Enhanced Surveillance"
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index c51d1aca1963..e12e86e47b00 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -242,12 +242,12 @@
if(istype(the_target, /obj/machinery/porta_turret))
var/obj/machinery/porta_turret/P = the_target
- if(P.in_faction(src)) //Don't attack if the turret is in the same faction
- return FALSE
- if(P.has_cover &&!P.raised) //Don't attack invincible turrets
+ if(!(P.turret_flags & TURRET_FLAG_SHOOT_FAUNA)) //Don't attack turrets that won't shoot us
return FALSE
if(P.machine_stat & BROKEN) //Or turrets that are already broken
return FALSE
+ if(faction_check(P.faction, faction)) //Or turrets in the same faction
+ return FALSE
return TRUE
if(istype(the_target, /obj/machinery/drill))
diff --git a/code/modules/projectiles/guns/energy/energy_gun.dm b/code/modules/projectiles/guns/energy/energy_gun.dm
index a746dcc7241c..8d379b852b8f 100644
--- a/code/modules/projectiles/guns/energy/energy_gun.dm
+++ b/code/modules/projectiles/guns/energy/energy_gun.dm
@@ -111,16 +111,21 @@
/obj/item/gun/energy/e_gun/turret
name = "hybrid turret gun"
- desc = "A heavy hybrid energy cannon with two settings: Stun and kill."
+ desc = "A heavy hybrid energy cannon with two settings: Stun and kill. ...It doesn't seem have a trigger, seems it can only be used as a turret."
icon_state = "turretlaser"
item_state = "turretlaser"
slot_flags = null
w_class = WEIGHT_CLASS_HUGE
+ default_ammo_type = null
ammo_type = list(/obj/item/ammo_casing/energy/electrode, /obj/item/ammo_casing/energy/laser)
weapon_weight = WEAPON_HEAVY
trigger_guard = TRIGGER_GUARD_NONE
ammo_x_offset = 2
+/obj/item/gun/energy/e_gun/turret/pre_fire(atom/target, mob/living/user, message, flag, params, zone_override, bonus_spread, dual_wielded_gun)
+ to_chat(user, span_notice("[src] is not designed to be fired by hand."))
+ return FALSE
+
/obj/item/gun/energy/e_gun/nuclear
name = "advanced energy gun"
desc = "An energy gun with an experimental miniaturized nuclear reactor that automatically charges the internal power cell."
diff --git a/code/modules/ruins/rockplanet_ruin_code/mining_base.dm b/code/modules/ruins/rockplanet_ruin_code/mining_base.dm
index aad89082116c..afd34e00a6eb 100644
--- a/code/modules/ruins/rockplanet_ruin_code/mining_base.dm
+++ b/code/modules/ruins/rockplanet_ruin_code/mining_base.dm
@@ -1,7 +1,7 @@
/obj/machinery/porta_turret/ship/nt/light/mining_base
req_ship_access = FALSE
- mode = 1
- turret_flags = TURRET_FLAG_SHOOT_ANOMALOUS
+ lethal = TRUE
+ turret_flags = TURRET_FLAG_SHOOT_FAUNA
/obj/machinery/porta_turret/ship/nt/light/mining_base/Initialize()
. = ..()
diff --git a/code/modules/ruins/sandplanet_ruin_code/cave_base.dm b/code/modules/ruins/sandplanet_ruin_code/cave_base.dm
index 9995ff25015e..91caf21e1edf 100644
--- a/code/modules/ruins/sandplanet_ruin_code/cave_base.dm
+++ b/code/modules/ruins/sandplanet_ruin_code/cave_base.dm
@@ -46,7 +46,7 @@
stun_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
lethal_projectile = /obj/projectile/beam/laser/heavylaser
lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
- turret_flags = TURRET_FLAG_SHOOT_ALL | TURRET_FLAG_SHOOT_HEADS | TURRET_FLAG_SHOOT_UNSHIELDED
+ turret_flags = TURRET_FLAG_SHOOT_ALLMOBS
//gut wrenching content
diff --git a/code/modules/vehicles/atv.dm b/code/modules/vehicles/atv.dm
index a785ba5985d7..bd0a0f04c84d 100644
--- a/code/modules/vehicles/atv.dm
+++ b/code/modules/vehicles/atv.dm
@@ -40,7 +40,6 @@
/obj/vehicle/ridden/atv/turret/Initialize()
. = ..()
turret = new(loc)
- turret.base = src
/obj/vehicle/ridden/atv/turret/Moved()
. = ..()
diff --git a/shiptest.dme b/shiptest.dme
index 0e6a2cdf295a..9d4958bdc454 100644
--- a/shiptest.dme
+++ b/shiptest.dme
@@ -450,6 +450,7 @@
#include "code\datums\ruins.dm"
#include "code\datums\saymode.dm"
#include "code\datums\shuttles.dm"
+#include "code\datums\simple_beam.dm"
#include "code\datums\soullink.dm"
#include "code\datums\spawners_menu.dm"
#include "code\datums\tgs_event_handler.dm"
@@ -1007,8 +1008,9 @@
#include "code\game\machinery\pipe\construction.dm"
#include "code\game\machinery\pipe\pipe_dispenser.dm"
#include "code\game\machinery\porta_turret\portable_turret.dm"
-#include "code\game\machinery\porta_turret\portable_turret_construct.dm"
-#include "code\game\machinery\porta_turret\portable_turret_cover.dm"
+#include "code\game\machinery\porta_turret\portable_turret_control.dm"
+#include "code\game\machinery\porta_turret\portable_turret_manual_control.dm"
+#include "code\game\machinery\porta_turret\portable_turret_types.dm"
#include "code\game\machinery\shuttle\custom_shuttle.dm"
#include "code\game\machinery\shuttle\ship_gravity.dm"
#include "code\game\machinery\shuttle\shuttle_engine.dm"
diff --git a/tgui/packages/tgui/interfaces/PortableTurret.js b/tgui/packages/tgui/interfaces/PortableTurret.js
deleted file mode 100644
index e66b522bf7ef..000000000000
--- a/tgui/packages/tgui/interfaces/PortableTurret.js
+++ /dev/null
@@ -1,121 +0,0 @@
-import { useBackend } from '../backend';
-import { Button, LabeledList, NoticeBox, Section } from '../components';
-import { Window } from '../layouts';
-
-export const PortableTurret = (props, context) => {
- const { act, data } = useBackend(context);
- const {
- silicon_user,
- locked,
- on,
- check_weapons,
- neutralize_criminals,
- neutralize_all,
- neutralize_unidentified,
- neutralize_nonmindshielded,
- neutralize_cyborgs,
- ignore_heads,
- manual_control,
- allow_manual_control,
- lasertag_turret,
- } = data;
- return (
-
-
-
- Swipe an ID card to {locked ? 'unlock' : 'lock'} this interface.
-
- <>
-
-
- act('manual')}
- />
- )
- }
- >
-
-
-
- {!lasertag_turret && (
- act('shootheads')}
- />
- }
- >
- act('shootall')}
- />
- act('authweapon')}
- />
- act('checkxenos')}
- />
- act('checkloyal')}
- />
- act('shootcriminals')}
- />
- act('shootborgs')}
- />
-
- )}
- >
-
-
- );
-};
diff --git a/tgui/packages/tgui/interfaces/TurretControl.js b/tgui/packages/tgui/interfaces/TurretControl.js
index 294e106ddea3..b2fe48e42bc8 100644
--- a/tgui/packages/tgui/interfaces/TurretControl.js
+++ b/tgui/packages/tgui/interfaces/TurretControl.js
@@ -1,17 +1,47 @@
import { useBackend } from '../backend';
-import { Button, LabeledList, Section } from '../components';
+import { Button, Flex, LabeledList, Section } from '../components';
import { Window } from '../layouts';
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
export const TurretControl = (props, context) => {
const { act, data } = useBackend(context);
- const locked = data.locked && !data.siliconUser;
- const { enabled, lethal, shootCyborgs } = data;
+ const {
+ allow_manual_control,
+ manual_control,
+ silicon_user,
+ lethal,
+ enabled,
+ dangerous_only,
+ retaliate,
+ shoot_fauna,
+ shoot_humans,
+ shoot_silicons,
+ only_nonfaction,
+ only_specificfaction,
+ } = data;
+ const locked = data.locked && !silicon_user;
+
return (
-
+
-
+ act('manual')}
+ />
+ )
+ }
+ >
-
-
+
+
+
+ act('shoot_silicons')}
+ onClick={() => act('toggle_dangerous')}
/>
-
-
+
+
+ act('toggle_retaliate')}
+ />
+
+
+
+
+ act('toggle_fauna')}
+ />
+
+
+ act('toggle_humans')}
+ />
+
+
+ act('toggle_silicons')}
+ />
+
+
+
+
+ act('toggle_nonfaction')}
+ />
+
+
+ act('toggle_specificfaction')}
+ />
+
+
diff --git a/tools/UpdatePaths/Scripts/3844_turrets_refactor.txt b/tools/UpdatePaths/Scripts/3844_turrets_refactor.txt
new file mode 100644
index 000000000000..2f655c6f68e1
--- /dev/null
+++ b/tools/UpdatePaths/Scripts/3844_turrets_refactor.txt
@@ -0,0 +1 @@
+/obj/machinery/porta_turret/@SUBTYPES : /obj/machinery/porta_turret/@SUBTYPES {@OLD;lethal=@OLD:mode;mode=@SKIP}
diff --git a/tools/UpdatePaths/__main__.py b/tools/UpdatePaths/__main__.py
index 804f34c88c09..4056d689fd32 100644
--- a/tools/UpdatePaths/__main__.py
+++ b/tools/UpdatePaths/__main__.py
@@ -119,7 +119,11 @@ def replace_def(match):
continue
if prop_value.startswith("@OLD"):
params = prop_value.split(":")
- if prop_name in old_props:
+ if len(params) > 1:
+ old_prop_name = params[1]
+ if old_prop_name in old_props:
+ out_props[prop_name] = old_props[old_prop_name]
+ elif prop_name in old_props:
out_props[prop_name] = old_props[params[1]] if len(params) > 1 else old_props[prop_name]
continue
out_props[prop_name] = prop_value