mirror of
https://github.com/ParadiseSS13/Paradise.git
synced 2026-07-20 03:25:49 +01:00
Merge remote-tracking branch 'ParadiseSS13/master' into pr/Kyep/14176
This commit is contained in:
@@ -624,11 +624,11 @@ Traitors and the like can also be revived with the previous role mostly intact.
|
||||
var/beepsound = input(usr, "What sound should the announcement make?", "Announcement Sound", "") as anything in MsgSound
|
||||
|
||||
GLOB.command_announcement.Announce(input, customname, MsgSound[beepsound], , , type)
|
||||
print_command_report(input, "[command_name()] Update")
|
||||
print_command_report(input, customname)
|
||||
if("No")
|
||||
//same thing as the blob stuff - it's not public, so it's classified, dammit
|
||||
GLOB.command_announcer.autosay("A classified message has been printed out at all communication consoles.")
|
||||
print_command_report(input, "Classified [command_name()] Update")
|
||||
print_command_report(input, "Classified: [customname]")
|
||||
else
|
||||
return
|
||||
|
||||
|
||||
@@ -164,6 +164,7 @@ GLOBAL_VAR_INIT(sent_strike_team, 0)
|
||||
R.set_frequency(DTH_FREQ)
|
||||
R.requires_tcomms = FALSE
|
||||
R.instant = TRUE
|
||||
R.freqlock = TRUE
|
||||
equip_to_slot_or_del(R, slot_l_ear)
|
||||
if(is_leader)
|
||||
equip_to_slot_or_del(new /obj/item/clothing/under/rank/centcom_officer(src), slot_w_uniform)
|
||||
|
||||
@@ -1,136 +0,0 @@
|
||||
#define ALARM_RESET_DELAY 100 // How long will the alarm/trigger remain active once origin/source has been found to be gone?
|
||||
|
||||
/datum/alarm_source
|
||||
var/source = null // The source trigger
|
||||
var/source_name = "" // The name of the source should it be lost (for example a destroyed camera)
|
||||
var/duration = 0 // How long this source will be alarming, 0 for indefinetely.
|
||||
var/severity = 1 // How severe the alarm from this source is.
|
||||
var/start_time = 0 // When this source began alarming.
|
||||
var/end_time = 0 // Use to set when this trigger should clear, in case the source is lost.
|
||||
|
||||
/datum/alarm_source/New(var/atom/source)
|
||||
src.source = source
|
||||
start_time = world.time
|
||||
source_name = source.get_source_name()
|
||||
|
||||
/datum/alarm
|
||||
var/atom/origin //Used to identify the alarm area.
|
||||
var/list/sources = new() //List of sources triggering the alarm. Used to determine when the alarm should be cleared.
|
||||
var/list/sources_assoc = new() //Associative list of source triggers. Used to efficiently acquire the alarm source.
|
||||
var/list/cameras //List of cameras that can be switched to, if the player has that capability.
|
||||
var/area/last_area //The last acquired area, used should origin be lost (for example a destroyed borg containing an alarming camera).
|
||||
var/area/last_name //The last acquired name, used should origin be lost
|
||||
var/area/last_camera_area //The last area in which cameras where fetched, used to see if the camera list should be updated.
|
||||
var/end_time //Used to set when this alarm should clear, in case the origin is lost.
|
||||
|
||||
/datum/alarm/New(var/atom/origin, var/atom/source, var/duration, var/severity)
|
||||
src.origin = origin
|
||||
|
||||
cameras() // Sets up both cameras and last alarm area.
|
||||
set_source_data(source, duration, severity)
|
||||
|
||||
/datum/alarm/process()
|
||||
// Has origin gone missing?
|
||||
if(!origin && !end_time)
|
||||
end_time = world.time + ALARM_RESET_DELAY
|
||||
for(var/datum/alarm_source/AS in sources)
|
||||
// Has the alarm passed its best before date?
|
||||
if((AS.end_time && world.time > AS.end_time) || (AS.duration && world.time > (AS.start_time + AS.duration)))
|
||||
sources -= AS
|
||||
// Has the source gone missing? Then reset the normal duration and set end_time
|
||||
if(!AS.source && !AS.end_time) // end_time is used instead of duration to ensure the reset doesn't remain in the future indefinetely.
|
||||
AS.duration = 0
|
||||
AS.end_time = world.time + ALARM_RESET_DELAY
|
||||
|
||||
/datum/alarm/proc/set_source_data(var/atom/source, var/duration, var/severity)
|
||||
var/datum/alarm_source/AS = sources_assoc[source]
|
||||
if(!AS)
|
||||
AS = new/datum/alarm_source(source)
|
||||
sources += AS
|
||||
sources_assoc[source] = AS
|
||||
// Currently only non-0 durations can be altered (normal alarms VS EMP blasts)
|
||||
if(AS.duration)
|
||||
duration = duration SECONDS
|
||||
AS.duration = duration
|
||||
AS.severity = severity
|
||||
|
||||
/datum/alarm/proc/clear(var/source)
|
||||
var/datum/alarm_source/AS = sources_assoc[source]
|
||||
sources -= AS
|
||||
sources_assoc -= source
|
||||
|
||||
/datum/alarm/proc/alarm_area()
|
||||
if(!origin)
|
||||
return last_area
|
||||
|
||||
last_area = origin.get_alarm_area()
|
||||
return last_area
|
||||
|
||||
/datum/alarm/proc/alarm_name()
|
||||
if(!origin)
|
||||
return last_name
|
||||
|
||||
last_name = origin.get_alarm_name()
|
||||
return last_name
|
||||
|
||||
/datum/alarm/proc/cameras()
|
||||
// If the alarm origin has changed area, for example a borg containing an alarming camera, reset the list of cameras
|
||||
if(cameras && (last_camera_area != alarm_area()))
|
||||
cameras = null
|
||||
|
||||
// The list of cameras is also reset by /proc/invalidateCameraCache()
|
||||
if(!cameras)
|
||||
cameras = origin ? origin.get_alarm_cameras() : last_area.get_alarm_cameras()
|
||||
|
||||
last_camera_area = last_area
|
||||
return cameras
|
||||
|
||||
/datum/alarm/proc/max_severity()
|
||||
var/max_severity = 0
|
||||
for(var/datum/alarm_source/AS in sources)
|
||||
max_severity = max(AS.severity, max_severity)
|
||||
|
||||
return max_severity
|
||||
|
||||
/******************
|
||||
* Assisting procs *
|
||||
******************/
|
||||
/atom/proc/get_alarm_area()
|
||||
var/area/A = get_area(src)
|
||||
return A
|
||||
|
||||
/area/get_alarm_area()
|
||||
return src
|
||||
|
||||
/atom/proc/get_alarm_name()
|
||||
var/area/A = get_area(src)
|
||||
return A.name
|
||||
|
||||
/area/get_alarm_name()
|
||||
return name
|
||||
|
||||
/mob/get_alarm_name()
|
||||
return name
|
||||
|
||||
/atom/proc/get_source_name()
|
||||
return name
|
||||
|
||||
/obj/machinery/camera/get_source_name()
|
||||
return c_tag
|
||||
|
||||
/atom/proc/get_alarm_cameras()
|
||||
var/area/A = get_area(src)
|
||||
return A.get_cameras()
|
||||
|
||||
/area/get_alarm_cameras()
|
||||
return get_cameras()
|
||||
|
||||
/mob/living/silicon/robot/get_alarm_cameras()
|
||||
var/list/cameras = ..()
|
||||
if(camera)
|
||||
cameras += camera
|
||||
|
||||
return cameras
|
||||
|
||||
/mob/living/silicon/robot/syndicate/get_alarm_cameras()
|
||||
return list()
|
||||
@@ -1,103 +0,0 @@
|
||||
#define ALARM_RAISED 1
|
||||
#define ALARM_CLEARED 0
|
||||
|
||||
/datum/alarm_handler
|
||||
var/category = ""
|
||||
var/list/datum/alarm/alarms = new // All alarms, to handle cases when an origin has been deleted with one or more active alarms
|
||||
var/list/datum/alarm/alarms_assoc = new // Associative list of alarms, to efficiently acquire them based on origin.
|
||||
var/list/listeners = new // A list of all objects interested in alarm changes.
|
||||
|
||||
/datum/alarm_handler/process()
|
||||
for(var/datum/alarm/A in alarms)
|
||||
A.process()
|
||||
check_alarm_cleared(A)
|
||||
|
||||
/datum/alarm_handler/proc/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
|
||||
var/new_alarm
|
||||
//Proper origin and source mandatory
|
||||
if(!(origin && source))
|
||||
return
|
||||
origin = origin.get_alarm_origin()
|
||||
|
||||
new_alarm = 0
|
||||
//see if there is already an alarm of this origin
|
||||
var/datum/alarm/existing = alarms_assoc[origin]
|
||||
if(existing)
|
||||
existing.set_source_data(source, duration, severity)
|
||||
else
|
||||
existing = new/datum/alarm(origin, source, duration, severity)
|
||||
new_alarm = 1
|
||||
|
||||
alarms |= existing
|
||||
alarms_assoc[origin] = existing
|
||||
if(new_alarm)
|
||||
alarms = dd_sortedObjectList(alarms)
|
||||
on_alarm_change(existing, ALARM_RAISED)
|
||||
|
||||
return new_alarm
|
||||
|
||||
/datum/alarm_handler/proc/clearAlarm(var/atom/origin, var/source)
|
||||
//Proper origin and source mandatory
|
||||
if(!(origin && source))
|
||||
return
|
||||
origin = origin.get_alarm_origin()
|
||||
|
||||
var/datum/alarm/existing = alarms_assoc[origin]
|
||||
if(existing)
|
||||
existing.clear(source)
|
||||
return check_alarm_cleared(existing)
|
||||
|
||||
/datum/alarm_handler/proc/has_major_alarms()
|
||||
if(alarms && alarms.len)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/alarm_handler/proc/major_alarms()
|
||||
return alarms
|
||||
|
||||
/datum/alarm_handler/proc/minor_alarms()
|
||||
return alarms
|
||||
|
||||
/datum/alarm_handler/proc/check_alarm_cleared(var/datum/alarm/alarm)
|
||||
if((alarm.end_time && world.time > alarm.end_time) || !alarm.sources.len)
|
||||
alarms -= alarm
|
||||
alarms_assoc -= alarm.origin
|
||||
on_alarm_change(alarm, ALARM_CLEARED)
|
||||
return 1
|
||||
return 0
|
||||
|
||||
/datum/alarm_handler/proc/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
|
||||
for(var/obj/machinery/camera/C in alarm.cameras())
|
||||
if(was_raised)
|
||||
C.network.Add(category)
|
||||
else
|
||||
C.network.Remove(category)
|
||||
notify_listeners(alarm, was_raised)
|
||||
|
||||
/datum/alarm_handler/proc/get_alarm_severity_for_origin(var/atom/origin)
|
||||
if(!origin)
|
||||
return
|
||||
|
||||
origin = origin.get_alarm_origin()
|
||||
var/datum/alarm/existing = alarms_assoc[origin]
|
||||
if(!existing)
|
||||
return
|
||||
|
||||
return existing.max_severity()
|
||||
|
||||
/atom/proc/get_alarm_origin()
|
||||
return src
|
||||
|
||||
/turf/get_alarm_origin()
|
||||
var/area/area = get_area(src)
|
||||
return area // Very important to get area.master, as dynamic lightning can and will split areas.
|
||||
|
||||
/datum/alarm_handler/proc/register(var/object, var/procName)
|
||||
listeners[object] = procName
|
||||
|
||||
/datum/alarm_handler/proc/unregister(var/object)
|
||||
listeners -= object
|
||||
|
||||
/datum/alarm_handler/proc/notify_listeners(var/alarm, var/was_raised)
|
||||
for(var/listener in listeners)
|
||||
call(listener, listeners[listener])(src, alarm, was_raised)
|
||||
@@ -1,19 +0,0 @@
|
||||
/datum/alarm_handler/atmosphere
|
||||
category = "Atmosphere Alarms"
|
||||
|
||||
/datum/alarm_handler/atmosphere/triggerAlarm(var/atom/origin, var/atom/source, var/duration = 0, var/severity = 1)
|
||||
..()
|
||||
|
||||
/datum/alarm_handler/atmosphere/major_alarms()
|
||||
var/list/major_alarms = new()
|
||||
for(var/datum/alarm/A in alarms)
|
||||
if(A.max_severity() > 1)
|
||||
major_alarms.Add(A)
|
||||
return major_alarms
|
||||
|
||||
/datum/alarm_handler/atmosphere/minor_alarms()
|
||||
var/list/minor_alarms = new()
|
||||
for(var/datum/alarm/A in alarms)
|
||||
if(A.max_severity() == 1)
|
||||
minor_alarms.Add(A)
|
||||
return minor_alarms
|
||||
@@ -1,2 +0,0 @@
|
||||
/datum/alarm_handler/burglar
|
||||
category = "Burglar Alarms"
|
||||
@@ -1,2 +0,0 @@
|
||||
/datum/alarm_handler/camera
|
||||
category = "Camera Alarms"
|
||||
@@ -1,11 +0,0 @@
|
||||
/datum/alarm_handler/fire
|
||||
category = "Fire Alarms"
|
||||
|
||||
/datum/alarm_handler/fire/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
|
||||
var/area/A = alarm.origin
|
||||
if(istype(A))
|
||||
if(was_raised)
|
||||
A.fire_alert()
|
||||
else
|
||||
A.fire_reset()
|
||||
..()
|
||||
@@ -1,2 +0,0 @@
|
||||
/datum/alarm_handler/motion
|
||||
category = "Motion Alarms"
|
||||
@@ -1,10 +0,0 @@
|
||||
/datum/alarm_handler/power
|
||||
category = "Power Alarms"
|
||||
|
||||
/datum/alarm_handler/power/on_alarm_change(var/datum/alarm/alarm, var/was_raised)
|
||||
var/area/A = alarm.origin
|
||||
if(istype(A))
|
||||
A.power_alert(was_raised)
|
||||
..()
|
||||
|
||||
/area/proc/power_alert(var/alarming)
|
||||
@@ -1,3 +1,9 @@
|
||||
#define WIRE_RECEIVE (1<<0) //Allows pulse(0) to call Activate()
|
||||
#define WIRE_PULSE (1<<1) //Allows pulse(0) to act on the holder
|
||||
#define WIRE_PULSE_SPECIAL (1<<2) //Allows pulse(0) to act on the holders special assembly
|
||||
#define WIRE_RADIO_RECEIVE (1<<3) //Allows pulse(1) to call Activate()
|
||||
#define WIRE_RADIO_PULSE (1<<4) //Allows pulse(1) to send a radio message
|
||||
|
||||
/obj/item/assembly
|
||||
name = "assembly"
|
||||
desc = "A small electronic device that should never exist."
|
||||
@@ -22,21 +28,12 @@
|
||||
var/wires = WIRE_RECEIVE | WIRE_PULSE
|
||||
var/datum/wires/connected = null // currently only used by timer/signaler
|
||||
|
||||
var/const/WIRE_RECEIVE = 1 //Allows Pulsed(0) to call Activate()
|
||||
var/const/WIRE_PULSE = 2 //Allows Pulse(0) to act on the holder
|
||||
var/const/WIRE_PULSE_SPECIAL = 4 //Allows Pulse(0) to act on the holders special assembly
|
||||
var/const/WIRE_RADIO_RECEIVE = 8 //Allows Pulsed(1) to call Activate()
|
||||
var/const/WIRE_RADIO_PULSE = 16 //Allows Pulse(1) to send a radio message
|
||||
|
||||
/obj/item/assembly/proc/activate() //What the device does when turned on
|
||||
return
|
||||
|
||||
/obj/item/assembly/proc/pulsed(radio = FALSE) //Called when another assembly acts on this one, var/radio will determine where it came from for wire calcs
|
||||
return
|
||||
|
||||
/obj/item/assembly/proc/pulse(radio = FALSE) //Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
|
||||
return
|
||||
|
||||
/obj/item/assembly/proc/toggle_secure() //Code that has to happen when the assembly is un\secured goes here
|
||||
return
|
||||
|
||||
@@ -79,7 +76,11 @@
|
||||
activate()
|
||||
return TRUE
|
||||
|
||||
/obj/item/assembly/pulse(radio = FALSE)
|
||||
//Called when this device attempts to act on another device, var/radio determines if it was sent via radio or direct
|
||||
/obj/item/assembly/proc/pulse(radio = FALSE)
|
||||
if(connected && wires)
|
||||
connected.pulse_assembly(src)
|
||||
return TRUE
|
||||
if(holder && (wires & WIRE_PULSE))
|
||||
holder.process_activation(src, 1, 0)
|
||||
if(holder && (wires & WIRE_PULSE_SPECIAL))
|
||||
|
||||
@@ -128,12 +128,6 @@
|
||||
if(usr)
|
||||
GLOB.lastsignalers.Add("[time] <B>:</B> [usr.key] used [src] @ location ([T.x],[T.y],[T.z]) <B>:</B> [format_frequency(frequency)]/[code]")
|
||||
|
||||
/obj/item/assembly/signaler/pulse(var/radio = FALSE)
|
||||
if(connected && wires)
|
||||
connected.Pulse(src)
|
||||
else
|
||||
return ..(radio)
|
||||
|
||||
/obj/item/assembly/signaler/receive_signal(datum/signal/signal)
|
||||
if(!receiving || !signal)
|
||||
return FALSE
|
||||
|
||||
@@ -16,7 +16,9 @@
|
||||
var/death = TRUE //Kill the mob
|
||||
var/roundstart = TRUE //fires on initialize
|
||||
var/instant = FALSE //fires on New
|
||||
var/flavour_text = "The mapper forgot to set this!"
|
||||
var/flavour_text = "" //flavour/fluff about the role, optional.
|
||||
var/description = "A description for this has not been set. This is either an oversight or an admin-spawned spawner not in normal use." //intended as OOC info about the role
|
||||
var/important_info = "" //important info such as rules that apply to you, etc. Optional.
|
||||
var/faction = null
|
||||
var/permanent = FALSE //If true, the spawner will not disappear upon running out of uses.
|
||||
var/random = FALSE //Don't set a name or gender, just go random
|
||||
@@ -336,7 +338,7 @@
|
||||
name = "sleeper"
|
||||
icon = 'icons/obj/cryogenic2.dmi'
|
||||
icon_state = "sleeper"
|
||||
flavour_text = "<span class='big bold'>You are a space doctor!</span>"
|
||||
flavour_text = "You are a space doctor!"
|
||||
assignedrole = "Space Doctor"
|
||||
|
||||
/obj/effect/mob_spawn/human/doctor/alive/equip(mob/living/carbon/human/H)
|
||||
@@ -470,11 +472,14 @@
|
||||
name = "bartender sleeper"
|
||||
icon = 'icons/obj/cryogenic2.dmi'
|
||||
icon_state = "sleeper"
|
||||
flavour_text = "<span class='big bold'>You are a space bartender!</span><b> Time to mix drinks and change lives.</b>"
|
||||
description = "Stuck on Lavaland, you could try getting back to civilisation...or serve drinks to those that wander by."
|
||||
flavour_text = "You are a space bartender! Time to mix drinks and change lives. Wait, where did your bar just get transported to?"
|
||||
assignedrole = "Space Bartender"
|
||||
|
||||
/obj/effect/mob_spawn/human/beach/alive/lifeguard
|
||||
flavour_text = "<span class='big bold'>You're a spunky lifeguard!</span><b> It's up to you to make sure nobody drowns or gets eaten by sharks and stuff.</b>"
|
||||
flavour_text = "You're a spunky lifeguard! It's up to you to make sure nobody drowns or gets eaten by sharks and stuff. Then suddenly your entire beach was transported to this strange hell.\
|
||||
You aren't trained for this, but you'll still keep your guests alive!"
|
||||
description = "Try to survive on lavaland with the pitiful equipment of a lifeguard. Or hide in your biodome."
|
||||
mob_gender = "female"
|
||||
name = "lifeguard sleeper"
|
||||
id_job = "Lifeguard"
|
||||
@@ -502,7 +507,8 @@
|
||||
name = "beach bum sleeper"
|
||||
icon = 'icons/obj/cryogenic2.dmi'
|
||||
icon_state = "sleeper"
|
||||
flavour_text = "You are a beach bum!"
|
||||
flavour_text = "You are a beach bum! You think something just happened to the beach but you don't really pay too much attention."
|
||||
description = "Try to survive on lavaland or just enjoy the beach, waiting for visitors."
|
||||
assignedrole = "Beach Bum"
|
||||
|
||||
/datum/outfit/beachbum
|
||||
@@ -523,6 +529,7 @@
|
||||
roundstart = FALSE
|
||||
icon = 'icons/effects/blood.dmi'
|
||||
icon_state = "remains"
|
||||
description = "Be a spooky scary skeleton." //not mapped in anywhere so admin spawner, who knows what they'll use this for.
|
||||
flavour_text = "By unknown powers, your skeletal remains have been reanimated! Walk this mortal plain and terrorize all living adventurers who dare cross your path."
|
||||
assignedrole = "Skeleton"
|
||||
|
||||
|
||||
@@ -64,9 +64,11 @@
|
||||
var/has_owner = FALSE
|
||||
var/can_transfer = TRUE //if golems can switch bodies to this new shell
|
||||
var/mob/living/owner = null //golem's owner if it has one
|
||||
flavour_text = "<span class='big bold'>You are a Free Golem.</span><b> Your family worships <span class='danger'>The Liberator</span>. In his infinite and divine wisdom, he set your clan free to \
|
||||
important_info = "You are not an antag. Do not mess with the station or create AIs."
|
||||
description = "As a Free Golem on lavaland, you are unable to use most weapons, but you can mine, research and make more of your kind. Earn enough mining points and you can even move your shuttle out of there."
|
||||
flavour_text = "You are a Free Golem. Your family worships The Liberator. In his infinite and divine wisdom, he set your clan free to \
|
||||
travel the stars with a single declaration: \"Yeah go do whatever.\" Though you are bound to the one who created you, it is customary in your society to repeat those same words to newborn \
|
||||
golems, so that no golem may ever be forced to serve again.</b>"
|
||||
golems, so that no golem may ever be forced to serve again."
|
||||
|
||||
/obj/effect/mob_spawn/human/golem/Initialize(mapload, datum/species/golem/species = null, mob/creator = null)
|
||||
if(species) //spawners list uses object name to register so this goes before ..()
|
||||
@@ -77,8 +79,10 @@
|
||||
if(!mapload && A)
|
||||
notify_ghosts("\A [initial(species.prefix)] golem shell has been completed in [A.name].", source = src)
|
||||
if(has_owner && creator)
|
||||
flavour_text = "<span class='big bold'>You are a Golem.</span><b> You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. \
|
||||
Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost.</b>"
|
||||
important_info = "Serve your creator, even if they are an antag."
|
||||
flavour_text = "You are a golem created to serve your creator."
|
||||
description = "You are a Golem. You move slowly, but are highly resistant to heat and cold as well as blunt trauma. You are unable to wear clothes, but can still use most tools. \
|
||||
Serve [creator], and assist [creator.p_them()] in completing [creator.p_their()] goals at any cost."
|
||||
owner = creator
|
||||
|
||||
/obj/effect/mob_spawn/human/golem/special(mob/living/new_spawn, name)
|
||||
|
||||
@@ -10,10 +10,11 @@
|
||||
death = FALSE
|
||||
random = TRUE
|
||||
mob_species = /datum/species/human
|
||||
flavour_text = "<span class='big bold'>You are a security officer working for Nanotrasen,</span><b> stationed onboard a state of the art research station. You vaguely recall rushing into a \
|
||||
description = "Work as a team with your fellow survivors aboard a ruined, ancient space station."
|
||||
important_info = ""
|
||||
flavour_text = "You are a security officer working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \
|
||||
cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
|
||||
your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \
|
||||
Work as a team with your fellow survivors and do not abandon them.</b>"
|
||||
your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod."
|
||||
uniform = /obj/item/clothing/under/retro/security
|
||||
shoes = /obj/item/clothing/shoes/jackboots
|
||||
id = /obj/item/card/id/away/old/sec
|
||||
@@ -35,10 +36,11 @@
|
||||
death = FALSE
|
||||
random = TRUE
|
||||
mob_species = /datum/species/human
|
||||
flavour_text = "<span class='big bold'>You are a medical working for Nanotrasen,</span><b> stationed onboard a state of the art research station. You vaguely recall rushing into a \
|
||||
description = "Work as a team with your fellow survivors aboard a ruined, ancient space station."
|
||||
important_info = ""
|
||||
flavour_text = "You are a medical doctor working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \
|
||||
cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
|
||||
your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \
|
||||
Work as a team with your fellow survivors and do not abandon them.</b>"
|
||||
your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod."
|
||||
uniform = /obj/item/clothing/under/retro/medical
|
||||
shoes = /obj/item/clothing/shoes/black
|
||||
id = /obj/item/card/id/away/old/med
|
||||
@@ -60,10 +62,11 @@
|
||||
death = FALSE
|
||||
random = TRUE
|
||||
mob_species = /datum/species/human
|
||||
flavour_text = "<span class='big bold'>You are an engineer working for Nanotrasen,</span><b> stationed onboard a state of the art research station. You vaguely recall rushing into a \
|
||||
description = "Work as a team with your fellow survivors aboard a ruined, ancient space station."
|
||||
important_info = ""
|
||||
flavour_text = "You are an engineer working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \
|
||||
cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
|
||||
your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \
|
||||
Work as a team with your fellow survivors and do not abandon them.</b>"
|
||||
your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod."
|
||||
uniform = /obj/item/clothing/under/retro/engineering
|
||||
shoes = /obj/item/clothing/shoes/workboots
|
||||
id = /obj/item/card/id/away/old/eng
|
||||
@@ -85,10 +88,11 @@
|
||||
death = FALSE
|
||||
random = TRUE
|
||||
mob_species = /datum/species/human
|
||||
flavour_text = "<span class='big bold'>You are a scientist working for Nanotrasen,</span><b> stationed onboard a state of the art research station. You vaguely recall rushing into a \
|
||||
description = "Work as a team with your fellow survivors aboard a ruined, ancient space station."
|
||||
important_info = ""
|
||||
flavour_text = "You are a scientist working for Nanotrasen, stationed onboard a state of the art research station. You vaguely recall rushing into a \
|
||||
cryogenics pod due to an oncoming radiation storm. The last thing you remember is the station's Artificial Program telling you that you would only be asleep for eight hours. As you open \
|
||||
your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod. \
|
||||
Work as a team with your fellow survivors and do not abandon them.</b>"
|
||||
your eyes, everything seems rusted and broken, a dark feeling swells in your gut as you climb out of your pod."
|
||||
uniform = /obj/item/clothing/under/retro/science
|
||||
shoes = /obj/item/clothing/shoes/laceup
|
||||
id = /obj/item/card/id/away/old/sci
|
||||
|
||||
@@ -309,6 +309,20 @@ GLOBAL_LIST_EMPTY(asset_datums)
|
||||
/datum/asset/chem_master/send(client)
|
||||
send_asset_list(client, assets, verify)
|
||||
|
||||
//Cloning pod sprites for UIs
|
||||
/datum/asset/cloning
|
||||
var/assets = list()
|
||||
var/verify = FALSE
|
||||
|
||||
/datum/asset/cloning/register()
|
||||
assets["pod_idle.gif"] = icon('icons/obj/cloning.dmi', "pod_idle")
|
||||
assets["pod_cloning.gif"] = icon('icons/obj/cloning.dmi', "pod_cloning")
|
||||
assets["pod_mess.gif"] = icon('icons/obj/cloning.dmi', "pod_mess")
|
||||
for(var/asset_name in assets)
|
||||
register_asset(asset_name, assets[asset_name])
|
||||
|
||||
/datum/asset/cloning/send(client)
|
||||
send_asset_list(client, assets, verify)
|
||||
|
||||
//Pipe sprites for UIs
|
||||
/datum/asset/rpd
|
||||
|
||||
@@ -166,7 +166,3 @@
|
||||
/datum/gear/hat/flowerpin
|
||||
display_name = "hair flower"
|
||||
path = /obj/item/clothing/head/hairflower
|
||||
|
||||
/datum/gear/hat/kitty
|
||||
display_name = "kitty headband"
|
||||
path = /obj/item/clothing/head/kitty
|
||||
|
||||
@@ -8,21 +8,3 @@
|
||||
strip_delay = 15
|
||||
put_on_delay = 25
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
/obj/item/clothing/ears/headphones
|
||||
name = "headphones"
|
||||
desc = "Unce unce unce unce."
|
||||
var/on = 0
|
||||
icon_state = "headphones0"
|
||||
item_state = null
|
||||
actions_types = list(/datum/action/item_action/toggle_headphones)
|
||||
|
||||
/obj/item/clothing/ears/headphones/attack_self(mob/user)
|
||||
on = !on
|
||||
icon_state = "headphones[on]"
|
||||
|
||||
for(var/X in actions)
|
||||
var/datum/action/A = X
|
||||
A.UpdateButtonIcon()
|
||||
|
||||
user.update_inv_ears()
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
resistance_flags = NONE
|
||||
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 10, "bomb" = 25, "bio" = 0, "rad" = 0, "fire" = 50, "acid" = 50)
|
||||
sprite_sheets = list(
|
||||
"Vox" = 'icons/mob/species/vox/suit.dmi'
|
||||
"Vox" = 'icons/mob/species/vox/suit.dmi',
|
||||
"Grey" = 'icons/mob/species/grey/suit.dmi'
|
||||
)
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
desc = "A slick, authoritative cloak designed for the Chief Engineer."
|
||||
icon_state = "cemantle"
|
||||
item_state = "cemantle"
|
||||
allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd)
|
||||
allowed = list(/obj/item/flashlight, /obj/item/tank, /obj/item/t_scanner, /obj/item/rcd, /obj/item/rpd)
|
||||
|
||||
//Chief Medical Officer
|
||||
/obj/item/clothing/suit/mantle/labcoat/chief_medical_officer
|
||||
@@ -231,7 +231,7 @@
|
||||
icon_state = "hazard"
|
||||
item_state = "hazard"
|
||||
blood_overlay_type = "armor"
|
||||
allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen)
|
||||
allowed = list (/obj/item/flashlight, /obj/item/t_scanner, /obj/item/tank/emergency_oxygen, /obj/item/rcd, /obj/item/rpd)
|
||||
resistance_flags = NONE
|
||||
|
||||
sprite_sheets = list(
|
||||
|
||||
@@ -357,7 +357,7 @@
|
||||
if(!fail_msg)
|
||||
to_chat(usr, "<span class='notice'>[TR.name] constructed.</span>")
|
||||
if(TR.alert_admins_on_craft)
|
||||
message_admins("[usr.ckey] has created a [TR.name] at [ADMIN_COORDJMP(usr)]")
|
||||
message_admins("[key_name_admin(usr)] has created a [TR.name] at [ADMIN_COORDJMP(usr)]")
|
||||
else
|
||||
to_chat(usr, "<span class ='warning'>Construction failed[fail_msg]</span>")
|
||||
busy = FALSE
|
||||
|
||||
@@ -46,10 +46,10 @@
|
||||
if(prob(APC_BREAK_PROBABILITY))
|
||||
// if it has internal wires, cut the power wires
|
||||
if(C.wires)
|
||||
if(!C.wires.IsIndexCut(APC_WIRE_MAIN_POWER1))
|
||||
C.wires.CutWireIndex(APC_WIRE_MAIN_POWER1)
|
||||
if(!C.wires.IsIndexCut(APC_WIRE_MAIN_POWER2))
|
||||
C.wires.CutWireIndex(APC_WIRE_MAIN_POWER2)
|
||||
if(!C.wires.is_cut(WIRE_MAIN_POWER1))
|
||||
C.wires.cut(WIRE_MAIN_POWER1)
|
||||
if(!C.wires.is_cut(WIRE_MAIN_POWER2))
|
||||
C.wires.cut(WIRE_MAIN_POWER2)
|
||||
// if it was operating, toggle off the breaker
|
||||
if(C.operating)
|
||||
C.toggle_breaker()
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
log_debug("Original brand intelligence machine: [originMachine] ([ADMIN_VV(originMachine,"VV")]) [ADMIN_JMP(originMachine)]")
|
||||
|
||||
/datum/event/brand_intelligence/tick()
|
||||
if(!originMachine || QDELETED(originMachine) || originMachine.shut_up || originMachine.wires.IsAllCut()) //if the original vending machine is missing or has it's voice switch flipped
|
||||
if(!originMachine || QDELETED(originMachine) || originMachine.shut_up || originMachine.wires.is_all_cut()) //if the original vending machine is missing or has it's voice switch flipped
|
||||
for(var/obj/machinery/vending/saved in infectedMachines)
|
||||
saved.shoot_inventory = 0
|
||||
if(originMachine)
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
SA.health = SA.maxHealth
|
||||
SA.del_on_death = FALSE
|
||||
greet_sentient(SA)
|
||||
print_command_report(sentience_report, "[command_name()] Update")
|
||||
print_command_report(sentience_report, "[command_name()] Update", FALSE)
|
||||
processing = TRUE // Let it naturally end, if it runs successfully
|
||||
|
||||
/datum/event/sentience/proc/greet_sentient(var/mob/living/carbon/human/M)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
materials = list(MAT_GLASS=100)
|
||||
var/light_intensity = 2
|
||||
light_color = LIGHT_COLOR_LIGHTBLUE
|
||||
resistance_flags = FLAMMABLE
|
||||
|
||||
/obj/item/reagent_containers/food/drinks/drinkingglass/shotglass/on_reagent_change()
|
||||
if(!isShotFlammable() && (resistance_flags & ON_FIRE))
|
||||
@@ -30,6 +31,7 @@
|
||||
overlays += filling
|
||||
name = "shot glass of " + reagents.get_master_reagent_name() //No matter what, the glass will tell you the reagent's name. Might be too abusable in the future.
|
||||
if(resistance_flags & ON_FIRE)
|
||||
cut_overlay(GLOB.fire_overlay, TRUE)
|
||||
overlays += "shotglass_fire"
|
||||
name = "flaming [name]"
|
||||
else
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
name = "-meat"
|
||||
var/subjectname = ""
|
||||
var/subjectjob = null
|
||||
tastes = list("tender meat" = 1)
|
||||
tastes = list("salty meat" = 1)
|
||||
|
||||
/obj/item/reagent_containers/food/snacks/meat/slab/meatproduct
|
||||
name = "meat product"
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
max_n_of_items = 1500 * B.rating
|
||||
|
||||
/obj/machinery/smartfridge/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
QDEL_NULL(wires)
|
||||
for(var/atom/movable/A in contents)
|
||||
A.forceMove(loc)
|
||||
|
||||
@@ -198,10 +198,17 @@
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/structure/beebox/crowbar_act(mob/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.use_tool(src, user, 0))
|
||||
return
|
||||
TOOL_ATTEMPT_DISMANTLE_MESSAGE
|
||||
if(I.use_tool(src, user, 50, volume = I.tool_volume))
|
||||
TOOL_DISMANTLE_SUCCESS_MESSAGE
|
||||
deconstruct(disassembled = TRUE)
|
||||
|
||||
/obj/structure/beebox/wrench_act(mob/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.tool_use_check(user, 0))
|
||||
return
|
||||
default_unfasten_wrench(user, I, time = 20)
|
||||
|
||||
/obj/structure/beebox/attack_hand(mob/user)
|
||||
@@ -261,15 +268,18 @@
|
||||
visible_message("<span class='notice'>[user] removes the queen from the apiary.</span>")
|
||||
queen_bee = null
|
||||
|
||||
/obj/structure/beebox/deconstruct(disassembled = TRUE)
|
||||
new /obj/item/stack/sheet/wood(loc, 20)
|
||||
/obj/structure/beebox/deconstruct(disassembled = FALSE)
|
||||
var/mat_drop = 20
|
||||
if(disassembled)
|
||||
mat_drop = 40
|
||||
new /obj/item/stack/sheet/wood(loc, mat_drop)
|
||||
for(var/mob/living/simple_animal/hostile/poison/bees/B in bees)
|
||||
if(B.loc == src)
|
||||
B.forceMove(drop_location())
|
||||
for(var/obj/item/honey_frame/HF in honey_frames)
|
||||
HF.forceMove(drop_location())
|
||||
honey_frames -= HF
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
/obj/structure/beebox/unwrenched
|
||||
anchored = FALSE
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
icon = 'icons/obj/objects.dmi'
|
||||
icon_state = "barrel"
|
||||
density = TRUE
|
||||
anchored = FALSE
|
||||
anchored = TRUE
|
||||
container_type = DRAINABLE | AMOUNT_VISIBLE
|
||||
pressure_resistance = 2 * ONE_ATMOSPHERE
|
||||
max_integrity = 300
|
||||
@@ -65,6 +65,26 @@
|
||||
to_chat(user, "<span class='notice'>You close [src], letting you draw from its tap.</span>")
|
||||
update_icon()
|
||||
|
||||
/obj/structure/fermenting_barrel/crowbar_act(mob/living/user, obj/item/I)
|
||||
. = TRUE
|
||||
if(!I.use_tool(src, user, 0))
|
||||
return
|
||||
TOOL_ATTEMPT_DISMANTLE_MESSAGE
|
||||
if(I.use_tool(src, user, 50, volume = I.tool_volume))
|
||||
TOOL_DISMANTLE_SUCCESS_MESSAGE
|
||||
deconstruct(disassembled = TRUE)
|
||||
|
||||
/obj/structure/fermenting_barrel/wrench_act(mob/living/user, obj/item/I)
|
||||
. = TRUE
|
||||
default_unfasten_wrench(user, I, time = 20)
|
||||
|
||||
/obj/structure/fermenting_barrel/deconstruct(disassembled = FALSE)
|
||||
var/mat_drop = 15
|
||||
if(disassembled)
|
||||
mat_drop = 30
|
||||
new /obj/item/stack/sheet/wood(drop_location(), mat_drop)
|
||||
..()
|
||||
|
||||
/obj/structure/fermenting_barrel/update_icon()
|
||||
if(open)
|
||||
icon_state = "barrel_open"
|
||||
|
||||
@@ -29,7 +29,8 @@
|
||||
if(istype(W,/obj/item/reagent_containers/syringe))
|
||||
if(!contains_sample)
|
||||
for(var/datum/reagent/blood/bloodSample in W.reagents.reagent_list)
|
||||
if(bloodSample.data["mind"] && bloodSample.data["cloneable"] == 1)
|
||||
var/datum/dna/dna = bloodSample.data["dna"]
|
||||
if(bloodSample.data["mind"] && bloodSample.data["cloneable"] && !(NO_SCAN in dna.species.species_traits))
|
||||
var/datum/mind/tempmind = bloodSample.data["mind"]
|
||||
if(tempmind.is_revivable())
|
||||
mind = bloodSample.data["mind"]
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Get all non admin_only instruments as a list of text ids.
|
||||
*/
|
||||
/proc/get_allowed_instrument_ids()
|
||||
. = list()
|
||||
for(var/id in SSinstruments.instrument_data)
|
||||
var/datum/instrument/I = SSinstruments.instrument_data[id]
|
||||
if(!I.admin_only)
|
||||
. += I.id
|
||||
|
||||
/**
|
||||
* # Instrument Datums
|
||||
*
|
||||
* Instrument datums hold the data for any given instrument, as well as data on how to play it and what bounds there are to playing it.
|
||||
*
|
||||
* The datums themselves are kept in SSinstruments in a list by their unique ID. The reason it uses ID instead of typepath is to support the runtime creation of instruments.
|
||||
* Since songs cache them while playing, there isn't realistic issues regarding performance from accessing.
|
||||
*/
|
||||
/datum/instrument
|
||||
/// Name of the instrument
|
||||
var/name = "Generic instrument"
|
||||
/// Uniquely identifies this instrument so runtime changes are possible as opposed to paths. If this is unset, things will use path instead.
|
||||
var/id
|
||||
/// Category
|
||||
var/category = "Unsorted"
|
||||
/// Used for categorization subtypes
|
||||
var/abstract_type = /datum/instrument
|
||||
/// Write here however many samples, follow this syntax: "%note num%"='%sample file%' eg. "27"='synthesizer/e2.ogg'. Key must never be lower than 0 and higher than 127
|
||||
var/list/real_samples
|
||||
/// assoc list key = /datum/instrument_key. do not fill this yourself!
|
||||
var/list/samples
|
||||
/// See __DEFINES/flags/instruments.dm
|
||||
var/instrument_flags = NONE
|
||||
/// For legacy instruments, the path to our notes
|
||||
var/legacy_instrument_path
|
||||
/// For legacy instruments, our file extension
|
||||
var/legacy_instrument_ext
|
||||
/// What songs are using us
|
||||
var/list/datum/song/songs_using = list()
|
||||
/// Don't touch this
|
||||
var/static/HIGHEST_KEY = 127
|
||||
/// Don't touch this x2
|
||||
var/static/LOWEST_KEY = 0
|
||||
/// Oh no - For truly troll instruments.
|
||||
var/admin_only = FALSE
|
||||
/// Volume multiplier. Synthesized instruments are quite loud and I don't like to cut off potential detail via editing. (someone correct me if this isn't a thing)
|
||||
var/volume_multiplier = 0.33
|
||||
|
||||
/datum/instrument/New()
|
||||
if(isnull(id))
|
||||
id = "[type]"
|
||||
|
||||
/datum/instrument/Destroy()
|
||||
SSinstruments.instrument_data -= id
|
||||
for(var/i in songs_using)
|
||||
var/datum/song/S = i
|
||||
S.set_instrument(null)
|
||||
real_samples = null
|
||||
samples = null
|
||||
songs_using = null
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Initializes the instrument, calculating its samples if necessary.
|
||||
*/
|
||||
/datum/instrument/proc/Initialize()
|
||||
if(instrument_flags & (INSTRUMENT_LEGACY | INSTRUMENT_DO_NOT_AUTOSAMPLE))
|
||||
return
|
||||
calculate_samples()
|
||||
|
||||
/**
|
||||
* Checks if this instrument is ready to play.
|
||||
*/
|
||||
/datum/instrument/proc/is_ready()
|
||||
if(instrument_flags & INSTRUMENT_LEGACY)
|
||||
return legacy_instrument_path && legacy_instrument_ext
|
||||
else if(instrument_flags & INSTRUMENT_DO_NOT_AUTOSAMPLE)
|
||||
return length(samples)
|
||||
return length(samples) >= 128
|
||||
|
||||
/**
|
||||
* For synthesized instruments, this is how the instrument generates the "keys" that a [/datum/song] uses to play notes.
|
||||
* Calculating them on the fly would be unperformant, so we do it during init and keep it all cached in a list.
|
||||
*/
|
||||
/datum/instrument/proc/calculate_samples()
|
||||
if(!length(real_samples))
|
||||
CRASH("No real samples defined for [id] [type] on calculate_samples() call.")
|
||||
var/list/real_keys = list()
|
||||
samples = list()
|
||||
for(var/key in real_samples)
|
||||
real_keys += text2num(key)
|
||||
sortTim(real_keys, /proc/cmp_numeric_asc, associative = FALSE)
|
||||
|
||||
for(var/i in 1 to (length(real_keys) - 1))
|
||||
var/from_key = real_keys[i]
|
||||
var/to_key = real_keys[i + 1]
|
||||
var/sample1 = real_samples[num2text(from_key)]
|
||||
var/sample2 = real_samples[num2text(to_key)]
|
||||
var/pivot = FLOOR((from_key + to_key) / 2, 1) //original code was a round but I replaced it because that's effectively a floor, thanks Baystation! who knows what was intended.
|
||||
for(var/key in from_key to pivot)
|
||||
samples[num2text(key)] = new /datum/instrument_key(sample1, key, key - from_key)
|
||||
for(var/key in (pivot + 1) to to_key)
|
||||
samples[num2text(key)] = new /datum/instrument_key(sample2, key, key - to_key)
|
||||
|
||||
// Fill in 0 to first key and last key to 127
|
||||
var/first_key = real_keys[1]
|
||||
var/last_key = real_keys[length(real_keys)]
|
||||
var/first_sample = real_samples[num2text(first_key)]
|
||||
var/last_sample = real_samples[num2text(last_key)]
|
||||
for(var/key in LOWEST_KEY to (first_key - 1))
|
||||
samples[num2text(key)] = new /datum/instrument_key(first_sample, key, key - first_key)
|
||||
for(var/key in last_key to HIGHEST_KEY)
|
||||
samples[num2text(key)] = new /datum/instrument_key(last_sample, key, key - last_key)
|
||||
@@ -0,0 +1,33 @@
|
||||
#define KEY_TWELTH (1/12)
|
||||
|
||||
/**
|
||||
* Instrument key datums contain everything needed to know how to play a specific
|
||||
* note of an instrument.*
|
||||
*/
|
||||
/datum/instrument_key
|
||||
/// The numerical key of what this is, from 1 to 127 on a standard piano keyboard.
|
||||
var/key
|
||||
/// The actual sample file that will be loaded when playing.
|
||||
var/sample
|
||||
/// The frequency to play the sample to get our desired note.
|
||||
var/frequency
|
||||
/// Deviation up/down from the pivot point that uses its sample. Used to calculate frequency.
|
||||
var/deviation
|
||||
|
||||
/datum/instrument_key/New(sample, key, deviation, frequency)
|
||||
src.sample = sample
|
||||
src.key = key
|
||||
src.deviation = deviation
|
||||
src.frequency = frequency
|
||||
if(!frequency && deviation)
|
||||
calculate()
|
||||
|
||||
/**
|
||||
* Calculates and stores our deviation.
|
||||
*/
|
||||
/datum/instrument_key/proc/calculate()
|
||||
if(!deviation)
|
||||
CRASH("Invalid calculate call: No deviation or sample in instrument_key")
|
||||
frequency = 2 ** (KEY_TWELTH * deviation)
|
||||
|
||||
#undef KEY_TWELTH
|
||||
@@ -0,0 +1,26 @@
|
||||
/datum/instrument/brass
|
||||
name = "Generic brass instrument"
|
||||
category = "Brass"
|
||||
abstract_type = /datum/instrument/brass
|
||||
|
||||
/datum/instrument/brass/crisis_section
|
||||
name = "Crisis Brass Section"
|
||||
id = "crbrass"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_brass/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/brass/crisis_brass/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/brass/crisis_brass/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/brass/crisis_brass/c5.ogg')
|
||||
|
||||
/datum/instrument/brass/crisis_trombone
|
||||
name = "Crisis Trombone"
|
||||
id = "crtrombone"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/brass/crisis_trombone/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/brass/crisis_trombone/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/brass/crisis_trombone/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/brass/crisis_trombone/c5.ogg')
|
||||
|
||||
/datum/instrument/brass/crisis_trumpet
|
||||
name = "Crisis Trumpet"
|
||||
id = "crtrumpet"
|
||||
real_samples = list("60"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/brass/crisis_trumpet/c5.ogg')
|
||||
@@ -0,0 +1,31 @@
|
||||
/datum/instrument/chromatic
|
||||
name = "Generic chromatic percussion instrument"
|
||||
category = "Chromatic percussion"
|
||||
abstract_type = /datum/instrument/chromatic
|
||||
|
||||
/datum/instrument/chromatic/vibraphone1
|
||||
name = "Crisis Vibraphone"
|
||||
id = "crvibr"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/chromatic/vibraphone1/c5.ogg')
|
||||
|
||||
/datum/instrument/chromatic/musicbox1
|
||||
name = "SGM Music Box"
|
||||
id = "sgmmbox"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/sgmbox/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/chromatic/sgmbox/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/chromatic/sgmbox/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/chromatic/sgmbox/c5.ogg')
|
||||
|
||||
/datum/instrument/chromatic/fluid_celeste
|
||||
name = "FluidR3 Celeste"
|
||||
id = "r3celeste"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/chromatic/fluid_celeste/c8.ogg')
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/instrument/fun
|
||||
name = "Generic Fun Instrument"
|
||||
category = "Fun"
|
||||
abstract_type = /datum/instrument/fun
|
||||
|
||||
/datum/instrument/fun/honk
|
||||
name = "!!HONK!!"
|
||||
id = "honk"
|
||||
real_samples = list("74"='sound/items/bikehorn.ogg') // Cluwne Heaven
|
||||
|
||||
/datum/instrument/fun/signal
|
||||
name = "Ping"
|
||||
id = "ping"
|
||||
real_samples = list("79"='sound/machines/ping.ogg')
|
||||
|
||||
/datum/instrument/fun/chime
|
||||
name = "Chime"
|
||||
id = "chime"
|
||||
real_samples = list("79"='sound/machines/chime.ogg')
|
||||
@@ -0,0 +1,36 @@
|
||||
/datum/instrument/guitar
|
||||
name = "Generic guitar-like instrument"
|
||||
category = "Guitar"
|
||||
abstract_type = /datum/instrument/guitar
|
||||
|
||||
/datum/instrument/guitar/steel_crisis
|
||||
name = "Crisis Steel String Guitar"
|
||||
id = "csteelgt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_steel/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_steel/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_steel/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_steel/c5.ogg')
|
||||
|
||||
/datum/instrument/guitar/nylon_crisis
|
||||
name = "Crisis Nylon String Guitar"
|
||||
id = "cnylongt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_nylon/c5.ogg')
|
||||
|
||||
/datum/instrument/guitar/clean_crisis
|
||||
name = "Crisis Clean Guitar"
|
||||
id = "ccleangt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_clean/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_clean/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_clean/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_clean/c5.ogg')
|
||||
|
||||
/datum/instrument/guitar/muted_crisis
|
||||
name = "Crisis Muted Guitar"
|
||||
id = "cmutedgt"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/guitar/crisis_muted/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/guitar/crisis_muted/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/guitar/crisis_muted/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/guitar/crisis_muted/c5.ogg')
|
||||
@@ -0,0 +1,86 @@
|
||||
//THESE ARE HARDCODED INSTRUMENT SAMPLES.
|
||||
//SONGS WILL BE AUTOMATICALLY SWITCHED TO LEGACY MODE IF THEY USE THIS KIND OF INSTRUMENT!
|
||||
//I'd prefer these stayed. They sound different from the mechanical synthesis of synthed instruments, and I quite like them that way. It's not legacy, it's hardcoded, old style. - kevinz000
|
||||
/datum/instrument/hardcoded
|
||||
abstract_type = /datum/instrument/hardcoded
|
||||
category = "Non-Synthesized"
|
||||
instrument_flags = INSTRUMENT_LEGACY
|
||||
volume_multiplier = 1 //not as loud as synth'd
|
||||
|
||||
/datum/instrument/hardcoded/accordion
|
||||
name = "Accordion"
|
||||
id = "accordion"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "accordion"
|
||||
|
||||
/datum/instrument/hardcoded/bikehorn
|
||||
name = "Bike Horn"
|
||||
id = "bikehorn"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "bikehorn"
|
||||
|
||||
/datum/instrument/hardcoded/eguitar
|
||||
name = "Electric Guitar"
|
||||
id = "eguitar"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "eguitar"
|
||||
|
||||
/datum/instrument/hardcoded/glockenspiel
|
||||
name = "Glockenspiel"
|
||||
id = "glockenspiel"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "glockenspiel"
|
||||
|
||||
/datum/instrument/hardcoded/guitar
|
||||
name = "Guitar"
|
||||
id = "guitar"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "guitar"
|
||||
|
||||
/datum/instrument/hardcoded/harmonica
|
||||
name = "Harmonica"
|
||||
id = "harmonica"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "harmonica"
|
||||
|
||||
/datum/instrument/hardcoded/piano
|
||||
name = "Piano"
|
||||
id = "piano"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "piano"
|
||||
|
||||
/datum/instrument/hardcoded/recorder
|
||||
name = "Recorder"
|
||||
id = "recorder"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "recorder"
|
||||
|
||||
/datum/instrument/hardcoded/saxophone
|
||||
name = "Saxophone"
|
||||
id = "saxophone"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "saxophone"
|
||||
|
||||
/datum/instrument/hardcoded/trombone
|
||||
name = "Trombone"
|
||||
id = "trombone"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "trombone"
|
||||
|
||||
/datum/instrument/hardcoded/violin
|
||||
name = "Violin"
|
||||
id = "violin"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "violin"
|
||||
|
||||
/datum/instrument/hardcoded/xylophone
|
||||
name = "Xylophone"
|
||||
id = "xylophone"
|
||||
legacy_instrument_ext = "mid"
|
||||
legacy_instrument_path = "xylophone"
|
||||
|
||||
/datum/instrument/hardcoded/banjo
|
||||
name = "Banjo"
|
||||
id = "banjo"
|
||||
legacy_instrument_ext = "ogg"
|
||||
legacy_instrument_path = "banjo"
|
||||
@@ -0,0 +1,53 @@
|
||||
//copy pasta of the space piano, don't hurt me -Pete
|
||||
/obj/item/instrument
|
||||
name = "generic instrument"
|
||||
force = 10
|
||||
max_integrity = 100
|
||||
resistance_flags = FLAMMABLE
|
||||
icon = 'icons/obj/musician.dmi'
|
||||
lefthand_file = 'icons/mob/inhands/equipment/instruments_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/equipment/instruments_righthand.dmi'
|
||||
/// Our song datum.
|
||||
var/datum/song/handheld/song
|
||||
/// Our allowed list of instrument ids. This is nulled on initialize.
|
||||
var/list/allowed_instrument_ids
|
||||
/// How far away our song datum can be heard.
|
||||
var/instrument_range = 15
|
||||
|
||||
/obj/item/instrument/Initialize(mapload)
|
||||
. = ..()
|
||||
song = new(src, allowed_instrument_ids, instrument_range)
|
||||
allowed_instrument_ids = null //We don't need this clogging memory after it's used.
|
||||
|
||||
/obj/item/instrument/Destroy()
|
||||
QDEL_NULL(song)
|
||||
return ..()
|
||||
|
||||
/obj/item/instrument/suicide_act(mob/user)
|
||||
user.visible_message("<span class='suicide'>[user] begins to play 'Gloomy Sunday'! It looks like [user.p_theyre()] trying to commit suicide!</span>")
|
||||
return BRUTELOSS
|
||||
|
||||
/obj/item/instrument/attack_self(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/item/instrument/tgui_data(mob/user)
|
||||
return song.tgui_data(user)
|
||||
|
||||
/obj/item/instrument/tgui_interact(mob/user)
|
||||
if(!isliving(user) || user.incapacitated())
|
||||
return
|
||||
song.tgui_interact(user)
|
||||
|
||||
/obj/item/instrument/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
return song.tgui_act(action, params)
|
||||
|
||||
/**
|
||||
* Whether the instrument should stop playing
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user
|
||||
*/
|
||||
/obj/item/instrument/proc/should_stop_playing(mob/user)
|
||||
return !(src in user) || !isliving(user) || user.incapacitated()
|
||||
@@ -0,0 +1,80 @@
|
||||
/obj/item/clothing/ears/headphones
|
||||
name = "headphones"
|
||||
desc = "Unce unce unce unce."
|
||||
icon_state = "headphones0"
|
||||
item_state = "headphones0"
|
||||
actions_types = list(/datum/action/item_action/change_headphones_song)
|
||||
var/datum/song/headphones/song
|
||||
|
||||
/obj/item/clothing/ears/headphones/Initialize(mapload)
|
||||
. = ..()
|
||||
song = new(src, "piano") // Piano is the default instrument but all instruments are allowed
|
||||
song.instrument_range = 0
|
||||
song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids
|
||||
// To update the icon
|
||||
RegisterSignal(src, COMSIG_SONG_START, .proc/start_playing)
|
||||
RegisterSignal(src, COMSIG_SONG_END, .proc/stop_playing)
|
||||
|
||||
/obj/item/clothing/ears/headphones/Destroy()
|
||||
QDEL_NULL(song)
|
||||
return ..()
|
||||
|
||||
/obj/item/clothing/ears/headphones/attack_self(mob/user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/item/clothing/ears/headphones/tgui_data(mob/user)
|
||||
return song.tgui_data(user)
|
||||
|
||||
/obj/item/clothing/ears/headphones/tgui_interact(mob/user)
|
||||
if(should_stop_playing(user) || user.incapacitated())
|
||||
return
|
||||
song.tgui_interact(user)
|
||||
|
||||
/obj/item/clothing/ears/headphones/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
return song.tgui_act(action, params)
|
||||
|
||||
/obj/item/clothing/ears/headphones/update_icon()
|
||||
var/mob/living/carbon/human/user = loc
|
||||
if(istype(user))
|
||||
user.update_action_buttons_icon()
|
||||
user.update_inv_ears()
|
||||
..()
|
||||
|
||||
/obj/item/clothing/ears/headphones/item_action_slot_check(slot)
|
||||
if(slot == slot_l_ear || slot == slot_r_ear)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Called by a component signal when our song starts playing.
|
||||
*/
|
||||
/obj/item/clothing/ears/headphones/proc/start_playing()
|
||||
icon_state = item_state = "headphones1"
|
||||
update_icon()
|
||||
|
||||
/**
|
||||
* Called by a component signal when our song stops playing.
|
||||
*/
|
||||
/obj/item/clothing/ears/headphones/proc/stop_playing()
|
||||
icon_state = item_state = "headphones0"
|
||||
update_icon()
|
||||
|
||||
/**
|
||||
* Whether the headphone's song should stop playing
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user
|
||||
*/
|
||||
/obj/item/clothing/ears/headphones/proc/should_stop_playing(mob/living/carbon/human/user)
|
||||
return !(src in user) || !istype(user) || !((src == user.l_ear) || (src == user.r_ear))
|
||||
|
||||
// special subtype so it uses the correct item type
|
||||
/datum/song/headphones
|
||||
|
||||
/datum/song/headphones/should_stop_playing(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
var/obj/item/clothing/ears/headphones/I = parent
|
||||
return I.should_stop_playing(user)
|
||||
@@ -0,0 +1,216 @@
|
||||
/obj/item/instrument/violin
|
||||
name = "space violin"
|
||||
desc = "A wooden musical instrument with four strings and a bow. \"The devil went down to space, he was looking for an assistant to grief.\""
|
||||
icon_state = "violin"
|
||||
item_state = "violin"
|
||||
hitsound = "swing_hit"
|
||||
allowed_instrument_ids = "violin"
|
||||
|
||||
/obj/item/instrument/violin/golden
|
||||
name = "golden violin"
|
||||
desc = "A golden musical instrument with four strings and a bow. \"The devil went down to space, he was looking for an assistant to grief.\""
|
||||
icon_state = "golden_violin"
|
||||
item_state = "golden_violin"
|
||||
resistance_flags = LAVA_PROOF | FIRE_PROOF | ACID_PROOF
|
||||
|
||||
/obj/item/instrument/piano_synth
|
||||
name = "synthesizer"
|
||||
desc = "An advanced electronic synthesizer that can be used as various instruments."
|
||||
icon_state = "synth"
|
||||
item_state = "synth"
|
||||
allowed_instrument_ids = "piano"
|
||||
|
||||
/obj/item/instrument/piano_synth/Initialize(mapload)
|
||||
. = ..()
|
||||
song.allowed_instrument_ids = SSinstruments.synthesizer_instrument_ids
|
||||
|
||||
/obj/item/instrument/banjo
|
||||
name = "banjo"
|
||||
desc = "A 'Mura' brand banjo. It's pretty much just a drum with a neck and strings."
|
||||
icon_state = "banjo"
|
||||
item_state = "banjo"
|
||||
attack_verb = list("scruggs-styled", "hum-diggitied", "shin-digged", "clawhammered")
|
||||
hitsound = 'sound/weapons/banjoslap.ogg'
|
||||
allowed_instrument_ids = "banjo"
|
||||
|
||||
/obj/item/instrument/guitar
|
||||
name = "guitar"
|
||||
desc = "It's made of wood and has bronze strings."
|
||||
icon_state = "guitar"
|
||||
item_state = "guitar"
|
||||
attack_verb = list("played metal on", "serenaded", "crashed", "smashed")
|
||||
hitsound = 'sound/weapons/guitarslam.ogg'
|
||||
allowed_instrument_ids = "guitar"
|
||||
|
||||
/obj/item/instrument/eguitar
|
||||
name = "electric guitar"
|
||||
desc = "Makes all your shredding needs possible."
|
||||
icon_state = "eguitar"
|
||||
item_state = "eguitar"
|
||||
force = 12
|
||||
attack_verb = list("played metal on", "shredded", "crashed", "smashed")
|
||||
hitsound = 'sound/weapons/stringsmash.ogg'
|
||||
allowed_instrument_ids = "eguitar"
|
||||
|
||||
/obj/item/instrument/glockenspiel
|
||||
name = "glockenspiel"
|
||||
desc = "Smooth metal bars perfect for any marching band."
|
||||
icon_state = "glockenspiel"
|
||||
item_state = "glockenspiel"
|
||||
allowed_instrument_ids = "glockenspiel"
|
||||
|
||||
/obj/item/instrument/accordion
|
||||
name = "accordion"
|
||||
desc = "Pun-Pun not included."
|
||||
icon_state = "accordion"
|
||||
item_state = "accordion"
|
||||
allowed_instrument_ids = "accordion"
|
||||
|
||||
/obj/item/instrument/trumpet
|
||||
name = "trumpet"
|
||||
desc = "To announce the arrival of the king!"
|
||||
icon_state = "trumpet"
|
||||
item_state = "trumpet"
|
||||
allowed_instrument_ids = "trombone"
|
||||
|
||||
/obj/item/instrument/trumpet/spectral
|
||||
name = "spectral trumpet"
|
||||
desc = "Things are about to get spooky!"
|
||||
icon_state = "spectral_trumpet"
|
||||
item_state = "spectral_trumpet"
|
||||
force = 0
|
||||
attack_verb = list("played", "jazzed", "trumpeted", "mourned", "dooted", "spooked")
|
||||
|
||||
/obj/item/instrument/trumpet/spectral/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/spooky)
|
||||
|
||||
/obj/item/instrument/trumpet/spectral/attack(mob/living/carbon/C, mob/user)
|
||||
playsound(src, 'sound/instruments/trombone/En4.mid', 100, 1, -1)
|
||||
..()
|
||||
|
||||
/obj/item/instrument/saxophone
|
||||
name = "saxophone"
|
||||
desc = "This soothing sound will be sure to leave your audience in tears."
|
||||
icon_state = "saxophone"
|
||||
item_state = "saxophone"
|
||||
allowed_instrument_ids = "saxophone"
|
||||
|
||||
/obj/item/instrument/saxophone/spectral
|
||||
name = "spectral saxophone"
|
||||
desc = "This spooky sound will be sure to leave mortals in bones."
|
||||
icon_state = "saxophone"
|
||||
item_state = "saxophone"
|
||||
force = 0
|
||||
attack_verb = list("played", "jazzed", "saxxed", "mourned", "dooted", "spooked")
|
||||
|
||||
/obj/item/instrument/saxophone/spectral/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/spooky)
|
||||
|
||||
/obj/item/instrument/saxophone/spectral/attack(mob/living/carbon/C, mob/user)
|
||||
playsound(src, 'sound/instruments/saxophone/En4.mid', 100,1,-1)
|
||||
..()
|
||||
|
||||
/obj/item/instrument/trombone
|
||||
name = "trombone"
|
||||
desc = "How can any pool table ever hope to compete?"
|
||||
icon_state = "trombone"
|
||||
allowed_instrument_ids = "trombone"
|
||||
item_state = "trombone"
|
||||
|
||||
/obj/item/instrument/trombone/spectral
|
||||
name = "spectral trombone"
|
||||
desc = "A skeleton's favorite instrument. Apply directly on the mortals."
|
||||
icon_state = "trombone"
|
||||
item_state = "trombone"
|
||||
force = 0
|
||||
attack_verb = list("played", "jazzed", "tromboned", "mourned", "dooted", "spooked")
|
||||
|
||||
/obj/item/instrument/trombone/spectral/Initialize()
|
||||
. = ..()
|
||||
AddComponent(/datum/component/spooky)
|
||||
|
||||
/obj/item/instrument/trombone/spectral/attack(mob/living/carbon/C, mob/user)
|
||||
playsound (src, 'sound/instruments/trombone/Cn4.mid', 100,1,-1)
|
||||
..()
|
||||
|
||||
/obj/item/instrument/recorder
|
||||
name = "recorder"
|
||||
desc = "Just like in school, playing ability and all."
|
||||
force = 5
|
||||
icon_state = "recorder"
|
||||
item_state = "recorder"
|
||||
allowed_instrument_ids = "recorder"
|
||||
|
||||
/obj/item/instrument/harmonica
|
||||
name = "harmonica"
|
||||
desc = "For when you get a bad case of the space blues."
|
||||
icon_state = "harmonica"
|
||||
item_state = "harmonica"
|
||||
force = 5
|
||||
w_class = WEIGHT_CLASS_SMALL
|
||||
allowed_instrument_ids = "harmonica"
|
||||
|
||||
/obj/item/instrument/xylophone
|
||||
name = "xylophone"
|
||||
desc = "A percussion instrument with a bright tone."
|
||||
icon_state = "xylophone"
|
||||
item_state = "xylophone"
|
||||
allowed_instrument_ids = "bikehorn"
|
||||
|
||||
/obj/item/instrument/bikehorn
|
||||
name = "gilded bike horn"
|
||||
desc = "An exquisitely decorated bike horn, capable of honking in a variety of notes."
|
||||
icon_state = "bike_horn"
|
||||
item_state = "bike_horn"
|
||||
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
|
||||
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
|
||||
attack_verb = list("beautifully honks")
|
||||
w_class = WEIGHT_CLASS_TINY
|
||||
force = 0
|
||||
throw_speed = 3
|
||||
throw_range = 7
|
||||
hitsound = 'sound/items/bikehorn.ogg'
|
||||
allowed_instrument_ids = "bikehorn"
|
||||
|
||||
// Crafting recipes
|
||||
/datum/crafting_recipe/violin
|
||||
name = "Violin"
|
||||
result = /obj/item/instrument/violin
|
||||
reqs = list(/obj/item/stack/sheet/wood = 5,
|
||||
/obj/item/stack/cable_coil = 6,
|
||||
/obj/item/stack/tape_roll = 5)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
|
||||
time = 8 SECONDS
|
||||
category = CAT_MISC
|
||||
|
||||
/datum/crafting_recipe/guitar
|
||||
name = "Guitar"
|
||||
result = /obj/item/instrument/guitar
|
||||
reqs = list(/obj/item/stack/sheet/wood = 5,
|
||||
/obj/item/stack/cable_coil = 6,
|
||||
/obj/item/stack/tape_roll = 5)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
|
||||
time = 8 SECONDS
|
||||
category = CAT_MISC
|
||||
|
||||
/datum/crafting_recipe/eguitar
|
||||
name = "Electric Guitar"
|
||||
result = /obj/item/instrument/eguitar
|
||||
reqs = list(/obj/item/stack/sheet/metal = 5,
|
||||
/obj/item/stack/cable_coil = 6,
|
||||
/obj/item/stack/tape_roll = 5)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
|
||||
time = 8 SECONDS
|
||||
category = CAT_MISC
|
||||
|
||||
/datum/crafting_recipe/banjo
|
||||
name = "Banjo"
|
||||
result = /obj/item/instrument/banjo
|
||||
reqs = list(/obj/item/stack/sheet/wood = 5,
|
||||
/obj/item/stack/cable_coil = 6,
|
||||
/obj/item/stack/tape_roll = 5)
|
||||
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
|
||||
time = 8 SECONDS
|
||||
category = CAT_MISC
|
||||
@@ -0,0 +1,47 @@
|
||||
/obj/structure/musician
|
||||
name = "Not A Piano"
|
||||
desc = "Something broke!"
|
||||
var/can_play_unanchored = FALSE
|
||||
var/list/allowed_instrument_ids
|
||||
var/datum/song/song
|
||||
|
||||
/obj/structure/musician/Initialize(mapload)
|
||||
. = ..()
|
||||
song = new(src, allowed_instrument_ids)
|
||||
allowed_instrument_ids = null
|
||||
|
||||
/obj/structure/musician/Destroy()
|
||||
QDEL_NULL(song)
|
||||
return ..()
|
||||
|
||||
/obj/structure/musician/attack_hand(mob/user)
|
||||
add_fingerprint(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/structure/musician/tgui_data(mob/user)
|
||||
return song.tgui_data(user)
|
||||
|
||||
/obj/structure/musician/tgui_interact(mob/user)
|
||||
song.tgui_interact(user)
|
||||
|
||||
/obj/structure/musician/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
return song.tgui_act(action, params)
|
||||
|
||||
/obj/structure/musician/wrench_act(mob/living/user, obj/item/I)
|
||||
default_unfasten_wrench(user, I, 40)
|
||||
return TRUE
|
||||
|
||||
/**
|
||||
* Whether the instrument should stop playing
|
||||
*
|
||||
* Arguments:
|
||||
* * user - The user
|
||||
*/
|
||||
/obj/structure/musician/proc/should_stop_playing(mob/user)
|
||||
if(!(anchored || can_play_unanchored))
|
||||
return TRUE
|
||||
if(!user)
|
||||
return FALSE
|
||||
return !CanUseTopic(user, GLOB.physical_state)
|
||||
@@ -0,0 +1,22 @@
|
||||
/obj/structure/piano
|
||||
parent_type = /obj/structure/musician // TODO: Can't edit maps right now due to a freeze, remove and update path when it's done
|
||||
name = "space minimoog"
|
||||
icon = 'icons/obj/musician.dmi'
|
||||
icon_state = "minimoog"
|
||||
anchored = TRUE
|
||||
density = TRUE
|
||||
allowed_instrument_ids = "piano"
|
||||
|
||||
/obj/structure/piano/unanchored
|
||||
anchored = FALSE
|
||||
|
||||
/obj/structure/piano/Initialize(mapload)
|
||||
. = ..()
|
||||
if(prob(50) && icon_state == initial(icon_state))
|
||||
name = "space minimoog"
|
||||
desc = "This is a minimoog, like a space piano, but more spacey!"
|
||||
icon_state = "minimoog"
|
||||
else
|
||||
name = "space piano"
|
||||
desc = "This is a space piano, like a regular piano, but always in tune! Even if the musician isn't."
|
||||
icon_state = "piano"
|
||||
@@ -0,0 +1,43 @@
|
||||
/datum/instrument/organ
|
||||
name = "Generic organ"
|
||||
category = "Organ"
|
||||
abstract_type = /datum/instrument/organ
|
||||
|
||||
/datum/instrument/organ/crisis_church
|
||||
name = "Crisis Church Organ"
|
||||
id = "crichugan"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_church/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_church/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_church/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_church/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_hammond
|
||||
name = "Crisis Hammond Organ"
|
||||
id = "crihamgan"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_hammond/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_hammond/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_hammond/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_hammond/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_accordian
|
||||
name = "Crisis Accordion"
|
||||
id = "crack"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_accordian/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_accordian/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_accordian/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_accordian/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_harmonica
|
||||
name = "Crisis Harmonica"
|
||||
id = "crharmony"
|
||||
real_samples = list("48"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_harmonica/c5.ogg')
|
||||
|
||||
/datum/instrument/organ/crisis_tango_accordian
|
||||
name = "Crisis Tango Accordion"
|
||||
id = "crtango"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/organ/crisis_tangaccordian/c5.ogg')
|
||||
@@ -0,0 +1,56 @@
|
||||
/datum/instrument/piano
|
||||
name = "Generic piano"
|
||||
category = "Piano"
|
||||
abstract_type = /datum/instrument/piano
|
||||
|
||||
/datum/instrument/piano/fluid_piano
|
||||
name = "FluidR3 Grand Piano"
|
||||
id = "r3grand"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_piano/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/fluid_piano/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/fluid_piano/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/fluid_piano/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/fluid_piano/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/fluid_piano/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/fluid_piano/c8.ogg')
|
||||
|
||||
/datum/instrument/piano/fluid_harpsichord
|
||||
name = "FluidR3 Harpsichord"
|
||||
id = "r3harpsi"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/fluid_harpsi/c8.ogg')
|
||||
|
||||
/datum/instrument/piano/crisis_harpsichord
|
||||
name = "Crisis Harpsichord"
|
||||
id = "crharpsi"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/crisis_harpsichord/c5.ogg')
|
||||
|
||||
/datum/instrument/piano/crisis_grandpiano_uni
|
||||
name = "Crisis Grand Piano One"
|
||||
id = "crgrand1"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/crisis_grand_piano/c8.ogg')
|
||||
|
||||
/datum/instrument/piano/crisis_brightpiano_uni
|
||||
name = "Crisis Bright Piano One"
|
||||
id = "crbright1"
|
||||
real_samples = list("36"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c2.ogg',
|
||||
"48"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c3.ogg',
|
||||
"60"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c4.ogg',
|
||||
"72"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c5.ogg',
|
||||
"84"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c6.ogg',
|
||||
"96"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c7.ogg',
|
||||
"108"='sound/instruments/synthesis_samples/piano/crisis_bright_piano/c8.ogg')
|
||||
@@ -0,0 +1,410 @@
|
||||
#define MUSICIAN_HEARCHECK_MINDELAY 4
|
||||
#define MUSIC_MAXLINES 1000
|
||||
#define MUSIC_MAXLINECHARS 300
|
||||
|
||||
/**
|
||||
* # Song datum
|
||||
*
|
||||
* These are the actual backend behind instruments.
|
||||
* They attach to an atom and provide the editor + playback functionality.
|
||||
*/
|
||||
/datum/song
|
||||
/// Name of the song
|
||||
var/name = "Untitled"
|
||||
|
||||
/// The atom we're attached to/playing from
|
||||
var/atom/parent
|
||||
|
||||
/// Our song lines
|
||||
var/list/lines
|
||||
|
||||
/// delay between notes in deciseconds
|
||||
var/tempo = 5
|
||||
|
||||
/// How far we can be heard
|
||||
var/instrument_range = 15
|
||||
|
||||
/// Are we currently playing?
|
||||
var/playing = FALSE
|
||||
|
||||
/// Are we currently editing?
|
||||
var/editing = TRUE
|
||||
/// Is the help screen open?
|
||||
var/help = FALSE
|
||||
|
||||
/// Repeats left
|
||||
var/repeat = 0
|
||||
/// Maximum times we can repeat
|
||||
var/max_repeats = 10
|
||||
|
||||
/// Our volume
|
||||
var/volume = 75
|
||||
/// Max volume
|
||||
var/max_volume = 75
|
||||
/// Min volume - This is so someone doesn't decide it's funny to set it to 0 and play invisible songs.
|
||||
var/min_volume = 1
|
||||
|
||||
/// What instruments our built in picker can use. The picker won't show unless this is longer than one.
|
||||
var/list/allowed_instrument_ids
|
||||
|
||||
//////////// Cached instrument variables /////////////
|
||||
/// Instrument we are currently using
|
||||
var/datum/instrument/using_instrument
|
||||
/// Cached legacy ext for legacy instruments
|
||||
var/cached_legacy_ext
|
||||
/// Cached legacy dir for legacy instruments
|
||||
var/cached_legacy_dir
|
||||
/// Cached list of samples, referenced directly from the instrument for synthesized instruments
|
||||
var/list/cached_samples
|
||||
/// Are we operating in legacy mode (so if the instrument is a legacy instrument)
|
||||
var/legacy = FALSE
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
/////////////////// Playing variables ////////////////
|
||||
/**
|
||||
* Build by compile_chords()
|
||||
* Must be rebuilt on instrument switch.
|
||||
* Compilation happens when we start playing and is cleared after we finish playing.
|
||||
* Format: list of chord lists, with chordlists having (key1, key2, key3, tempodiv)
|
||||
*/
|
||||
var/list/compiled_chords
|
||||
/// Current section of a long chord we're on, so we don't need to make a billion chords, one for every unit ticklag.
|
||||
var/elapsed_delay
|
||||
/// Amount of delay to wait before playing the next chord
|
||||
var/delay_by
|
||||
/// Current chord we're on.
|
||||
var/current_chord
|
||||
/// Channel as text = current volume percentage but it's 0 to 100 instead of 0 to 1.
|
||||
var/list/channels_playing
|
||||
/// List of channels that aren't being used, as text. This is to prevent unnecessary freeing and reallocations from SSsounds/SSinstruments.
|
||||
var/list/channels_idle
|
||||
/// Person playing us
|
||||
var/mob/user_playing
|
||||
//////////////////////////////////////////////////////
|
||||
|
||||
/// Last world.time we checked for who can hear us
|
||||
var/last_hearcheck = 0
|
||||
/// The list of mobs that can hear us
|
||||
var/list/hearing_mobs
|
||||
/// If this is enabled, some things won't be strictly cleared when they usually are (liked compiled_chords on play stop)
|
||||
var/debug_mode = FALSE
|
||||
/// Max sound channels to occupy
|
||||
var/max_sound_channels = CHANNELS_PER_INSTRUMENT
|
||||
/// Current channels, so we can save a length() call.
|
||||
var/using_sound_channels = 0
|
||||
/// Last channel to play. text.
|
||||
var/last_channel_played
|
||||
/// Should we not decay our last played note?
|
||||
var/full_sustain_held_note = TRUE
|
||||
|
||||
/////////////////////// DO NOT TOUCH THESE ///////////////////
|
||||
var/octave_min = INSTRUMENT_MIN_OCTAVE
|
||||
var/octave_max = INSTRUMENT_MAX_OCTAVE
|
||||
var/key_min = INSTRUMENT_MIN_KEY
|
||||
var/key_max = INSTRUMENT_MAX_KEY
|
||||
var/static/list/note_offset_lookup
|
||||
var/static/list/accent_lookup
|
||||
//////////////////////////////////////////////////////////////
|
||||
|
||||
///////////// !!FUN!! - Only works in synthesized mode! /////////////////
|
||||
/// Note numbers to shift.
|
||||
var/note_shift = 0
|
||||
var/note_shift_min = -100
|
||||
var/note_shift_max = 100
|
||||
var/can_noteshift = TRUE
|
||||
/// The kind of sustain we're using
|
||||
var/sustain_mode = SUSTAIN_LINEAR
|
||||
/// When a note is considered dead if it is below this in volume
|
||||
var/sustain_dropoff_volume = INSTRUMENT_MIN_SUSTAIN_DROPOFF
|
||||
/// Total duration of linear sustain for 100 volume note to get to SUSTAIN_DROPOFF
|
||||
var/sustain_linear_duration = 5
|
||||
/// Exponential sustain dropoff rate per decisecond
|
||||
var/sustain_exponential_dropoff = 1.4
|
||||
////////// DO NOT DIRECTLY SET THESE!
|
||||
/// Do not directly set, use update_sustain()
|
||||
var/cached_linear_dropoff = 10
|
||||
/// Do not directly set, use update_sustain()
|
||||
var/cached_exponential_dropoff = 1.045
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
var/static/list/valid_files[0] // Cache to avoid running fexists() every time
|
||||
|
||||
/datum/song/New(atom/parent, list/instrument_ids, new_range)
|
||||
SSinstruments.on_song_new(src)
|
||||
lines = list()
|
||||
tempo = sanitize_tempo(tempo)
|
||||
src.parent = parent
|
||||
if(instrument_ids)
|
||||
allowed_instrument_ids = islist(instrument_ids) ? instrument_ids : list(instrument_ids)
|
||||
if(length(allowed_instrument_ids))
|
||||
set_instrument(allowed_instrument_ids[1])
|
||||
hearing_mobs = list()
|
||||
volume = clamp(volume, min_volume, max_volume)
|
||||
channels_playing = list()
|
||||
channels_idle = list()
|
||||
if(!note_offset_lookup)
|
||||
note_offset_lookup = list(9, 11, 0, 2, 4, 5, 7)
|
||||
accent_lookup = list("b" = -1, "s" = 1, "#" = 1, "n" = 0)
|
||||
update_sustain()
|
||||
if(new_range)
|
||||
instrument_range = new_range
|
||||
|
||||
/datum/song/Destroy()
|
||||
stop_playing()
|
||||
SSinstruments.on_song_del(src)
|
||||
lines = null
|
||||
using_instrument = null
|
||||
allowed_instrument_ids = null
|
||||
parent = null
|
||||
return ..()
|
||||
|
||||
/**
|
||||
* Checks and stores which mobs can hear us. Terminates sounds for mobs that leave our range.
|
||||
*/
|
||||
/datum/song/proc/do_hearcheck()
|
||||
last_hearcheck = world.time
|
||||
var/list/old = hearing_mobs.Copy()
|
||||
hearing_mobs.len = 0
|
||||
var/turf/source = get_turf(parent)
|
||||
for(var/mob/M in GLOB.player_list)
|
||||
var/dist = get_dist(M, source)
|
||||
if(dist > instrument_range) // Distance check
|
||||
continue
|
||||
if(!isInSight(M, source)) // Visibility check (direct line of sight)
|
||||
continue
|
||||
hearing_mobs[M] = dist
|
||||
var/list/exited = old - hearing_mobs
|
||||
for(var/i in exited)
|
||||
terminate_sound_mob(i)
|
||||
|
||||
/**
|
||||
* Sets our instrument, caching anything necessary for faster accessing. Accepts an ID, typepath, or instantiated instrument datum.
|
||||
*/
|
||||
/datum/song/proc/set_instrument(datum/instrument/I)
|
||||
terminate_all_sounds()
|
||||
var/old_legacy
|
||||
if(using_instrument)
|
||||
using_instrument.songs_using -= src
|
||||
old_legacy = (using_instrument.instrument_flags & INSTRUMENT_LEGACY)
|
||||
using_instrument = null
|
||||
cached_samples = null
|
||||
cached_legacy_ext = null
|
||||
cached_legacy_dir = null
|
||||
legacy = null
|
||||
if(istext(I) || ispath(I))
|
||||
I = SSinstruments.instrument_data[I]
|
||||
if(istype(I))
|
||||
using_instrument = I
|
||||
I.songs_using += src
|
||||
var/instrument_legacy = (I.instrument_flags & INSTRUMENT_LEGACY)
|
||||
if(instrument_legacy)
|
||||
cached_legacy_ext = I.legacy_instrument_ext
|
||||
cached_legacy_dir = I.legacy_instrument_path
|
||||
legacy = TRUE
|
||||
else
|
||||
cached_samples = I.samples
|
||||
legacy = FALSE
|
||||
if(isnull(old_legacy) || (old_legacy != instrument_legacy))
|
||||
if(playing)
|
||||
compile_chords()
|
||||
|
||||
/**
|
||||
* Attempts to start playing our song.
|
||||
*/
|
||||
/datum/song/proc/start_playing(mob/user)
|
||||
if(playing)
|
||||
return
|
||||
if(!using_instrument?.is_ready())
|
||||
to_chat(user, "<span class='warning'>An error has occured with [src]. Please reset the instrument.</span>")
|
||||
return
|
||||
compile_chords()
|
||||
if(!length(compiled_chords))
|
||||
to_chat(user, "<span class='warning'>Song is empty.</span>")
|
||||
return
|
||||
playing = TRUE
|
||||
SStgui.update_uis(parent)
|
||||
//we can not afford to runtime, since we are going to be doing sound channel reservations and if we runtime it means we have a channel allocation leak.
|
||||
//wrap the rest of the stuff to ensure stop_playing() is called.
|
||||
do_hearcheck()
|
||||
SEND_SIGNAL(parent, COMSIG_SONG_START)
|
||||
elapsed_delay = 0
|
||||
delay_by = 0
|
||||
current_chord = 1
|
||||
user_playing = user
|
||||
START_PROCESSING(SSinstruments, src)
|
||||
|
||||
/**
|
||||
* Stops playing, terminating all sounds if in synthesized mode. Clears hearing_mobs.
|
||||
*/
|
||||
/datum/song/proc/stop_playing()
|
||||
if(!playing)
|
||||
return
|
||||
playing = FALSE
|
||||
if(!debug_mode)
|
||||
compiled_chords = null
|
||||
STOP_PROCESSING(SSinstruments, src)
|
||||
SEND_SIGNAL(parent, COMSIG_SONG_END)
|
||||
terminate_all_sounds(TRUE)
|
||||
hearing_mobs.len = 0
|
||||
SStgui.update_uis(parent)
|
||||
user_playing = null
|
||||
|
||||
/**
|
||||
* Processes our song.
|
||||
*/
|
||||
/datum/song/proc/process_song(wait)
|
||||
if(!length(compiled_chords) || current_chord > length(compiled_chords) || should_stop_playing(user_playing))
|
||||
stop_playing()
|
||||
return
|
||||
var/list/chord = compiled_chords[current_chord]
|
||||
if(++elapsed_delay >= delay_by)
|
||||
play_chord(chord)
|
||||
elapsed_delay = 0
|
||||
delay_by = tempodiv_to_delay(chord[length(chord)])
|
||||
current_chord++
|
||||
if(current_chord > length(compiled_chords))
|
||||
if(repeat)
|
||||
repeat--
|
||||
current_chord = 1
|
||||
SStgui.update_uis(parent)
|
||||
return
|
||||
else
|
||||
stop_playing()
|
||||
return
|
||||
|
||||
/**
|
||||
* Converts a tempodiv to ticks to elapse before playing the next chord, taking into account our tempo.
|
||||
*/
|
||||
/datum/song/proc/tempodiv_to_delay(tempodiv)
|
||||
tempodiv = tempodiv || world.tick_lag // Default to world.tick_lag in case someone's trying to be smart
|
||||
return max(1, round((tempo / tempodiv) / world.tick_lag, 1))
|
||||
|
||||
/**
|
||||
* Compiles chords.
|
||||
*/
|
||||
/datum/song/proc/compile_chords()
|
||||
legacy ? compile_legacy() : compile_synthesized()
|
||||
|
||||
/**
|
||||
* Plays a chord.
|
||||
*/
|
||||
/datum/song/proc/play_chord(list/chord)
|
||||
// last value is timing information
|
||||
for(var/i in 1 to (length(chord) - 1))
|
||||
legacy? playkey_legacy(chord[i][1], chord[i][2], chord[i][3], user_playing) : playkey_synth(chord[i], user_playing)
|
||||
|
||||
/**
|
||||
* Checks if we should halt playback.
|
||||
*/
|
||||
/datum/song/proc/should_stop_playing(mob/user)
|
||||
return QDELETED(parent) || !using_instrument || !playing
|
||||
|
||||
/**
|
||||
* Sanitizes tempo to a value that makes sense and fits the current world.tick_lag.
|
||||
*/
|
||||
/datum/song/proc/sanitize_tempo(new_tempo)
|
||||
new_tempo = abs(new_tempo)
|
||||
return clamp(round(new_tempo, world.tick_lag), world.tick_lag, 5 SECONDS)
|
||||
|
||||
/**
|
||||
* Gets our beats per minute based on our tempo.
|
||||
*/
|
||||
/datum/song/proc/get_bpm()
|
||||
return 600 / tempo
|
||||
|
||||
/**
|
||||
* Sets our tempo from a beats-per-minute, sanitizing it to a valid number first.
|
||||
*/
|
||||
/datum/song/proc/set_bpm(bpm)
|
||||
tempo = sanitize_tempo(600 / bpm)
|
||||
|
||||
/datum/song/process(wait)
|
||||
if(!playing)
|
||||
return PROCESS_KILL
|
||||
// it's expected this ticks at every world.tick_lag. if it lags, do not attempt to catch up.
|
||||
process_song(world.tick_lag)
|
||||
process_decay(world.tick_lag)
|
||||
|
||||
/**
|
||||
* Updates our cached linear/exponential falloff stuff, saving calculations down the line.
|
||||
*/
|
||||
/datum/song/proc/update_sustain()
|
||||
// Exponential is easy
|
||||
cached_exponential_dropoff = sustain_exponential_dropoff
|
||||
// Linear, not so much, since it's a target duration from 100 volume rather than an exponential rate.
|
||||
var/target_duration = sustain_linear_duration
|
||||
var/volume_diff = max(0, 100 - sustain_dropoff_volume)
|
||||
var/volume_decrease_per_decisecond = volume_diff / target_duration
|
||||
cached_linear_dropoff = volume_decrease_per_decisecond
|
||||
|
||||
/**
|
||||
* Setter for setting output volume.
|
||||
*/
|
||||
/datum/song/proc/set_volume(volume)
|
||||
src.volume = clamp(volume, max(0, min_volume), min(100, max_volume))
|
||||
update_sustain()
|
||||
// We don't want to send the whole payload (song included) just for volume
|
||||
var/datum/tgui/ui = SStgui.get_open_ui(usr, parent, "main")
|
||||
if(ui)
|
||||
ui.push_data(list("volume" = volume), force = TRUE)
|
||||
|
||||
/**
|
||||
* Setter for setting how low the volume has to get before a note is considered "dead" and dropped
|
||||
*/
|
||||
/datum/song/proc/set_dropoff_volume(volume, no_refresh = FALSE)
|
||||
sustain_dropoff_volume = clamp(volume, INSTRUMENT_MIN_SUSTAIN_DROPOFF, 100)
|
||||
update_sustain()
|
||||
if(!no_refresh)
|
||||
SStgui.update_uis(parent)
|
||||
|
||||
/**
|
||||
* Setter for setting exponential falloff factor.
|
||||
*/
|
||||
/datum/song/proc/set_exponential_drop_rate(drop, no_refresh = FALSE)
|
||||
sustain_exponential_dropoff = clamp(drop, INSTRUMENT_EXP_FALLOFF_MIN, INSTRUMENT_EXP_FALLOFF_MAX)
|
||||
update_sustain()
|
||||
if(!no_refresh)
|
||||
SStgui.update_uis(parent)
|
||||
|
||||
/**
|
||||
* Setter for setting linear falloff duration.
|
||||
*/
|
||||
/datum/song/proc/set_linear_falloff_duration(duration, no_refresh = FALSE)
|
||||
sustain_linear_duration = clamp(duration, 0.1, INSTRUMENT_MAX_TOTAL_SUSTAIN)
|
||||
update_sustain()
|
||||
if(!no_refresh)
|
||||
SStgui.update_uis(parent)
|
||||
|
||||
/datum/song/vv_edit_var(var_name, var_value)
|
||||
. = ..()
|
||||
if(.)
|
||||
switch(var_name)
|
||||
if(NAMEOF(src, volume))
|
||||
set_volume(var_value)
|
||||
if(NAMEOF(src, sustain_dropoff_volume))
|
||||
set_dropoff_volume(var_value)
|
||||
if(NAMEOF(src, sustain_exponential_dropoff))
|
||||
set_exponential_drop_rate(var_value)
|
||||
if(NAMEOF(src, sustain_linear_duration))
|
||||
set_linear_falloff_duration(var_value)
|
||||
|
||||
// subtype for handheld instruments, like violin
|
||||
/datum/song/handheld
|
||||
|
||||
/datum/song/handheld/should_stop_playing(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
var/obj/item/instrument/I = parent
|
||||
return I.should_stop_playing(user)
|
||||
|
||||
// subtype for stationary structures, like pianos
|
||||
/datum/song/stationary
|
||||
|
||||
/datum/song/stationary/should_stop_playing(mob/user)
|
||||
. = ..()
|
||||
if(.)
|
||||
return TRUE
|
||||
var/obj/structure/musician/M = parent
|
||||
return M.should_stop_playing(user)
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/datum/song/tgui_data(mob/user)
|
||||
var/data[0]
|
||||
|
||||
// General
|
||||
data["playing"] = playing
|
||||
data["repeat"] = repeat
|
||||
data["maxRepeats"] = max_repeats
|
||||
data["editing"] = editing
|
||||
data["lines"] = lines
|
||||
data["tempo"] = tempo
|
||||
data["minTempo"] = world.tick_lag
|
||||
data["maxTempo"] = 5 SECONDS
|
||||
data["tickLag"] = world.tick_lag
|
||||
data["help"] = help
|
||||
|
||||
// Status
|
||||
var/list/allowed_instrument_names = list()
|
||||
for(var/i in allowed_instrument_ids)
|
||||
var/datum/instrument/I = SSinstruments.get_instrument(i)
|
||||
if(I)
|
||||
allowed_instrument_names += I.name
|
||||
data["allowedInstrumentNames"] = allowed_instrument_names
|
||||
data["instrumentLoaded"] = !isnull(using_instrument)
|
||||
if(using_instrument)
|
||||
data["instrument"] = using_instrument.name
|
||||
data["canNoteShift"] = can_noteshift
|
||||
if(can_noteshift)
|
||||
data["noteShift"] = note_shift
|
||||
data["noteShiftMin"] = note_shift_min
|
||||
data["noteShiftMax"] = note_shift_max
|
||||
data["sustainMode"] = sustain_mode
|
||||
switch(sustain_mode)
|
||||
if(SUSTAIN_LINEAR)
|
||||
data["sustainLinearDuration"] = sustain_linear_duration
|
||||
if(SUSTAIN_EXPONENTIAL)
|
||||
data["sustainExponentialDropoff"] = sustain_exponential_dropoff
|
||||
data["ready"] = using_instrument?.is_ready()
|
||||
data["legacy"] = legacy
|
||||
data["volume"] = volume
|
||||
data["minVolume"] = min_volume
|
||||
data["maxVolume"] = max_volume
|
||||
data["sustainDropoffVolume"] = sustain_dropoff_volume
|
||||
data["sustainHeldNote"] = full_sustain_held_note
|
||||
|
||||
return data
|
||||
|
||||
/datum/song/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, parent, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, parent, ui_key, "Instrument", parent?.name || "Instrument", 700, 500)
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE) // NO!!! Don't auto-update this!!
|
||||
|
||||
/datum/song/tgui_act(action, params)
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("newsong")
|
||||
lines = new()
|
||||
tempo = sanitize_tempo(5) // default 120 BPM
|
||||
name = ""
|
||||
if("import")
|
||||
var/t = ""
|
||||
do
|
||||
t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
|
||||
if(!in_range(parent, usr))
|
||||
return
|
||||
|
||||
if(length_char(t) >= MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
var/cont = input(usr, "Your message is too long! Would you like to continue editing it?", "", "yes") in list("yes", "no")
|
||||
if(cont == "no")
|
||||
break
|
||||
while(length_char(t) > MUSIC_MAXLINES * MUSIC_MAXLINECHARS)
|
||||
parse_song(t)
|
||||
return FALSE
|
||||
if("help")
|
||||
help = !help
|
||||
if("edit")
|
||||
editing = !editing
|
||||
if("repeat") //Changing this from a toggle to a number of repeats to avoid infinite loops.
|
||||
if(playing)
|
||||
return //So that people cant keep adding to repeat. If the do it intentionally, it could result in the server crashing.
|
||||
repeat = clamp(round(text2num(params["new"])), 0, max_repeats)
|
||||
if("tempo")
|
||||
tempo = sanitize_tempo(text2num(params["new"]))
|
||||
if("play")
|
||||
INVOKE_ASYNC(src, .proc/start_playing, usr)
|
||||
if("newline")
|
||||
var/newline = html_encode(input("Enter your line: ", parent.name) as text|null)
|
||||
if(!newline || !in_range(parent, usr))
|
||||
return
|
||||
if(length(lines) > MUSIC_MAXLINES)
|
||||
return
|
||||
if(length(newline) > MUSIC_MAXLINECHARS)
|
||||
newline = copytext(newline, 1, MUSIC_MAXLINECHARS)
|
||||
lines.Add(newline)
|
||||
if("deleteline")
|
||||
var/num = round(text2num(params["line"]))
|
||||
if(num > length(lines) || num < 1)
|
||||
return
|
||||
lines.Cut(num, num + 1)
|
||||
if("modifyline")
|
||||
var/num = round(text2num(params["line"]))
|
||||
var/content = stripped_input(usr, "Enter your line: ", parent.name, lines[num], MUSIC_MAXLINECHARS)
|
||||
if(!content || !in_range(parent, usr))
|
||||
return
|
||||
if(num > length(lines) || num < 1)
|
||||
return
|
||||
lines[num] = content
|
||||
if("stop")
|
||||
stop_playing()
|
||||
if("setlinearfalloff")
|
||||
set_linear_falloff_duration(round(text2num(params["new"]) * 10, world.tick_lag), TRUE)
|
||||
if("setexpfalloff")
|
||||
set_exponential_drop_rate(round(text2num(params["new"]), 0.00001), TRUE)
|
||||
if("setvolume")
|
||||
set_volume(round(text2num(params["new"]), 1))
|
||||
if("setdropoffvolume")
|
||||
set_dropoff_volume(round(text2num(params["new"]), 0.01), TRUE)
|
||||
if("switchinstrument")
|
||||
if(!length(allowed_instrument_ids))
|
||||
return
|
||||
else if(length(allowed_instrument_ids) == 1)
|
||||
set_instrument(allowed_instrument_ids[1])
|
||||
return
|
||||
var/choice = params["name"]
|
||||
for(var/i in allowed_instrument_ids)
|
||||
var/datum/instrument/I = SSinstruments.get_instrument(i)
|
||||
if(I && I.name == choice)
|
||||
set_instrument(I)
|
||||
if("setnoteshift")
|
||||
note_shift = clamp(round(text2num(params["new"])), note_shift_min, note_shift_max)
|
||||
if("setsustainmode")
|
||||
var/static/list/sustain_modes
|
||||
if(!length(sustain_modes))
|
||||
sustain_modes = list("Linear" = SUSTAIN_LINEAR, "Exponential" = SUSTAIN_EXPONENTIAL)
|
||||
var/choice = params["new"]
|
||||
sustain_mode = sustain_modes[choice] || sustain_mode
|
||||
if("togglesustainhold")
|
||||
full_sustain_held_note = !full_sustain_held_note
|
||||
if("reset")
|
||||
var/default_instrument = allowed_instrument_ids[1]
|
||||
if(using_instrument != SSinstruments.instrument_data[default_instrument])
|
||||
set_instrument(default_instrument)
|
||||
note_shift = initial(note_shift)
|
||||
sustain_mode = initial(sustain_mode)
|
||||
set_linear_falloff_duration(initial(sustain_linear_duration), TRUE)
|
||||
set_exponential_drop_rate(initial(sustain_exponential_dropoff), TRUE)
|
||||
set_dropoff_volume(initial(sustain_dropoff_volume), TRUE)
|
||||
else
|
||||
return FALSE
|
||||
parent.add_fingerprint(usr)
|
||||
|
||||
/**
|
||||
* Parses a song the user has input into lines and stores them.
|
||||
*/
|
||||
/datum/song/proc/parse_song(text)
|
||||
set waitfor = FALSE
|
||||
//split into lines
|
||||
stop_playing()
|
||||
lines = splittext(text, "\n")
|
||||
if(length(lines))
|
||||
var/bpm_string = "BPM: "
|
||||
if(findtext(lines[1], bpm_string, 1, length(bpm_string) + 1))
|
||||
var/divisor = text2num(copytext(lines[1], length(bpm_string) + 1)) || 120 // default
|
||||
tempo = sanitize_tempo(600 / round(divisor, 1))
|
||||
lines.Cut(1, 2)
|
||||
else
|
||||
tempo = sanitize_tempo(5) // default 120 BPM
|
||||
if(length(lines) > MUSIC_MAXLINES)
|
||||
to_chat(usr, "Too many lines!")
|
||||
lines.Cut(MUSIC_MAXLINES + 1)
|
||||
var/linenum = 1
|
||||
for(var/l in lines)
|
||||
if(length_char(l) > MUSIC_MAXLINECHARS)
|
||||
to_chat(usr, "Line [linenum] too long!")
|
||||
lines.Remove(l)
|
||||
else
|
||||
linenum++
|
||||
SStgui.update_uis(parent)
|
||||
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Compiles our lines into "chords" with filenames for legacy playback. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
|
||||
*/
|
||||
/datum/song/proc/compile_legacy()
|
||||
if(!length(src.lines))
|
||||
return
|
||||
var/list/lines = src.lines //cache for hyepr speed!
|
||||
compiled_chords = list()
|
||||
var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
|
||||
var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
|
||||
for(var/line in lines)
|
||||
var/list/chords = splittext(lowertext(line), ",")
|
||||
for(var/chord in chords)
|
||||
var/list/compiled_chord = list()
|
||||
var/tempodiv = 1
|
||||
var/list/notes_tempodiv = splittext(chord, "/")
|
||||
var/len = length(notes_tempodiv)
|
||||
if(len >= 2)
|
||||
tempodiv = text2num(notes_tempodiv[2])
|
||||
if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that.
|
||||
var/list/notes = splittext(notes_tempodiv[1], "-")
|
||||
for(var/note in notes)
|
||||
if(length(note) == 0)
|
||||
continue
|
||||
// 1-7, A-G
|
||||
var/key = text2ascii(note) - 96
|
||||
if((key < 1) || (key > 7))
|
||||
continue
|
||||
for(var/i in 2 to length(note))
|
||||
var/oct_acc = copytext(note, i, i + 1)
|
||||
var/num = text2num(oct_acc)
|
||||
if(!num) //it's an accidental
|
||||
accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks.
|
||||
else //octave
|
||||
octaves[key] = clamp(num, octave_min, octave_max)
|
||||
compiled_chord[++compiled_chord.len] = list(key, accents[key], octaves[key])
|
||||
compiled_chord += tempodiv //this goes last
|
||||
if(length(compiled_chord))
|
||||
compiled_chords[++compiled_chords.len] = compiled_chord
|
||||
|
||||
/**
|
||||
* Proc to play a legacy note. Just plays the sound to hearing mobs (and does hearcheck if necessary), no fancy channel/sustain/management.
|
||||
*
|
||||
* Arguments:
|
||||
* * note - number from 1-7 for A-G
|
||||
* * acc - either "b", "n", or "#"
|
||||
* * oct - 1-8 (or 9 for C)
|
||||
*/
|
||||
/datum/song/proc/playkey_legacy(note, acc as text, oct, mob/user)
|
||||
// handle accidental -> B<>C of E<>F
|
||||
if(acc == "b" && (note == 3 || note == 6)) // C or F
|
||||
if(note == 3)
|
||||
oct--
|
||||
note--
|
||||
acc = "n"
|
||||
else if(acc == "#" && (note == 2 || note == 5)) // B or E
|
||||
if(note == 2)
|
||||
oct++
|
||||
note++
|
||||
acc = "n"
|
||||
else if(acc == "#" && (note == 7)) //G#
|
||||
note = 1
|
||||
acc = "b"
|
||||
else if(acc == "#") // mass convert all sharps to flats, octave jump already handled
|
||||
acc = "b"
|
||||
note++
|
||||
|
||||
// check octave, C is allowed to go to 9
|
||||
if(oct < 1 || (note == 3 ? oct > 9 : oct > 8))
|
||||
return
|
||||
|
||||
// now generate name
|
||||
var/filename = "sound/instruments/[cached_legacy_dir]/[ascii2text(note + 64)][acc][oct].[cached_legacy_ext]"
|
||||
var/soundfile = file(filename)
|
||||
// make sure the note exists
|
||||
var/cached_fexists = valid_files[filename]
|
||||
if(!isnull(cached_fexists))
|
||||
if(!cached_fexists)
|
||||
return
|
||||
else if(!fexists(soundfile))
|
||||
valid_files[filename] = FALSE
|
||||
return
|
||||
else
|
||||
valid_files[filename] = TRUE
|
||||
// and play
|
||||
var/turf/source = get_turf(parent)
|
||||
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
|
||||
do_hearcheck()
|
||||
var/sound/music_played = sound(soundfile)
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
if(!(M.client?.prefs?.sound & SOUND_INSTRUMENTS))
|
||||
continue
|
||||
M.playsound_local(source, null, volume * using_instrument.volume_multiplier, falloff = 5, S = music_played)
|
||||
// Could do environment and echo later but not for now
|
||||
@@ -0,0 +1,134 @@
|
||||
/**
|
||||
* Compiles our lines into "chords" with numbers. This makes there have to be a bit of lag at the beginning of the song, but repeats will not have to parse it again, and overall playback won't be impacted by as much lag.
|
||||
*/
|
||||
/datum/song/proc/compile_synthesized()
|
||||
if(!length(src.lines))
|
||||
return
|
||||
var/list/lines = src.lines //cache for hyepr speed!
|
||||
compiled_chords = list()
|
||||
var/list/octaves = list(3, 3, 3, 3, 3, 3, 3)
|
||||
var/list/accents = list("n", "n", "n", "n", "n", "n", "n")
|
||||
for(var/line in lines)
|
||||
var/list/chords = splittext(lowertext(line), ",")
|
||||
for(var/chord in chords)
|
||||
var/list/compiled_chord = list()
|
||||
var/tempodiv = 1
|
||||
var/list/notes_tempodiv = splittext(chord, "/")
|
||||
var/len = length(notes_tempodiv)
|
||||
if(len >= 2)
|
||||
tempodiv = text2num(notes_tempodiv[2])
|
||||
if(len) //some dunkass is going to do ,,,, to make 3 rests instead of ,/1 because there's no standardization so let's be prepared for that.
|
||||
var/list/notes = splittext(notes_tempodiv[1], "-")
|
||||
for(var/note in notes)
|
||||
if(length(note) == 0)
|
||||
continue
|
||||
// 1-7, A-G
|
||||
var/key = text2ascii(note) - 96
|
||||
if((key < 1) || (key > 7))
|
||||
continue
|
||||
for(var/i in 2 to length(note))
|
||||
var/oct_acc = copytext(note, i, i + 1)
|
||||
var/num = text2num(oct_acc)
|
||||
if(!num) //it's an accidental
|
||||
accents[key] = oct_acc //if they misspelled it/fucked up that's on them lmao, no safety checks.
|
||||
else //octave
|
||||
octaves[key] = clamp(num, octave_min, octave_max)
|
||||
compiled_chord += clamp((note_offset_lookup[key] + octaves[key] * 12 + accent_lookup[accents[key]]), key_min, key_max)
|
||||
compiled_chord += tempodiv //this goes last
|
||||
if(length(compiled_chord))
|
||||
compiled_chords[++compiled_chords.len] = compiled_chord
|
||||
|
||||
/**
|
||||
* Plays a specific numerical key from our instrument to anyone who can hear us.
|
||||
* Does a hearing check if enough time has passed.
|
||||
*/
|
||||
/datum/song/proc/playkey_synth(key, mob/user)
|
||||
if(can_noteshift)
|
||||
key = clamp(key + note_shift, key_min, key_max)
|
||||
if((world.time - MUSICIAN_HEARCHECK_MINDELAY) > last_hearcheck)
|
||||
do_hearcheck()
|
||||
var/datum/instrument_key/K = using_instrument.samples[num2text(key)] //See how fucking easy it is to make a number text? You don't need a complicated 9 line proc!
|
||||
//Should probably add channel limiters here at some point but I don't care right now.
|
||||
var/channel = pop_channel()
|
||||
if(isnull(channel))
|
||||
return FALSE
|
||||
. = TRUE
|
||||
var/sound/copy = sound(K.sample)
|
||||
var/volume = src.volume * using_instrument.volume_multiplier
|
||||
copy.frequency = K.frequency
|
||||
copy.volume = volume
|
||||
var/channel_text = num2text(channel)
|
||||
channels_playing[channel_text] = 100
|
||||
last_channel_played = channel_text
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
if(!(M.client?.prefs?.sound & SOUND_INSTRUMENTS))
|
||||
continue
|
||||
M.playsound_local(get_turf(parent), null, volume, FALSE, K.frequency, INSTRUMENT_DISTANCE_NO_FALLOFF, channel, null, copy, distance_multiplier = INSTRUMENT_DISTANCE_FALLOFF_BUFF)
|
||||
// Could do environment and echo later but not for now
|
||||
|
||||
/**
|
||||
* Stops all sounds we are "responsible" for. Only works in synthesized mode.
|
||||
*/
|
||||
/datum/song/proc/terminate_all_sounds(clear_channels = TRUE)
|
||||
for(var/i in hearing_mobs)
|
||||
terminate_sound_mob(i)
|
||||
if(clear_channels && channels_playing)
|
||||
channels_playing.len = 0
|
||||
channels_idle.len = 0
|
||||
SSinstruments.current_instrument_channels -= using_sound_channels
|
||||
using_sound_channels = 0
|
||||
SSsounds.free_datum_channels(src)
|
||||
|
||||
/**
|
||||
* Stops all sounds we are responsible for in a given person. Only works in synthesized mode.
|
||||
*/
|
||||
/datum/song/proc/terminate_sound_mob(mob/M)
|
||||
for(var/channel in channels_playing)
|
||||
M.stop_sound_channel(text2num(channel))
|
||||
|
||||
/**
|
||||
* Pops a channel we have reserved so we don't have to release and re-request them from SSsounds every time we play a note. This is faster.
|
||||
*/
|
||||
/datum/song/proc/pop_channel()
|
||||
if(length(channels_idle)) //just pop one off of here if we have one available
|
||||
. = text2num(channels_idle[1])
|
||||
channels_idle.Cut(1, 2)
|
||||
return
|
||||
if(using_sound_channels >= max_sound_channels)
|
||||
return
|
||||
. = SSinstruments.reserve_instrument_channel(src)
|
||||
if(!isnull(.))
|
||||
using_sound_channels++
|
||||
|
||||
/**
|
||||
* Decays our channels and updates their volumes to mobs who can hear us.
|
||||
*
|
||||
* Arguments:
|
||||
* * wait_ds - the deciseconds we should decay by. This is to compensate for any lag, as otherwise songs would get pretty nasty during high time dilation.
|
||||
*/
|
||||
/datum/song/proc/process_decay(wait_ds)
|
||||
var/linear_dropoff = cached_linear_dropoff * wait_ds
|
||||
var/exponential_dropoff = cached_exponential_dropoff ** wait_ds
|
||||
for(var/channel in channels_playing)
|
||||
if(full_sustain_held_note && (channel == last_channel_played))
|
||||
continue
|
||||
var/current_volume = channels_playing[channel]
|
||||
switch(sustain_mode)
|
||||
if(SUSTAIN_LINEAR)
|
||||
current_volume -= linear_dropoff
|
||||
if(SUSTAIN_EXPONENTIAL)
|
||||
current_volume /= exponential_dropoff
|
||||
channels_playing[channel] = current_volume
|
||||
var/dead = current_volume <= sustain_dropoff_volume
|
||||
var/channelnumber = text2num(channel)
|
||||
if(dead)
|
||||
channels_playing -= channel
|
||||
channels_idle += channel
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
M.stop_sound_channel(channelnumber)
|
||||
else
|
||||
for(var/i in hearing_mobs)
|
||||
var/mob/M = i
|
||||
M.set_sound_channel_volume(channelnumber, (current_volume * 0.01) * volume * using_instrument.volume_multiplier)
|
||||
@@ -0,0 +1,19 @@
|
||||
/datum/instrument/tones
|
||||
name = "Ideal tone"
|
||||
category = "Tones"
|
||||
abstract_type = /datum/instrument/tones
|
||||
|
||||
/datum/instrument/tones/square_wave
|
||||
name = "Ideal square wave"
|
||||
id = "square"
|
||||
real_samples = list("81"='sound/instruments/synthesis_samples/tones/Square.ogg')
|
||||
|
||||
/datum/instrument/tones/sine_wave
|
||||
name = "Ideal sine wave"
|
||||
id = "sine"
|
||||
real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sine.ogg')
|
||||
|
||||
/datum/instrument/tones/saw_wave
|
||||
name = "Ideal sawtooth wave"
|
||||
id = "saw"
|
||||
real_samples = list("81"='sound/instruments/synthesis_samples/tones/Sawtooth.ogg')
|
||||
@@ -13,15 +13,3 @@
|
||||
possible_destinations = "laborcamp_away"
|
||||
circuit = /obj/item/circuitboard/labor_shuttle/one_way
|
||||
req_access = list( )
|
||||
|
||||
/obj/machinery/computer/shuttle/labor/one_way/Topic(href, href_list)
|
||||
if(href_list["move"])
|
||||
var/obj/docking_port/mobile/M = SSshuttle.getShuttle("laborcamp")
|
||||
if(!M)
|
||||
to_chat(usr, "<span class='warning'>Cannot locate shuttle!</span>")
|
||||
return 0
|
||||
var/obj/docking_port/stationary/S = M.get_docked()
|
||||
if(S && S.name == "laborcamp_away")
|
||||
to_chat(usr, "<span class='warning'>Shuttle is already at the outpost!</span>")
|
||||
return 0
|
||||
..()
|
||||
|
||||
@@ -1,104 +1,21 @@
|
||||
/**********************Mining Equipment Locker**************************/
|
||||
// Use this define to register something as a purchasable!
|
||||
// * n — The proper name of the purchasable
|
||||
// * o — The object type path of the purchasable to spawn
|
||||
// * p — The price of the purchasable in mining points
|
||||
#define EQUIPMENT(n, o, p) n = new /datum/data/mining_equipment(n, o, p)
|
||||
|
||||
/**********************Mining Equipment Vendor**************************/
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor
|
||||
name = "mining equipment vendor"
|
||||
desc = "An equipment vendor for miners, points collected at an ore redemption machine can be spent here."
|
||||
icon = 'icons/obj/machines/mining_machines.dmi'
|
||||
icon_state = "mining"
|
||||
density = 1
|
||||
anchored = 1.0
|
||||
density = TRUE
|
||||
anchored = TRUE
|
||||
var/obj/item/card/id/inserted_id
|
||||
var/list/prize_list = list( //if you add something to this, please, for the love of god, sort it by price/type. use tabs and not spaces.
|
||||
new /datum/data/mining_equipment("1 Marker Beacon", /obj/item/stack/marker_beacon, 10),
|
||||
new /datum/data/mining_equipment("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100),
|
||||
new /datum/data/mining_equipment("30 Marker Beacons", /obj/item/stack/marker_beacon/thirty, 300),
|
||||
new /datum/data/mining_equipment("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100),
|
||||
new /datum/data/mining_equipment("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100),
|
||||
new /datum/data/mining_equipment("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150),
|
||||
new /datum/data/mining_equipment("Soap", /obj/item/soap/nanotrasen, 200),
|
||||
new /datum/data/mining_equipment("Laser Pointer", /obj/item/laser_pointer, 300),
|
||||
new /datum/data/mining_equipment("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300),
|
||||
new /datum/data/mining_equipment("Stabilizing Serum", /obj/item/hivelordstabilizer, 400),
|
||||
new /datum/data/mining_equipment("Space Cash", /obj/item/stack/spacecash/c1000, 2000),
|
||||
new /datum/data/mining_equipment("Point Transfer Card", /obj/item/card/mining_point_card, 500),
|
||||
new /datum/data/mining_equipment("Fulton Beacon", /obj/item/fulton_core, 400),
|
||||
new /datum/data/mining_equipment("Shelter Capsule", /obj/item/survivalcapsule, 400),
|
||||
new /datum/data/mining_equipment("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500),
|
||||
new /datum/data/mining_equipment("Explorer's Webbing", /obj/item/storage/belt/mining, 500),
|
||||
new /datum/data/mining_equipment("Survival Medipen", /obj/item/reagent_containers/hypospray/autoinjector/survival, 500),
|
||||
new /datum/data/mining_equipment("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600),
|
||||
new /datum/data/mining_equipment("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600),
|
||||
new /datum/data/mining_equipment("Jaunter", /obj/item/wormhole_jaunter, 750),
|
||||
new /datum/data/mining_equipment("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750),
|
||||
new /datum/data/mining_equipment("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
|
||||
new /datum/data/mining_equipment("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800),
|
||||
new /datum/data/mining_equipment("Resonator", /obj/item/resonator, 800),
|
||||
new /datum/data/mining_equipment("Fulton Pack", /obj/item/extraction_pack, 1000),
|
||||
new /datum/data/mining_equipment("Lazarus Injector", /obj/item/lazarus_injector, 1000),
|
||||
new /datum/data/mining_equipment("Silver Pickaxe", /obj/item/pickaxe/silver, 1000),
|
||||
new /datum/data/mining_equipment("Mining Conscription Kit", /obj/item/storage/backpack/duffel/mining_conscript/full, 1500),
|
||||
new /datum/data/mining_equipment("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000),
|
||||
new /datum/data/mining_equipment("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000),
|
||||
new /datum/data/mining_equipment("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000),
|
||||
new /datum/data/mining_equipment("Super Resonator", /obj/item/resonator/upgraded, 2500),
|
||||
new /datum/data/mining_equipment("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
|
||||
new /datum/data/mining_equipment("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000),
|
||||
new /datum/data/mining_equipment("Nanotrasen Minebot", /obj/item/mining_drone_cube, 800),
|
||||
new /datum/data/mining_equipment("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400),
|
||||
new /datum/data/mining_equipment("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400),
|
||||
new /datum/data/mining_equipment("Minebot Cooldown Upgrade", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600),
|
||||
new /datum/data/mining_equipment("Minebot AI Upgrade", /obj/item/slimepotion/sentience/mining, 1000),
|
||||
new /datum/data/mining_equipment("KA Minebot Passthrough", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100),
|
||||
new /datum/data/mining_equipment("Lazarus Capsule", /obj/item/mobcapsule, 800),
|
||||
new /datum/data/mining_equipment("Lazarus Capsule belt", /obj/item/storage/belt/lazarus, 200),
|
||||
new /datum/data/mining_equipment("KA White Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer, 100),
|
||||
new /datum/data/mining_equipment("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150),
|
||||
new /datum/data/mining_equipment("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250),
|
||||
new /datum/data/mining_equipment("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300),
|
||||
new /datum/data/mining_equipment("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000),
|
||||
new /datum/data/mining_equipment("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000),
|
||||
new /datum/data/mining_equipment("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000),
|
||||
new /datum/data/mining_equipment("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000)
|
||||
)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem
|
||||
name = "golem ship equipment vendor"
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem/New()
|
||||
..()
|
||||
component_parts = list()
|
||||
component_parts += new /obj/item/circuitboard/mining_equipment_vendor/golem(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stack/sheet/glass(null)
|
||||
RefreshParts()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem/Initialize()
|
||||
. = ..()
|
||||
desc += "\nIt seems a few selections have been added."
|
||||
prize_list += list(
|
||||
new /datum/data/mining_equipment("Extra Id", /obj/item/card/id/golem, 250),
|
||||
new /datum/data/mining_equipment("Science Backpack", /obj/item/storage/backpack/science, 250),
|
||||
new /datum/data/mining_equipment("Full Toolbelt", /obj/item/storage/belt/utility/full/multitool, 250),
|
||||
new /datum/data/mining_equipment("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 250),
|
||||
new /datum/data/mining_equipment("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500),
|
||||
new /datum/data/mining_equipment("Grey Slime Extract", /obj/item/slime_extract/grey, 1000),
|
||||
new /datum/data/mining_equipment("KA Trigger Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1000),
|
||||
new /datum/data/mining_equipment("Shuttle Console Board", /obj/item/circuitboard/shuttle/golem_ship, 2000),
|
||||
new /datum/data/mining_equipment("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000)
|
||||
|
||||
)
|
||||
|
||||
/datum/data/mining_equipment
|
||||
var/equipment_name = "generic"
|
||||
var/equipment_path = null
|
||||
var/cost = 0
|
||||
|
||||
/datum/data/mining_equipment/New(name, path, equipment_cost)
|
||||
equipment_name = name
|
||||
equipment_path = path
|
||||
cost = equipment_cost
|
||||
var/list/prize_list // Initialized just below! (if you're wondering why - check CONTRIBUTING.md, look for: "hidden" init proc)
|
||||
var/dirty_items = FALSE // Used to refresh the static/redundant data in case the machine gets VV'd
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/New()
|
||||
..()
|
||||
@@ -110,6 +27,72 @@
|
||||
component_parts += new /obj/item/stack/sheet/glass(null)
|
||||
RefreshParts()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/Initialize(mapload)
|
||||
. = ..()
|
||||
prize_list = list()
|
||||
prize_list["Gear"] = list(
|
||||
EQUIPMENT("Advanced Scanner", /obj/item/t_scanner/adv_mining_scanner, 800),
|
||||
EQUIPMENT("Explorer's Webbing", /obj/item/storage/belt/mining, 500),
|
||||
EQUIPMENT("Fulton Beacon", /obj/item/fulton_core, 400),
|
||||
EQUIPMENT("Mining Conscription Kit", /obj/item/storage/backpack/duffel/mining_conscript, 1500),
|
||||
EQUIPMENT("Jetpack Upgrade", /obj/item/tank/jetpack/suit, 2000),
|
||||
EQUIPMENT("Jump Boots", /obj/item/clothing/shoes/bhop, 2500),
|
||||
EQUIPMENT("Lazarus Capsule", /obj/item/mobcapsule, 800),
|
||||
EQUIPMENT("Lazarus Capsule belt", /obj/item/storage/belt/lazarus, 200),
|
||||
EQUIPMENT("Mining Hardsuit", /obj/item/clothing/suit/space/hardsuit/mining, 2000),
|
||||
EQUIPMENT("Tracking Implant Kit", /obj/item/storage/box/minertracker, 600),
|
||||
)
|
||||
prize_list["Consumables"] = list(
|
||||
EQUIPMENT("10 Marker Beacons", /obj/item/stack/marker_beacon/ten, 100),
|
||||
EQUIPMENT("Brute First-Aid Kit", /obj/item/storage/firstaid/brute, 600),
|
||||
EQUIPMENT("Fulton Pack", /obj/item/extraction_pack, 1000),
|
||||
EQUIPMENT("Jaunter", /obj/item/wormhole_jaunter, 750),
|
||||
EQUIPMENT("Lazarus Injector", /obj/item/lazarus_injector, 1000),
|
||||
EQUIPMENT("Point Transfer Card", /obj/item/card/mining_point_card, 500),
|
||||
EQUIPMENT("Shelter Capsule", /obj/item/survivalcapsule, 400),
|
||||
EQUIPMENT("Stabilizing Serum", /obj/item/hivelordstabilizer, 400),
|
||||
EQUIPMENT("Survival Medipen", /obj/item/reagent_containers/hypospray/autoinjector/survival, 500),
|
||||
)
|
||||
prize_list["Kinetic Accelerator"] = list(
|
||||
EQUIPMENT("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
|
||||
EQUIPMENT("KA Adjustable Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer/adjustable, 150),
|
||||
EQUIPMENT("KA AoE Damage", /obj/item/borg/upgrade/modkit/aoe/mobs, 2000),
|
||||
EQUIPMENT("KA Cooldown Decrease", /obj/item/borg/upgrade/modkit/cooldown, 1000),
|
||||
EQUIPMENT("KA Damage Increase", /obj/item/borg/upgrade/modkit/damage, 1000),
|
||||
EQUIPMENT("KA Hyper Chassis", /obj/item/borg/upgrade/modkit/chassis_mod/orange, 300),
|
||||
EQUIPMENT("KA Minebot Passthrough", /obj/item/borg/upgrade/modkit/minebot_passthrough, 100),
|
||||
EQUIPMENT("KA Range Increase", /obj/item/borg/upgrade/modkit/range, 1000),
|
||||
EQUIPMENT("KA Super Chassis", /obj/item/borg/upgrade/modkit/chassis_mod, 250),
|
||||
EQUIPMENT("KA White Tracer Rounds", /obj/item/borg/upgrade/modkit/tracer, 100),
|
||||
)
|
||||
prize_list["Digging Tools"] = list(
|
||||
EQUIPMENT("Diamond Pickaxe", /obj/item/pickaxe/diamond, 2000),
|
||||
EQUIPMENT("Kinetic Accelerator", /obj/item/gun/energy/kinetic_accelerator, 750),
|
||||
EQUIPMENT("Kinetic Crusher", /obj/item/twohanded/kinetic_crusher, 750),
|
||||
EQUIPMENT("Resonator", /obj/item/resonator, 800),
|
||||
EQUIPMENT("Silver Pickaxe", /obj/item/pickaxe/silver, 1000),
|
||||
EQUIPMENT("Super Resonator", /obj/item/resonator/upgraded, 2500),
|
||||
)
|
||||
prize_list["Minebot"] = list(
|
||||
EQUIPMENT("Nanotrasen Minebot", /obj/item/mining_drone_cube, 800),
|
||||
EQUIPMENT("Minebot AI Upgrade", /obj/item/slimepotion/sentience/mining, 1000),
|
||||
EQUIPMENT("Minebot Armor Upgrade", /obj/item/mine_bot_upgrade/health, 400),
|
||||
EQUIPMENT("Minebot Cooldown Upgrade", /obj/item/borg/upgrade/modkit/cooldown/minebot, 600),
|
||||
EQUIPMENT("Minebot Melee Upgrade", /obj/item/mine_bot_upgrade, 400),
|
||||
)
|
||||
prize_list["Miscellaneous"] = list(
|
||||
EQUIPMENT("Absinthe", /obj/item/reagent_containers/food/drinks/bottle/absinthe/premium, 100),
|
||||
EQUIPMENT("Alien Toy", /obj/item/clothing/mask/facehugger/toy, 300),
|
||||
EQUIPMENT("Cigar", /obj/item/clothing/mask/cigarette/cigar/havana, 150),
|
||||
EQUIPMENT("GAR Meson Scanners", /obj/item/clothing/glasses/meson/gar, 500),
|
||||
EQUIPMENT("Laser Pointer", /obj/item/laser_pointer, 300),
|
||||
EQUIPMENT("Luxury Shelter Capsule", /obj/item/survivalcapsule/luxury, 3000),
|
||||
EQUIPMENT("Soap", /obj/item/soap/nanotrasen, 200),
|
||||
EQUIPMENT("Space Cash", /obj/item/stack/spacecash/c1000, 2000),
|
||||
EQUIPMENT("Whiskey", /obj/item/reagent_containers/food/drinks/bottle/whiskey, 100),
|
||||
)
|
||||
prize_list["Extra"] = list() // Used in child vendors
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/power_change()
|
||||
..()
|
||||
update_icon()
|
||||
@@ -126,90 +109,126 @@
|
||||
/obj/machinery/mineral/equipment_vendor/attack_hand(mob/user)
|
||||
if(..())
|
||||
return
|
||||
interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/attack_ghost(mob/user)
|
||||
interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/interact(mob/user)
|
||||
user.set_machine(src)
|
||||
/obj/machinery/mineral/equipment_vendor/tgui_data(mob/user)
|
||||
var/list/data[0]
|
||||
|
||||
var/dat
|
||||
dat +="<div class='statusDisplay'>"
|
||||
if(istype(inserted_id))
|
||||
dat += "You have [inserted_id.mining_points] mining points collected. <A href='?src=[UID()];choice=eject'>Eject ID.</A><br>"
|
||||
// ID
|
||||
if(inserted_id)
|
||||
data["has_id"] = TRUE
|
||||
data["id"] = list(
|
||||
"name" = inserted_id.registered_name,
|
||||
"points" = inserted_id.mining_points,
|
||||
)
|
||||
else
|
||||
dat += "No ID inserted. <A href='?src=[UID()];choice=insert'>Insert ID.</A><br>"
|
||||
dat += "</div>"
|
||||
dat += "<br><b>Equipment point cost list:</b><BR><table border='0' width='200'>"
|
||||
for(var/datum/data/mining_equipment/prize in prize_list)
|
||||
dat += "<tr><td>[prize.equipment_name]</td><td>[prize.cost]</td><td><A href='?src=[UID()];purchase=\ref[prize]'>Purchase</A></td></tr>"
|
||||
dat += "</table>"
|
||||
var/datum/browser/popup = new(user, "miningvendor", "Mining Equipment Vendor", 400, 350)
|
||||
popup.set_content(dat)
|
||||
popup.open()
|
||||
data["has_id"] = FALSE
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/Topic(href, href_list)
|
||||
return data
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/tgui_static_data(mob/user)
|
||||
var/list/static_data[0]
|
||||
|
||||
// Available items - in static data because we don't wanna compute this list every time! It hardly changes.
|
||||
static_data["items"] = list()
|
||||
for(var/cat in prize_list)
|
||||
var/list/cat_items = list()
|
||||
for(var/prize_name in prize_list[cat])
|
||||
var/datum/data/mining_equipment/prize = prize_list[cat][prize_name]
|
||||
cat_items[prize_name] = list("name" = prize_name, "price" = prize.cost)
|
||||
static_data["items"][cat] = cat_items
|
||||
|
||||
return static_data
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/vv_edit_var(var_name, var_value)
|
||||
// Gotta update the static data in case an admin VV's the items for some reason..!
|
||||
if(var_name == "prize_list")
|
||||
dirty_items = TRUE
|
||||
return ..()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
// Update static data if need be
|
||||
if(dirty_items)
|
||||
if(!ui)
|
||||
ui = SStgui.get_open_ui(user, src, ui_key)
|
||||
if(ui) // OK so ui?. somehow breaks the implied src so this is needed
|
||||
ui.initial_static_data = tgui_static_data(user)
|
||||
dirty_items = FALSE
|
||||
|
||||
// Open the window
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "MiningVendor", name, 400, 450)
|
||||
ui.open()
|
||||
ui.set_autoupdate(FALSE)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/tgui_act(action, params)
|
||||
if(..())
|
||||
return 1
|
||||
return
|
||||
|
||||
if(href_list["choice"])
|
||||
if(istype(inserted_id))
|
||||
if(href_list["choice"] == "eject")
|
||||
inserted_id.loc = loc
|
||||
inserted_id.verb_pickup()
|
||||
inserted_id = null
|
||||
else if(href_list["choice"] == "insert")
|
||||
var/obj/item/card/id/I = usr.get_active_hand()
|
||||
if(istype(I))
|
||||
if(!usr.drop_item())
|
||||
return
|
||||
I.loc = src
|
||||
inserted_id = I
|
||||
else
|
||||
to_chat(usr, "<span class='danger'>No valid ID.</span>")
|
||||
|
||||
if(href_list["purchase"])
|
||||
if(istype(inserted_id))
|
||||
var/datum/data/mining_equipment/prize = locate(href_list["purchase"])
|
||||
if(!prize || !(prize in prize_list) || prize.cost > inserted_id.mining_points)
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("logoff")
|
||||
if(!inserted_id)
|
||||
return
|
||||
usr.put_in_hands(inserted_id)
|
||||
inserted_id = null
|
||||
if("purchase")
|
||||
if(!inserted_id)
|
||||
return
|
||||
var/category = params["cat"] // meow
|
||||
var/name = params["name"]
|
||||
if(!(category in prize_list) || !(name in prize_list[category])) // Not trying something that's not in the list, are you?
|
||||
return
|
||||
var/datum/data/mining_equipment/prize = prize_list[category][name]
|
||||
if(prize.cost > inserted_id.mining_points) // shouldn't be able to access this since the button is greyed out, but..
|
||||
to_chat(usr, "<span class='danger'>You have insufficient points.</span>")
|
||||
return
|
||||
|
||||
inserted_id.mining_points -= prize.cost
|
||||
new prize.equipment_path(src.loc)
|
||||
updateUsrDialog()
|
||||
new prize.equipment_path(loc)
|
||||
else
|
||||
return FALSE
|
||||
add_fingerprint()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/attackby(obj/item/I, mob/user, params)
|
||||
if(default_deconstruction_screwdriver(user, "mining-open", "mining", I))
|
||||
updateUsrDialog()
|
||||
return
|
||||
if(panel_open)
|
||||
if(istype(I, /obj/item/crowbar))
|
||||
if(inserted_id)
|
||||
inserted_id.forceMove(loc) //Prevents deconstructing the ORM from deleting whatever ID was inside it.
|
||||
default_deconstruction_crowbar(user, I)
|
||||
return 1
|
||||
return TRUE
|
||||
if(istype(I, /obj/item/mining_voucher))
|
||||
if(!powered())
|
||||
return
|
||||
else
|
||||
RedeemVoucher(I, user)
|
||||
redeem_voucher(I, user)
|
||||
return
|
||||
if(istype(I,/obj/item/card/id))
|
||||
if(istype(I, /obj/item/card/id))
|
||||
if(!powered())
|
||||
return
|
||||
else
|
||||
var/obj/item/card/id/C = usr.get_active_hand()
|
||||
if(istype(C) && !istype(inserted_id))
|
||||
if(!usr.drop_item())
|
||||
return
|
||||
C.forceMove(src)
|
||||
inserted_id = C
|
||||
interact(user)
|
||||
var/obj/item/card/id/C = user.get_active_hand()
|
||||
if(istype(C) && !istype(inserted_id))
|
||||
if(!user.drop_item())
|
||||
return
|
||||
C.forceMove(src)
|
||||
inserted_id = C
|
||||
tgui_interact(user)
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/proc/RedeemVoucher(obj/item/mining_voucher/voucher, mob/redeemer)
|
||||
/**
|
||||
* Called when someone slaps the machine with a mining voucher
|
||||
*
|
||||
* Arguments:
|
||||
* * voucher - The voucher card item
|
||||
* * redeemer - The person holding it
|
||||
*/
|
||||
/obj/machinery/mineral/equipment_vendor/proc/redeem_voucher(obj/item/mining_voucher/voucher, mob/redeemer)
|
||||
var/items = list("Survival Capsule and Explorer's Webbing", "Resonator Kit", "Minebot Kit", "Extraction and Rescue Kit", "Crusher Kit", "Mining Conscription Kit")
|
||||
|
||||
var/selection = input(redeemer, "Pick your equipment", "Mining Voucher Redemption") as null|anything in items
|
||||
@@ -240,11 +259,51 @@
|
||||
qdel(voucher)
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/ex_act(severity, target)
|
||||
do_sparks(5, 1, src)
|
||||
do_sparks(5, TRUE, src)
|
||||
if(prob(50 / severity) && severity < 3)
|
||||
qdel(src)
|
||||
|
||||
/**********************Mining Equipment Locker Items**************************/
|
||||
/**********************Mining Equiment Vendor (Golem)**************************/
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem
|
||||
name = "golem ship equipment vendor"
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem/New()
|
||||
..()
|
||||
component_parts = list()
|
||||
component_parts += new /obj/item/circuitboard/mining_equipment_vendor/golem(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stock_parts/matter_bin(null)
|
||||
component_parts += new /obj/item/stack/sheet/glass(null)
|
||||
RefreshParts()
|
||||
|
||||
/obj/machinery/mineral/equipment_vendor/golem/Initialize()
|
||||
. = ..()
|
||||
desc += "\nIt seems a few selections have been added."
|
||||
prize_list["Extra"] += list(
|
||||
EQUIPMENT("Extra ID", /obj/item/card/id/golem, 250),
|
||||
EQUIPMENT("Science Backpack", /obj/item/storage/backpack/science, 250),
|
||||
EQUIPMENT("Full Toolbelt", /obj/item/storage/belt/utility/full/multitool, 250),
|
||||
EQUIPMENT("Monkey Cube", /obj/item/reagent_containers/food/snacks/monkeycube, 250),
|
||||
EQUIPMENT("Royal Cape of the Liberator", /obj/item/bedsheet/rd/royal_cape, 500),
|
||||
EQUIPMENT("Grey Slime Extract", /obj/item/slime_extract/grey, 1000),
|
||||
EQUIPMENT("KA Trigger Modification Kit", /obj/item/borg/upgrade/modkit/trigger_guard, 1000),
|
||||
EQUIPMENT("Shuttle Console Board", /obj/item/circuitboard/shuttle/golem_ship, 2000),
|
||||
EQUIPMENT("The Liberator's Legacy", /obj/item/storage/box/rndboards, 2000),
|
||||
)
|
||||
|
||||
/**********************Mining Equipment Datum**************************/
|
||||
|
||||
/datum/data/mining_equipment
|
||||
var/equipment_name = "generic"
|
||||
var/equipment_path = null
|
||||
var/cost = 0
|
||||
|
||||
/datum/data/mining_equipment/New(name, path, equipment_cost)
|
||||
equipment_name = name
|
||||
equipment_path = path
|
||||
cost = equipment_cost
|
||||
|
||||
/**********************Mining Equipment Voucher**********************/
|
||||
|
||||
@@ -300,3 +359,5 @@
|
||||
/obj/item/storage/backpack/duffel/mining_conscript/full/New()
|
||||
..()
|
||||
new /obj/item/card/id/mining_access_card(src)
|
||||
|
||||
#undef EQUIPMENT
|
||||
|
||||
@@ -220,6 +220,7 @@ GLOBAL_LIST_INIT(sand_recipes, list(\
|
||||
var/datum/wires/explosive/gibtonite/wires
|
||||
|
||||
/obj/item/twohanded/required/gibtonite/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
QDEL_NULL(wires)
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -780,4 +780,4 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
|
||||
set category = "Ghost"
|
||||
|
||||
var/datum/spawners_menu/menu = new /datum/spawners_menu(src)
|
||||
menu.ui_interact(src)
|
||||
menu.tgui_interact(src)
|
||||
|
||||
@@ -1,20 +1,10 @@
|
||||
/mob/dead/observer/say(var/message)
|
||||
/mob/dead/observer/say(message)
|
||||
message = sanitize(copytext(message, 1, MAX_MESSAGE_LEN))
|
||||
|
||||
if(!message)
|
||||
return
|
||||
|
||||
log_ghostsay(message, src)
|
||||
|
||||
if(src.client)
|
||||
if(src.client.prefs.muted & MUTE_DEADCHAT)
|
||||
to_chat(src, "<span class='warning'>You cannot talk in deadchat (muted).</span>")
|
||||
return
|
||||
|
||||
if(src.client.handle_spam_prevention(message,MUTE_DEADCHAT))
|
||||
return
|
||||
|
||||
. = src.say_dead(message)
|
||||
return say_dead(message)
|
||||
|
||||
|
||||
/mob/dead/observer/emote(act, type, message, force)
|
||||
|
||||
@@ -171,7 +171,14 @@
|
||||
|
||||
to_chat(src, heard)
|
||||
|
||||
/mob/proc/hear_holopad_talk(list/message_pieces, var/verb = "says", var/mob/speaker = null)
|
||||
/mob/proc/hear_holopad_talk(list/message_pieces, verb = "says", mob/speaker = null)
|
||||
if(sleeping || stat == UNCONSCIOUS)
|
||||
hear_sleep(multilingual_to_message(message_pieces))
|
||||
return
|
||||
|
||||
if(!can_hear())
|
||||
return
|
||||
|
||||
var/message = combine_message(message_pieces, verb, speaker)
|
||||
|
||||
var/name = speaker.name
|
||||
|
||||
@@ -186,7 +186,7 @@
|
||||
if(prob(80))
|
||||
new_name += " [pick(list("Hadii","Kaytam","Zhan-Khazan","Hharar","Njarir'Akhan"))]"
|
||||
else
|
||||
new_name += ..(gender,1)
|
||||
new_name += " [..(gender,1)]"
|
||||
return new_name
|
||||
|
||||
/datum/language/vulpkanin
|
||||
|
||||
@@ -301,14 +301,13 @@
|
||||
else if(nutrition >= NUTRITION_LEVEL_FAT)
|
||||
msg += "[p_they(TRUE)] [p_are()] quite chubby.\n"
|
||||
|
||||
if(!ismachineperson(src) && blood_volume < BLOOD_VOLUME_SAFE)
|
||||
if(blood_volume < BLOOD_VOLUME_SAFE)
|
||||
msg += "[p_they(TRUE)] [p_have()] pale skin.\n"
|
||||
|
||||
if(bleedsuppress)
|
||||
msg += "[p_they(TRUE)] [p_are()] bandaged with something.\n"
|
||||
else if(bleed_rate)
|
||||
var/bleed_message = !ismachineperson(src) ? "bleeding" : "leaking"
|
||||
msg += "<B>[p_they(TRUE)] [p_are()] [bleed_message]!</B>\n"
|
||||
msg += "<B>[p_they(TRUE)] [p_are()] bleeding!</B>\n"
|
||||
|
||||
if(reagents.has_reagent("teslium"))
|
||||
msg += "[p_they(TRUE)] [p_are()] emitting a gentle blue glow!\n"
|
||||
|
||||
@@ -572,41 +572,34 @@ emp_act
|
||||
if(M.a_intent == INTENT_HARM)
|
||||
if(w_uniform)
|
||||
w_uniform.add_fingerprint(M)
|
||||
var/damage = rand(15, 30)
|
||||
var/damage = prob(90) ? 20 : 0
|
||||
if(!damage)
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, TRUE, -1)
|
||||
visible_message("<span class='danger'>[M] has lunged at [src]!</span>")
|
||||
return 0
|
||||
var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected))
|
||||
var/armor_block = run_armor_check(affecting, "melee")
|
||||
var/armor_block = run_armor_check(affecting, "melee", armour_penetration = 10)
|
||||
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, TRUE, -1)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
|
||||
apply_damage(damage, BRUTE, affecting, armor_block)
|
||||
if(damage >= 25)
|
||||
visible_message("<span class='danger'>[M] has wounded [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has wounded [src]!</span>")
|
||||
apply_effect(4, WEAKEN, armor_block)
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
updatehealth("alien attack")
|
||||
|
||||
if(M.a_intent == INTENT_DISARM)
|
||||
if(prob(80))
|
||||
if(M.a_intent == INTENT_DISARM) //Always drop item in hand, if no item, get stun instead.
|
||||
var/obj/item/I = get_active_hand()
|
||||
if(I && unEquip(I))
|
||||
playsound(loc, 'sound/weapons/slash.ogg', 25, TRUE, -1)
|
||||
visible_message("<span class='danger'>[M] disarms [src]!</span>", "<span class='userdanger'>[M] disarms you!</span>", "<span class='hear'>You hear aggressive shuffling!</span>")
|
||||
to_chat(M, "<span class='danger'>You disarm [src]!</span>")
|
||||
else
|
||||
var/obj/item/organ/external/affecting = get_organ(ran_zone(M.zone_selected))
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 25, 1, -1)
|
||||
apply_effect(5, WEAKEN, run_armor_check(affecting, "melee"))
|
||||
add_attack_logs(M, src, "Alien tackled")
|
||||
visible_message("<span class='danger'>[M] has tackled down [src]!</span>")
|
||||
else
|
||||
if(prob(99)) //this looks fucking stupid but it was previously 'var/randn = rand(1, 100); if(randn <= 99)'
|
||||
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
|
||||
drop_item()
|
||||
visible_message("<span class='danger'>[M] disarmed [src]!</span>")
|
||||
else
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has tried to disarm [src]!</span>")
|
||||
|
||||
/mob/living/carbon/human/attack_animal(mob/living/simple_animal/M)
|
||||
. = ..()
|
||||
|
||||
@@ -74,6 +74,10 @@
|
||||
..()
|
||||
H.gender = NEUTER
|
||||
|
||||
/datum/species/diona/on_species_loss(mob/living/carbon/human/H)
|
||||
. = ..()
|
||||
H.clear_alert("nolight")
|
||||
|
||||
/datum/species/diona/handle_reagents(mob/living/carbon/human/H, datum/reagent/R)
|
||||
if(R.id == "glyphosate" || R.id == "atrazine")
|
||||
H.adjustToxLoss(3) //Deal aditional damage
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
death_message = "gives a short series of shrill beeps, their chassis shuddering before falling limp, nonfunctional."
|
||||
death_sounds = list('sound/voice/borg_deathsound.ogg') //I've made this a list in the event we add more sounds for dead robots.
|
||||
|
||||
species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_SCAN, NO_INTORGANS, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NO_GERMS, NO_DECAY, NOTRANSSTING) //Computers that don't decay? What a lie!
|
||||
species_traits = list(IS_WHITELISTED, NO_BREATHE, NO_BLOOD, NO_SCAN, NO_INTORGANS, NO_PAIN, NO_DNA, RADIMMUNE, VIRUSIMMUNE, NO_GERMS, NO_DECAY, NOTRANSSTING) //Computers that don't decay? What a lie!
|
||||
clothing_flags = HAS_UNDERWEAR | HAS_UNDERSHIRT | HAS_SOCKS
|
||||
bodyflags = HAS_SKIN_COLOR | HAS_HEAD_MARKINGS | HAS_HEAD_ACCESSORY | ALL_RPARTS
|
||||
dietflags = 0 //IPCs can't eat, so no diet
|
||||
@@ -30,10 +30,6 @@
|
||||
blood_color = "#1F181F"
|
||||
flesh_color = "#AAAAAA"
|
||||
|
||||
blood_color = "#3C3C3C"
|
||||
exotic_blood = "oil"
|
||||
blood_damage_type = STAMINA
|
||||
|
||||
//Default styles for created mobs.
|
||||
default_hair = "Blue IPC Screen"
|
||||
dies_at_threshold = TRUE
|
||||
|
||||
@@ -351,8 +351,6 @@ proc/get_radio_key_from_channel(var/channel)
|
||||
return
|
||||
|
||||
if(stat)
|
||||
if(stat == DEAD)
|
||||
return say_dead(message_pieces)
|
||||
return
|
||||
|
||||
if(is_muzzled())
|
||||
|
||||
@@ -50,7 +50,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
var/list/connected_robots = list()
|
||||
var/aiRestorePowerRoutine = 0
|
||||
//var/list/laws = list()
|
||||
var/alarms = list("Motion" = list(), "Fire" = list(), "Atmosphere" = list(), "Power" = list(), "Camera" = list())
|
||||
alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera", "Burglar")
|
||||
var/viewalerts = 0
|
||||
var/icon/holo_icon//Default is assigned when AI is created.
|
||||
var/obj/mecha/controlled_mech //For controlled_mech a mech, to determine whether to relaymove or use the AI eye.
|
||||
@@ -173,19 +173,19 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
add_language("Galactic Common", 1)
|
||||
add_language("Sol Common", 1)
|
||||
add_language("Tradeband", 1)
|
||||
add_language("Neo-Russkiya", 0)
|
||||
add_language("Gutter", 0)
|
||||
add_language("Sinta'unathi", 0)
|
||||
add_language("Siik'tajr", 0)
|
||||
add_language("Canilunzt", 0)
|
||||
add_language("Skrellian", 0)
|
||||
add_language("Vox-pidgin", 0)
|
||||
add_language("Orluum", 0)
|
||||
add_language("Rootspeak", 0)
|
||||
add_language("Neo-Russkiya", 1)
|
||||
add_language("Gutter", 1)
|
||||
add_language("Sinta'unathi", 1)
|
||||
add_language("Siik'tajr", 1)
|
||||
add_language("Canilunzt", 1)
|
||||
add_language("Skrellian", 1)
|
||||
add_language("Vox-pidgin", 1)
|
||||
add_language("Orluum", 1)
|
||||
add_language("Rootspeak", 1)
|
||||
add_language("Trinary", 1)
|
||||
add_language("Chittin", 0)
|
||||
add_language("Bubblish", 0)
|
||||
add_language("Clownish", 0)
|
||||
add_language("Chittin", 1)
|
||||
add_language("Bubblish", 1)
|
||||
add_language("Clownish", 1)
|
||||
|
||||
if(!safety)//Only used by AIize() to successfully spawn an AI.
|
||||
if(!B)//If there is no player/brain inside.
|
||||
@@ -242,6 +242,46 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
return
|
||||
show_borg_info()
|
||||
|
||||
/mob/living/silicon/ai/proc/ai_alerts()
|
||||
var/list/dat = list("<HEAD><TITLE>Current Station Alerts</TITLE><META HTTP-EQUIV='Refresh' CONTENT='10'></HEAD><BODY>\n")
|
||||
dat += "<A HREF='?src=[UID()];mach_close=aialerts'>Close</A><BR><BR>"
|
||||
var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
|
||||
for(var/cat in temp_alarm_list)
|
||||
if(!(cat in alarms_listend_for))
|
||||
continue
|
||||
dat += text("<B>[]</B><BR>\n", cat)
|
||||
var/list/list/L = temp_alarm_list[cat].Copy()
|
||||
for(var/alarm in L)
|
||||
var/list/list/alm = L[alarm].Copy()
|
||||
var/area_name = alm[1]
|
||||
var/C = alm[2]
|
||||
var/list/list/sources = alm[3].Copy()
|
||||
for(var/thing in sources)
|
||||
var/atom/A = locateUID(thing)
|
||||
if(A && A.z != z)
|
||||
L -= alarm
|
||||
continue
|
||||
dat += "<NOBR>"
|
||||
if(C && islist(C))
|
||||
var/dat2 = ""
|
||||
for(var/cam in C)
|
||||
var/obj/machinery/camera/I = locateUID(cam)
|
||||
if(!QDELETED(I))
|
||||
dat2 += text("[]<A HREF=?src=[UID()];switchcamera=[cam]>[]</A>", (dat2 == "") ? "" : " | ", I.c_tag)
|
||||
dat += text("-- [] ([])", area_name, (dat2 != "") ? dat2 : "No Camera")
|
||||
else
|
||||
dat += text("-- [] (No Camera)", area_name)
|
||||
if(sources.len > 1)
|
||||
dat += text("- [] sources", sources.len)
|
||||
dat += "</NOBR><BR>\n"
|
||||
if(!L.len)
|
||||
dat += "-- All Systems Nominal<BR>\n"
|
||||
dat += "<BR>\n"
|
||||
|
||||
viewalerts = TRUE
|
||||
var/dat_text = dat.Join("")
|
||||
src << browse(dat_text, "window=aialerts&can_close=0")
|
||||
|
||||
/mob/living/silicon/ai/proc/show_borg_info()
|
||||
stat(null, text("Connected cyborgs: [connected_robots.len]"))
|
||||
for(var/thing in connected_robots)
|
||||
@@ -612,7 +652,7 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
if(href_list["switchcamera"])
|
||||
switchCamera(locate(href_list["switchcamera"])) in GLOB.cameranet.cameras
|
||||
if(href_list["showalerts"])
|
||||
subsystem_alarm_monitor()
|
||||
ai_alerts()
|
||||
if(href_list["show_paper"])
|
||||
if(last_paper_seen)
|
||||
src << browse(last_paper_seen, "window=show_paper")
|
||||
@@ -787,12 +827,49 @@ GLOBAL_LIST_INIT(ai_verbs_default, list(
|
||||
|
||||
Bot.call_bot(src, waypoint)
|
||||
|
||||
/mob/living/silicon/ai/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
|
||||
if(!(class in alarms_listend_for))
|
||||
return
|
||||
if(alarmsource.z != z)
|
||||
return
|
||||
if(stat == DEAD)
|
||||
return TRUE
|
||||
if(O)
|
||||
var/obj/machinery/camera/C = locateUID(O[1])
|
||||
if(O.len == 1 && !QDELETED(C) && C.can_use())
|
||||
queueAlarm("--- [class] alarm detected in [A.name]! (<A HREF=?src=[UID()];switchcamera=[O[1]]>[C.c_tag]</A>)", class)
|
||||
else if(O && O.len)
|
||||
var/foo = 0
|
||||
var/dat2 = ""
|
||||
for(var/thing in O)
|
||||
var/obj/machinery/camera/I = locateUID(thing)
|
||||
if(!QDELETED(I))
|
||||
dat2 += text("[]<A HREF=?src=[UID()];switchcamera=[thing]>[]</A>", (!foo) ? "" : " | ", I.c_tag) //I'm not fixing this shit...
|
||||
foo = 1
|
||||
queueAlarm(text ("--- [] alarm detected in []! ([])", class, A.name, dat2), class)
|
||||
else
|
||||
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
|
||||
else
|
||||
queueAlarm(text("--- [] alarm detected in []! (No Camera)", class, A.name), class)
|
||||
if(viewalerts)
|
||||
ai_alerts()
|
||||
|
||||
/mob/living/silicon/ai/alarm_cancelled(src, class, area/A, obj/origin, cleared)
|
||||
if(cleared)
|
||||
if(!(class in alarms_listend_for))
|
||||
return
|
||||
if(origin.z != z)
|
||||
return
|
||||
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
|
||||
if(viewalerts)
|
||||
ai_alerts()
|
||||
|
||||
/mob/living/silicon/ai/proc/switchCamera(obj/machinery/camera/C)
|
||||
|
||||
if(!tracking)
|
||||
cameraFollow = null
|
||||
|
||||
if(!C || stat == DEAD) //C.can_use())
|
||||
if(QDELETED(C) || stat == DEAD) //C.can_use())
|
||||
return FALSE
|
||||
|
||||
if(!eyeobj)
|
||||
|
||||
@@ -132,8 +132,6 @@
|
||||
sleep(50)
|
||||
theAPC = null
|
||||
|
||||
process_queued_alarms()
|
||||
|
||||
/mob/living/silicon/ai/updatehealth(reason = "none given")
|
||||
if(status_flags & GODMODE)
|
||||
health = 100
|
||||
|
||||
@@ -563,6 +563,10 @@
|
||||
var/obj/item/holder/H = ..()
|
||||
if(!istype(H))
|
||||
return
|
||||
if(stat == DEAD)
|
||||
H.icon = 'icons/mob/pai.dmi'
|
||||
H.icon_state = "[chassis]_dead"
|
||||
return
|
||||
if(resting)
|
||||
icon_state = "[chassis]"
|
||||
resting = 0
|
||||
|
||||
@@ -312,8 +312,3 @@ proc/robot_healthscan(mob/user, mob/living/M)
|
||||
to_chat(user, "[capitalize(O.name)]: <font color='red'>[O.damage]</font>")
|
||||
if(!organ_found)
|
||||
to_chat(user, "<span class='warning'>No prosthetics located.</span>")
|
||||
|
||||
if(ismachineperson(H))
|
||||
to_chat(user, "<span class='notice'>Internal Fluid Level:[H.blood_volume]/[H.max_blood]</span>")
|
||||
if(H.bleed_rate)
|
||||
to_chat(user, "<span class='warning'>Warning:External component leak detected!</span>")
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
emote("deathgasp", force = TRUE)
|
||||
|
||||
if(module)
|
||||
module.handle_death(gibbed)
|
||||
module.handle_death(src, gibbed)
|
||||
|
||||
// Only execute the below if we successfully died
|
||||
. = ..(gibbed)
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
// Disable the microphone wire on Drones
|
||||
if(radio)
|
||||
radio.wires.CutWireIndex(RADIO_WIRE_TRANSMIT)
|
||||
radio.wires.cut(WIRE_RADIO_TRANSMIT)
|
||||
|
||||
if(camera && ("Robots" in camera.network))
|
||||
camera.network.Add("Engineering")
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
handle_robot_cell()
|
||||
process_locks()
|
||||
update_items()
|
||||
process_queued_alarms()
|
||||
|
||||
|
||||
/mob/living/silicon/robot/proc/handle_robot_cell()
|
||||
@@ -46,7 +45,7 @@
|
||||
|
||||
/mob/living/silicon/robot/proc/handle_equipment()
|
||||
if(camera && !scrambledcodes)
|
||||
if(stat == DEAD || wires.IsCameraCut())
|
||||
if(stat == DEAD || wires.is_cut(WIRE_BORG_CAMERA))
|
||||
camera.status = 0
|
||||
else
|
||||
camera.status = 1
|
||||
|
||||
@@ -59,7 +59,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
var/ear_protection = 0
|
||||
var/damage_protection = 0
|
||||
var/emp_protection = FALSE
|
||||
var/xeno_disarm_chance = 85
|
||||
|
||||
var/list/force_modules = list()
|
||||
var/allow_rename = TRUE
|
||||
@@ -72,11 +71,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
var/list/req_access
|
||||
var/ident = 0
|
||||
//var/list/laws = list()
|
||||
var/alarms = list("Motion"=list(), "Fire"=list(), "Atmosphere"=list(), "Power"=list(), "Camera"=list())
|
||||
var/viewalerts = 0
|
||||
var/modtype = "Default"
|
||||
var/lower_mod = 0
|
||||
var/datum/effect_system/spark_spread/spark_system//So they can initialize sparks whenever/N
|
||||
var/datum/effect_system/spark_spread/spark_system //So they can initialize sparks whenever/N
|
||||
var/jeton = 0
|
||||
var/low_power_mode = 0 //whether the robot has no charge left.
|
||||
var/weapon_lock = 0
|
||||
@@ -112,7 +110,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
/mob/living/silicon/robot/get_cell()
|
||||
return cell
|
||||
|
||||
/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/New(loc, syndie = FALSE, unfinished = FALSE, alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
spark_system = new /datum/effect_system/spark_spread()
|
||||
spark_system.set_up(5, 0, src)
|
||||
spark_system.attach(src)
|
||||
@@ -134,13 +132,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
radio = new /obj/item/radio/borg(src)
|
||||
common_radio = radio
|
||||
|
||||
init(ai_to_sync_to = ai_to_sync_to)
|
||||
init(alien, connect_to_AI, ai_to_sync_to)
|
||||
|
||||
if(has_camera && !camera)
|
||||
camera = new /obj/machinery/camera(src)
|
||||
camera.c_tag = real_name
|
||||
camera.network = list("SS13","Robots")
|
||||
if(wires.IsCameraCut()) // 5 = BORG CAMERA
|
||||
if(wires.is_cut(WIRE_BORG_CAMERA)) // 5 = BORG CAMERA
|
||||
camera.status = 0
|
||||
|
||||
if(mmi == null)
|
||||
@@ -172,18 +170,20 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
scanner = new(src)
|
||||
scanner.Grant(src)
|
||||
|
||||
/mob/living/silicon/robot/proc/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/proc/init(alien, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
||||
make_laws()
|
||||
additional_law_channels["Binary"] = ":b "
|
||||
if(!connect_to_AI)
|
||||
return
|
||||
var/found_ai = ai_to_sync_to
|
||||
if(!found_ai)
|
||||
found_ai = select_active_ai_with_fewest_borgs()
|
||||
if(found_ai)
|
||||
lawupdate = 1
|
||||
lawupdate = TRUE
|
||||
connect_to_ai(found_ai)
|
||||
else
|
||||
lawupdate = 0
|
||||
lawupdate = FALSE
|
||||
|
||||
playsound(loc, 'sound/voice/liveagain.ogg', 75, 1)
|
||||
|
||||
@@ -270,6 +270,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
//If there's an MMI in the robot, have it ejected when the mob goes away. --NEO
|
||||
//Improved /N
|
||||
/mob/living/silicon/robot/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
if(mmi && mind)//Safety for when a cyborg gets dust()ed. Or there is no MMI inside.
|
||||
var/turf/T = get_turf(loc)//To hopefully prevent run time errors.
|
||||
if(T) mmi.loc = T
|
||||
@@ -543,6 +544,43 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
src.verbs -= GLOB.robot_verbs_default
|
||||
src.verbs -= silicon_subsystems
|
||||
|
||||
/mob/living/silicon/robot/verb/cmd_robot_alerts()
|
||||
set category = "Robot Commands"
|
||||
set name = "Show Alerts"
|
||||
if(usr.stat == DEAD)
|
||||
to_chat(src, "<span class='userdanger'>Alert: You are dead.</span>")
|
||||
return //won't work if dead
|
||||
robot_alerts()
|
||||
|
||||
/mob/living/silicon/robot/proc/robot_alerts()
|
||||
var/list/dat = list()
|
||||
var/list/list/temp_alarm_list = SSalarm.alarms.Copy()
|
||||
for(var/cat in temp_alarm_list)
|
||||
if(!(cat in alarms_listend_for))
|
||||
continue
|
||||
dat += text("<B>[cat]</B><BR>\n")
|
||||
var/list/list/L = temp_alarm_list[cat].Copy()
|
||||
for(var/alarm in L)
|
||||
var/list/list/alm = L[alarm].Copy()
|
||||
var/list/list/sources = alm[3].Copy()
|
||||
var/area_name = alm[1]
|
||||
for(var/thing in sources)
|
||||
var/atom/A = locateUID(thing)
|
||||
if(A && A.z != z)
|
||||
L -= alarm
|
||||
continue
|
||||
dat += "<NOBR>"
|
||||
dat += text("-- [area_name]")
|
||||
dat += "</NOBR><BR>\n"
|
||||
if(!L.len)
|
||||
dat += "-- All Systems Nominal<BR>\n"
|
||||
dat += "<BR>\n"
|
||||
|
||||
var/datum/browser/alerts = new(usr, "robotalerts", "Current Station Alerts", 400, 410)
|
||||
var/dat_text = dat.Join("")
|
||||
alerts.set_content(dat_text)
|
||||
alerts.open()
|
||||
|
||||
/mob/living/silicon/robot/proc/ionpulse()
|
||||
if(!ionpulse_on)
|
||||
return
|
||||
@@ -604,6 +642,23 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
/mob/living/silicon/robot/InCritical()
|
||||
return low_power_mode
|
||||
|
||||
/mob/living/silicon/robot/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
|
||||
if(!(class in alarms_listend_for))
|
||||
return
|
||||
if(alarmsource.z != z)
|
||||
return
|
||||
if(stat == DEAD)
|
||||
return
|
||||
queueAlarm(text("--- [class] alarm detected in [A.name]!"), class)
|
||||
|
||||
/mob/living/silicon/robot/alarm_cancelled(src, class, area/A, obj/origin, cleared)
|
||||
if(cleared)
|
||||
if(!(class in alarms_listend_for))
|
||||
return
|
||||
if(origin.z != z)
|
||||
return
|
||||
queueAlarm("--- [class] alarm in [A.name] has been cleared.", class, 0)
|
||||
|
||||
/mob/living/silicon/robot/ex_act(severity)
|
||||
switch(severity)
|
||||
if(1.0)
|
||||
@@ -795,7 +850,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
opened = FALSE
|
||||
update_icons()
|
||||
return
|
||||
else if(wiresexposed && wires.IsAllCut())
|
||||
else if(wiresexposed && wires.is_all_cut())
|
||||
//Cell is out, wires are exposed, remove MMI, produce damaged chassis, baleet original mob.
|
||||
if(!mmi)
|
||||
to_chat(user, "[src] has no brain to remove.")
|
||||
@@ -1023,10 +1078,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
src << browse(null, t1)
|
||||
return 1
|
||||
|
||||
if(href_list["showalerts"])
|
||||
subsystem_alarm_monitor()
|
||||
return 1
|
||||
|
||||
if(href_list["mod"])
|
||||
var/obj/item/O = locate(href_list["mod"])
|
||||
if(istype(O) && (O.loc == src))
|
||||
@@ -1041,6 +1092,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
activate_module(O)
|
||||
installed_modules()
|
||||
|
||||
//Show alerts window if user clicked on "Show alerts" in chat
|
||||
if(href_list["showalerts"])
|
||||
robot_alerts()
|
||||
return TRUE
|
||||
|
||||
if(href_list["deact"])
|
||||
var/obj/item/O = locate(href_list["deact"])
|
||||
if(activated(O))
|
||||
@@ -1063,7 +1119,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
return 1
|
||||
|
||||
/mob/living/silicon/robot/proc/radio_menu()
|
||||
radio.interact(src)//Just use the radio's Topic() instead of bullshit special-snowflake code
|
||||
radio.interact(src)
|
||||
|
||||
/mob/living/silicon/robot/proc/control_headlamp()
|
||||
if(stat || lamp_recharging || low_power_mode)
|
||||
@@ -1233,7 +1289,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
|
||||
/mob/living/silicon/robot/proc/SetLockdown(var/state = 1)
|
||||
// They stay locked down if their wire is cut.
|
||||
if(wires.LockedCut())
|
||||
if(wires.is_cut(WIRE_BORG_LOCKED))
|
||||
state = 1
|
||||
if(state)
|
||||
throw_alert("locked", /obj/screen/alert/locked)
|
||||
@@ -1343,14 +1399,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
eye_protection = 2 // Immunity to flashes and the visual part of flashbangs
|
||||
ear_protection = 1 // Immunity to the audio part of flashbangs
|
||||
damage_protection = 10 // Reduce all incoming damage by this number
|
||||
xeno_disarm_chance = 20
|
||||
allow_rename = FALSE
|
||||
modtype = "Commando"
|
||||
faction = list("nanotrasen")
|
||||
is_emaggable = FALSE
|
||||
default_cell_type = /obj/item/stock_parts/cell/bluespace
|
||||
|
||||
/mob/living/silicon/robot/deathsquad/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/deathsquad/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
laws = new /datum/ai_laws/deathsquad
|
||||
module = new /obj/item/robot_module/deathsquad(src)
|
||||
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
||||
@@ -1380,7 +1435,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
var/eprefix = "Amber"
|
||||
|
||||
|
||||
/mob/living/silicon/robot/ert/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/ert/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
laws = new /datum/ai_laws/ert_override
|
||||
radio = new /obj/item/radio/borg/ert(src)
|
||||
radio.recalculateChannels()
|
||||
@@ -1413,7 +1468,6 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
damage_protection = 5 // Reduce all incoming damage by this number
|
||||
eprefix = "Gamma"
|
||||
magpulse = 1
|
||||
xeno_disarm_chance = 40
|
||||
|
||||
|
||||
/mob/living/silicon/robot/destroyer
|
||||
@@ -1433,10 +1487,9 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
|
||||
ear_protection = 1 // Immunity to the audio part of flashbangs
|
||||
emp_protection = TRUE // Immunity to EMP, due to heavy shielding
|
||||
damage_protection = 20 // Reduce all incoming damage by this number. Very high in the case of /destroyer borgs, since it is an admin-only borg.
|
||||
xeno_disarm_chance = 10
|
||||
default_cell_type = /obj/item/stock_parts/cell/bluespace
|
||||
|
||||
/mob/living/silicon/robot/destroyer/init(alien = FALSE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
/mob/living/silicon/robot/destroyer/init(alien = FALSE, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
|
||||
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
|
||||
additional_law_channels["Binary"] = ":b "
|
||||
laws = new /datum/ai_laws/deathsquad
|
||||
|
||||
@@ -2,19 +2,17 @@
|
||||
if(M.a_intent == INTENT_DISARM)
|
||||
if(!lying)
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_DISARM)
|
||||
if(prob(xeno_disarm_chance))
|
||||
Stun(7)
|
||||
step(src, get_dir(M,src))
|
||||
spawn(5)
|
||||
step(src, get_dir(M,src))
|
||||
add_attack_logs(M, src, "Alien pushed over")
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 50, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has forced back [src]!</span>",\
|
||||
"<span class='userdanger'>[M] has forced back [src]!</span>")
|
||||
var/obj/item/I = get_active_hand()
|
||||
if(I)
|
||||
uneq_active()
|
||||
visible_message("<span class='danger'>[M] disarmed [src]!</span>", "<span class='userdanger'>[M] has disabled [src]'s active module!</span>")
|
||||
add_attack_logs(M, src, "alien disarmed")
|
||||
else
|
||||
playsound(loc, 'sound/weapons/slashmiss.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] took a swipe at [src]!</span>",\
|
||||
"<span class='userdanger'>[M] took a swipe at [src]!</span>")
|
||||
Stun(2)
|
||||
step(src, get_dir(M,src))
|
||||
add_attack_logs(M, src, "Alien pushed over")
|
||||
visible_message("<span class='danger'>[M] forces back [src]!</span>", "<span class='userdanger'>[M] forces back [src]!</span>")
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 50, TRUE, -1)
|
||||
else
|
||||
..()
|
||||
return
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
/obj/item/robot_module/proc/handle_custom_removal(component_id, mob/living/user, obj/item/W)
|
||||
return FALSE
|
||||
|
||||
/obj/item/robot_module/proc/handle_death(gibbed)
|
||||
/obj/item/robot_module/proc/handle_death(mob/living/silicon/robot/R, gibbed)
|
||||
return
|
||||
|
||||
/obj/item/robot_module/standard
|
||||
@@ -255,7 +255,7 @@
|
||||
|
||||
fix_modules()
|
||||
|
||||
/obj/item/robot_module/engineering/handle_death()
|
||||
/obj/item/robot_module/engineering/handle_death(mob/living/silicon/robot/R, gibbed)
|
||||
var/obj/item/gripper/G = locate(/obj/item/gripper) in modules
|
||||
if(G)
|
||||
G.drop_gripped_item(silent = TRUE)
|
||||
@@ -368,6 +368,11 @@
|
||||
R.add_language("Clownish",1)
|
||||
R.add_language("Neo-Russkiya", 1)
|
||||
|
||||
/obj/item/robot_module/butler/handle_death(mob/living/silicon/robot/R, gibbed)
|
||||
var/obj/item/storage/bag/tray/cyborg/T = locate(/obj/item/storage/bag/tray/cyborg) in modules
|
||||
if(istype(T))
|
||||
T.drop_inventory(R)
|
||||
|
||||
|
||||
/obj/item/robot_module/miner
|
||||
name = "miner robot module"
|
||||
@@ -623,7 +628,7 @@
|
||||
..()
|
||||
|
||||
|
||||
/obj/item/robot_module/drone/handle_death()
|
||||
/obj/item/robot_module/drone/handle_death(mob/living/silicon/robot/R, gibbed)
|
||||
var/obj/item/gripper/G = locate(/obj/item/gripper) in modules
|
||||
if(G)
|
||||
G.drop_gripped_item(silent = TRUE)
|
||||
|
||||
@@ -11,9 +11,11 @@
|
||||
var/list/stating_laws = list()// Channels laws are currently being stated on
|
||||
var/list/alarms_to_show = list()
|
||||
var/list/alarms_to_clear = list()
|
||||
var/list/alarm_types_show = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
|
||||
var/list/alarm_types_clear = list("Motion" = 0, "Fire" = 0, "Atmosphere" = 0, "Power" = 0, "Camera" = 0)
|
||||
var/list/alarms_listend_for = list("Motion", "Fire", "Atmosphere", "Power", "Camera")
|
||||
//var/list/hud_list[10]
|
||||
var/list/speech_synthesizer_langs = list() //which languages can be vocalized by the speech synthesizer
|
||||
var/list/alarm_handlers = list() // List of alarm handlers this silicon is registered to
|
||||
var/designation = ""
|
||||
var/obj/item/camera/siliconcam/aiCamera = null //photography
|
||||
//Used in say.dm, allows for pAIs to have different say flavor text, as well as silicons, although the latter is not implemented.
|
||||
@@ -25,9 +27,6 @@
|
||||
|
||||
//var/sensor_mode = 0 //Determines the current HUD.
|
||||
|
||||
var/next_alarm_notice
|
||||
var/list/datum/alarm/queued_alarms = new()
|
||||
|
||||
hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD)
|
||||
|
||||
|
||||
@@ -46,6 +45,8 @@
|
||||
diag_hud_set_health()
|
||||
add_language("Galactic Common")
|
||||
init_subsystems()
|
||||
RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
|
||||
RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
|
||||
|
||||
/mob/living/silicon/med_hud_set_health()
|
||||
return //we use a different hud
|
||||
@@ -55,10 +56,93 @@
|
||||
|
||||
/mob/living/silicon/Destroy()
|
||||
GLOB.silicon_mob_list -= src
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.unregister(src)
|
||||
return ..()
|
||||
|
||||
/mob/living/silicon/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
|
||||
return
|
||||
|
||||
/mob/living/silicon/proc/queueAlarm(message, type, incoming = TRUE)
|
||||
var/in_cooldown = (alarms_to_show.len > 0 || alarms_to_clear.len > 0)
|
||||
if(incoming)
|
||||
alarms_to_show += message
|
||||
alarm_types_show[type] += 1
|
||||
else
|
||||
alarms_to_clear += message
|
||||
alarm_types_clear[type] += 1
|
||||
|
||||
if(in_cooldown)
|
||||
return
|
||||
|
||||
addtimer(CALLBACK(src, .proc/show_alarms), 3 SECONDS)
|
||||
|
||||
/mob/living/silicon/proc/show_alarms()
|
||||
if(alarms_to_show.len < 5)
|
||||
for(var/msg in alarms_to_show)
|
||||
to_chat(src, msg)
|
||||
else if(length(alarms_to_show))
|
||||
|
||||
var/list/msg = list("--- ")
|
||||
|
||||
if(alarm_types_show["Burglar"])
|
||||
msg += "BURGLAR: [alarm_types_show["Burglar"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Motion"])
|
||||
msg += "MOTION: [alarm_types_show["Motion"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Fire"])
|
||||
msg += "FIRE: [alarm_types_show["Fire"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Atmosphere"])
|
||||
msg += "ATMOSPHERE: [alarm_types_show["Atmosphere"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Power"])
|
||||
msg += "POWER: [alarm_types_show["Power"]] alarms detected. - "
|
||||
|
||||
if(alarm_types_show["Camera"])
|
||||
msg += "CAMERA: [alarm_types_show["Camera"]] alarms detected. - "
|
||||
|
||||
msg += "<A href=?src=[UID()];showalerts=1'>\[Show Alerts\]</a>"
|
||||
var/msg_text = msg.Join("")
|
||||
to_chat(src, msg_text)
|
||||
|
||||
if(alarms_to_clear.len < 3)
|
||||
for(var/msg in alarms_to_clear)
|
||||
to_chat(src, msg)
|
||||
|
||||
else if(alarms_to_clear.len)
|
||||
var/list/msg = list("--- ")
|
||||
|
||||
if(alarm_types_clear["Motion"])
|
||||
msg += "MOTION: [alarm_types_clear["Motion"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Fire"])
|
||||
msg += "FIRE: [alarm_types_clear["Fire"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Atmosphere"])
|
||||
msg += "ATMOSPHERE: [alarm_types_clear["Atmosphere"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_clear["Power"])
|
||||
msg += "POWER: [alarm_types_clear["Power"]] alarms cleared. - "
|
||||
|
||||
if(alarm_types_show["Camera"])
|
||||
msg += "CAMERA: [alarm_types_clear["Camera"]] alarms cleared. - "
|
||||
|
||||
msg += "<A href=?src=[UID()];showalerts=1'>\[Show Alerts\]</a>"
|
||||
|
||||
var/msg_text = msg.Join("")
|
||||
to_chat(src, msg_text)
|
||||
|
||||
|
||||
alarms_to_show.Cut()
|
||||
alarms_to_clear.Cut()
|
||||
for(var/key in alarm_types_show)
|
||||
alarm_types_show[key] = 0
|
||||
for(var/key in alarm_types_clear)
|
||||
alarm_types_clear[key] = 0
|
||||
|
||||
/mob/living/silicon/rename_character(oldname, newname)
|
||||
// we actually don't want it changing minds and stuff
|
||||
if(!newname)
|
||||
@@ -283,63 +367,6 @@
|
||||
if("Disable")
|
||||
to_chat(src, "Sensor augmentations disabled.")
|
||||
|
||||
/mob/living/silicon/proc/receive_alarm(var/datum/alarm_handler/alarm_handler, var/datum/alarm/alarm, was_raised)
|
||||
if(!next_alarm_notice)
|
||||
next_alarm_notice = world.time + 10 SECONDS
|
||||
|
||||
var/list/alarms = queued_alarms[alarm_handler]
|
||||
if(was_raised)
|
||||
// Raised alarms are always set
|
||||
alarms[alarm] = 1
|
||||
else
|
||||
// Alarms that were raised but then cleared before the next notice are instead removed
|
||||
if(alarm in alarms)
|
||||
alarms -= alarm
|
||||
// And alarms that have only been cleared thus far are set as such
|
||||
else
|
||||
alarms[alarm] = -1
|
||||
|
||||
/mob/living/silicon/proc/process_queued_alarms()
|
||||
if(next_alarm_notice && (world.time > next_alarm_notice))
|
||||
next_alarm_notice = 0
|
||||
|
||||
var/alarm_raised = 0
|
||||
for(var/datum/alarm_handler/AH in queued_alarms)
|
||||
var/list/alarms = queued_alarms[AH]
|
||||
var/reported = 0
|
||||
for(var/datum/alarm/A in alarms)
|
||||
if(alarms[A] == 1)
|
||||
if(!reported)
|
||||
reported = 1
|
||||
to_chat(src, "<span class='warning'>--- [AH.category] Detected ---</span>")
|
||||
raised_alarm(A)
|
||||
|
||||
for(var/datum/alarm_handler/AH in queued_alarms)
|
||||
var/list/alarms = queued_alarms[AH]
|
||||
var/reported = 0
|
||||
for(var/datum/alarm/A in alarms)
|
||||
if(alarms[A] == -1)
|
||||
if(!reported)
|
||||
reported = 1
|
||||
to_chat(src, "<span class='notice'>--- [AH.category] Cleared ---</span>")
|
||||
to_chat(src, "\The [A.alarm_name()].")
|
||||
|
||||
if(alarm_raised)
|
||||
to_chat(src, "<A HREF=?src=[UID()];showalerts=1>\[Show Alerts\]</A>")
|
||||
|
||||
for(var/datum/alarm_handler/AH in queued_alarms)
|
||||
var/list/alarms = queued_alarms[AH]
|
||||
alarms.Cut()
|
||||
|
||||
/mob/living/silicon/proc/raised_alarm(var/datum/alarm/A)
|
||||
to_chat(src, "[A.alarm_name()]!")
|
||||
|
||||
/mob/living/silicon/ai/raised_alarm(var/datum/alarm/A)
|
||||
var/cameratext = ""
|
||||
for(var/obj/machinery/camera/C in A.cameras())
|
||||
cameratext += "[(cameratext == "")? "" : "|"]<A HREF=?src=[UID()];switchcamera=\ref[C]>[C.c_tag]</A>"
|
||||
to_chat(src, "[A.alarm_name()]! ([(cameratext)? cameratext : "No Camera"])")
|
||||
|
||||
/mob/living/silicon/adjustToxLoss(var/amount)
|
||||
return STATUS_UPDATE_NONE
|
||||
|
||||
|
||||
@@ -3,11 +3,10 @@
|
||||
|
||||
/mob/living/silicon/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(..()) //if harm or disarm intent
|
||||
var/damage = rand(10, 20)
|
||||
var/damage = 20
|
||||
if(prob(90))
|
||||
playsound(loc, 'sound/weapons/slash.ogg', 25, 1, -1)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", "<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
if(prob(8))
|
||||
flash_eyes(affect_silicon = 1)
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
@@ -64,6 +63,6 @@
|
||||
else
|
||||
M.do_attack_animation(src, ATTACK_EFFECT_PUNCH)
|
||||
playsound(loc, 'sound/effects/bang.ogg', 10, 1)
|
||||
visible_message("<span class='danger'>[M] punches [src], but doesn't leave a dent.</span>", \
|
||||
"<span class='userdanger'>[M] punches [src], but doesn't leave a dent.!</span>")
|
||||
visible_message("<span class='notice'>[M] punches [src], but doesn't leave a dent.</span>", \
|
||||
"<span class='notice'>[M] punches [src], but doesn't leave a dent.</span>")
|
||||
return FALSE
|
||||
|
||||
@@ -8,13 +8,11 @@
|
||||
|
||||
/mob/living/silicon
|
||||
var/list/silicon_subsystems = list(
|
||||
/mob/living/silicon/proc/subsystem_alarm_monitor,
|
||||
/mob/living/silicon/proc/subsystem_law_manager
|
||||
)
|
||||
|
||||
/mob/living/silicon/ai
|
||||
silicon_subsystems = list(
|
||||
/mob/living/silicon/proc/subsystem_alarm_monitor,
|
||||
/mob/living/silicon/proc/subsystem_atmos_control,
|
||||
/mob/living/silicon/proc/subsystem_crew_monitor,
|
||||
/mob/living/silicon/proc/subsystem_law_manager,
|
||||
@@ -23,7 +21,6 @@
|
||||
|
||||
/mob/living/silicon/robot/drone
|
||||
silicon_subsystems = list(
|
||||
/mob/living/silicon/proc/subsystem_alarm_monitor,
|
||||
/mob/living/silicon/proc/subsystem_law_manager,
|
||||
/mob/living/silicon/proc/subsystem_power_monitor
|
||||
)
|
||||
@@ -32,30 +29,11 @@
|
||||
register_alarms = 0
|
||||
|
||||
/mob/living/silicon/proc/init_subsystems()
|
||||
alarm_monitor = new(src)
|
||||
atmos_control = new(src)
|
||||
crew_monitor = new(src)
|
||||
law_manager = new(src)
|
||||
power_monitor = new(src)
|
||||
|
||||
if(!register_alarms)
|
||||
return
|
||||
|
||||
var/list/register_to = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
|
||||
for(var/datum/alarm_handler/AH in register_to)
|
||||
AH.register(src, /mob/living/silicon/proc/receive_alarm)
|
||||
queued_alarms[AH] = list() // Makes sure alarms remain listed in consistent order
|
||||
alarm_handlers |= AH
|
||||
|
||||
/********************
|
||||
* Alarm Monitor *
|
||||
********************/
|
||||
/mob/living/silicon/proc/subsystem_alarm_monitor()
|
||||
set name = "Alarm Monitor"
|
||||
set category = "Subsystems"
|
||||
|
||||
alarm_monitor.ui_interact(usr, state = GLOB.self_state)
|
||||
|
||||
/********************
|
||||
* Atmos Control *
|
||||
********************/
|
||||
|
||||
@@ -42,13 +42,18 @@
|
||||
|
||||
/mob/living/simple_animal/attack_alien(mob/living/carbon/alien/humanoid/M)
|
||||
if(..()) //if harm or disarm intent.
|
||||
var/damage = rand(15, 30)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
attack_threshold_check(damage)
|
||||
return
|
||||
if(M.a_intent == INTENT_DISARM)
|
||||
playsound(loc, 'sound/weapons/pierce.ogg', 25, TRUE, -1)
|
||||
visible_message("<span class='danger'>[M] [response_disarm] [name]!</span>", "<span class='userdanger'>[M] [response_disarm] you!</span>")
|
||||
add_attack_logs(M, src, "Alien disarmed")
|
||||
else
|
||||
var/damage = rand(15, 30)
|
||||
visible_message("<span class='danger'>[M] has slashed at [src]!</span>", \
|
||||
"<span class='userdanger'>[M] has slashed at [src]!</span>")
|
||||
playsound(loc, 'sound/weapons/slice.ogg', 25, 1, -1)
|
||||
add_attack_logs(M, src, "Alien attacked")
|
||||
attack_threshold_check(damage)
|
||||
return TRUE
|
||||
|
||||
/mob/living/simple_animal/attack_larva(mob/living/carbon/alien/larva/L)
|
||||
if(..()) //successful larva bite
|
||||
|
||||
@@ -94,7 +94,6 @@
|
||||
hud_possible = list(DIAG_STAT_HUD, DIAG_BOT_HUD, DIAG_HUD, DIAG_PATH_HUD = HUD_LIST_LIST)//Diagnostic HUD views
|
||||
|
||||
/obj/item/radio/headset/bot
|
||||
subspace_transmission = 1
|
||||
requires_tcomms = FALSE
|
||||
canhear_range = 0
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
RegisterSignal(src, COMSIG_CROSSED_MOVABLE, .proc/human_squish_check)
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
unload(0)
|
||||
QDEL_NULL(wires)
|
||||
QDEL_NULL(cell)
|
||||
@@ -142,7 +143,7 @@
|
||||
if(open)
|
||||
icon_state="mulebot-hatch"
|
||||
else
|
||||
icon_state = "mulebot[!wires.MobAvoid()]"
|
||||
icon_state = "mulebot[wires.is_cut(WIRE_MOB_AVOIDANCE)]"
|
||||
overlays.Cut()
|
||||
if(load && !ismob(load))//buckling handles the mob offsets
|
||||
load.pixel_y = initial(load.pixel_y) + 9
|
||||
@@ -158,9 +159,9 @@
|
||||
qdel(src)
|
||||
if(2)
|
||||
for(var/i = 1; i < 3; i++)
|
||||
wires.RandomCut()
|
||||
wires.cut_random()
|
||||
if(3)
|
||||
wires.RandomCut()
|
||||
wires.cut_random()
|
||||
return
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/bullet_act(obj/item/projectile/Proj)
|
||||
@@ -169,7 +170,7 @@
|
||||
unload(0)
|
||||
if(prob(25))
|
||||
visible_message("<span class='danger'>Something shorts out inside [src]!</span>")
|
||||
wires.RandomCut()
|
||||
wires.cut_random()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/Topic(href, list/href_list)
|
||||
if(..())
|
||||
@@ -318,7 +319,7 @@
|
||||
|
||||
// returns true if the bot has power
|
||||
/mob/living/simple_animal/bot/mulebot/proc/has_power()
|
||||
return !open && cell && cell.charge > 0 && wires.HasPower()
|
||||
return !open && cell && cell.charge > 0 && !wires.is_cut(WIRE_MAIN_POWER1) && !wires.is_cut(WIRE_MAIN_POWER2)
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/proc/buzz(type)
|
||||
switch(type)
|
||||
@@ -362,7 +363,7 @@
|
||||
if(istype(AM,/obj/structure/closet/crate))
|
||||
CRATE = AM
|
||||
else
|
||||
if(wires.LoadCheck())
|
||||
if(!wires.is_cut(WIRE_LOADCHECK))
|
||||
buzz(SIGH)
|
||||
return // if not hacked, only allow crates to be loaded
|
||||
|
||||
@@ -459,8 +460,7 @@
|
||||
on = 0
|
||||
return
|
||||
if(on)
|
||||
var/speed = (wires.Motor1() ? 1 : 0) + (wires.Motor2() ? 2 : 0)
|
||||
// to_chat(world, "speed: [speed]")
|
||||
var/speed = (wires.is_cut(WIRE_MOTOR1) ? 1 : 0) + (wires.is_cut(WIRE_MOTOR2) ? 2 : 0)
|
||||
var/num_steps = 0
|
||||
switch(speed)
|
||||
if(0)
|
||||
@@ -624,7 +624,7 @@
|
||||
// not loaded
|
||||
if(auto_pickup) // find a crate
|
||||
var/atom/movable/AM
|
||||
if(wires.LoadCheck()) // if hacked, load first unanchored thing we find
|
||||
if(wires.is_cut(WIRE_LOADCHECK)) // if hacked, load first unanchored thing we find
|
||||
for(var/atom/movable/A in get_step(loc, loaddir))
|
||||
if(!A.anchored)
|
||||
AM = A
|
||||
@@ -672,7 +672,7 @@
|
||||
|
||||
// called when bot bumps into anything
|
||||
/mob/living/simple_animal/bot/mulebot/Bump(atom/obs)
|
||||
if(!wires.MobAvoid()) // usually just bumps, but if avoidance disabled knock over mobs
|
||||
if(wires.is_cut(WIRE_MOB_AVOIDANCE)) // usually just bumps, but if avoidance disabled knock over mobs
|
||||
var/mob/M = obs
|
||||
if(ismob(M))
|
||||
if(istype(M,/mob/living/silicon/robot))
|
||||
@@ -736,8 +736,8 @@
|
||||
..()
|
||||
|
||||
/mob/living/simple_animal/bot/mulebot/receive_signal(datum/signal/signal)
|
||||
if(!wires.RemoteRX() || ..())
|
||||
return 1
|
||||
if(wires.is_cut(WIRE_REMOTE_RX) || ..())
|
||||
return TRUE
|
||||
|
||||
var/recv = signal.data["command"]
|
||||
|
||||
@@ -772,7 +772,7 @@
|
||||
|
||||
// send a radio signal with multiple data key/values
|
||||
/mob/living/simple_animal/bot/mulebot/post_signal_multiple(var/freq, var/list/keyval)
|
||||
if(!wires.RemoteTX())
|
||||
if(wires.is_cut(WIRE_REMOTE_TX))
|
||||
return
|
||||
|
||||
..()
|
||||
@@ -803,7 +803,7 @@
|
||||
|
||||
//Update navigation data. Called when commanded to deliver, return home, or a route update is needed...
|
||||
/mob/living/simple_animal/bot/mulebot/proc/get_nav()
|
||||
if(!on || !wires.BeaconRX())
|
||||
if(!on || wires.is_cut(WIRE_BEACON_RX))
|
||||
return
|
||||
|
||||
for(var/obj/machinery/navbeacon/NB in GLOB.deliverybeacons)
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/purple/Life(seconds, times_fired)
|
||||
. = ..()
|
||||
if(.) // if mob is NOT dead
|
||||
if(stat != DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive.
|
||||
if(!degenerate && spider_myqueen)
|
||||
if(dcheck_counter >= 10)
|
||||
dcheck_counter = 0
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/queen/Life(seconds, times_fired)
|
||||
. = ..()
|
||||
if(.) // if mob is NOT dead
|
||||
if(stat != DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive.
|
||||
if(ckey && canlay < 12 && hasnested) // max 12 eggs worth stored at any one time, realistically that's tons.
|
||||
if(world.time > (spider_lastspawn + spider_spawnfrequency))
|
||||
if(eggslaid >= 20)
|
||||
|
||||
@@ -298,7 +298,7 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
|
||||
|
||||
/mob/living/simple_animal/hostile/poison/terror_spider/Life(seconds, times_fired)
|
||||
. = ..()
|
||||
if(!.) // if mob is dead
|
||||
if(stat == DEAD) // Can't use if(.) for this due to the fact it can sometimes return FALSE even when mob is alive.
|
||||
if(prob(2))
|
||||
// 2% chance every cycle to decompose
|
||||
visible_message("<span class='notice'>\The dead body of the [src] decomposes!</span>")
|
||||
|
||||
+16
-7
@@ -44,18 +44,27 @@
|
||||
usr.emote(message)
|
||||
|
||||
|
||||
/mob/proc/say_dead(var/message)
|
||||
if(!(client && client.holder))
|
||||
if(!config.dsay_allowed)
|
||||
to_chat(src, "<span class='danger'>Deadchat is globally muted.</span>")
|
||||
/mob/proc/say_dead(message)
|
||||
if(client)
|
||||
if(!client.holder)
|
||||
if(!config.dsay_allowed)
|
||||
to_chat(src, "<span class='danger'>Deadchat is globally muted.</span>")
|
||||
return
|
||||
|
||||
if(client.prefs.muted & MUTE_DEADCHAT)
|
||||
to_chat(src, "<span class='warning'>You cannot talk in deadchat (muted).</span>")
|
||||
return
|
||||
|
||||
if(client && !(client.prefs.toggles & CHAT_DEAD))
|
||||
to_chat(usr, "<span class='danger'>You have deadchat muted.</span>")
|
||||
return
|
||||
if(!(client.prefs.toggles & CHAT_DEAD))
|
||||
to_chat(src, "<span class='danger'>You have deadchat muted.</span>")
|
||||
return
|
||||
|
||||
if(client.handle_spam_prevention(message, MUTE_DEADCHAT))
|
||||
return
|
||||
|
||||
say_dead_direct("[pick("complains", "moans", "whines", "laments", "blubbers", "salts")], <span class='message'>\"[message]\"</span>", src)
|
||||
create_log(DEADCHAT_LOG, message)
|
||||
log_ghostsay(message, src)
|
||||
|
||||
/mob/proc/say_understands(var/mob/other, var/datum/language/speaking = null)
|
||||
if(stat == DEAD)
|
||||
|
||||
@@ -44,26 +44,37 @@
|
||||
|
||||
|
||||
|
||||
//human -> robot
|
||||
/mob/living/carbon/human/proc/Robotize()
|
||||
/**
|
||||
For transforming humans into robots (cyborgs).
|
||||
|
||||
Arguments:
|
||||
* cell_type: A type path of the cell the new borg should receive.
|
||||
* connect_to_default_AI: TRUE if you want /robot/New() to handle connecting the borg to the AI with the least borgs.
|
||||
* AI: A reference to the AI we want to connect to.
|
||||
*/
|
||||
/mob/living/carbon/human/proc/Robotize(cell_type = null, connect_to_default_AI = TRUE, mob/living/silicon/ai/AI = null)
|
||||
if(notransform)
|
||||
return
|
||||
for(var/obj/item/W in src)
|
||||
unEquip(W)
|
||||
regenerate_icons()
|
||||
|
||||
notransform = 1
|
||||
canmove = 0
|
||||
icon = null
|
||||
invisibility = 101
|
||||
for(var/t in bodyparts)
|
||||
qdel(t)
|
||||
for(var/i in internal_organs)
|
||||
qdel(i)
|
||||
|
||||
var/mob/living/silicon/robot/O = new /mob/living/silicon/robot( loc )
|
||||
// Creating a new borg here will connect them to a default AI and notify that AI, if `connect_to_default_AI` is TRUE.
|
||||
var/mob/living/silicon/robot/O = new /mob/living/silicon/robot(loc, connect_to_AI = connect_to_default_AI)
|
||||
|
||||
// cyborgs produced by Robotize get an automatic power cell
|
||||
O.cell = new /obj/item/stock_parts/cell/high(O)
|
||||
// If `AI` is passed in, we want to connect to that AI specifically.
|
||||
if(AI)
|
||||
O.lawupdate = TRUE
|
||||
O.connect_to_ai(AI)
|
||||
|
||||
if(!cell_type)
|
||||
O.cell = new /obj/item/stock_parts/cell/high(O)
|
||||
else
|
||||
O.cell = new cell_type(O)
|
||||
|
||||
O.gender = gender
|
||||
O.invisibility = 0
|
||||
@@ -77,9 +88,8 @@
|
||||
else
|
||||
O.key = key
|
||||
|
||||
O.loc = loc
|
||||
O.forceMove(loc)
|
||||
O.job = "Cyborg"
|
||||
O.notify_ai(1)
|
||||
|
||||
if(O.mind && O.mind.assigned_role == "Cyborg")
|
||||
if(O.mind.role_alt_title == "Robot")
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
/obj/machinery/modular_computer/console/preset/engineering/install_programs()
|
||||
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
|
||||
hard_drive.store_file(new/datum/computer_file/program/power_monitor())
|
||||
hard_drive.store_file(new/datum/computer_file/program/alarm_monitor())
|
||||
// hard_drive.store_file(new/datum/computer_file/program/alarm_monitor()) //TO-DO:TGUI--Uncomment Modular computers
|
||||
hard_drive.store_file(new/datum/computer_file/program/supermatter_monitor())
|
||||
|
||||
// ===== RESEARCH CONSOLE =====
|
||||
@@ -63,7 +63,6 @@
|
||||
var/obj/item/computer_hardware/hard_drive/hard_drive = cpu.all_components[MC_HDD]
|
||||
hard_drive.store_file(new/datum/computer_file/program/chatclient())
|
||||
hard_drive.store_file(new/datum/computer_file/program/card_mod())
|
||||
hard_drive.store_file(new/datum/computer_file/program/comm())
|
||||
|
||||
// ===== CIVILIAN CONSOLE =====
|
||||
/obj/machinery/modular_computer/console/preset/civilian
|
||||
|
||||
@@ -1,385 +0,0 @@
|
||||
/datum/computer_file/program/comm
|
||||
filename = "comm"
|
||||
filedesc = "Command and communications"
|
||||
program_icon_state = "comm"
|
||||
extended_desc = "Used to command and control the station. Can relay long-range communications. This program can not be run on tablet computers."
|
||||
required_access = ACCESS_HEADS
|
||||
requires_ntnet = 1
|
||||
size = 12
|
||||
usage_flags = PROGRAM_CONSOLE | PROGRAM_LAPTOP
|
||||
network_destination = "station long-range communication array"
|
||||
|
||||
var/authenticated = COMM_AUTHENTICATION_NONE
|
||||
var/list/messagetitle = list()
|
||||
var/list/messagetext = list()
|
||||
var/currmsg = 0
|
||||
var/aicurrmsg = 0
|
||||
var/menu_state = COMM_SCREEN_MAIN
|
||||
var/ai_menu_state = COMM_SCREEN_MAIN
|
||||
var/message_cooldown = 0
|
||||
var/centcomm_message_cooldown = 0
|
||||
var/tmp_alertlevel = 0
|
||||
|
||||
var/stat_msg1
|
||||
var/stat_msg2
|
||||
var/display_type="blank"
|
||||
|
||||
var/datum/announcement/priority/crew_announcement = new
|
||||
|
||||
/datum/computer_file/program/comm/New()
|
||||
GLOB.shuttle_caller_list += src
|
||||
..()
|
||||
crew_announcement.newscast = 0
|
||||
|
||||
/datum/computer_file/program/comm/Destroy()
|
||||
GLOB.shuttle_caller_list -= src
|
||||
SSshuttle.autoEvac()
|
||||
return ..()
|
||||
|
||||
/datum/computer_file/program/comm/proc/is_authenticated(mob/user, loud = 1)
|
||||
if(authenticated == COMM_AUTHENTICATION_MAX)
|
||||
return COMM_AUTHENTICATION_MAX
|
||||
else if(user.can_admin_interact())
|
||||
return COMM_AUTHENTICATION_MAX
|
||||
else if(authenticated)
|
||||
return COMM_AUTHENTICATION_MIN
|
||||
else
|
||||
if(loud)
|
||||
to_chat(user, "<span class='warning'>Access denied.</span>")
|
||||
return COMM_AUTHENTICATION_NONE
|
||||
|
||||
/datum/computer_file/program/comm/proc/change_security_level(mob/user, new_level)
|
||||
tmp_alertlevel = new_level
|
||||
var/old_level = GLOB.security_level
|
||||
if(!tmp_alertlevel)
|
||||
tmp_alertlevel = SEC_LEVEL_GREEN
|
||||
if(tmp_alertlevel < SEC_LEVEL_GREEN)
|
||||
tmp_alertlevel = SEC_LEVEL_GREEN
|
||||
if(tmp_alertlevel > SEC_LEVEL_BLUE)
|
||||
tmp_alertlevel = SEC_LEVEL_BLUE //Cannot engage delta with this
|
||||
set_security_level(tmp_alertlevel)
|
||||
if(GLOB.security_level != old_level)
|
||||
log_game("[key_name(user)] has changed the security level to [get_security_level()].")
|
||||
message_admins("[key_name_admin(user)] has changed the security level to [get_security_level()].")
|
||||
switch(GLOB.security_level)
|
||||
if(SEC_LEVEL_GREEN)
|
||||
feedback_inc("alert_comms_green", 1)
|
||||
if(SEC_LEVEL_BLUE)
|
||||
feedback_inc("alert_comms_blue", 1)
|
||||
tmp_alertlevel = 0
|
||||
|
||||
/datum/computer_file/program/comm/proc/setCurrentMessage(mob/user, value)
|
||||
if(isAI(user) || isrobot(user))
|
||||
aicurrmsg = value
|
||||
else
|
||||
currmsg = value
|
||||
|
||||
/datum/computer_file/program/comm/proc/getCurrentMessage(mob/user)
|
||||
if(isAI(user) || isrobot(user))
|
||||
return aicurrmsg
|
||||
else
|
||||
return currmsg
|
||||
|
||||
/datum/computer_file/program/comm/proc/setMenuState(mob/user, value)
|
||||
if(isAI(user) || isrobot(user))
|
||||
ai_menu_state=value
|
||||
else
|
||||
menu_state=value
|
||||
|
||||
/datum/computer_file/program/comm/proc/getMenuState(mob/user)
|
||||
if(isAI(user) || isrobot(user))
|
||||
return ai_menu_state
|
||||
else
|
||||
return menu_state
|
||||
|
||||
/datum/computer_file/program/comm/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui)
|
||||
if(!ui)
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
|
||||
assets.send(user)
|
||||
ui = new(user, src, ui_key, "comm_program.tmpl", "Command and communications program", 575, 500)
|
||||
ui.set_layout_key("program")
|
||||
ui.open()
|
||||
|
||||
/datum/computer_file/program/comm/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/list/data = get_header_data()
|
||||
data["is_ai"] = isAI(user) || isrobot(user)
|
||||
data["menu_state"] = data["is_ai"] ? ai_menu_state : menu_state
|
||||
data["emagged"] = computer ? computer.emagged : null
|
||||
data["authenticated"] = is_authenticated(user, 0)
|
||||
data["screen"] = getMenuState(usr)
|
||||
|
||||
data["stat_display"] = list(
|
||||
"type" = display_type,
|
||||
"line_1" = (stat_msg1 ? stat_msg1 : "-----"),
|
||||
"line_2" = (stat_msg2 ? stat_msg2 : "-----"),
|
||||
|
||||
"presets" = list(
|
||||
list("name" = "blank", "label" = "Clear", "desc" = "Blank slate"),
|
||||
list("name" = "shuttle", "label" = "Shuttle ETA", "desc" = "Display how much time is left."),
|
||||
list("name" = "message", "label" = "Message", "desc" = "A custom message.")
|
||||
),
|
||||
|
||||
"alerts"=list(
|
||||
list("alert" = "default", "label" = "Nanotrasen", "desc" = "Oh god."),
|
||||
list("alert" = "redalert", "label" = "Red Alert", "desc" = "Nothing to do with communists."),
|
||||
list("alert" = "lockdown", "label" = "Lockdown", "desc" = "Let everyone know they're on lockdown."),
|
||||
list("alert" = "biohazard", "label" = "Biohazard", "desc" = "Great for virus outbreaks and parties."),
|
||||
)
|
||||
)
|
||||
|
||||
data["security_level"] = GLOB.security_level
|
||||
data["str_security_level"] = capitalize(get_security_level())
|
||||
data["levels"] = list(
|
||||
list("id" = SEC_LEVEL_GREEN, "name" = "Green"),
|
||||
list("id" = SEC_LEVEL_BLUE, "name" = "Blue"),
|
||||
)
|
||||
|
||||
var/list/msg_data = list()
|
||||
for(var/i = 1; i <= messagetext.len; i++)
|
||||
msg_data.Add(list(list("title" = messagetitle[i], "body" = messagetext[i], "id" = i)))
|
||||
|
||||
data["messages"] = msg_data
|
||||
if((data["is_ai"] && aicurrmsg) || (!data["is_ai"] && currmsg))
|
||||
data["current_message"] = data["is_ai"] ? messagetext[aicurrmsg] : messagetext[currmsg]
|
||||
data["current_message_title"] = data["is_ai"] ? messagetitle[aicurrmsg] : messagetitle[currmsg]
|
||||
|
||||
data["lastCallLoc"] = SSshuttle.emergencyLastCallLoc ? format_text(SSshuttle.emergencyLastCallLoc.name) : null
|
||||
|
||||
var/shuttle[0]
|
||||
switch(SSshuttle.emergency.mode)
|
||||
if(SHUTTLE_IDLE, SHUTTLE_RECALL)
|
||||
shuttle["callStatus"] = 2 //#define
|
||||
else
|
||||
shuttle["callStatus"] = 1
|
||||
if(SSshuttle.emergency.mode == SHUTTLE_CALL)
|
||||
var/timeleft = SSshuttle.emergency.timeLeft()
|
||||
shuttle["eta"] = "[timeleft / 60 % 60]:[add_zero(num2text(timeleft % 60), 2)]"
|
||||
|
||||
data["shuttle"] = shuttle
|
||||
|
||||
return data
|
||||
|
||||
/datum/computer_file/program/comm/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
var/turf/T = get_turf(computer)
|
||||
if(!is_secure_level(T.z))
|
||||
to_chat(usr, "<span class='warning'>Unable to establish a connection: You're too far away from the station!</span>")
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_login"])
|
||||
if(!ishuman(usr))
|
||||
to_chat(usr, "<span class='warning'>Access denied.</span>")
|
||||
return
|
||||
|
||||
var/list/access = usr.get_access()
|
||||
if(ACCESS_HEADS in access)
|
||||
authenticated = COMM_AUTHENTICATION_MIN
|
||||
|
||||
if(ACCESS_CAPTAIN in access)
|
||||
authenticated = COMM_AUTHENTICATION_MAX
|
||||
var/mob/living/carbon/human/H = usr
|
||||
var/obj/item/card/id = H.get_idcard(TRUE)
|
||||
if(istype(id))
|
||||
crew_announcement.announcer = GetNameAndAssignmentFromId(id)
|
||||
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
|
||||
if(href_list["PRG_logout"])
|
||||
authenticated = COMM_AUTHENTICATION_NONE
|
||||
crew_announcement.announcer = ""
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
|
||||
if(is_authenticated(usr))
|
||||
switch(href_list["PRG_operation"])
|
||||
if("main")
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
|
||||
if("changeseclevel")
|
||||
setMenuState(usr,COMM_SCREEN_SECLEVEL)
|
||||
|
||||
if("newalertlevel")
|
||||
if(isAI(usr) || isrobot(usr))
|
||||
to_chat(usr, "<span class='warning'>Firewalls prevent you from changing the alert level.</span>")
|
||||
return 1
|
||||
else if(usr.can_admin_interact())
|
||||
change_security_level(usr, text2num(href_list["level"]))
|
||||
return 1
|
||||
else if(!ishuman(usr))
|
||||
to_chat(usr, "<span class='warning'>Security measures prevent you from changing the alert level.</span>")
|
||||
return 1
|
||||
|
||||
var/mob/living/carbon/human/L = usr
|
||||
var/obj/item/card = L.get_active_hand()
|
||||
var/obj/item/card/id/I = (card && card.GetID()) || L.wear_id || L.wear_pda
|
||||
if(istype(I, /obj/item/pda))
|
||||
var/obj/item/pda/pda = I
|
||||
I = pda.id
|
||||
if(I && istype(I))
|
||||
if(ACCESS_CAPTAIN in I.access)
|
||||
change_security_level(usr, text2num(href_list["level"]))
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>You are not authorized to do this.</span>")
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>You need to swipe your ID.</span>")
|
||||
|
||||
if("announce")
|
||||
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
|
||||
if(message_cooldown)
|
||||
to_chat(usr, "<span class='warning'>Please allow at least one minute to pass between announcements.</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
var/input = input(usr, "Please write a message to announce to the station crew.", "Priority Announcement")
|
||||
if(!input || message_cooldown || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
crew_announcement.Announce(input)
|
||||
message_cooldown = 1
|
||||
spawn(600)//One minute cooldown
|
||||
message_cooldown = 0
|
||||
|
||||
if("callshuttle")
|
||||
var/input = clean_input("Please enter the reason for calling the shuttle.", "Shuttle Call Reason.","")
|
||||
if(!input || ..() || !is_authenticated(usr))
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
|
||||
call_shuttle_proc(usr, input)
|
||||
if(SSshuttle.emergency.timer)
|
||||
post_status("shuttle")
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
|
||||
if("cancelshuttle")
|
||||
if(isAI(usr) || isrobot(usr))
|
||||
to_chat(usr, "<span class='warning'>Firewalls prevent you from recalling the shuttle.</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
var/response = alert("Are you sure you wish to recall the shuttle?", "Confirm", "Yes", "No")
|
||||
if(response == "Yes")
|
||||
cancel_call_proc(usr)
|
||||
if(SSshuttle.emergency.timer)
|
||||
post_status("shuttle")
|
||||
setMenuState(usr, COMM_SCREEN_MAIN)
|
||||
|
||||
if("messagelist")
|
||||
currmsg = 0
|
||||
if(href_list["msgid"])
|
||||
setCurrentMessage(usr, text2num(href_list["msgid"]))
|
||||
setMenuState(usr,COMM_SCREEN_MESSAGES)
|
||||
|
||||
if("delmessage")
|
||||
if(href_list["msgid"])
|
||||
currmsg = text2num(href_list["msgid"])
|
||||
var/response = alert("Are you sure you wish to delete this message?", "Confirm", "Yes", "No")
|
||||
if(response == "Yes")
|
||||
if(currmsg)
|
||||
var/id = getCurrentMessage()
|
||||
var/title = messagetitle[id]
|
||||
var/text = messagetext[id]
|
||||
messagetitle.Remove(title)
|
||||
messagetext.Remove(text)
|
||||
if(currmsg == id)
|
||||
currmsg = 0
|
||||
if(aicurrmsg == id)
|
||||
aicurrmsg = 0
|
||||
setMenuState(usr,COMM_SCREEN_MESSAGES)
|
||||
|
||||
if("status")
|
||||
setMenuState(usr,COMM_SCREEN_STAT)
|
||||
|
||||
// Status display stuff
|
||||
if("setstat")
|
||||
display_type=href_list["statdisp"]
|
||||
switch(display_type)
|
||||
if("message")
|
||||
post_status("message", stat_msg1, stat_msg2, usr)
|
||||
if("alert")
|
||||
post_status("alert", href_list["alert"], user = usr)
|
||||
else
|
||||
post_status(href_list["statdisp"], user = usr)
|
||||
setMenuState(usr, COMM_SCREEN_STAT)
|
||||
|
||||
if("setmsg1")
|
||||
stat_msg1 = clean_input("Line 1", "Enter Message Text", stat_msg1)
|
||||
setMenuState(usr, COMM_SCREEN_STAT)
|
||||
|
||||
if("setmsg2")
|
||||
stat_msg2 = clean_input("Line 2", "Enter Message Text", stat_msg2)
|
||||
setMenuState(usr, COMM_SCREEN_STAT)
|
||||
|
||||
if("nukerequest")
|
||||
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
|
||||
if(centcomm_message_cooldown)
|
||||
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
var/input = stripped_input(usr, "Please enter the reason for requesting the nuclear self-destruct codes. Misuse of the nuclear request system will not be tolerated under any circumstances. Transmission does not guarantee a response.", "Self Destruct Code Request.","")
|
||||
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
Nuke_request(input, usr)
|
||||
to_chat(usr, "<span class='notice'>Request sent.</span>")
|
||||
log_game("[key_name(usr)] has requested the nuclear codes from Centcomm")
|
||||
GLOB.priority_announcement.Announce("The codes for the on-station nuclear self-destruct have been requested by [usr]. Confirmation or denial of this request will be sent shortly.", "Nuclear Self Destruct Codes Requested",'sound/AI/commandreport.ogg')
|
||||
centcomm_message_cooldown = 1
|
||||
spawn(6000)//10 minute cooldown
|
||||
centcomm_message_cooldown = 0
|
||||
setMenuState(usr,COMM_SCREEN_MAIN)
|
||||
|
||||
if("MessageCentcomm")
|
||||
if(is_authenticated(usr) == COMM_AUTHENTICATION_MAX)
|
||||
if(centcomm_message_cooldown)
|
||||
to_chat(usr, "<span class='warning'>Arrays recycling. Please stand by.</span>")
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
var/input = stripped_input(usr, "Please choose a message to transmit to Centcomm via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "")
|
||||
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
Centcomm_announce(input, usr)
|
||||
to_chat(usr, "Message transmitted.")
|
||||
log_game("[key_name(usr)] has made a Centcomm announcement: [input]")
|
||||
centcomm_message_cooldown = 1
|
||||
spawn(6000)//10 minute cooldown
|
||||
centcomm_message_cooldown = 0
|
||||
setMenuState(usr,COMM_SCREEN_MAIN)
|
||||
|
||||
// OMG SYNDICATE ...LETTERHEAD
|
||||
if("MessageSyndicate")
|
||||
if((is_authenticated(usr) == COMM_AUTHENTICATION_MAX) && (computer && computer.emagged))
|
||||
if(centcomm_message_cooldown)
|
||||
to_chat(usr, "Arrays recycling. Please stand by.")
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
var/input = stripped_input(usr, "Please choose a message to transmit to \[ABNORMAL ROUTING CORDINATES\] via quantum entanglement. Please be aware that this process is very expensive, and abuse will lead to... termination. Transmission does not guarantee a response.", "To abort, send an empty message.", "")
|
||||
if(!input || ..() || !(is_authenticated(usr) == COMM_AUTHENTICATION_MAX))
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
Syndicate_announce(input, usr)
|
||||
to_chat(usr, "Message transmitted.")
|
||||
log_game("[key_name(usr)] has made a Syndicate announcement: [input]")
|
||||
centcomm_message_cooldown = 1
|
||||
spawn(6000)//10 minute cooldown
|
||||
centcomm_message_cooldown = 0
|
||||
setMenuState(usr,COMM_SCREEN_MAIN)
|
||||
|
||||
if("RestartNanoMob")
|
||||
if(SSmob_hunt)
|
||||
if(SSmob_hunt.manual_reboot())
|
||||
var/loading_msg = pick("Respawning spawns", "Reticulating splines", "Flipping hat",
|
||||
"Capturing all of them", "Fixing minor text issues", "Being the very best",
|
||||
"Nerfing this", "Not communicating with playerbase", "Coding a ripoff in a 2D spaceman game")
|
||||
to_chat(usr, "<span class='notice'>Restarting Nano-Mob Hunter GO! game server. [loading_msg]...</span>")
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>Nano-Mob Hunter GO! game server reboot failed due to recent restart. Please wait before re-attempting.</span>")
|
||||
else
|
||||
to_chat(usr, "<span class='danger'>Nano-Mob Hunter GO! game server is offline for extended maintenance. Contact your Central Command administrators for more info if desired.</span>")
|
||||
|
||||
SSnanoui.update_uis(src)
|
||||
return 1
|
||||
@@ -7,65 +7,71 @@
|
||||
requires_ntnet = 1
|
||||
network_destination = "alarm monitoring network"
|
||||
size = 5
|
||||
var/list/datum/alarm_handler/alarm_handlers
|
||||
var/tgui_id = "NtosStationAlertConsole"
|
||||
var/ui_x = 315
|
||||
var/ui_y = 500
|
||||
var/has_alert = 0
|
||||
var/list/alarms_listend_for = list("Fire", "Atmosphere", "Power")
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/New()
|
||||
/datum/computer_file/program/alarm_monitor/process_tick()
|
||||
..()
|
||||
alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm)
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.register(src, /datum/computer_file/program/alarm_monitor/proc/update_icon)
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/Destroy()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.unregister(src)
|
||||
QDEL_NULL(alarm_handlers)
|
||||
return ..()
|
||||
if(has_alert)
|
||||
program_icon_state = "alert-red"
|
||||
ui_header = "alarm_red.gif"
|
||||
update_computer_icon()
|
||||
else
|
||||
program_icon_state = "alert-green"
|
||||
ui_header = "alarm_green.gif"
|
||||
update_computer_icon()
|
||||
return TRUE
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/proc/update_icon()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
if(AH.has_major_alarms())
|
||||
program_icon_state = "alert-red"
|
||||
ui_header = "alarm_red.gif"
|
||||
update_computer_icon()
|
||||
return 1
|
||||
program_icon_state = "alert-green"
|
||||
ui_header = "alarm_green.gif"
|
||||
update_computer_icon()
|
||||
return 0
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
var/datum/asset/assets = get_asset_datum(/datum/asset/simple/headers)
|
||||
assets.send(user)
|
||||
ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring", 575, 700)
|
||||
ui.set_auto_update(1)
|
||||
ui.set_layout_key("program")
|
||||
ui.open()
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/ui_data(mob/user)
|
||||
/datum/computer_file/program/alarm_monitor/tgui_data(mob/user)
|
||||
var/list/data = get_header_data()
|
||||
|
||||
var/categories[0]
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
categories[++categories.len] = list("category" = AH.category, "alarms" = list())
|
||||
for(var/datum/alarm/A in AH.major_alarms())
|
||||
var/cameras[0]
|
||||
var/lost_sources[0]
|
||||
|
||||
if(isAI(user))
|
||||
for(var/obj/machinery/camera/C in A.cameras())
|
||||
cameras[++cameras.len] = C.nano_structure()
|
||||
for(var/datum/alarm_source/AS in A.sources)
|
||||
if(!AS.source)
|
||||
lost_sources[++lost_sources.len] = AS.source_name
|
||||
|
||||
categories[categories.len]["alarms"] += list(list(
|
||||
"name" = sanitize(A.alarm_name()),
|
||||
"origin_lost" = A.origin == null,
|
||||
"has_cameras" = cameras.len,
|
||||
"cameras" = cameras,
|
||||
"lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
|
||||
data["categories"] = categories
|
||||
data["alarms"] = list()
|
||||
for(var/class in SSalarm.alarms)
|
||||
if(!(class in alarms_listend_for))
|
||||
continue
|
||||
data["alarms"][class] = list()
|
||||
for(var/area in alarms[class])
|
||||
data["alarms"][class] += area
|
||||
|
||||
return data
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/proc/alarm_triggered(src, class, area/A, list/O, obj/alarmsource)
|
||||
if(is_station_level(alarmsource.z))
|
||||
if(!(A.type in GLOB.the_station_areas))
|
||||
return
|
||||
else if(!is_mining_level(alarmsource.z) || istype(A, /area/ruin))
|
||||
return
|
||||
update_alarm_display()
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/proc/alarm_cancelled(src, class, area/A, obj/origin, cleared)
|
||||
if(is_station_level(origin.z))
|
||||
if(!(A.type in GLOB.the_station_areas))
|
||||
return
|
||||
else if(!is_mining_level(origin.z) || istype(A, /area/ruin))
|
||||
return
|
||||
update_alarm_display()
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/proc/update_alarm_display()
|
||||
has_alert = FALSE
|
||||
for(var/cat in alarms)
|
||||
if(!(cat in alarms_listend_for))
|
||||
continue
|
||||
var/list/L = alarms[cat]
|
||||
if(length(L))
|
||||
has_alert = TRUE
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/run_program(mob/user)
|
||||
. = ..(user)
|
||||
GLOB.alarmdisplay += src
|
||||
RegisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM, .proc/alarm_triggered)
|
||||
RegisterSignal(SSalarm, COMSIG_CANCELLED_ALARM, .proc/alarm_cancelled)
|
||||
|
||||
/datum/computer_file/program/alarm_monitor/kill_program(forced = FALSE)
|
||||
GLOB.alarmdisplay -= src
|
||||
UnregisterSignal(SSalarm, COMSIG_TRIGGERED_ALARM)
|
||||
UnregisterSignal(SSalarm, COMSIG_CANCELLED_ALARM)
|
||||
..()
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
/datum/nano_module/alarm_monitor
|
||||
name = "Alarm monitor"
|
||||
var/list_cameras = 0 // Whether or not to list camera references. A future goal would be to merge this with the enginering/security camera console. Currently really only for AI-use.
|
||||
var/list/datum/alarm_handler/alarm_handlers // The particular list of alarm handlers this alarm monitor should present to the user.
|
||||
|
||||
/datum/nano_module/alarm_monitor/all/New()
|
||||
..()
|
||||
alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.fire_alarm, SSalarms.motion_alarm, SSalarms.power_alarm)
|
||||
|
||||
/datum/nano_module/alarm_monitor/engineering/New()
|
||||
..()
|
||||
alarm_handlers = list(SSalarms.atmosphere_alarm, SSalarms.fire_alarm, SSalarms.power_alarm)
|
||||
|
||||
/datum/nano_module/alarm_monitor/security/New()
|
||||
..()
|
||||
alarm_handlers = list(SSalarms.burglar_alarm, SSalarms.camera_alarm, SSalarms.motion_alarm)
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/register(var/object, var/procName)
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.register(object, procName)
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/unregister(var/object)
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
AH.unregister(object)
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/all_alarms()
|
||||
var/list/all_alarms = new()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
all_alarms += AH.alarms
|
||||
|
||||
return all_alarms
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/major_alarms()
|
||||
var/list/all_alarms = new()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
all_alarms += AH.major_alarms()
|
||||
|
||||
return all_alarms
|
||||
|
||||
/datum/nano_module/alarm_monitor/proc/minor_alarms()
|
||||
var/list/all_alarms = new()
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
all_alarms += AH.minor_alarms()
|
||||
|
||||
return all_alarms
|
||||
|
||||
/datum/nano_module/alarm_monitor/Topic(ref, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
if(href_list["switchTo"])
|
||||
var/obj/machinery/camera/C = locate(href_list["switchTo"]) in GLOB.cameranet.cameras
|
||||
if(!C || !isAI(usr))
|
||||
return
|
||||
|
||||
usr.switch_to_camera(C)
|
||||
return 1
|
||||
|
||||
/datum/nano_module/alarm_monitor/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.default_state)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "alarm_monitor.tmpl", "Alarm Monitoring Console", 800, 800, state = state)
|
||||
ui.open()
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/datum/nano_module/alarm_monitor/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
|
||||
var/categories[0]
|
||||
for(var/datum/alarm_handler/AH in alarm_handlers)
|
||||
categories[++categories.len] = list("category" = AH.category, "alarms" = list())
|
||||
for(var/datum/alarm/A in AH.major_alarms())
|
||||
var/cameras[0]
|
||||
var/lost_sources[0]
|
||||
|
||||
if(isAI(user))
|
||||
for(var/obj/machinery/camera/C in A.cameras())
|
||||
cameras[++cameras.len] = C.nano_structure()
|
||||
for(var/datum/alarm_source/AS in A.sources)
|
||||
if(!AS.source)
|
||||
lost_sources[++lost_sources.len] = AS.source_name
|
||||
|
||||
categories[categories.len]["alarms"] += list(list(
|
||||
"name" = sanitize(A.alarm_name()),
|
||||
"origin_lost" = A.origin == null,
|
||||
"has_cameras" = cameras.len,
|
||||
"cameras" = cameras,
|
||||
"lost_sources" = lost_sources.len ? sanitize(english_list(lost_sources, nothing_text = "", and_text = ", ")) : ""))
|
||||
data["categories"] = categories
|
||||
|
||||
return data
|
||||
@@ -12,24 +12,32 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
insert_anim = "faxsend"
|
||||
pass_flags = PASSTABLE
|
||||
var/fax_network = "Local Fax Network"
|
||||
var/syndie_restricted = FALSE //is it a syndicate base fax restricted from contacting NT assets?
|
||||
/// If true, prevents fax machine from sending messages to NT machines
|
||||
var/syndie_restricted = FALSE
|
||||
|
||||
var/long_range_enabled = 0 // Can we send messages off the station?
|
||||
/// Can we send messages off-station?
|
||||
var/long_range_enabled = FALSE
|
||||
req_one_access = list(ACCESS_LAWYER, ACCESS_HEADS, ACCESS_ARMORY)
|
||||
|
||||
use_power = IDLE_POWER_USE
|
||||
idle_power_usage = 30
|
||||
active_power_usage = 200
|
||||
|
||||
var/obj/item/card/id/scan = null // identification
|
||||
/// ID card inserted into the machine, used to log in with
|
||||
var/obj/item/card/id/scan = null
|
||||
|
||||
var/authenticated = 0
|
||||
var/sendcooldown = 0 // to avoid spamming fax messages
|
||||
/// Whether the machine is "logged in" or not
|
||||
var/authenticated = FALSE
|
||||
/// Next world.time at which this fax machine can send a message to CC/syndicate
|
||||
var/sendcooldown = 0
|
||||
/// After sending a message to CC/syndicate, cannot send another to them for this many deciseconds
|
||||
var/cooldown_time = 1800
|
||||
|
||||
var/department = "Unknown" // our department
|
||||
/// Our department, determines whether this machine gets faxes sent to a department
|
||||
var/department = "Unknown"
|
||||
|
||||
var/destination = "Not Selected" // the department we're sending to
|
||||
/// Target department to send outgoing faxes to
|
||||
var/destination
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/New()
|
||||
..()
|
||||
@@ -44,7 +52,7 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
/obj/machinery/photocopier/faxmachine/longrange
|
||||
name = "long range fax machine"
|
||||
fax_network = "Central Command Quantum Entanglement Network"
|
||||
long_range_enabled = 1
|
||||
long_range_enabled = TRUE
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/longrange/syndie
|
||||
name = "syndicate long range fax machine"
|
||||
@@ -58,17 +66,17 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
GLOB.hidden_departments |= department
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/attack_hand(mob/user)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/attack_ghost(mob/user)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/attackby(obj/item/item, mob/user, params)
|
||||
if(istype(item,/obj/item/card/id) && !scan)
|
||||
scan(item)
|
||||
else if(istype(item, /obj/item/paper) || istype(item, /obj/item/photo) || istype(item, /obj/item/paper_bundle))
|
||||
..()
|
||||
SSnanoui.update_uis(src)
|
||||
SStgui.update_uis(src)
|
||||
else
|
||||
return ..()
|
||||
|
||||
@@ -80,42 +88,6 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
else
|
||||
to_chat(user, "<span class='warning'>You swipe the card through [src], but nothing happens.</span>")
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "faxmachine.tmpl", "Fax Machine UI", 540, 450)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
var/is_authenticated = is_authenticated(user)
|
||||
|
||||
if(scan)
|
||||
data["scan_name"] = scan.name
|
||||
else
|
||||
data["scan_name"] = "-----"
|
||||
data["authenticated"] = is_authenticated
|
||||
if(!is_authenticated)
|
||||
data["network"] = "Disconnected"
|
||||
else if(!emagged)
|
||||
data["network"] = fax_network
|
||||
else
|
||||
data["network"] = "ERR*?*%!*"
|
||||
if(copyitem)
|
||||
data["paper"] = copyitem.name
|
||||
data["paperinserted"] = 1
|
||||
else
|
||||
data["paper"] = "-----"
|
||||
data["paperinserted"] = 0
|
||||
data["destination"] = destination
|
||||
data["cooldown"] = sendcooldown
|
||||
if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments))
|
||||
data["respectcooldown"] = 1
|
||||
else
|
||||
data["respectcooldown"] = 0
|
||||
|
||||
return data
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/proc/is_authenticated(mob/user)
|
||||
if(authenticated)
|
||||
return TRUE
|
||||
@@ -123,87 +95,134 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/Topic(href, href_list)
|
||||
if(..())
|
||||
return 1
|
||||
/obj/machinery/photocopier/faxmachine/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
ui = new(user, src, ui_key, "FaxMachine", name, 540, 300, master_ui, state)
|
||||
ui.open()
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["authenticated"] = is_authenticated(user)
|
||||
data["scan_name"] = scan ? scan.name : FALSE
|
||||
if(!data["authenticated"])
|
||||
data["network"] = "Disconnected"
|
||||
else if(!emagged)
|
||||
data["network"] = fax_network
|
||||
else
|
||||
data["network"] = "ERR*?*%!*"
|
||||
data["paper"] = copyitem ? copyitem.name : FALSE
|
||||
data["paperinserted"] = copyitem ? TRUE : FALSE
|
||||
data["destination"] = destination ? destination : FALSE
|
||||
data["sendError"] = FALSE
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
data["sendError"] = "No Power"
|
||||
else if(!data["authenticated"])
|
||||
data["sendError"] = "Not Logged In"
|
||||
else if(!data["paper"])
|
||||
data["sendError"] = "Nothing Inserted"
|
||||
else if(!data["destination"])
|
||||
data["sendError"] = "Destination Not Set"
|
||||
else if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments))
|
||||
var/cooldown_seconds = cooldown_seconds()
|
||||
if(cooldown_seconds)
|
||||
data["sendError"] = "Re-aligning in [cooldown_seconds] seconds..."
|
||||
return data
|
||||
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
var/is_authenticated = is_authenticated(usr)
|
||||
if(href_list["send"])
|
||||
if(copyitem && is_authenticated)
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("scan") // insert/remove your ID card
|
||||
scan()
|
||||
if("auth") // log in/out
|
||||
if(!is_authenticated && scan)
|
||||
if(scan.registered_name in GLOB.fax_blacklist)
|
||||
to_chat(usr, "<span class='warning'>Login rejected: individual is blacklisted from fax network.</span>")
|
||||
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||
. = FALSE
|
||||
else if(check_access(scan))
|
||||
authenticated = TRUE
|
||||
else // ID doesn't have access to this machine
|
||||
to_chat(usr, "<span class='warning'>Login rejected: ID card does not have required access.</span>")
|
||||
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||
. = FALSE
|
||||
else if(is_authenticated)
|
||||
authenticated = FALSE
|
||||
if("paper") // insert/eject paper/paperbundle/photo
|
||||
if(copyitem)
|
||||
copyitem.forceMove(get_turf(src))
|
||||
if(ishuman(usr))
|
||||
if(!usr.get_active_hand() && Adjacent(usr))
|
||||
usr.put_in_hands(copyitem)
|
||||
to_chat(usr, "<span class='notice'>You eject [copyitem] from [src].</span>")
|
||||
copyitem = null
|
||||
else
|
||||
var/obj/item/I = usr.get_active_hand()
|
||||
if(istype(I, /obj/item/paper) || istype(I, /obj/item/photo) || istype(I, /obj/item/paper_bundle))
|
||||
usr.drop_item()
|
||||
copyitem = I
|
||||
I.forceMove(src)
|
||||
to_chat(usr, "<span class='notice'>You insert [I] into [src].</span>")
|
||||
flick(insert_anim, src)
|
||||
else
|
||||
to_chat(usr, "<span class='warning'>[src] only accepts paper, paper bundles, and photos.</span>")
|
||||
. = FALSE
|
||||
if("rename") // rename the item that is currently in the fax machine
|
||||
if(copyitem)
|
||||
var/n_name = sanitize(copytext(input(usr, "What would you like to label the fax?", "Fax Labelling", copyitem.name) as text, 1, MAX_MESSAGE_LEN))
|
||||
if((copyitem && copyitem.loc == src && usr.stat == 0))
|
||||
if(istype(copyitem, /obj/item/paper))
|
||||
copyitem.name = "[(n_name ? text("[n_name]") : initial(copyitem.name))]"
|
||||
copyitem.desc = "This is a paper titled '" + copyitem.name + "'."
|
||||
else if(istype(copyitem, /obj/item/photo))
|
||||
copyitem.name = "[(n_name ? text("[n_name]") : "photo")]"
|
||||
else if(istype(copyitem, /obj/item/paper_bundle))
|
||||
copyitem.name = "[(n_name ? text("[n_name]") : "paper")]"
|
||||
else
|
||||
. = FALSE
|
||||
else
|
||||
. = FALSE
|
||||
else
|
||||
. = FALSE
|
||||
if("dept") // choose which department receives the fax
|
||||
if(is_authenticated)
|
||||
var/lastdestination = destination
|
||||
var/list/combineddepartments = GLOB.alldepartments.Copy()
|
||||
if(long_range_enabled)
|
||||
combineddepartments += GLOB.admin_departments.Copy()
|
||||
if(emagged)
|
||||
combineddepartments += GLOB.hidden_admin_departments.Copy()
|
||||
combineddepartments += GLOB.hidden_departments.Copy()
|
||||
if(syndie_restricted)
|
||||
combineddepartments = GLOB.hidden_admin_departments.Copy()
|
||||
combineddepartments += GLOB.hidden_departments.Copy()
|
||||
for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes)
|
||||
if(F.emagged)//we can contact emagged faxes on the station
|
||||
combineddepartments |= F.department
|
||||
destination = input(usr, "To which department?", "Choose a department", "") as null|anything in combineddepartments
|
||||
if(!destination)
|
||||
destination = lastdestination
|
||||
if("send") // actually send the fax
|
||||
if(!copyitem || !is_authenticated || !destination)
|
||||
return
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return
|
||||
if((destination in GLOB.admin_departments) || (destination in GLOB.hidden_admin_departments))
|
||||
var/cooldown_seconds = cooldown_seconds()
|
||||
if(cooldown_seconds > 0)
|
||||
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||
to_chat(usr, "<span class='warning'>[src] is not ready for another [cooldown_seconds] seconds.</span>")
|
||||
return
|
||||
send_admin_fax(usr, destination)
|
||||
sendcooldown = world.time + cooldown_time
|
||||
else
|
||||
sendfax(destination, usr)
|
||||
|
||||
if(sendcooldown)
|
||||
spawn(sendcooldown) // cooldown time
|
||||
sendcooldown = 0
|
||||
SSnanoui.update_uis(src)
|
||||
|
||||
if(href_list["paper"])
|
||||
if(copyitem)
|
||||
copyitem.forceMove(get_turf(src))
|
||||
if(ishuman(usr))
|
||||
if(!usr.get_active_hand() && Adjacent(usr))
|
||||
usr.put_in_hands(copyitem)
|
||||
to_chat(usr, "<span class='notice'>You eject \the [copyitem] from \the [src].</span>")
|
||||
copyitem = null
|
||||
else
|
||||
var/obj/item/I = usr.get_active_hand()
|
||||
if(istype(I, /obj/item/paper) || istype(I, /obj/item/photo) || istype(I, /obj/item/paper_bundle))
|
||||
usr.drop_item()
|
||||
copyitem = I
|
||||
I.forceMove(src)
|
||||
to_chat(usr, "<span class='notice'>You insert \the [I] into \the [src].</span>")
|
||||
flick(insert_anim, src)
|
||||
|
||||
if(href_list["scan"])
|
||||
scan()
|
||||
|
||||
if(href_list["dept"])
|
||||
if(is_authenticated)
|
||||
var/lastdestination = destination
|
||||
var/list/combineddepartments = GLOB.alldepartments.Copy()
|
||||
if(long_range_enabled)
|
||||
combineddepartments += GLOB.admin_departments.Copy()
|
||||
|
||||
if(emagged)
|
||||
combineddepartments += GLOB.hidden_admin_departments.Copy()
|
||||
combineddepartments += GLOB.hidden_departments.Copy()
|
||||
|
||||
if(syndie_restricted)
|
||||
combineddepartments = GLOB.hidden_admin_departments.Copy()
|
||||
combineddepartments += GLOB.hidden_departments.Copy()
|
||||
for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes)
|
||||
if(F.emagged)//we can contact emagged faxes on the station
|
||||
combineddepartments |= F.department
|
||||
|
||||
destination = input(usr, "To which department?", "Choose a department", "") as null|anything in combineddepartments
|
||||
if(!destination)
|
||||
destination = lastdestination
|
||||
|
||||
if(href_list["auth"])
|
||||
if(!is_authenticated && scan)
|
||||
if(scan.registered_name in GLOB.fax_blacklist)
|
||||
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 0)
|
||||
else if(check_access(scan))
|
||||
authenticated = 1
|
||||
else if(is_authenticated)
|
||||
authenticated = 0
|
||||
|
||||
if(href_list["rename"])
|
||||
if(copyitem)
|
||||
var/n_name = sanitize(copytext(input(usr, "What would you like to label the fax?", "Fax Labelling", copyitem.name) as text, 1, MAX_MESSAGE_LEN))
|
||||
if((copyitem && copyitem.loc == src && usr.stat == 0))
|
||||
if(istype(copyitem, /obj/item/paper))
|
||||
copyitem.name = "[(n_name ? text("[n_name]") : initial(copyitem.name))]"
|
||||
copyitem.desc = "This is a paper titled '" + copyitem.name + "'."
|
||||
else if(istype(copyitem, /obj/item/photo))
|
||||
copyitem.name = "[(n_name ? text("[n_name]") : "photo")]"
|
||||
else if(istype(copyitem, /obj/item/paper_bundle))
|
||||
copyitem.name = "[(n_name ? text("[n_name]") : "paper")]"
|
||||
|
||||
SSnanoui.update_uis(src)
|
||||
if(.)
|
||||
add_fingerprint(usr)
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/proc/scan(var/obj/item/card/id/card = null)
|
||||
if(scan) // Card is in machine
|
||||
@@ -226,7 +245,7 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
usr.drop_item()
|
||||
card.forceMove(src)
|
||||
scan = card
|
||||
SSnanoui.update_uis(src)
|
||||
SStgui.update_uis(src)
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/verb/eject_id()
|
||||
set category = null
|
||||
@@ -237,25 +256,20 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
return
|
||||
|
||||
if(scan)
|
||||
to_chat(usr, "You remove \the [scan] from \the [src].")
|
||||
to_chat(usr, "You remove [scan] from [src].")
|
||||
scan.forceMove(get_turf(src))
|
||||
if(!usr.get_active_hand() && Adjacent(usr))
|
||||
usr.put_in_hands(scan)
|
||||
scan = null
|
||||
else
|
||||
to_chat(usr, "There is nothing to remove from \the [src].")
|
||||
to_chat(usr, "There is nothing to remove from [src].")
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/proc/sendfax(var/destination,var/mob/sender)
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return
|
||||
|
||||
use_power(200)
|
||||
|
||||
use_power(active_power_usage)
|
||||
var/success = 0
|
||||
for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes)
|
||||
if(F.department == destination)
|
||||
success = F.receivefax(copyitem)
|
||||
|
||||
if(success)
|
||||
var/datum/fax/F = new /datum/fax()
|
||||
F.name = copyitem.name
|
||||
@@ -272,10 +286,10 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/proc/receivefax(var/obj/item/incoming)
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
if(department == "Unknown")
|
||||
return 0 //You can't send faxes to "Unknown"
|
||||
return FALSE //You can't send faxes to "Unknown"
|
||||
|
||||
flick("faxreceive", src)
|
||||
|
||||
@@ -291,19 +305,13 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
else if(istype(incoming, /obj/item/paper_bundle))
|
||||
bundlecopy(incoming)
|
||||
else
|
||||
return 0
|
||||
return FALSE
|
||||
|
||||
use_power(active_power_usage)
|
||||
return 1
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/proc/send_admin_fax(var/mob/sender, var/destination)
|
||||
if(stat & (BROKEN|NOPOWER))
|
||||
return
|
||||
|
||||
if(sendcooldown)
|
||||
return
|
||||
|
||||
use_power(200)
|
||||
use_power(active_power_usage)
|
||||
|
||||
if(!(istype(copyitem, /obj/item/paper) || istype(copyitem, /obj/item/paper_bundle) || istype(copyitem, /obj/item/photo)))
|
||||
visible_message("[src] beeps, \"Error transmitting message.\"")
|
||||
@@ -327,10 +335,12 @@ GLOBAL_LIST_EMPTY(fax_blacklist)
|
||||
for(var/obj/machinery/photocopier/faxmachine/F in GLOB.allfaxes)
|
||||
if(F.department == destination)
|
||||
F.receivefax(copyitem)
|
||||
sendcooldown = cooldown_time
|
||||
spawn(50)
|
||||
visible_message("[src] beeps, \"Message transmitted successfully.\"")
|
||||
visible_message("[src] beeps, \"Message transmitted successfully.\"")
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/proc/cooldown_seconds()
|
||||
if(sendcooldown < world.time)
|
||||
return 0
|
||||
return round((sendcooldown - world.time) / 10)
|
||||
|
||||
/obj/machinery/photocopier/faxmachine/proc/message_admins(var/mob/sender, var/faxname, var/faxtype, var/obj/item/sent, font_colour="#9A04D1")
|
||||
var/msg = "<span class='boldnotice'><font color='[font_colour]'>[faxname]: </font> [key_name_admin(sender)] | REPLY: (<A HREF='?_src_=holder;[faxname == "SYNDICATE FAX" ? "SyndicateReply" : "CentcommReply"]=[sender.UID()]'>RADIO</A>) (<a href='?_src_=holder;AdminFaxCreate=\ref[sender];originfax=\ref[src];faxtype=[faxtype];replyto=\ref[sent]'>FAX</a>) ([ADMIN_SM(sender,"SM")]) | REJECT: (<A HREF='?_src_=holder;FaxReplyTemplate=[sender.UID()];originfax=\ref[src]'>TEMPLATE</A>) ([ADMIN_BSA(sender,"BSA")]) (<A HREF='?_src_=holder;EvilFax=[sender.UID()];originfax=\ref[src]'>EVILFAX</A>) </span>: Receiving '[sent.name]' via secure connection... <a href='?_src_=holder;AdminFaxView=\ref[sent]'>view message</a>"
|
||||
|
||||
+140
-179
@@ -25,6 +25,11 @@
|
||||
|
||||
#define APC_UPDATE_ICON_COOLDOWN 200 // 20 seconds
|
||||
|
||||
// main_status var
|
||||
#define APC_EXTERNAL_POWER_NOTCONNECTED 0
|
||||
#define APC_EXTERNAL_POWER_NOENERGY 1
|
||||
#define APC_EXTERNAL_POWER_GOOD 2
|
||||
|
||||
// APC malf status
|
||||
#define APC_MALF_NOT_HACKED 1
|
||||
#define APC_MALF_HACKED 2 // APC hacked by user, and user is in its core.
|
||||
@@ -75,7 +80,7 @@
|
||||
var/lastused_equip = 0
|
||||
var/lastused_environ = 0
|
||||
var/lastused_total = 0
|
||||
var/main_status = 0
|
||||
var/main_status = APC_EXTERNAL_POWER_NOTCONNECTED
|
||||
powernet = 0 // set so that APCs aren't found as powernet nodes //Hackish, Horrible, was like this before I changed it :(
|
||||
var/malfhack = 0 //New var for my changes to AI malf. --NeoFite
|
||||
var/mob/living/silicon/ai/malfai = null //See above --NeoFite
|
||||
@@ -170,6 +175,7 @@
|
||||
addtimer(CALLBACK(src, .proc/update), 5)
|
||||
|
||||
/obj/machinery/power/apc/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
GLOB.apcs -= src
|
||||
if(malfai && operating)
|
||||
malfai.malf_picker.processing_time = clamp(malfai.malf_picker.processing_time - 10,0,1000)
|
||||
@@ -212,12 +218,11 @@
|
||||
if(isarea(A))
|
||||
area = A
|
||||
// no-op, keep the name
|
||||
else if(isarea(A) && src.areastring == null)
|
||||
else if(isarea(A) && !areastring)
|
||||
area = A
|
||||
name = "\improper [area.name] APC"
|
||||
else
|
||||
area = get_area_name(areastring)
|
||||
name = "\improper [area.name] APC"
|
||||
name = "\improper [get_area_name(area, TRUE)] APC"
|
||||
area.apc |= src
|
||||
|
||||
update_icon()
|
||||
@@ -429,8 +434,8 @@
|
||||
//attack with an item - open/close cover, insert cell, or (un)lock interface
|
||||
/obj/machinery/power/apc/attackby(obj/item/W, mob/living/user, params)
|
||||
|
||||
if(issilicon(user) && get_dist(src,user)>1)
|
||||
return src.attack_hand(user)
|
||||
if(issilicon(user) && get_dist(src, user) > 1)
|
||||
return attack_hand(user)
|
||||
|
||||
else if (istype(W, /obj/item/stock_parts/cell) && opened) // trying to put a cell inside
|
||||
if(cell)
|
||||
@@ -445,7 +450,7 @@
|
||||
W.forceMove(src)
|
||||
cell = W
|
||||
user.visible_message(\
|
||||
"[user.name] has inserted the power cell to [src.name]!",\
|
||||
"[user.name] has inserted the power cell to [name]!",\
|
||||
"<span class='notice'>You insert the power cell.</span>")
|
||||
chargecount = 0
|
||||
update_icon()
|
||||
@@ -474,7 +479,7 @@
|
||||
return
|
||||
user.visible_message("[user.name] adds cables to the APC frame.", \
|
||||
"<span class='notice'>You start adding cables to the APC frame...</span>")
|
||||
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
|
||||
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
|
||||
if(do_after(user, 20, target = src))
|
||||
if(C.get_amount() < 10 || !C)
|
||||
return
|
||||
@@ -499,10 +504,10 @@
|
||||
|
||||
user.visible_message("[user.name] inserts the power control board into [src].", \
|
||||
"<span class='notice'>You start to insert the power control board into the frame...</span>")
|
||||
playsound(src.loc, 'sound/items/deconstruct.ogg', 50, 1)
|
||||
playsound(loc, 'sound/items/deconstruct.ogg', 50, TRUE)
|
||||
if(do_after(user, 10, target = src))
|
||||
if(has_electronics==0)
|
||||
has_electronics = 1
|
||||
if(!has_electronics)
|
||||
has_electronics = TRUE
|
||||
locked = FALSE
|
||||
to_chat(user, "<span class='notice'>You place the power control board inside the frame.</span>")
|
||||
qdel(W)
|
||||
@@ -549,11 +554,11 @@
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You are trying to remove the power control board...</span>" )
|
||||
if(I.use_tool(src, user, 50, volume = I.tool_volume))
|
||||
if(has_electronics==1)
|
||||
has_electronics = 0
|
||||
if(has_electronics)
|
||||
has_electronics = FALSE
|
||||
if(stat & BROKEN)
|
||||
user.visible_message(\
|
||||
"[user.name] has broken the power control board inside [src.name]!",
|
||||
"[user.name] has broken the power control board inside [name]!",
|
||||
"<span class='notice'>You break the charred power control board and remove the remains.</span>",
|
||||
"<span class='italics'>You hear a crack.</span>")
|
||||
return
|
||||
@@ -561,19 +566,19 @@
|
||||
else if(emagged) // We emag board, not APC's frame
|
||||
emagged = FALSE
|
||||
user.visible_message(
|
||||
"[user.name] has discarded emaged power control board from [src.name]!",
|
||||
"<span class='notice'>You discarded shorten board.</span>")
|
||||
"[user.name] has discarded the shorted power control board from [name]!",
|
||||
"<span class='notice'>You discarded the shorted board.</span>")
|
||||
return
|
||||
else if(malfhack) // AI hacks board, not APC's frame
|
||||
user.visible_message(\
|
||||
"[user.name] has discarded strangely programmed power control board from [src.name]!",
|
||||
"<span class='notice'>You discarded strangely programmed board.</span>")
|
||||
"[user.name] has discarded strangely the programmed power control board from [name]!",
|
||||
"<span class='notice'>You discarded the strangely programmed board.</span>")
|
||||
malfai = null
|
||||
malfhack = 0
|
||||
return
|
||||
else
|
||||
user.visible_message(\
|
||||
"[user.name] has removed the power control board from [src.name]!",
|
||||
"[user.name] has removed the power control board from [name]!",
|
||||
"<span class='notice'>You remove the power control board.</span>")
|
||||
new /obj/item/apc_electronics(loc)
|
||||
return
|
||||
@@ -648,7 +653,7 @@
|
||||
else if(stat & (BROKEN|MAINT))
|
||||
to_chat(user, "<span class='warning'>Nothing happens!</span>")
|
||||
else
|
||||
if(allowed(usr) && !isWireCut(APC_WIRE_IDSCAN) && !malfhack)
|
||||
if(allowed(usr) && !wires.is_cut(WIRE_IDSCAN) && !malfhack)
|
||||
locked = !locked
|
||||
to_chat(user, "<span class='notice'>You [ locked ? "lock" : "unlock"] the APC interface.</span>")
|
||||
update_icon()
|
||||
@@ -711,36 +716,29 @@
|
||||
|
||||
// attack with hand - remove cell (if cover open) or interact with the APC
|
||||
/obj/machinery/power/apc/attack_hand(mob/user)
|
||||
// if(!can_use(user)) This already gets called in interact() and in topic()
|
||||
// return
|
||||
if(!user)
|
||||
return
|
||||
src.add_fingerprint(user)
|
||||
add_fingerprint(user)
|
||||
|
||||
if(usr == user && opened && (!issilicon(user)))
|
||||
if(usr == user && opened && !issilicon(user))
|
||||
if(cell)
|
||||
if(issilicon(user))
|
||||
cell.loc=src.loc // Drop it, whoops.
|
||||
else
|
||||
user.put_in_hands(cell)
|
||||
user.put_in_hands(cell)
|
||||
cell.add_fingerprint(user)
|
||||
cell.update_icon()
|
||||
|
||||
src.cell = null
|
||||
user.visible_message("<span class='warning'>[user.name] removes the power cell from [src.name]!", "You remove the power cell.</span>")
|
||||
// to_chat(user, "You remove the power cell.")
|
||||
charging = 0
|
||||
src.update_icon()
|
||||
cell = null
|
||||
user.visible_message("<span class='warning'>[user.name] removes [cell] from [src]!", "You remove the [cell].</span>")
|
||||
charging = FALSE
|
||||
update_icon()
|
||||
return
|
||||
if(stat & (BROKEN|MAINT))
|
||||
return
|
||||
|
||||
src.interact(user)
|
||||
interact(user)
|
||||
|
||||
/obj/machinery/power/apc/attack_ghost(mob/user)
|
||||
if(panel_open)
|
||||
wires.Interact(user)
|
||||
return ui_interact(user)
|
||||
return tgui_interact(user)
|
||||
|
||||
/obj/machinery/power/apc/interact(mob/user)
|
||||
if(!user)
|
||||
@@ -749,7 +747,7 @@
|
||||
if(panel_open)
|
||||
wires.Interact(user)
|
||||
|
||||
return ui_interact(user)
|
||||
return tgui_interact(user)
|
||||
|
||||
|
||||
/obj/machinery/power/apc/proc/get_malf_status(mob/living/silicon/ai/malf)
|
||||
@@ -770,22 +768,16 @@
|
||||
else
|
||||
return APC_MALF_NOT_HACKED
|
||||
|
||||
/obj/machinery/power/apc/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
if(!user)
|
||||
return
|
||||
|
||||
// update the ui if it exists, returns null if no ui is passed/found
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
/obj/machinery/power/apc/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = FALSE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
// the ui does not exist, so we'll create a new one
|
||||
ui = new(user, src, ui_key, "apc.tmpl", "[area.name] - APC", 510, issilicon(user) ? 535 : 460)
|
||||
ui = new(user, src, ui_key, "APC", name, 510, 460, master_ui, state)
|
||||
ui.open()
|
||||
// Auto update every Master Controller tick
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/power/apc/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
/obj/machinery/power/apc/tgui_data(mob/user)
|
||||
var/list/data = list()
|
||||
data["locked"] = is_locked(user)
|
||||
data["normallyLocked"] = locked
|
||||
data["isOperating"] = operating
|
||||
data["externalPower"] = main_status
|
||||
data["powerCellStatus"] = cell ? cell.percent() : null
|
||||
@@ -853,10 +845,6 @@
|
||||
// to_chat(world, "[area.power_equip]")
|
||||
area.power_change()
|
||||
|
||||
/obj/machinery/power/apc/proc/isWireCut(var/wireIndex)
|
||||
return wires.IsIndexCut(wireIndex)
|
||||
|
||||
|
||||
/obj/machinery/power/apc/proc/can_use(var/mob/user, var/loud = 0) //used by attack_hand() and Topic()
|
||||
if(user.can_admin_interact())
|
||||
return 1
|
||||
@@ -866,7 +854,7 @@
|
||||
var/mob/living/silicon/ai/AI = user
|
||||
var/mob/living/silicon/robot/robot = user
|
||||
if( \
|
||||
src.aidisabled || \
|
||||
aidisabled || \
|
||||
malfhack && istype(malfai) && \
|
||||
( \
|
||||
(istype(AI) && (malfai!=AI && malfai != AI.parent)) || \
|
||||
@@ -877,21 +865,21 @@
|
||||
to_chat(user, "<span class='danger'>\The [src] has AI control disabled!</span>")
|
||||
user << browse(null, "window=apc")
|
||||
user.unset_machine()
|
||||
return 0
|
||||
return FALSE
|
||||
else
|
||||
if((!in_range(src, user) || !istype(src.loc, /turf)))
|
||||
return 0
|
||||
if((!in_range(src, user) || !istype(loc, /turf)))
|
||||
return FALSE
|
||||
|
||||
var/mob/living/carbon/human/H = user
|
||||
if(istype(H))
|
||||
if(H.getBrainLoss() >= 60)
|
||||
for(var/mob/M in viewers(src, null))
|
||||
to_chat(M, "<span class='danger'>[H] stares cluelessly at [src] and drools.</span>")
|
||||
return 0
|
||||
return FALSE
|
||||
else if(prob(H.getBrainLoss()))
|
||||
to_chat(user, "<span class='danger'>You momentarily forget how to use [src].</span>")
|
||||
return 0
|
||||
return 1
|
||||
return FALSE
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/power/apc/proc/is_authenticated(mob/user as mob)
|
||||
if(user.can_admin_interact())
|
||||
@@ -909,104 +897,59 @@
|
||||
else
|
||||
return locked
|
||||
|
||||
/obj/machinery/power/apc/Topic(href, href_list, var/usingUI = 1)
|
||||
if(..())
|
||||
return 1
|
||||
|
||||
if(!can_use(usr, 1))
|
||||
return 1
|
||||
|
||||
if(href_list["lock"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
coverlocked = !coverlocked
|
||||
|
||||
else if(href_list["breaker"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
toggle_breaker()
|
||||
|
||||
else if(href_list["toggle_nightshift"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
if(last_nightshift_switch > world.time + 100) // don't spam...
|
||||
to_chat(usr, "<span class='warning'>[src]'s night lighting circuit breaker is still cycling!</span>")
|
||||
return
|
||||
last_nightshift_switch = world.time
|
||||
set_nightshift(!nightshift_lights)
|
||||
|
||||
else if(href_list["cmode"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
chargemode = !chargemode
|
||||
if(!chargemode)
|
||||
charging = 0
|
||||
update_icon()
|
||||
|
||||
else if(href_list["eqp"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
var/val = text2num(href_list["eqp"])
|
||||
equipment = setsubsystem(val)
|
||||
update_icon()
|
||||
update()
|
||||
|
||||
else if(href_list["lgt"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
var/val = text2num(href_list["lgt"])
|
||||
lighting = setsubsystem(val)
|
||||
update_icon()
|
||||
update()
|
||||
|
||||
else if(href_list["env"])
|
||||
if(!is_authenticated(usr))
|
||||
return
|
||||
|
||||
var/val = text2num(href_list["env"])
|
||||
environ = setsubsystem(val)
|
||||
update_icon()
|
||||
update()
|
||||
else if( href_list["close"] )
|
||||
SSnanoui.close_user_uis(usr, src)
|
||||
|
||||
return 0
|
||||
else if(href_list["close2"])
|
||||
usr << browse(null, "window=apcwires")
|
||||
|
||||
return 0
|
||||
|
||||
else if(href_list["overload"])
|
||||
if(issilicon(usr) && !aidisabled)
|
||||
overload_lighting()
|
||||
|
||||
else if(href_list["malfhack"])
|
||||
if(get_malf_status(usr))
|
||||
malfhack(usr)
|
||||
|
||||
else if(href_list["occupyapc"])
|
||||
if(get_malf_status(usr))
|
||||
malfoccupy(usr)
|
||||
|
||||
else if(href_list["deoccupyapc"])
|
||||
if(get_malf_status(usr))
|
||||
malfvacate()
|
||||
|
||||
else if(href_list["toggleaccess"])
|
||||
if(istype(usr, /mob/living/silicon))
|
||||
if(emagged || aidisabled || (stat & (BROKEN|MAINT)))
|
||||
to_chat(usr, "The APC does not respond to the command.")
|
||||
/obj/machinery/power/apc/tgui_act(action, params)
|
||||
if(..() || !can_use(usr, TRUE) || (locked && !usr.has_unlimited_silicon_privilege && (action != "toggle_nightshift") && !usr.can_admin_interact()))
|
||||
return
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("lock")
|
||||
if(usr.has_unlimited_silicon_privilege)
|
||||
if(emagged || stat & BROKEN)
|
||||
to_chat(usr, "<span class='warning'>The APC does not respond to the command!</span>")
|
||||
return FALSE
|
||||
else
|
||||
locked = !locked
|
||||
update_icon()
|
||||
else
|
||||
locked = !locked
|
||||
to_chat(usr, "<span class='warning'>Access Denied!</span>")
|
||||
return FALSE
|
||||
if("cover")
|
||||
coverlocked = !coverlocked
|
||||
if("breaker")
|
||||
toggle_breaker(usr)
|
||||
if("toggle_nightshift")
|
||||
if(last_nightshift_switch > world.time + 100) // don't spam...
|
||||
to_chat(usr, "<span class='warning'>[src]'s night lighting circuit breaker is still cycling!</span>")
|
||||
return FALSE
|
||||
last_nightshift_switch = world.time
|
||||
set_nightshift(!nightshift_lights)
|
||||
if("charge")
|
||||
chargemode = !chargemode
|
||||
if("channel")
|
||||
if(params["eqp"])
|
||||
equipment = setsubsystem(text2num(params["eqp"]))
|
||||
update_icon()
|
||||
|
||||
return 0
|
||||
update()
|
||||
else if(params["lgt"])
|
||||
lighting = setsubsystem(text2num(params["lgt"]))
|
||||
update_icon()
|
||||
update()
|
||||
else if(params["env"])
|
||||
environ = setsubsystem(text2num(params["env"]))
|
||||
update_icon()
|
||||
update()
|
||||
if("overload")
|
||||
if(usr.has_unlimited_silicon_privilege)
|
||||
overload_lighting()
|
||||
if("hack")
|
||||
if(get_malf_status(usr))
|
||||
malfhack(usr)
|
||||
if("occupy")
|
||||
if(get_malf_status(usr))
|
||||
malfoccupy(usr)
|
||||
if("deoccupy")
|
||||
if(get_malf_status(usr))
|
||||
malfvacate()
|
||||
|
||||
/obj/machinery/power/apc/proc/toggle_breaker()
|
||||
operating = !operating
|
||||
@@ -1037,7 +980,7 @@
|
||||
if(!malf.can_shunt)
|
||||
to_chat(malf, "<span class='warning'>You cannot shunt!</span>")
|
||||
return
|
||||
if(!is_station_level(src.z))
|
||||
if(!is_station_level(z))
|
||||
return
|
||||
occupier = new /mob/living/silicon/ai(src,malf.laws,null,1)
|
||||
occupier.adjustOxyLoss(malf.getOxyLoss())
|
||||
@@ -1084,21 +1027,21 @@
|
||||
|
||||
/obj/machinery/power/apc/proc/ion_act()
|
||||
//intended to be exactly the same as an AI malf attack
|
||||
if(!src.malfhack && is_station_level(src.z))
|
||||
if(!malfhack && is_station_level(z))
|
||||
if(prob(3))
|
||||
src.locked = 1
|
||||
if(src.cell.charge > 0)
|
||||
src.cell.charge = 0
|
||||
locked = TRUE
|
||||
if(cell.charge > 0)
|
||||
cell.charge = 0
|
||||
cell.corrupt()
|
||||
src.malfhack = 1
|
||||
malfhack = TRUE
|
||||
update_icon()
|
||||
var/datum/effect_system/smoke_spread/smoke = new
|
||||
smoke.set_up(3, 0, src.loc)
|
||||
smoke.set_up(3, 0, loc)
|
||||
smoke.attach(src)
|
||||
smoke.start()
|
||||
do_sparks(3, 1, src)
|
||||
for(var/mob/M in viewers(src))
|
||||
M.show_message("<span class='danger'>The [src.name] suddenly lets out a blast of smoke and some sparks!", 3, "<span class='danger'>You hear sizzling electronics.</span>", 2)
|
||||
M.show_message("<span class='danger'>The [name] suddenly lets out a blast of smoke and some sparks!", 3, "<span class='danger'>You hear sizzling electronics.</span>", 2)
|
||||
|
||||
|
||||
/obj/machinery/power/apc/surplus()
|
||||
@@ -1141,12 +1084,12 @@
|
||||
|
||||
var/excess = surplus()
|
||||
|
||||
if(!src.avail())
|
||||
main_status = 0
|
||||
if(!avail())
|
||||
main_status = APC_EXTERNAL_POWER_NOTCONNECTED
|
||||
else if(excess < 0)
|
||||
main_status = 1
|
||||
main_status = APC_EXTERNAL_POWER_NOENERGY
|
||||
else
|
||||
main_status = 2
|
||||
main_status = APC_EXTERNAL_POWER_GOOD
|
||||
|
||||
if(debug)
|
||||
log_debug("Status: [main_status] - Excess: [excess] - Last Equip: [lastused_equip] - Last Light: [lastused_light] - Longterm: [longtermpower]")
|
||||
@@ -1192,31 +1135,31 @@
|
||||
lighting = autoset(lighting, 1)
|
||||
environ = autoset(environ, 1)
|
||||
autoflag = 3
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.clearAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(TRUE, src)
|
||||
else if(cell.charge < 1250 && cell.charge > 750 && longtermpower < 0) // <30%, turn off equipment
|
||||
if(autoflag != 2)
|
||||
equipment = autoset(equipment, 2)
|
||||
lighting = autoset(lighting, 1)
|
||||
environ = autoset(environ, 1)
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.triggerAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(FALSE, src)
|
||||
autoflag = 2
|
||||
else if(cell.charge < 750 && cell.charge > 10) // <15%, turn off lighting & equipment
|
||||
if((autoflag > 1 && longtermpower < 0) || (autoflag > 1 && longtermpower >= 0))
|
||||
equipment = autoset(equipment, 2)
|
||||
lighting = autoset(lighting, 2)
|
||||
environ = autoset(environ, 1)
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.triggerAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(FALSE, src)
|
||||
autoflag = 1
|
||||
else if(cell.charge <= 0) // zero charge, turn all off
|
||||
if(autoflag != 0)
|
||||
equipment = autoset(equipment, 0)
|
||||
lighting = autoset(lighting, 0)
|
||||
environ = autoset(environ, 0)
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.triggerAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(FALSE, src)
|
||||
autoflag = 0
|
||||
|
||||
// now trickle-charge the cell
|
||||
@@ -1261,7 +1204,7 @@
|
||||
if(shock_mobs.len)
|
||||
var/mob/living/L = pick(shock_mobs)
|
||||
L.electrocute_act(rand(5, 25), "electrical arc")
|
||||
playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, 1)
|
||||
playsound(get_turf(L), 'sound/effects/eleczap.ogg', 75, TRUE)
|
||||
Beam(L, icon_state = "lightning[rand(1, 12)]", icon = 'icons/effects/effects.dmi', time = 5)
|
||||
|
||||
else // no cell, switch everything off
|
||||
@@ -1271,8 +1214,8 @@
|
||||
equipment = autoset(equipment, 0)
|
||||
lighting = autoset(lighting, 0)
|
||||
environ = autoset(environ, 0)
|
||||
if(report_power_alarm && is_station_contact(z))
|
||||
SSalarms.power_alarm.triggerAlarm(loc, src)
|
||||
if(report_power_alarm)
|
||||
area.poweralert(FALSE, src)
|
||||
autoflag = 0
|
||||
|
||||
// update icon & area power if anything changed
|
||||
@@ -1372,4 +1315,22 @@
|
||||
L.update(FALSE)
|
||||
CHECK_TICK
|
||||
|
||||
/obj/machinery/power/apc/proc/relock_callback()
|
||||
locked = TRUE
|
||||
updateDialog()
|
||||
|
||||
/obj/machinery/power/apc/proc/check_main_power_callback()
|
||||
if(!wires.is_cut(WIRE_MAIN_POWER1) && !wires.is_cut(WIRE_MAIN_POWER2))
|
||||
shorted = FALSE
|
||||
updateDialog()
|
||||
|
||||
/obj/machinery/power/apc/proc/check_ai_control_callback()
|
||||
if(!wires.is_cut(WIRE_AI_CONTROL))
|
||||
aidisabled = FALSE
|
||||
updateDialog()
|
||||
|
||||
#undef APC_UPDATE_ICON_COOLDOWN
|
||||
|
||||
#undef APC_EXTERNAL_POWER_NOTCONNECTED
|
||||
#undef APC_EXTERNAL_POWER_NOENERGY
|
||||
#undef APC_EXTERNAL_POWER_GOOD
|
||||
|
||||
+16
-13
@@ -229,14 +229,15 @@ By design, d1 is the smallest direction and d2 is the highest
|
||||
if(current_size >= STAGE_FIVE)
|
||||
deconstruct()
|
||||
|
||||
obj/structure/cable/proc/cable_color(var/colorC)
|
||||
if(colorC)
|
||||
if(colorC == "rainbow")
|
||||
color = color_rainbow()
|
||||
else
|
||||
color = colorC
|
||||
obj/structure/cable/proc/cable_color(colorC)
|
||||
if(!colorC)
|
||||
color = COLOR_RED
|
||||
else if(colorC == "rainbow")
|
||||
color = color_rainbow()
|
||||
else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them
|
||||
color = COLOR_ORANGE
|
||||
else
|
||||
color = "#DD0000"
|
||||
color = colorC
|
||||
|
||||
/obj/structure/cable/proc/color_rainbow()
|
||||
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
|
||||
@@ -845,13 +846,15 @@ GLOBAL_LIST_INIT(cable_coil_recipes, list (new/datum/stack_recipe("cable restrai
|
||||
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_WHITE, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
|
||||
..()
|
||||
|
||||
/obj/item/stack/cable_coil/proc/cable_color(var/colorC)
|
||||
if(colorC)
|
||||
if(colorC == "rainbow")
|
||||
colorC = color_rainbow()
|
||||
color = colorC
|
||||
else
|
||||
/obj/item/stack/cable_coil/proc/cable_color(colorC)
|
||||
if(!colorC)
|
||||
color = COLOR_RED
|
||||
else if(colorC == "rainbow")
|
||||
color = color_rainbow()
|
||||
else if(colorC == "orange") //byond only knows 16 colors by name, and orange isn't one of them
|
||||
color = COLOR_ORANGE
|
||||
else
|
||||
color = colorC
|
||||
|
||||
/obj/item/stack/cable_coil/proc/color_rainbow()
|
||||
color = pick(COLOR_RED, COLOR_BLUE, COLOR_GREEN, COLOR_PINK, COLOR_YELLOW, COLOR_CYAN)
|
||||
|
||||
@@ -177,6 +177,8 @@
|
||||
var/nightshift_light_power = 0.45
|
||||
var/nightshift_light_color = "#FFDDCC"
|
||||
|
||||
var/bulb_emergency_colour = "#FF3232" // determines the colour of the light while it's in emergency mode
|
||||
|
||||
// the smaller bulb light fixture
|
||||
|
||||
/obj/machinery/light/small
|
||||
@@ -238,7 +240,11 @@
|
||||
|
||||
switch(status) // set icon_states
|
||||
if(LIGHT_OK)
|
||||
icon_state = "[base_state][on]"
|
||||
var/area/A = get_area(src)
|
||||
if(A && A.fire)
|
||||
icon_state = "[base_state]_emergency"
|
||||
else
|
||||
icon_state = "[base_state][on]"
|
||||
if(LIGHT_EMPTY)
|
||||
icon_state = "[base_state]-empty"
|
||||
on = FALSE
|
||||
@@ -260,10 +266,20 @@
|
||||
on = FALSE
|
||||
update_icon()
|
||||
if(on)
|
||||
var/BR = nightshift_enabled ? nightshift_light_range : brightness_range
|
||||
var/PO = nightshift_enabled ? nightshift_light_power : brightness_power
|
||||
var/CO = nightshift_enabled ? nightshift_light_color : brightness_color
|
||||
var/matching = light_range == BR && light_power == PO && light_color == CO
|
||||
var/BR = brightness_range
|
||||
var/PO = brightness_power
|
||||
var/CO = brightness_color
|
||||
if(color)
|
||||
CO = color
|
||||
var/area/A = get_area(src)
|
||||
if(A && A.fire)
|
||||
CO = bulb_emergency_colour
|
||||
else if(nightshift_enabled)
|
||||
BR = nightshift_light_range
|
||||
PO = nightshift_light_power
|
||||
if(!color)
|
||||
CO = nightshift_light_color
|
||||
var/matching = light && BR == light.light_range && PO == light.light_power && CO == light.light_color
|
||||
if(!matching)
|
||||
switchcount++
|
||||
if(rigged)
|
||||
|
||||
@@ -310,16 +310,25 @@ field_generator power level display
|
||||
//This is here to help fight the "hurr durr, release singulo cos nobody will notice before the
|
||||
//singulo eats the evidence". It's not fool-proof but better than nothing.
|
||||
//I want to avoid using global variables.
|
||||
spawn(1)
|
||||
var/temp = 1 //stops spam
|
||||
for(var/thing in GLOB.singularities)
|
||||
var/obj/singularity/O = thing
|
||||
if(O.last_warning && temp)
|
||||
if((world.time - O.last_warning) > 50) //to stop message-spam
|
||||
temp = 0
|
||||
message_admins("A singulo exists and a containment field has failed. Location: [get_area(src)] (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</A>)",1)
|
||||
investigate_log("has <font color='red'>failed</font> whilst a singulo exists.","singulo")
|
||||
O.last_warning = world.time
|
||||
INVOKE_ASYNC(src, .proc/admin_alert)
|
||||
|
||||
/obj/machinery/field/generator/proc/admin_alert()
|
||||
var/temp = TRUE //stops spam
|
||||
for(var/thing in GLOB.singularities)
|
||||
var/obj/singularity/O = thing
|
||||
if(O.last_warning && temp && atoms_share_level(O, src))
|
||||
if((world.time - O.last_warning) > 50) //to stop message-spam
|
||||
temp = FALSE
|
||||
// To the person who asks "Hey affected, why are you using this massive operator when you can use AREACOORD?" Well, ill tell you
|
||||
// get_area_name is fucking broken and uses a for(x in world) search
|
||||
// It doesnt even work, is expensive, and returns 0
|
||||
// Im not refactoring one thing which could risk breaking all admin location logs
|
||||
// Fight me
|
||||
// [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)] works much better and actually works at all
|
||||
// Oh and yes, this exact comment was pasted from the exact same thing I did to tcomms code. Dont at me.
|
||||
message_admins("A singularity exists and a containment field has failed on the same Z-Level. Singulo location: [O ? "[get_location_name(O, TRUE)] [COORD(O)]" : "nonexistent location"] [ADMIN_JMP(O)] | Field generator location: [src ? "[get_location_name(src, TRUE)] [COORD(src)]" : "nonexistent location"] [ADMIN_JMP(src)]")
|
||||
investigate_log("has <font color='red'>failed</font> whilst a singulo exists.","singulo")
|
||||
O.last_warning = world.time
|
||||
|
||||
/obj/machinery/field/generator/shock_field(mob/living/user)
|
||||
if(fields.len)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/area/engine/engineering/power_alert(var/alarming)
|
||||
if(alarming)
|
||||
investigate_log("has a power alarm!","singulo")
|
||||
/area/engine/engineering/poweralert(state, source)
|
||||
if(state != poweralm)
|
||||
investigate_log("has a power alarm!", "singulo")
|
||||
..()
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
use_log = list()
|
||||
|
||||
/obj/machinery/particle_accelerator/control_box/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
if(active)
|
||||
toggle_power()
|
||||
QDEL_NULL(wires)
|
||||
@@ -41,6 +42,11 @@
|
||||
else if(construction_state == 2) // Wires exposed
|
||||
wires.Interact(user)
|
||||
|
||||
/obj/machinery/particle_accelerator/control_box/multitool_act(mob/living/user, obj/item/I)
|
||||
if(construction_state == 2) // Wires exposed
|
||||
wires.Interact(user)
|
||||
return TRUE
|
||||
|
||||
/obj/machinery/particle_accelerator/control_box/update_state()
|
||||
if(construction_state < 3)
|
||||
use_power = NO_POWER_USE
|
||||
@@ -93,18 +99,18 @@
|
||||
usr.unset_machine()
|
||||
return
|
||||
if(href_list["togglep"])
|
||||
if(!wires.IsIndexCut(PARTICLE_TOGGLE_WIRE))
|
||||
if(!wires.is_cut(WIRE_PARTICLE_POWER))
|
||||
toggle_power()
|
||||
|
||||
else if(href_list["scan"])
|
||||
part_scan()
|
||||
|
||||
else if(href_list["strengthup"])
|
||||
if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE))
|
||||
if(!wires.is_cut(WIRE_PARTICLE_STRENGTH))
|
||||
add_strength()
|
||||
|
||||
else if(href_list["strengthdown"])
|
||||
if(!wires.IsIndexCut(PARTICLE_STRENGTH_WIRE))
|
||||
if(!wires.is_cut(WIRE_PARTICLE_STRENGTH))
|
||||
remove_strength()
|
||||
|
||||
updateDialog()
|
||||
|
||||
+76
-72
@@ -227,8 +227,8 @@
|
||||
if(terminal)
|
||||
terminal.master = null
|
||||
terminal = null
|
||||
return 1
|
||||
return 0
|
||||
return TRUE
|
||||
return FALSE
|
||||
|
||||
/obj/machinery/power/smes/proc/make_terminal(user, tempDir, tempLoc)
|
||||
// create a terminal object at the same position as original turf loc
|
||||
@@ -339,89 +339,91 @@
|
||||
|
||||
/obj/machinery/power/smes/attack_ai(mob/user)
|
||||
add_hiddenprint(user)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/power/smes/attack_ghost(mob/user)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/power/smes/attack_hand(mob/user)
|
||||
add_fingerprint(user)
|
||||
ui_interact(user)
|
||||
tgui_interact(user)
|
||||
|
||||
/obj/machinery/power/smes/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
|
||||
/obj/machinery/power/smes/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = TRUE, datum/tgui/master_ui = null, datum/tgui_state/state = GLOB.tgui_default_state)
|
||||
if(stat & BROKEN)
|
||||
return
|
||||
|
||||
|
||||
// update the ui if it exists, returns null if no ui is passed/found
|
||||
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
|
||||
if(!ui)
|
||||
// the ui does not exist, so we'll create a new() one
|
||||
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
|
||||
ui = new(user, src, ui_key, "smes.tmpl", "SMES Power Storage Unit", 540, 380)
|
||||
// open the new ui window
|
||||
ui = new(user, src, ui_key, "Smes", name, 340, 350, master_ui, state)
|
||||
ui.open()
|
||||
// auto update every Master Controller tick
|
||||
ui.set_auto_update(1)
|
||||
|
||||
/obj/machinery/power/smes/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
|
||||
var/data[0]
|
||||
|
||||
data["nameTag"] = name_tag
|
||||
data["storedCapacity"] = round(100.0*charge/capacity, 0.1)
|
||||
data["charging"] = inputting
|
||||
data["chargeMode"] = input_attempt
|
||||
data["chargeLevel"] = input_level
|
||||
data["chargeMax"] = input_level_max
|
||||
data["outputOnline"] = output_attempt
|
||||
data["outputLevel"] = output_level
|
||||
data["outputMax"] = output_level_max
|
||||
data["outputLoad"] = round(output_used)
|
||||
|
||||
if(outputting)
|
||||
data["outputting"] = 2 // smes is outputting
|
||||
else if(!outputting && output_attempt)
|
||||
data["outputting"] = 1 // smes is online but not outputting because it's charge level is too low
|
||||
else
|
||||
data["outputting"] = 0 // smes is not outputting
|
||||
|
||||
/obj/machinery/power/smes/tgui_data(mob/user)
|
||||
var/list/data = list(
|
||||
"capacity" = capacity,
|
||||
"capacityPercent" = round(100*charge/capacity, 0.1),
|
||||
"charge" = charge,
|
||||
"inputAttempt" = input_attempt,
|
||||
"inputting" = inputting,
|
||||
"inputLevel" = input_level,
|
||||
"inputLevel_text" = DisplayPower(input_level),
|
||||
"inputLevelMax" = input_level_max,
|
||||
"inputAvailable" = input_available,
|
||||
"outputAttempt" = output_attempt,
|
||||
"outputting" = outputting,
|
||||
"outputLevel" = output_level,
|
||||
"outputLevel_text" = DisplayPower(output_level),
|
||||
"outputLevelMax" = output_level_max,
|
||||
"outputUsed" = round(output_used),
|
||||
)
|
||||
return data
|
||||
|
||||
/obj/machinery/power/smes/Topic(href, href_list)
|
||||
/obj/machinery/power/smes/tgui_act(action, params)
|
||||
if(..())
|
||||
return 1
|
||||
return
|
||||
. = TRUE
|
||||
switch(action)
|
||||
if("tryinput")
|
||||
inputting(!input_attempt)
|
||||
update_icon()
|
||||
if("tryoutput")
|
||||
outputting(!output_attempt)
|
||||
update_icon()
|
||||
if("input")
|
||||
var/target = params["target"]
|
||||
var/adjust = text2num(params["adjust"])
|
||||
if(target == "min")
|
||||
target = 0
|
||||
else if(target == "max")
|
||||
target = input_level_max
|
||||
else if(adjust)
|
||||
target = input_level + adjust
|
||||
else if(text2num(target) != null)
|
||||
target = text2num(target)
|
||||
else
|
||||
. = FALSE
|
||||
if(.)
|
||||
input_level = clamp(target, 0, input_level_max)
|
||||
if("output")
|
||||
var/target = params["target"]
|
||||
var/adjust = text2num(params["adjust"])
|
||||
if(target == "min")
|
||||
target = 0
|
||||
else if(target == "max")
|
||||
target = output_level_max
|
||||
else if(adjust)
|
||||
target = output_level + adjust
|
||||
else if(text2num(target) != null)
|
||||
target = text2num(target)
|
||||
else
|
||||
. = FALSE
|
||||
if(.)
|
||||
output_level = clamp(target, 0, output_level_max)
|
||||
else
|
||||
. = FALSE
|
||||
if(.)
|
||||
log_smes(usr)
|
||||
|
||||
if( href_list["cmode"] )
|
||||
inputting(!input_attempt)
|
||||
update_icon()
|
||||
|
||||
else if( href_list["online"] )
|
||||
outputting(!output_attempt)
|
||||
update_icon()
|
||||
|
||||
else if( href_list["input"] )
|
||||
switch( href_list["input"] )
|
||||
if("min")
|
||||
input_level = 0
|
||||
if("max")
|
||||
input_level = input_level_max
|
||||
if("set")
|
||||
input_level = input(usr, "Enter new input level (0-[input_level_max])", "SMES Input Power Control", input_level) as num
|
||||
input_level = max(0, min(input_level_max, input_level)) // clamp to range
|
||||
|
||||
else if( href_list["output"] )
|
||||
switch( href_list["output"] )
|
||||
if("min")
|
||||
output_level = 0
|
||||
if("max")
|
||||
output_level = output_level_max
|
||||
if("set")
|
||||
output_level = input(usr, "Enter new output level (0-[output_level_max])", "SMES Output Power Control", output_level) as num
|
||||
output_level = max(0, min(output_level_max, output_level)) // clamp to range
|
||||
|
||||
investigate_log("input/output; [input_level>output_level?"<font color='green'>":"<font color='red'>"][input_level]/[output_level]</font> | Output-mode: [output_attempt?"<font color='green'>on</font>":"<font color='red'>off</font>"] | Input-mode: [input_attempt?"<font color='green'>auto</font>":"<font color='red'>off</font>"] by [usr.key]","singulo")
|
||||
|
||||
return 1
|
||||
/obj/machinery/power/smes/proc/log_smes(mob/user)
|
||||
investigate_log("input/output; [input_level>output_level?"<font color='green'>":"<font color='red'>"][input_level]/[output_level]</font> | Charge: [charge] | Output-mode: [output_attempt?"<font color='green'>on</font>":"<font color='red'>off</font>"] | Input-mode: [input_attempt?"<font color='green'>auto</font>":"<font color='red'>off</font>"] by [user ? key_name(user) : "outside forces"]", "singulo")
|
||||
|
||||
/obj/machinery/power/smes/proc/ion_act()
|
||||
if(is_station_level(src.z))
|
||||
@@ -448,6 +450,7 @@
|
||||
smoke.attach(src)
|
||||
smoke.start()
|
||||
|
||||
|
||||
/obj/machinery/power/smes/proc/inputting(var/do_input)
|
||||
input_attempt = do_input
|
||||
if(!input_attempt)
|
||||
@@ -459,14 +462,15 @@
|
||||
outputting = 0
|
||||
|
||||
/obj/machinery/power/smes/emp_act(severity)
|
||||
inputting(rand(0,1))
|
||||
outputting(rand(0,1))
|
||||
inputting(rand(0, 1))
|
||||
outputting(rand(0, 1))
|
||||
output_level = rand(0, output_level_max)
|
||||
input_level = rand(0, input_level_max)
|
||||
charge -= 1e6/severity
|
||||
if(charge < 0)
|
||||
charge = 0
|
||||
update_icon()
|
||||
log_smes()
|
||||
..()
|
||||
|
||||
/obj/machinery/power/smes/engineering
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
RefreshParts()
|
||||
|
||||
/obj/machinery/power/tesla_coil/Destroy()
|
||||
SStgui.close_uis(wires)
|
||||
QDEL_NULL(wires)
|
||||
return ..()
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@
|
||||
if(keep)
|
||||
stored_ammo.Insert(1,b)
|
||||
update_mat_value()
|
||||
update_icon()
|
||||
return b
|
||||
|
||||
/obj/item/ammo_box/proc/give_round(obj/item/ammo_casing/R, replace_spent = 0)
|
||||
|
||||
@@ -133,6 +133,9 @@
|
||||
/obj/item/ammo_box/magazine/internal/shot/riot/short
|
||||
max_ammo = 3
|
||||
|
||||
/obj/item/ammo_box/magazine/internal/shot/riot/buckshot
|
||||
ammo_type = /obj/item/ammo_casing/shotgun/buckshot
|
||||
|
||||
/obj/item/ammo_box/magazine/internal/grenadelauncher
|
||||
name = "grenade launcher internal magazine"
|
||||
ammo_type = /obj/item/ammo_casing/a40mm
|
||||
@@ -380,10 +383,7 @@
|
||||
origin_tech = "combat=3;syndicate=1"
|
||||
caliber = "shotgun"
|
||||
max_ammo = 8
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/update_icon()
|
||||
..()
|
||||
icon_state = "[initial(icon_state)]-[CEILING(ammo_count(0)/8, 1)*8]"
|
||||
multiple_sprites = 2
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/buckshot
|
||||
name = "shotgun magazine (12g buckshot slugs)"
|
||||
@@ -411,6 +411,24 @@
|
||||
icon_state = "m12gbc"
|
||||
ammo_type = /obj/item/ammo_casing/shotgun/breaching
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/XtrLrg
|
||||
name = "\improper XL shotgun magazine (12g slugs)"
|
||||
desc = "An extra large drum magazine."
|
||||
icon_state = "m12gXlSl"
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
ammo_type = /obj/item/ammo_casing/shotgun
|
||||
max_ammo = 16
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/XtrLrg/buckshot
|
||||
name = "\improper XL shotgun magazine (12g buckshot)"
|
||||
icon_state = "m12gXlBs"
|
||||
ammo_type = /obj/item/ammo_casing/shotgun/buckshot
|
||||
|
||||
/obj/item/ammo_box/magazine/m12g/XtrLrg/dragon
|
||||
name = "\improper XL shotgun magazine (12g dragon's breath)"
|
||||
icon_state = "m12gXlDb"
|
||||
ammo_type = /obj/item/ammo_casing/shotgun/incendiary/dragonsbreath
|
||||
|
||||
/obj/item/ammo_box/magazine/toy
|
||||
name = "foam force META magazine"
|
||||
ammo_type = /obj/item/ammo_casing/caseless/foam_dart
|
||||
|
||||
@@ -95,6 +95,7 @@
|
||||
if(!user.unEquip(A))
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You screw [S] onto [src].</span>")
|
||||
playsound(src, 'sound/items/screwdriver.ogg', 40, 1)
|
||||
suppressed = A
|
||||
S.oldsound = fire_sound
|
||||
S.initial_w_class = w_class
|
||||
@@ -120,6 +121,7 @@
|
||||
..()
|
||||
return
|
||||
to_chat(user, "<span class='notice'>You unscrew [suppressed] from [src].</span>")
|
||||
playsound(src, 'sound/items/screwdriver.ogg', 40, 1)
|
||||
user.put_in_hands(suppressed)
|
||||
fire_sound = S.oldsound
|
||||
w_class = S.initial_w_class
|
||||
|
||||
@@ -275,13 +275,27 @@
|
||||
if(magazine)
|
||||
overlays.Cut()
|
||||
overlays += "[magazine.icon_state]"
|
||||
return
|
||||
if(istype(magazine, /obj/item/ammo_box/magazine/m12g/XtrLrg))
|
||||
w_class = WEIGHT_CLASS_BULKY
|
||||
else
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
else
|
||||
w_class = WEIGHT_CLASS_NORMAL
|
||||
|
||||
/obj/item/gun/projectile/automatic/shotgun/bulldog/update_icon()
|
||||
overlays.Cut()
|
||||
update_magazine()
|
||||
icon_state = "bulldog[chambered ? "" : "-e"]"
|
||||
|
||||
/obj/item/gun/projectile/automatic/shotgun/bulldog/attackby(var/obj/item/A as obj, mob/user as mob, params)
|
||||
if(istype(A, /obj/item/ammo_box/magazine/m12g/XtrLrg))
|
||||
if(istype(loc, /obj/item/storage)) // To prevent inventory exploits
|
||||
var/obj/item/storage/Strg = loc
|
||||
if(Strg.max_w_class < WEIGHT_CLASS_BULKY)
|
||||
to_chat(user, "<span class='warning'>You can't reload [src], with a XL mag, while it's in a normal bag.</span>")
|
||||
return
|
||||
return ..()
|
||||
|
||||
/obj/item/gun/projectile/automatic/shotgun/bulldog/afterattack(atom/target as mob|obj|turf|area, mob/living/user as mob|obj, flag)
|
||||
..()
|
||||
empty_alarm()
|
||||
|
||||
@@ -197,6 +197,8 @@
|
||||
..()
|
||||
post_sawoff()
|
||||
|
||||
/obj/item/gun/projectile/shotgun/riot/buckshot //comes pre-loaded with buckshot rather than rubber
|
||||
mag_type = /obj/item/ammo_box/magazine/internal/shot/riot/buckshot
|
||||
|
||||
|
||||
///////////////////////
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user