mirror of
https://github.com/SPLURT-Station/S.P.L.U.R.T-Station-13.git
synced 2025-12-09 16:07:40 +00:00
1053 lines
34 KiB
Plaintext
1053 lines
34 KiB
Plaintext
#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
|
|
|
|
/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
|
|
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 = 50 //when inactive, this turret takes up constant 50 Equipment power
|
|
active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power
|
|
req_access = list(ACCESS_SEC_DOORS)
|
|
power_channel = EQUIP //drains power from the EQUIPMENT channel
|
|
|
|
var/base_icon_state = "standard"
|
|
var/scan_range = 7
|
|
var/atom/base = null //for turrets inside other objects
|
|
|
|
var/raised = 0 //if the turret cover is "open" and the turret is raised
|
|
var/raising= 0 //if the turret is currently opening or closing its cover
|
|
|
|
max_integrity = 160 //the turret's health
|
|
integrity_failure = 80
|
|
armor = list("melee" = 50, "bullet" = 30, "laser" = 30, "energy" = 30, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 90, "acid" = 90)
|
|
|
|
var/locked = TRUE //if the turret's behaviour control access is locked
|
|
var/controllock = FALSE //if the turret responds to control panels
|
|
|
|
var/installation = /obj/item/gun/energy/e_gun/turret //the type of weapon installed by default
|
|
var/obj/item/gun/stored_gun = null
|
|
var/gun_charge = 0 //the charge of the gun when retrieved from wreckage
|
|
|
|
var/mode = TURRET_STUN
|
|
|
|
var/stun_projectile = null //stun mode projectile type
|
|
var/stun_projectile_sound
|
|
var/lethal_projectile = null //lethal mode projectile type
|
|
var/lethal_projectile_sound
|
|
|
|
var/reqpower = 500 //power needed per shot
|
|
var/always_up = 0 //Will stay active
|
|
var/has_cover = 1 //Hides the cover
|
|
|
|
var/obj/machinery/porta_turret_cover/cover = null //the cover that is covering this turret
|
|
|
|
var/last_fired = 0 //world.time the turret last fired
|
|
var/shot_delay = 15 //ticks until next shot (1.5 ?)
|
|
|
|
|
|
var/check_records = 1 //checks if it can use the security records
|
|
var/criminals = 1 //checks if it can shoot people on arrest
|
|
var/auth_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have
|
|
var/stun_all = 0 //if this is active, the turret shoots everything that isn't security or head of staff
|
|
var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos)
|
|
var/shoot_unloyal = 0 //checks if it can shoot people that aren't loyalty implantd
|
|
|
|
var/attacked = 0 //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!)
|
|
|
|
var/on = TRUE //determines if the turret is on
|
|
|
|
var/list/faction = list("turret") // Same faction mobs will never be shot at, no matter the other settings
|
|
|
|
var/datum/effect_system/spark_spread/spark_system //the spark system, used for generating... sparks?
|
|
|
|
var/obj/machinery/turretid/cp = null
|
|
|
|
var/wall_turret_direction //The turret will try to shoot from a turf in that direction when in a wall
|
|
|
|
var/manual_control = FALSE //
|
|
var/datum/action/turret_quit/quit_action
|
|
var/datum/action/turret_toggle/toggle_action
|
|
var/mob/remote_controller
|
|
|
|
/obj/machinery/porta_turret/Initialize()
|
|
. = ..()
|
|
if(!base)
|
|
base = src
|
|
update_icon()
|
|
//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/popUp)
|
|
|
|
/obj/machinery/porta_turret/update_icon()
|
|
cut_overlays()
|
|
if(!anchored)
|
|
icon_state = "turretCover"
|
|
return
|
|
if(stat & BROKEN)
|
|
icon_state = "[base_icon_state]_broken"
|
|
else
|
|
if(powered())
|
|
if(on && raised)
|
|
switch(mode)
|
|
if(TURRET_STUN)
|
|
icon_state = "[base_icon_state]_stun"
|
|
if(TURRET_LETHAL)
|
|
icon_state = "[base_icon_state]_lethal"
|
|
else
|
|
icon_state = "[base_icon_state]_off"
|
|
else
|
|
icon_state = "[base_icon_state]_unpowered"
|
|
|
|
|
|
/obj/machinery/porta_turret/proc/setup(obj/item/gun/turret_gun)
|
|
if(stored_gun)
|
|
qdel(stored_gun)
|
|
stored_gun = null
|
|
|
|
if(installation && !turret_gun)
|
|
stored_gun = new installation(src)
|
|
else if (turret_gun)
|
|
stored_gun = turret_gun
|
|
|
|
var/list/gun_properties = stored_gun.get_turret_properties()
|
|
|
|
//required properties
|
|
stun_projectile = gun_properties["stun_projectile"]
|
|
stun_projectile_sound = gun_properties["stun_projectile_sound"]
|
|
lethal_projectile = gun_properties["lethal_projectile"]
|
|
lethal_projectile_sound = gun_properties["lethal_projectile_sound"]
|
|
base_icon_state = gun_properties["base_icon_state"]
|
|
|
|
//optional properties
|
|
if(gun_properties["shot_delay"])
|
|
shot_delay = gun_properties["shot_delay"]
|
|
if(gun_properties["reqpower"])
|
|
reqpower = gun_properties["reqpower"]
|
|
|
|
update_icon()
|
|
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()
|
|
return ..()
|
|
|
|
/obj/machinery/porta_turret/ui_interact(mob/user)
|
|
. = ..()
|
|
var/dat
|
|
dat += "Status: <a href='?src=[REF(src)];power=1'>[on ? "On" : "Off"]</a><br>"
|
|
dat += "Behaviour controls are [locked ? "locked" : "unlocked"]<br>"
|
|
|
|
if(!locked)
|
|
dat += "Check for Weapon Authorization: <A href='?src=[REF(src)];operation=authweapon'>[auth_weapons ? "Yes" : "No"]</A><BR>"
|
|
dat += "Check Security Records: <A href='?src=[REF(src)];operation=checkrecords'>[check_records ? "Yes" : "No"]</A><BR>"
|
|
dat += "Neutralize Identified Criminals: <A href='?src=[REF(src)];operation=shootcrooks'>[criminals ? "Yes" : "No"]</A><BR>"
|
|
dat += "Neutralize All Non-Security and Non-Command Personnel: <A href='?src=[REF(src)];operation=shootall'>[stun_all ? "Yes" : "No"]</A><BR>"
|
|
dat += "Neutralize All Unidentified Life Signs: <A href='?src=[REF(src)];operation=checkxenos'>[check_anomalies ? "Yes" : "No"]</A><BR>"
|
|
dat += "Neutralize All Non-Loyalty Implanted Personnel: <A href='?src=[REF(src)];operation=checkloyal'>[shoot_unloyal ? "Yes" : "No"]</A><BR>"
|
|
if(issilicon(user))
|
|
if(!manual_control)
|
|
var/mob/living/silicon/S = user
|
|
if(S.hack_software)
|
|
dat += "Assume direct control : <a href='?src=[REF(src)];operation=manual'>Manual Control</a><br>"
|
|
else
|
|
dat += "Warning! Remote control protocol enabled.<br>"
|
|
|
|
|
|
var/datum/browser/popup = new(user, "autosec", "Automatic Portable Turret Installation", 300, 300)
|
|
popup.set_content(dat)
|
|
popup.open()
|
|
|
|
/obj/machinery/porta_turret/Topic(href, href_list)
|
|
if(..())
|
|
return
|
|
usr.set_machine(src)
|
|
add_fingerprint(usr)
|
|
|
|
if(href_list["power"] && !locked)
|
|
if(anchored) //you can't turn a turret on/off if it's not anchored/secured
|
|
on = !on //toggle on/off
|
|
else
|
|
to_chat(usr, "<span class='notice'>It has to be secured first!</span>")
|
|
interact(usr)
|
|
return
|
|
|
|
if(href_list["operation"])
|
|
switch(href_list["operation"]) //toggles customizable behavioural protocols
|
|
if("authweapon")
|
|
auth_weapons = !auth_weapons
|
|
if("checkrecords")
|
|
check_records = !check_records
|
|
if("shootcrooks")
|
|
criminals = !criminals
|
|
if("shootall")
|
|
stun_all = !stun_all
|
|
if("checkxenos")
|
|
check_anomalies = !check_anomalies
|
|
if("checkloyal")
|
|
shoot_unloyal = !shoot_unloyal
|
|
if("manual")
|
|
if(issilicon(usr) && !manual_control)
|
|
give_control(usr)
|
|
interact(usr)
|
|
|
|
/obj/machinery/porta_turret/power_change()
|
|
if(!anchored)
|
|
update_icon()
|
|
remove_control()
|
|
return
|
|
if(stat & BROKEN)
|
|
update_icon()
|
|
remove_control()
|
|
else
|
|
if( powered() )
|
|
stat &= ~NOPOWER
|
|
update_icon()
|
|
else
|
|
spawn(rand(0, 15))
|
|
stat |= NOPOWER
|
|
remove_control()
|
|
update_icon()
|
|
|
|
|
|
/obj/machinery/porta_turret/attackby(obj/item/I, mob/user, params)
|
|
if(stat & BROKEN)
|
|
if(istype(I, /obj/item/crowbar))
|
|
//If the turret is destroyed, you can remove it with a crowbar to
|
|
//try and salvage its components
|
|
to_chat(user, "<span class='notice'>You begin prying the metal coverings off...</span>")
|
|
if(I.use_tool(src, user, 20))
|
|
if(prob(70))
|
|
if(stored_gun)
|
|
stored_gun.forceMove(loc)
|
|
to_chat(user, "<span class='notice'>You remove the turret and salvage some components.</span>")
|
|
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, "<span class='notice'>You remove the turret but did not manage to salvage anything.</span>")
|
|
qdel(src)
|
|
|
|
else if((istype(I, /obj/item/wrench)) && (!on))
|
|
if(raised)
|
|
return
|
|
|
|
//This code handles moving the turret around. After all, it's a portable turret!
|
|
if(!anchored && !isinspace())
|
|
setAnchored(TRUE)
|
|
invisibility = INVISIBILITY_MAXIMUM
|
|
update_icon()
|
|
to_chat(user, "<span class='notice'>You secure the exterior bolts on the turret.</span>")
|
|
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)
|
|
setAnchored(FALSE)
|
|
to_chat(user, "<span class='notice'>You unsecure the exterior bolts on the turret.</span>")
|
|
power_change()
|
|
invisibility = 0
|
|
qdel(cover) //deletes the cover, and the turret instance itself becomes its own cover.
|
|
|
|
else if(I.GetID())
|
|
//Behavior lock/unlock mangement
|
|
if(allowed(user))
|
|
locked = !locked
|
|
to_chat(user, "<span class='notice'>Controls are now [locked ? "locked" : "unlocked"].</span>")
|
|
else
|
|
to_chat(user, "<span class='notice'>Access denied.</span>")
|
|
else if(istype(I, /obj/item/multitool) && !locked)
|
|
var/obj/item/multitool/M = I
|
|
M.buffer = src
|
|
to_chat(user, "<span class='notice'>You add [src] to multitool buffer.</span>")
|
|
else
|
|
return ..()
|
|
|
|
/obj/machinery/porta_turret/emag_act(mob/user)
|
|
if(obj_flags & EMAGGED)
|
|
return
|
|
to_chat(user, "<span class='warning'>You short out [src]'s threat assessment circuits.</span>")
|
|
visible_message("[src] hums oddly...")
|
|
obj_flags |= EMAGGED
|
|
controllock = TRUE
|
|
on = FALSE //turns off the turret temporarily
|
|
update_icon()
|
|
sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit
|
|
on = TRUE //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)
|
|
. = ..()
|
|
if (. & EMP_PROTECT_SELF)
|
|
return
|
|
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
|
|
check_records = pick(0, 1)
|
|
criminals = pick(0, 1)
|
|
auth_weapons = pick(0, 1)
|
|
stun_all = pick(0, 0, 0, 0, 1) //stun_all is a pretty big deal, so it's least likely to get turned on
|
|
|
|
on = FALSE
|
|
remove_control()
|
|
|
|
spawn(rand(60,600))
|
|
if(!on)
|
|
on = TRUE
|
|
|
|
/obj/machinery/porta_turret/take_damage(damage, damage_type = BRUTE, damage_flag = 0, sound_effect = 1)
|
|
. = ..()
|
|
if(.) //damage received
|
|
if(prob(30))
|
|
spark_system.start()
|
|
if(on && !attacked && !(obj_flags & EMAGGED))
|
|
attacked = TRUE
|
|
addtimer(CALLBACK(src, .proc/reset_attacked), 60)
|
|
|
|
/obj/machinery/porta_turret/proc/reset_attacked()
|
|
attacked = FALSE
|
|
|
|
/obj/machinery/porta_turret/deconstruct(disassembled = TRUE)
|
|
qdel(src)
|
|
|
|
/obj/machinery/porta_turret/obj_break(damage_flag)
|
|
if(!(flags_1 & NODECONSTRUCT_1) && !(stat & BROKEN))
|
|
stat |= BROKEN //enables the BROKEN bit
|
|
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(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 || (stat & (NOPOWER|BROKEN)) || manual_control)
|
|
return
|
|
|
|
var/list/targets = list()
|
|
for(var/mob/A in view(scan_range, base))
|
|
if(A.invisibility > SEE_INVISIBLE_LIVING)
|
|
continue
|
|
|
|
if(check_anomalies)//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
|
|
if(issilicon(A))
|
|
var/mob/living/silicon/sillycone = A
|
|
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
|
|
|
|
targets += sillycone
|
|
|
|
if(iscarbon(A))
|
|
var/mob/living/carbon/C = A
|
|
//If not emagged, only target non downed carbons
|
|
if(mode != TURRET_LETHAL && (C.stat || C.handcuffed || C.recoveringstam))//CIT CHANGE - replaces check for lying with check for recoveringstam
|
|
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(check_anomalies) //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(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(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_icon()
|
|
|
|
/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((stun_all || attacked) && !allowed(perp))
|
|
//if the turret has been attacked or is angry, target all non-sec people
|
|
if(!allowed(perp))
|
|
return 10
|
|
|
|
if(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(check_records) //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(shoot_unloyal)
|
|
if (!perp.has_trait(TRAIT_MINDSHIELD))
|
|
threatcount += 4
|
|
|
|
return threatcount
|
|
|
|
|
|
/obj/machinery/porta_turret/proc/in_faction(mob/target)
|
|
for(var/faction1 in faction)
|
|
if(faction1 in target.faction)
|
|
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?
|
|
return
|
|
|
|
if(!(obj_flags & EMAGGED)) //if it hasn't been emagged, cooldown before shooting again
|
|
if(last_fired + shot_delay > world.time)
|
|
return
|
|
last_fired = world.time
|
|
|
|
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) && !is_blocked_turf(closer) && 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) && !is_blocked_turf(closer) && T.Adjacent(closer))
|
|
T = closer
|
|
break
|
|
|
|
update_icon()
|
|
var/obj/item/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, 1)
|
|
else
|
|
use_power(reqpower * 2)
|
|
A = new lethal_projectile(T)
|
|
playsound(loc, lethal_projectile_sound, 75, 1)
|
|
|
|
|
|
//Shooting Code:
|
|
A.preparePixelProjectile(target, T)
|
|
A.firer = src
|
|
A.fire()
|
|
return A
|
|
|
|
/obj/machinery/porta_turret/proc/setState(on, mode)
|
|
if(controllock)
|
|
return
|
|
src.on = on
|
|
if(!on)
|
|
popDown()
|
|
src.mode = mode
|
|
power_change()
|
|
|
|
|
|
/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.setState(P.on,!P.mode)
|
|
|
|
/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
|
|
always_up = TRUE
|
|
popUp()
|
|
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, "<span class='warning'>Your uplink to [src] has been severed!</span>")
|
|
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()
|
|
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)
|
|
stun_projectile = /obj/item/projectile/bullet
|
|
lethal_projectile = /obj/item/projectile/bullet
|
|
lethal_projectile_sound = 'sound/weapons/gunshot.ogg'
|
|
stun_projectile_sound = 'sound/weapons/gunshot.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/energy
|
|
icon_state = "standard_stun"
|
|
base_icon_state = "standard"
|
|
stun_projectile = /obj/item/projectile/energy/electrode
|
|
stun_projectile_sound = 'sound/weapons/taser.ogg'
|
|
lethal_projectile = /obj/item/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_stun"
|
|
base_icon_state = "standard"
|
|
stun_projectile = /obj/item/projectile/energy/electrode
|
|
stun_projectile_sound = 'sound/weapons/taser.ogg'
|
|
lethal_projectile = /obj/item/projectile/beam/laser/heavylaser
|
|
lethal_projectile_sound = 'sound/weapons/lasercannonfire.ogg'
|
|
desc = "An energy blaster auto-turret."
|
|
|
|
|
|
/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/pod
|
|
integrity_failure = 20
|
|
max_integrity = 40
|
|
stun_projectile = /obj/item/projectile/bullet/syndicate_turret
|
|
lethal_projectile = /obj/item/projectile/bullet/syndicate_turret
|
|
|
|
/obj/machinery/porta_turret/ai
|
|
faction = list("silicon")
|
|
|
|
/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/aux_base
|
|
name = "perimeter defense turret"
|
|
desc = "A plasma beam turret calibrated to defend outposts against non-humanoid fauna. It is more effective when exposed to the environment."
|
|
installation = null
|
|
lethal_projectile = /obj/item/projectile/plasma/turret
|
|
lethal_projectile_sound = 'sound/weapons/plasma_cutter.ogg'
|
|
mode = TURRET_LETHAL //It would be useless in stun mode anyway
|
|
faction = list("neutral","silicon","turret") //Minebots, medibots, etc that should not be shot.
|
|
|
|
/obj/machinery/porta_turret/aux_base/assess_perp(mob/living/carbon/human/perp)
|
|
return 0 //Never shoot humanoids. You are on your own if Ashwalkers or the like attack!
|
|
|
|
/obj/machinery/porta_turret/aux_base/setup()
|
|
return
|
|
|
|
/obj/machinery/porta_turret/aux_base/interact(mob/user) //Controlled solely from the base console.
|
|
return
|
|
|
|
/obj/machinery/porta_turret/aux_base/Initialize()
|
|
. = ..()
|
|
cover.name = name
|
|
cover.desc = desc
|
|
|
|
/obj/machinery/porta_turret/centcom_shuttle
|
|
installation = null
|
|
max_integrity = 260
|
|
always_up = 1
|
|
use_power = NO_POWER_USE
|
|
has_cover = 0
|
|
scan_range = 9
|
|
stun_projectile = /obj/item/projectile/beam/laser
|
|
lethal_projectile = /obj/item/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","silicon","turret")
|
|
mode = TURRET_LETHAL
|
|
|
|
/obj/machinery/porta_turret/centcom_shuttle/ComponentInitialize()
|
|
. = ..()
|
|
AddComponent(/datum/component/empprotection, EMP_PROTECT_SELF | EMP_PROTECT_WIRES)
|
|
|
|
/obj/machinery/porta_turret/centcom_shuttle/assess_perp(mob/living/carbon/human/perp)
|
|
return 0
|
|
|
|
/obj/machinery/porta_turret/centcom_shuttle/setup()
|
|
return
|
|
|
|
/obj/machinery/porta_turret/centcom_shuttle/weak
|
|
max_integrity = 120
|
|
integrity_failure = 60
|
|
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/item/projectile/beam/weak/penetrator
|
|
lethal_projectile = /obj/item/projectile/beam/weak/penetrator
|
|
faction = list("neutral","silicon","turret")
|
|
|
|
/obj/machinery/porta_turret/centcom_shuttle/ballistic
|
|
stun_projectile = /obj/item/projectile/bullet
|
|
lethal_projectile = /obj/item/projectile/bullet
|
|
lethal_projectile_sound = 'sound/weapons/gunshot.ogg'
|
|
stun_projectile_sound = 'sound/weapons/gunshot.ogg'
|
|
desc = "A ballistic machine gun auto-turret."
|
|
|
|
|
|
////////////////////////
|
|
//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"
|
|
density = FALSE
|
|
var/enabled = 1
|
|
var/lethal = 0
|
|
var/locked = TRUE
|
|
var/control_area = null //can be area name, path or nothing.
|
|
var/ailock = 0 // AI cannot use this
|
|
req_access = list(ACCESS_AI_UPLOAD)
|
|
var/list/obj/machinery/porta_turret/turrets = list()
|
|
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
|
|
|
|
/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 ..()
|
|
|
|
/obj/machinery/turretid/Initialize(mapload) //map-placed turrets autolink turrets
|
|
. = ..()
|
|
if(!mapload)
|
|
return
|
|
|
|
if(control_area)
|
|
control_area = get_area_instance_from_text(control_area)
|
|
if(control_area == null)
|
|
control_area = get_area(src)
|
|
stack_trace("Bad control_area path for [src], [src.control_area]")
|
|
else if(!control_area)
|
|
control_area = get_area(src)
|
|
|
|
for(var/obj/machinery/porta_turret/T in control_area)
|
|
turrets |= T
|
|
T.cp = src
|
|
|
|
/obj/machinery/turretid/examine(mob/user)
|
|
..()
|
|
if(issilicon(user) && (!stat & BROKEN))
|
|
to_chat(user, "<span class='notice'>Ctrl-click [src] to [ enabled ? "disable" : "enable"] turrets.</span>")
|
|
to_chat(user, "<span class='notice'>Alt-click [src] to set turrets to [ lethal ? "stun" : "kill"].</span>")
|
|
|
|
/obj/machinery/turretid/attackby(obj/item/I, mob/user, params)
|
|
if(stat & BROKEN)
|
|
return
|
|
|
|
if (istype(I, /obj/item/multitool))
|
|
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 (issilicon(user))
|
|
return attack_hand(user)
|
|
|
|
if ( get_dist(src, user) == 0 ) // trying to unlock the interface
|
|
if (allowed(usr))
|
|
if(obj_flags & EMAGGED)
|
|
to_chat(user, "<span class='notice'>The turret control is unresponsive.</span>")
|
|
return
|
|
|
|
locked = !locked
|
|
to_chat(user, "<span class='notice'>You [ locked ? "lock" : "unlock"] the panel.</span>")
|
|
if (locked)
|
|
if (user.machine==src)
|
|
user.unset_machine()
|
|
user << browse(null, "window=turretid")
|
|
else
|
|
if (user.machine==src)
|
|
attack_hand(user)
|
|
else
|
|
to_chat(user, "<span class='warning'>Access denied.</span>")
|
|
|
|
/obj/machinery/turretid/emag_act(mob/user)
|
|
if(obj_flags & EMAGGED)
|
|
return
|
|
to_chat(user, "<span class='danger'>You short out the turret controls' access analysis module.</span>")
|
|
obj_flags |= EMAGGED
|
|
locked = FALSE
|
|
if(user && user.machine == src)
|
|
attack_hand(user)
|
|
|
|
/obj/machinery/turretid/attack_ai(mob/user)
|
|
if(!ailock || IsAdminGhost(user))
|
|
return attack_hand(user)
|
|
else
|
|
to_chat(user, "<span class='notice'>There seems to be a firewall preventing you from accessing this device.</span>")
|
|
|
|
/obj/machinery/turretid/ui_interact(mob/user)
|
|
. = ..()
|
|
if ( get_dist(src, user) > 0 )
|
|
if ( !(issilicon(user) || IsAdminGhost(user)) )
|
|
to_chat(user, "<span class='notice'>You are too far away.</span>")
|
|
user.unset_machine()
|
|
user << browse(null, "window=turretid")
|
|
return
|
|
|
|
var/t = ""
|
|
|
|
if(locked && !(issilicon(user) || IsAdminGhost(user)))
|
|
t += "<div class='notice icon'>Swipe ID card to unlock interface</div>"
|
|
else
|
|
if(!issilicon(user) && !IsAdminGhost(user))
|
|
t += "<div class='notice icon'>Swipe ID card to lock interface</div>"
|
|
t += "Turrets [enabled?"activated":"deactivated"] - <A href='?src=[REF(src)];toggleOn=1'>[enabled?"Disable":"Enable"]?</a><br>"
|
|
t += "Currently set for [lethal?"lethal":"stun repeatedly"] - <A href='?src=[REF(src)];toggleLethal=1'>Change to [lethal?"Stun repeatedly":"Lethal"]?</a><br>"
|
|
|
|
var/datum/browser/popup = new(user, "turretid", "Turret Control Panel ([get_area_name(src, TRUE)])")
|
|
popup.set_content(t)
|
|
popup.set_title_image(user.browse_rsc_icon(src.icon, src.icon_state))
|
|
popup.open()
|
|
|
|
/obj/machinery/turretid/Topic(href, href_list)
|
|
if(..())
|
|
return
|
|
if (locked)
|
|
if(!(issilicon(usr) || IsAdminGhost(usr)))
|
|
to_chat(usr, "Control panel is locked!")
|
|
return
|
|
if (href_list["toggleOn"])
|
|
toggle_on()
|
|
else if (href_list["toggleLethal"])
|
|
toggle_lethal()
|
|
attack_hand(usr)
|
|
|
|
/obj/machinery/turretid/proc/toggle_lethal()
|
|
lethal = !lethal
|
|
updateTurrets()
|
|
|
|
/obj/machinery/turretid/proc/toggle_on()
|
|
enabled = !enabled
|
|
updateTurrets()
|
|
|
|
/obj/machinery/turretid/proc/updateTurrets()
|
|
for (var/obj/machinery/porta_turret/aTurret in turrets)
|
|
aTurret.setState(enabled, lethal)
|
|
update_icon()
|
|
|
|
/obj/machinery/turretid/power_change()
|
|
..()
|
|
update_icon()
|
|
|
|
/obj/machinery/turretid/update_icon()
|
|
..()
|
|
if(stat & NOPOWER)
|
|
icon_state = "control_off"
|
|
else if (enabled)
|
|
if (lethal)
|
|
icon_state = "control_kill"
|
|
else
|
|
icon_state = "control_stun"
|
|
else
|
|
icon_state = "control_standby"
|
|
|
|
/obj/item/wallframe/turret_control
|
|
name = "turret control frame"
|
|
desc = "Used for building turret control panels."
|
|
icon_state = "apc"
|
|
result_path = /obj/machinery/turretid
|
|
materials = list(MAT_METAL=MINERAL_MATERIAL_AMOUNT)
|
|
|
|
/obj/item/gun/proc/get_turret_properties()
|
|
. = list()
|
|
.["lethal_projectile"] = null
|
|
.["lethal_projectile_sound"] = null
|
|
.["stun_projectile"] = null
|
|
.["stun_projectile_sound"] = null
|
|
.["base_icon_state"] = "standard"
|
|
|
|
/obj/item/gun/energy/get_turret_properties()
|
|
. = ..()
|
|
|
|
var/obj/item/ammo_casing/primary_ammo = ammo_type[1]
|
|
|
|
.["stun_projectile"] = initial(primary_ammo.projectile_type)
|
|
.["stun_projectile_sound"] = initial(primary_ammo.fire_sound)
|
|
|
|
if(ammo_type.len > 1)
|
|
var/obj/item/ammo_casing/secondary_ammo = ammo_type[2]
|
|
.["lethal_projectile"] = initial(secondary_ammo.projectile_type)
|
|
.["lethal_projectile_sound"] = initial(secondary_ammo.fire_sound)
|
|
else
|
|
.["lethal_projectile"] = .["stun_projectile"]
|
|
.["lethal_projectile_sound"] = .["stun_projectile_sound"]
|
|
|
|
/obj/item/gun/ballistic/get_turret_properties()
|
|
. = ..()
|
|
var/obj/item/ammo_box/mag = mag_type
|
|
var/obj/item/ammo_casing/primary_ammo = initial(mag.ammo_type)
|
|
|
|
.["base_icon_state"] = "syndie"
|
|
.["stun_projectile"] = initial(primary_ammo.projectile_type)
|
|
.["stun_projectile_sound"] = initial(primary_ammo.fire_sound)
|
|
.["lethal_projectile"] = .["stun_projectile"]
|
|
.["lethal_projectile_sound"] = .["stun_projectile_sound"]
|
|
|
|
|
|
/obj/item/gun/energy/laser/bluetag/get_turret_properties()
|
|
. = ..()
|
|
.["stun_projectile"] = /obj/item/projectile/beam/lasertag/bluetag
|
|
.["lethal_projectile"] = /obj/item/projectile/beam/lasertag/bluetag
|
|
.["base_icon_state"] = "blue"
|
|
.["shot_delay"] = 30
|
|
.["team_color"] = "blue"
|
|
|
|
/obj/item/gun/energy/laser/redtag/get_turret_properties()
|
|
. = ..()
|
|
.["stun_projectile"] = /obj/item/projectile/beam/lasertag/redtag
|
|
.["lethal_projectile"] = /obj/item/projectile/beam/lasertag/redtag
|
|
.["base_icon_state"] = "red"
|
|
.["shot_delay"] = 30
|
|
.["team_color"] = "red"
|
|
|
|
/obj/item/gun/energy/e_gun/turret/get_turret_properties()
|
|
. = ..()
|
|
|
|
/obj/machinery/porta_turret/lasertag
|
|
req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE)
|
|
check_records = 0
|
|
criminals = 0
|
|
auth_weapons = 1
|
|
stun_all = 0
|
|
check_anomalies = 0
|
|
var/team_color
|
|
|
|
/obj/machinery/porta_turret/lasertag/assess_perp(mob/living/carbon/human/perp)
|
|
. = 0
|
|
if(team_color == "blue") //Lasertag turrets target the opposing team, how great is that? -Sieve
|
|
. = 0 //But does not target anyone else
|
|
if(istype(perp.wear_suit, /obj/item/clothing/suit/redtag))
|
|
. += 4
|
|
if(perp.is_holding_item_of_type(/obj/item/gun/energy/laser/redtag))
|
|
. += 4
|
|
if(istype(perp.belt, /obj/item/gun/energy/laser/redtag))
|
|
. += 2
|
|
|
|
if(team_color == "red")
|
|
. = 0
|
|
if(istype(perp.wear_suit, /obj/item/clothing/suit/bluetag))
|
|
. += 4
|
|
if(perp.is_holding_item_of_type(/obj/item/gun/energy/laser/bluetag))
|
|
. += 4
|
|
if(istype(perp.belt, /obj/item/gun/energy/laser/bluetag))
|
|
. += 2
|
|
|
|
/obj/machinery/porta_turret/lasertag/setup(obj/item/gun/gun)
|
|
var/list/properties = ..()
|
|
if(properties["team_color"])
|
|
team_color = properties["team_color"]
|
|
|
|
/obj/machinery/porta_turret/lasertag/ui_interact(mob/user)
|
|
. = ..()
|
|
if(ishuman(user))
|
|
var/mob/living/carbon/human/H = user
|
|
if(team_color == "blue" && istype(H.wear_suit, /obj/item/clothing/suit/redtag))
|
|
return
|
|
if(team_color == "red" && istype(H.wear_suit, /obj/item/clothing/suit/bluetag))
|
|
return
|
|
|
|
var/dat = "Status: <a href='?src=[REF(src)];power=1'>[on ? "On" : "Off"]</a>"
|
|
|
|
var/datum/browser/popup = new(user, "autosec", "Automatic Portable Turret Installation", 300, 300)
|
|
popup.set_content(dat)
|
|
popup.open()
|
|
|
|
//lasertag presets
|
|
/obj/machinery/porta_turret/lasertag/red
|
|
installation = /obj/item/gun/energy/laser/redtag
|
|
team_color = "red"
|
|
|
|
/obj/machinery/porta_turret/lasertag/blue
|
|
installation = /obj/item/gun/energy/laser/bluetag
|
|
team_color = "blue"
|
|
|
|
/obj/machinery/porta_turret/lasertag/bullet_act(obj/item/projectile/P)
|
|
. = ..()
|
|
if(on)
|
|
if(team_color == "blue")
|
|
if(istype(P, /obj/item/projectile/beam/lasertag/redtag))
|
|
on = FALSE
|
|
spawn(100)
|
|
on = TRUE
|
|
else if(team_color == "red")
|
|
if(istype(P, /obj/item/projectile/beam/lasertag/bluetag))
|
|
on = FALSE
|
|
spawn(100)
|
|
on = TRUE
|