Merge branch 'master' into Borg-tweaks-1

This commit is contained in:
SabreML
2020-09-15 16:25:12 +01:00
committed by GitHub
372 changed files with 10840 additions and 5350 deletions
+6
View File
@@ -28,6 +28,12 @@
#define AIRLOCK_ASSEMBLY_NEEDS_ELECTRONICS 1
#define AIRLOCK_ASSEMBLY_NEEDS_SCREWDRIVER 2
//used by airlocks and airlock wires.
#define AICONTROLDISABLED_OFF 0 // Silicons can control the airlock normally.
#define AICONTROLDISABLED_ON 1 // Silicons cannot control the airlock, but can hack the airlock.
#define AICONTROLDISABLED_BYPASS 2 // Silicons can control the airlock because they succeeded on the hack
#define AICONTROLDISABLED_PERMA 3 // Wire cutting an airlock on AICONTROLDISABLED_BYPASS toggles it between AICONTROLDISABLED_BYPASS and this.
//plastic flaps construction states
#define PLASTIC_FLAPS_NORMAL 0
#define PLASTIC_FLAPS_DETACHED 1
+29
View File
@@ -0,0 +1,29 @@
#define INSTRUMENT_MIN_OCTAVE 1
#define INSTRUMENT_MAX_OCTAVE 9
#define INSTRUMENT_MIN_KEY 0
#define INSTRUMENT_MAX_KEY 127
/// Max number of playing notes per instrument.
#define CHANNELS_PER_INSTRUMENT 128
/// Distance multiplier that makes us not be impacted by 3d sound as much. This is a multiplier so lower it is the closer we will pretend to be to people.
#define INSTRUMENT_DISTANCE_FALLOFF_BUFF 0.2
/// How many tiles instruments have no falloff for
#define INSTRUMENT_DISTANCE_NO_FALLOFF 3
/// Maximum length a note should ever go for
#define INSTRUMENT_MAX_TOTAL_SUSTAIN (5 SECONDS)
/// These are per decisecond.
#define INSTRUMENT_EXP_FALLOFF_MIN 1.025 //100/(1.025^50) calculated for [INSTRUMENT_MIN_SUSTAIN_DROPOFF] to be 30.
#define INSTRUMENT_EXP_FALLOFF_MAX 10
/// Minimum volume for when the sound is considered dead.
#define INSTRUMENT_MIN_SUSTAIN_DROPOFF 0.1
#define SUSTAIN_LINEAR 1
#define SUSTAIN_EXPONENTIAL 2
// /datum/instrument instrument_flags
#define INSTRUMENT_LEGACY (1<<0) //Legacy instrument. Implies INSTRUMENT_DO_NOT_AUTOSAMPLE
#define INSTRUMENT_DO_NOT_AUTOSAMPLE (1<<1) //Do not automatically sample
+5
View File
@@ -92,3 +92,8 @@
// Firelock states
#define FD_OPEN 1
#define FD_CLOSED 2
// Computer login types
#define LOGIN_TYPE_NORMAL 1
#define LOGIN_TYPE_AI 2
#define LOGIN_TYPE_ROBOT 3
+1
View File
@@ -12,6 +12,7 @@
#define CHANNEL_HIGHEST_AVAILABLE 1017
#define MAX_INSTRUMENT_CHANNELS (128 * 6)
#define SOUND_MINIMUM_PRESSURE 10
#define FALLOFF_SOUNDS 0.5
+7 -5
View File
@@ -45,11 +45,13 @@
// Subsystems shutdown in the reverse of the order they initialize in
// The numbers just define the ordering, they are meaningless otherwise.
#define INIT_ORDER_TITLE 100 // This **MUST** load first or people will se blank lobby screens
#define INIT_ORDER_GARBAGE 19
#define INIT_ORDER_DBCORE 18
#define INIT_ORDER_BLACKBOX 17
#define INIT_ORDER_SERVER_MAINT 16
#define INIT_ORDER_INPUT 15
#define INIT_ORDER_GARBAGE 21
#define INIT_ORDER_DBCORE 20
#define INIT_ORDER_BLACKBOX 19
#define INIT_ORDER_SERVER_MAINT 18
#define INIT_ORDER_INPUT 17
#define INIT_ORDER_SOUNDS 16
#define INIT_ORDER_INSTRUMENTS 15
#define INIT_ORDER_RESEARCH 14
#define INIT_ORDER_EVENTS 13
#define INIT_ORDER_JOBS 12
+8
View File
@@ -0,0 +1,8 @@
// TGUI defines
#define TGUI_MODAL_INPUT_MAX_LENGTH 1024
#define TGUI_MODAL_INPUT_MAX_LENGTH_NAME 64 // Names for generally anything don't go past 32, let alone 64.
#define TGUI_MODAL_OPEN 1
#define TGUI_MODAL_DELEGATE 2
#define TGUI_MODAL_ANSWER 3
#define TGUI_MODAL_CLOSE 4
+69 -58
View File
@@ -145,12 +145,10 @@
/mob/living/silicon/ai/MiddleClickOn(var/atom/A)
A.AIMiddleClick(src)
/*
The following criminally helpful code is just the previous code cleaned up;
I have no idea why it was in atoms.dm instead of respective files.
*/
/atom/proc/AICtrlShiftClick(var/mob/user) // Examines
// DEFAULT PROCS TO OVERRIDE
/atom/proc/AICtrlShiftClick(mob/user) // Examines
if(user.client)
user.examinate(src)
return
@@ -158,70 +156,82 @@
/atom/proc/AIAltShiftClick()
return
/obj/machinery/door/airlock/AIAltShiftClick() // Sets/Unsets Emergency Access Override
if(density)
Topic(src, list("src" = UID(), "command"="emergency", "activate" = "1"), 1) // 1 meaning no window (consistency!)
else
Topic(src, list("src" = UID(), "command"="emergency", "activate" = "0"), 1)
return
/atom/proc/AIShiftClick(var/mob/user)
/atom/proc/AIShiftClick(mob/living/user) // borgs use this too
if(user.client)
user.examinate(src)
return
/obj/machinery/door/airlock/AIShiftClick() // Opens and closes doors!
if(density)
Topic(src, list("src" = UID(), "command"="open", "activate" = "1"), 1) // 1 meaning no window (consistency!)
else
Topic(src, list("src" = UID(), "command"="open", "activate" = "0"), 1)
/atom/proc/AICtrlClick(mob/living/silicon/ai/user)
return
/atom/proc/AICtrlClick(var/mob/living/silicon/ai/user)
return
/obj/machinery/door/airlock/AICtrlClick() // Bolts doors
if(locked)
Topic(src, list("src" = UID(), "command"="bolts", "activate" = "0"), 1)// 1 meaning no window (consistency!)
else
Topic(src, list("src" = UID(), "command"="bolts", "activate" = "1"), 1)
/obj/machinery/power/apc/AICtrlClick() // turns off/on APCs.
Topic("breaker=1", list("breaker"="1"), 0) // 0 meaning no window (consistency! wait...)
/obj/machinery/turretid/AICtrlClick() //turns off/on Turrets
Topic(src, list("src" = UID(), "command"="enable", "value"="[!enabled]"), 1) // 1 meaning no window (consistency!)
/atom/proc/AIAltClick(var/atom/A)
/atom/proc/AIAltClick(atom/A)
AltClick(A)
/obj/machinery/door/airlock/AIAltClick() // Electrifies doors.
if(!electrified_until)
// permanent shock
Topic(src, list("src" = UID(), "command"="electrify_permanently", "activate" = "1"), 1) // 1 meaning no window (consistency!)
else
// disable/6 is not in Topic; disable/5 disables both temporary and permanent shock
Topic(src, list("src" = UID(), "command"="electrify_permanently", "activate" = "0"), 1)
/atom/proc/AIMiddleClick(mob/living/user)
return
/mob/living/silicon/ai/TurfAdjacent(turf/T)
return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T))
// APC
/obj/machinery/power/apc/AICtrlClick(mob/living/user) // turns off/on APCs.
toggle_breaker(user)
// TURRETCONTROL
/obj/machinery/turretid/AICtrlClick(mob/living/silicon/ai/user) //turns off/on Turrets
enabled = !enabled
updateTurrets()
/obj/machinery/turretid/AIAltClick() //toggles lethal on turrets
Topic(src, list("src" = UID(), "command"="lethal", "value"="[!lethal]"), 1) // 1 meaning no window (consistency!)
if(lethal_is_configurable)
lethal = !lethal
updateTurrets()
/atom/proc/AIMiddleClick()
return
// AIRLOCKS
/obj/machinery/door/airlock/AIMiddleClick() // Toggles door bolt lights.
if(!src.lights)
Topic(src, list("src" = UID(), "command"="lights", "activate" = "1"), 1) // 1 meaning no window (consistency!)
/obj/machinery/door/airlock/AIAltShiftClick(mob/user) // Sets/Unsets Emergency Access Override
emergency = !emergency
update_icon()
/obj/machinery/door/airlock/AIShiftClick(mob/user) // Opens and closes doors!
if(welded)
to_chat(user, "<span class='warning'>The airlock has been welded shut!</span>")
if(locked)
locked = !locked
if(density)
open()
else
Topic(src, list("src" = UID(), "command"="lights", "activate" = "0"), 1)
return
close()
/obj/machinery/ai_slipper/AICtrlClick() //Turns liquid dispenser on or off
ToggleOn()
/obj/machinery/door/airlock/AICtrlClick(mob/living/silicon/ai/user) // Bolts doors
locked = !locked
update_icon()
/obj/machinery/ai_slipper/AIAltClick() //Dispenses liquid if on
Activate()
/obj/machinery/door/airlock/AIAltClick(mob/living/silicon/ai/user) // Electrifies doors.
if(wires.is_cut(WIRE_ELECTRIFY))
to_chat(user, "<span class='warning'>The electrification wire is cut - Cannot electrify the door.</span>")
if(isElectrified())
electrify(0) // un-shock
else
electrify(-1) // permanent shock
/obj/machinery/door/airlock/AIMiddleClick(mob/living/user) // Toggles door bolt lights.
if(wires.is_cut(WIRE_BOLT_LIGHT))
to_chat(user, "<span class='warning'>The bolt lights wire has been cut - The door bolt lights are permanently disabled.</span>")
else if(lights)
lights = FALSE
to_chat(user, "<span class='notice'>The door bolt lights have been disabled.</span>")
else if(!lights)
lights = TRUE
to_chat(user, "<span class='notice'>The door bolt lights have been enabled.</span>")
update_icon()
// FIRE ALARMS
/obj/machinery/firealarm/AICtrlClick()
if(enabled)
@@ -229,9 +239,10 @@
else
alarm()
//
// Override AdjacentQuick for AltClicking
//
// AI-CONTROLLED SLIP GENERATOR IN AI CORE
/mob/living/silicon/ai/TurfAdjacent(var/turf/T)
return (GLOB.cameranet && GLOB.cameranet.checkTurfVis(T))
/obj/machinery/ai_slipper/AICtrlClick(mob/living/silicon/ai/user) //Turns liquid dispenser on or off
ToggleOn()
/obj/machinery/ai_slipper/AIAltClick() //Dispenses liquid if on
Activate()
@@ -0,0 +1,86 @@
PROCESSING_SUBSYSTEM_DEF(instruments)
name = "Instruments"
init_order = INIT_ORDER_INSTRUMENTS
wait = 1
flags = SS_TICKER|SS_BACKGROUND|SS_KEEP_TIMING
offline_implications = "Instruments will no longer play. No immediate action is needed."
/// List of all instrument data, associative id = datum
var/list/datum/instrument/instrument_data
/// List of all song datums.
var/list/datum/song/songs
/// Max lines in songs
var/musician_maxlines = 600
/// Max characters per line in songs
var/musician_maxlinechars = 300
/// Deciseconds between hearchecks. Too high and instruments seem to lag when people are moving around in terms of who can hear it. Too low and the server lags from this.
var/musician_hearcheck_mindelay = 5
/// Maximum instrument channels total instruments are allowed to use. This is so you don't have instruments deadlocking all sound channels.
var/max_instrument_channels = MAX_INSTRUMENT_CHANNELS
/// Current number of channels allocated for instruments
var/current_instrument_channels = 0
/// Single cached list for synthesizer instrument ids, so you don't have to have a new list with every synthesizer.
var/list/synthesizer_instrument_ids
/datum/controller/subsystem/processing/instruments/Initialize()
initialize_instrument_data()
synthesizer_instrument_ids = get_allowed_instrument_ids()
return ..()
/**
* Initializes all instrument datums
*/
/datum/controller/subsystem/processing/instruments/proc/initialize_instrument_data()
instrument_data = list()
for(var/path in subtypesof(/datum/instrument))
var/datum/instrument/I = path
if(initial(I.abstract_type) == path)
continue
I = new path
I.Initialize()
if(!I.id)
qdel(I)
continue
else
instrument_data[I.id] = I
CHECK_TICK
/**
* Reserves a sound channel for a given instrument datum
*
* Arguments:
* * I - The instrument datum
*/
/datum/controller/subsystem/processing/instruments/proc/reserve_instrument_channel(datum/instrument/I)
if(current_instrument_channels > max_instrument_channels)
return
. = SSsounds.reserve_sound_channel(I)
if(!isnull(.))
current_instrument_channels++
/**
* Called when a datum/song is created
*
* Arguments:
* * S - The created datum/song
*/
/datum/controller/subsystem/processing/instruments/proc/on_song_new(datum/song/S)
LAZYADD(songs, S)
/**
* Called when a datum/song is deleted
*
* Arguments:
* * S - The deleted datum/song
*/
/datum/controller/subsystem/processing/instruments/proc/on_song_del(datum/song/S)
LAZYREMOVE(songs, S)
/**
* Returns the instrument datum at the given ID or path
*
* Arguments:
* * id_or_path - The ID or path of the instrument
*/
/datum/controller/subsystem/processing/instruments/proc/get_instrument(id_or_path)
return instrument_data["[id_or_path]"]
+165
View File
@@ -0,0 +1,165 @@
#define DATUMLESS "NO_DATUM"
SUBSYSTEM_DEF(sounds)
name = "Sounds"
init_order = INIT_ORDER_SOUNDS
flags = SS_NO_FIRE
offline_implications = "Sounds may not play correctly. Shuttle call recommended."
var/using_channels_max = CHANNEL_HIGHEST_AVAILABLE // BYOND max channels
/// Amount of channels to reserve for random usage rather than reservations being allowed to reserve all channels. Also a nice safeguard for when someone screws up.
var/random_channels_min = 50
// Hey uh these two needs to be initialized fast because the whole "things get deleted before init" thing.
/// Assoc list, "[channel]" = either the datum using it or TRUE for an unsafe-reserved (datumless reservation) channel
var/list/using_channels
/// Assoc list datum = list(channel1, channel2, ...) for what channels something reserved.
var/list/using_channels_by_datum
// Special datastructure for fast channel management
/// List of all channels as numbers
var/list/channel_list
/// Associative list of all reserved channels associated to their position. "[channel_number]" = index as number
var/list/reserved_channels
/// lower iteration position - Incremented and looped to get "random" sound channels for normal sounds. The channel at this index is returned when asking for a random channel.
var/channel_random_low
/// higher reserve position - decremented and incremented to reserve sound channels, anything above this is reserved. The channel at this index is the highest unreserved channel.
var/channel_reserve_high
/datum/controller/subsystem/sounds/Initialize()
setup_available_channels()
return ..()
/**
* Sets up all available sound channels
*/
/datum/controller/subsystem/sounds/proc/setup_available_channels()
channel_list = list()
reserved_channels = list()
using_channels = list()
using_channels_by_datum = list()
for(var/i in 1 to using_channels_max)
channel_list += i
channel_random_low = 1
channel_reserve_high = length(channel_list)
/**
* Removes a channel from using list
*
* Arguments:
* * channel - The channel number
*/
/datum/controller/subsystem/sounds/proc/free_sound_channel(channel)
var/text_channel = num2text(channel)
var/using = using_channels[text_channel]
using_channels -= text_channel
if(!using) // datum channel
using_channels_by_datum[using] -= channel
if(!length(using_channels_by_datum[using]))
using_channels_by_datum -= using
free_channel(channel)
/**
* Frees all the channels a datum is using
*
* Arguments:
* * D - The datum
*/
/datum/controller/subsystem/sounds/proc/free_datum_channels(datum/D)
var/list/L = using_channels_by_datum[D]
if(!L)
return
for(var/channel in L)
using_channels -= num2text(channel)
free_channel(channel)
using_channels_by_datum -= D
/**
* Frees all datumless channels
*/
/datum/controller/subsystem/sounds/proc/free_datumless_channels()
free_datum_channels(DATUMLESS)
/**
* NO AUTOMATIC CLEANUP - If you use this, you better manually free it later!
*
* Returns an integer for channel
*/
/datum/controller/subsystem/sounds/proc/reserve_sound_channel_datumless()
. = reserve_channel()
if(!.) // oh no..
return FALSE
var/text_channel = num2text(.)
using_channels[text_channel] = DATUMLESS
LAZYADD(using_channels_by_datum[DATUMLESS], .)
/**
* Reserves a channel for a datum. Automatic cleanup only when the datum is deleted.
*
* Returns an integer for channel
* Arguments:
* * D - The datum
*/
/datum/controller/subsystem/sounds/proc/reserve_sound_channel(datum/D)
if(!D) // i don't like typechecks but someone will fuck it up
CRASH("Attempted to reserve sound channel without datum using the managed proc.")
. = reserve_channel()
if(!.)
return FALSE
var/text_channel = num2text(.)
using_channels[text_channel] = D
LAZYADD(using_channels_by_datum[D], .)
/**
* Reserves a channel and updates the datastructure. Private proc.
*/
/datum/controller/subsystem/sounds/proc/reserve_channel()
PRIVATE_PROC(TRUE)
if(channel_reserve_high <= random_channels_min) // out of channels
return
var/channel = channel_list[channel_reserve_high]
reserved_channels[num2text(channel)] = channel_reserve_high--
return channel
/**
* Frees a channel and updates the datastructure. Private proc.
*/
/datum/controller/subsystem/sounds/proc/free_channel(number)
PRIVATE_PROC(TRUE)
var/text_channel = num2text(number)
var/index = reserved_channels[text_channel]
if(!index)
CRASH("Attempted to (internally) free a channel that wasn't reserved.")
reserved_channels -= text_channel
// push reserve index up, which makes it now on a channel that is reserved
channel_reserve_high++
// swap the reserved channel with the unreserved channel so the reserve index is now on an unoccupied channel and the freed channel is next to be used.
channel_list.Swap(channel_reserve_high, index)
// now, an existing reserved channel will likely (exception: unreserving last reserved channel) be at index
// get it, and update position.
var/text_reserved = num2text(channel_list[index])
if(!reserved_channels[text_reserved]) // if it isn't already reserved make sure we don't accidently mistakenly put it on reserved list!
return
reserved_channels[text_reserved] = index
/**
* Random available channel, returns text
*/
/datum/controller/subsystem/sounds/proc/random_available_channel_text()
if(channel_random_low > channel_reserve_high)
channel_random_low = 1
. = "[channel_list[channel_random_low++]]"
/**
* Random available channel, returns number
*/
/datum/controller/subsystem/sounds/proc/random_available_channel()
if(channel_random_low > channel_reserve_high)
channel_random_low = 1
. = channel_list[channel_random_low++]
/**
* How many channels we have left
*/
/datum/controller/subsystem/sounds/proc/available_channels_left()
return length(channel_list) - random_channels_min
#undef DATUMLESS
+3 -16
View File
@@ -190,9 +190,6 @@
/datum/action/item_action/toggle_mister
name = "Toggle Mister"
/datum/action/item_action/toggle_headphones
name = "Toggle Headphones"
/datum/action/item_action/toggle_helmet_light
name = "Toggle Helmet Light"
@@ -232,19 +229,6 @@
button.name = name
..()
/datum/action/item_action/synthswitch
name = "Change Synthesizer Instrument"
desc = "Change the type of instrument your synthesizer is playing as."
/datum/action/item_action/synthswitch/Trigger()
if(istype(target, /obj/item/instrument/piano_synth))
var/obj/item/instrument/piano_synth/synth = target
var/chosen = input("Choose the type of instrument you want to use", "Instrument Selection", "piano") as null|anything in synth.insTypes
if(!synth.insTypes[chosen])
return
return synth.changeInstrument(chosen)
return ..()
/datum/action/item_action/vortex_recall
name = "Vortex Recall"
desc = "Recall yourself, and anyone nearby, to an attuned hierophant beacon at any time.<br>If the beacon is still attached, will detach it."
@@ -257,6 +241,9 @@
return 0
return ..()
/datum/action/item_action/change_headphones_song
name = "Change Headphones Song"
/datum/action/item_action/toggle
/datum/action/item_action/toggle/New(Target)
+58
View File
@@ -0,0 +1,58 @@
/datum/component/spooky
var/too_spooky = TRUE //will it spawn a new instrument?
/datum/component/spooky/Initialize()
RegisterSignal(parent, COMSIG_ITEM_ATTACK, .proc/spectral_attack)
/datum/component/spooky/proc/spectral_attack(datum/source, mob/living/carbon/C, mob/user)
if(ishuman(user)) //this weapon wasn't meant for mortals.
var/mob/living/carbon/human/U = user
if(!istype(U.dna.species, /datum/species/skeleton))
U.adjustStaminaLoss(35) //Extra Damage
U.Jitter(35)
U.stuttering = 20
if(U.getStaminaLoss() > 95)
to_chat(U, "<font color='red' size='4'><b>Your ears weren't meant for this spectral sound.</b></font>")
spectral_change(U)
return
if(ishuman(C))
var/mob/living/carbon/human/H = C
if(istype(H.dna.species, /datum/species/skeleton))
return //undeads are unaffected by the spook-pocalypse.
C.Jitter(35)
C.stuttering = 20
if(!istype(H.dna.species, /datum/species/diona) && !istype(H.dna.species, /datum/species/machine) && !istype(H.dna.species, /datum/species/slime) && !istype(H.dna.species, /datum/species/golem) && !istype(H.dna.species, /datum/species/plasmaman))
C.adjustStaminaLoss(25) //boneless humanoids don't lose the will to live
to_chat(C, "<font color='red' size='4'><B>DOOT</B></font>")
spectral_change(H)
else //the sound will spook monkeys.
C.Jitter(15)
C.stuttering = 20
/datum/component/spooky/proc/spectral_change(mob/living/carbon/human/H, mob/user)
if((H.getStaminaLoss() > 95) && (!istype(H.dna.species, /datum/species/diona) && !istype(H.dna.species, /datum/species/machine) && !istype(H.dna.species, /datum/species/slime) && !istype(H.dna.species, /datum/species/golem) && !istype(H.dna.species, /datum/species/plasmaman) && !istype(H.dna.species, /datum/species/skeleton)))
H.Stun(20)
H.set_species(/datum/species/skeleton)
H.visible_message("<span class='warning'>[H] has given up on life as a mortal.</span>")
var/T = get_turf(H)
if(too_spooky)
if(prob(30))
new/obj/item/instrument/saxophone/spectral(T)
else if(prob(30))
new/obj/item/instrument/trumpet/spectral(T)
else if(prob(30))
new/obj/item/instrument/trombone/spectral(T)
else
to_chat(H, "<span class='boldwarning'>The spooky gods forgot to ship your instrument. Better luck next unlife.</span>")
to_chat(H, "<span class='boldnotice'>You are the spooky skeleton!</span>")
to_chat(H, "<span class='boldnotice'>A new life and identity has begun. Help your fellow skeletons into bringing out the spooky-pocalypse. You haven't forgotten your past life, and are still beholden to past loyalties.</span>")
change_name(H) //time for a new name!
/datum/component/spooky/proc/change_name(mob/living/carbon/human/H)
var/t = stripped_input(H, "Enter your new skeleton name", H.real_name, null, MAX_NAME_LEN)
if(!t)
t = "spooky skeleton"
H.real_name = t
H.name = t
+1 -1
View File
@@ -71,7 +71,7 @@
var/list/atoms_cache = output_atoms
var/sound/S = sound(soundfile)
if(direct)
S.channel = open_sound_channel()
S.channel = SSsounds.random_available_channel()
S.volume = volume
for(var/i in 1 to atoms_cache.len)
var/atom/thing = atoms_cache[i]
+8
View File
@@ -736,6 +736,14 @@ GLOBAL_LIST_INIT(uplink_items, subtypesof(/datum/uplink_item))
cost = 12 // normally 18
gamemodes = list(/datum/game_mode/nuclear)
/datum/uplink_item/ammo/bulldog_XLmagsbag
name = "Bulldog - 12g XL Magazine Duffel Bag"
desc = "A duffel bag containing three 16 round drum magazines(Slug, Buckshot, Dragon's Breath)."
reference = "12XLDB"
item = /obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags
cost = 12 // normally 18
gamemodes = list(/datum/game_mode/nuclear)
/datum/uplink_item/ammo/smg
name = "C-20r - .45 Magazine"
desc = "An additional 20-round .45 magazine for use in the C-20r submachine gun. These bullets pack a lot of punch that can knock most targets down, but do limited overall damage."
+15 -14
View File
@@ -33,7 +33,7 @@
. += "The door bolts [A.locked ? "have fallen!" : "look up."]"
. += "The door bolt lights are [(A.lights && haspower) ? "on." : "off!"]"
. += "The test light is [haspower ? "on." : "off!"]"
. += "The 'AI control allowed' light is [(A.aiControlDisabled == 0 && !A.emagged && haspower) ? "on" : "off"]."
. += "The 'AI control allowed' light is [(A.aiControlDisabled == AICONTROLDISABLED_OFF && !A.emagged && haspower) ? "on" : "off"]."
. += "The 'Check Wiring' light is [(A.safe == 0 && haspower) ? "on" : "off"]."
. += "The 'Check Timing Mechanism' light is [(A.normalspeed == 0 && haspower) ? "on" : "off"]."
. += "The emergency lights are [(A.emergency && haspower) ? "on" : "off"]."
@@ -74,16 +74,16 @@
if(!mend)
//one wire for AI control. Cutting this prevents the AI from controlling the door unless it has hacked the door through the power connection (which takes about a minute). If both main and backup power are cut, as well as this wire, then the AI cannot operate or hack the door at all.
//aiControlDisabled: If 1, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
if(A.aiControlDisabled == 0)
A.aiControlDisabled = 1
else if(A.aiControlDisabled == -1)
A.aiControlDisabled = 2
//aiControlDisabled: see explanation in code\__DEFINES\construction.dm#32
if(A.aiControlDisabled == AICONTROLDISABLED_OFF)
A.aiControlDisabled = AICONTROLDISABLED_ON
else if(A.aiControlDisabled == AICONTROLDISABLED_PERMA)
A.aiControlDisabled = AICONTROLDISABLED_BYPASS
else
if(A.aiControlDisabled == 1)
A.aiControlDisabled = 0
else if(A.aiControlDisabled == 2)
A.aiControlDisabled = -1
if(A.aiControlDisabled == AICONTROLDISABLED_ON)
A.aiControlDisabled = AICONTROLDISABLED_OFF
else if(A.aiControlDisabled == AICONTROLDISABLED_BYPASS)
A.aiControlDisabled = AICONTROLDISABLED_PERMA
if(WIRE_ELECTRIFY)
if(!mend)
@@ -136,10 +136,11 @@
A.loseBackupPower()
if(WIRE_AI_CONTROL)
if(A.aiControlDisabled == 0)
A.aiControlDisabled = 1
else if(A.aiControlDisabled == -1)
A.aiControlDisabled = 2
if(A.aiControlDisabled == AICONTROLDISABLED_OFF)
A.aiControlDisabled = AICONTROLDISABLED_ON
else if(A.aiControlDisabled == AICONTROLDISABLED_PERMA)
A.aiControlDisabled = AICONTROLDISABLED_BYPASS
addtimer(CALLBACK(A, /obj/machinery/door/airlock/.proc/ai_control_callback), 1 SECONDS)
if(WIRE_ELECTRIFY)
+343 -403
View File
@@ -7,6 +7,11 @@
#define NEGATE_MUTATION_THRESHOLD 30 // Occupants with over ## percent radiation threshold will not gain mutations
#define PAGE_UI "ui"
#define PAGE_SE "se"
#define PAGE_BUFFER "buffer"
#define PAGE_REJUVENATORS "rejuvenators"
//list("data" = null, "owner" = null, "label" = null, "type" = null, "ue" = 0),
/datum/dna2/record
var/datum/dna/dna = null
@@ -161,6 +166,7 @@
occupant = usr
icon_state = "scanner_occupied"
add_fingerprint(usr)
SStgui.update_uis(src)
/obj/machinery/dna_scannernew/MouseDrop_T(atom/movable/O, mob/user)
if(!istype(O))
@@ -214,6 +220,7 @@
return
beaker = I
SStgui.update_uis(src)
I.forceMove(src)
user.visible_message("[user] adds \a [I] to \the [src]!", "You add \a [I] to \the [src]!")
return
@@ -260,6 +267,7 @@
M.forceMove(src)
occupant = M
icon_state = "scanner_occupied"
SStgui.update_uis(src)
// search for ghosts, if the corpse is empty and the scanner is connected to a cloner
if(locate(/obj/machinery/computer/cloning, get_step(src, NORTH)) \
@@ -281,6 +289,7 @@
occupant.forceMove(loc)
occupant = null
icon_state = "scanner_open"
SStgui.update_uis(src)
/obj/machinery/dna_scannernew/force_eject_occupant()
go_out(null, TRUE)
@@ -296,6 +305,7 @@
occupant = null
updateUsrDialog()
update_icon()
SStgui.update_uis(src)
// Checks if occupants can be irradiated/mutated - prevents exploits where wearing full rad protection would still let you gain mutations
/obj/machinery/dna_scannernew/proc/radiation_check()
@@ -333,12 +343,11 @@
var/injector_ready = FALSE //Quick fix for issue 286 (screwdriver the screen twice to restore injector) -Pete
var/obj/machinery/dna_scannernew/connected = null
var/obj/item/disk/data/disk = null
var/selected_menu_key = null
var/selected_menu_key = PAGE_UI
anchored = TRUE
use_power = IDLE_POWER_USE
idle_power_usage = 10
active_power_usage = 400
var/waiting_for_user_input = 0 // Fix for #274 (Mash create block injector without answering dialog to make unlimited injectors) - N3X
/obj/machinery/computer/scan_consolenew/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/disk/data)) //INSERT SOME diskS
@@ -347,7 +356,7 @@
I.forceMove(src)
disk = I
to_chat(user, "You insert [I].")
SSnanoui.update_uis(src) // update all UIs attached to src()
SStgui.update_uis(src)
return
else
return ..()
@@ -399,35 +408,18 @@
if(stat & (NOPOWER|BROKEN))
return
ui_interact(user)
tgui_interact(user)
/**
* The ui_interact proc is used to open and update Nano UIs
* If ui_interact is not used then the UI will not update correctly
* ui_interact is currently defined for /atom/movable
*
* @param user /mob The mob who is interacting with this ui
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
* @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
*
* @return nothing
*/
/obj/machinery/computer/scan_consolenew/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
/obj/machinery/computer/scan_consolenew/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)
if(user == connected.occupant)
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, "dna_modifier.tmpl", "DNA Modifier Console", 660, 700)
// open the new ui window
ui = new(user, src, ui_key, "DNAModifier", name, 660, 700, master_ui, state)
ui.open()
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/computer/scan_consolenew/ui_data(mob/user, datum/topic_state/state)
/obj/machinery/computer/scan_consolenew/tgui_data(mob/user)
var/data[0]
data["selectedMenuKey"] = selected_menu_key
data["locked"] = connected.locked
@@ -501,9 +493,12 @@
for(var/datum/reagent/R in connected.beaker.reagents.reagent_list)
data["beakerVolume"] += R.volume
// Transfer modal information if there is one
data["modal"] = tgui_modal_data(src)
return data
/obj/machinery/computer/scan_consolenew/Topic(href, href_list)
/obj/machinery/computer/scan_consolenew/tgui_act(action, params)
if(..())
return FALSE // don't update uis
if(!istype(usr.loc, /turf))
@@ -512,405 +507,350 @@
return FALSE // don't update uis
if(irradiating) // Make sure that it isn't already irradiating someone...
return FALSE // don't update uis
if(stat & (NOPOWER|BROKEN))
return
add_fingerprint(usr)
if(href_list["selectMenuKey"])
selected_menu_key = href_list["selectMenuKey"]
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["toggleLock"])
if((connected && connected.occupant))
connected.locked = !(connected.locked)
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["pulseRadiation"])
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SSnanoui.update_uis(src) // update all UIs attached to src
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(!connected.occupant)
return TRUE // return 1 forces an update to all Nano uis attached to src
var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return TRUE
if(prob(95))
if(prob(75))
randmutb(connected.occupant)
else
randmuti(connected.occupant)
else
if(prob(95))
randmutg(connected.occupant)
else
randmuti(connected.occupant)
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["radiationDuration"])
if(text2num(href_list["radiationDuration"]) > 0)
if(radiation_duration < 20)
radiation_duration += 2
else
if(radiation_duration > 2)
radiation_duration -= 2
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["radiationIntensity"])
if(text2num(href_list["radiationIntensity"]) > 0)
if(radiation_intensity < 10)
radiation_intensity++
else
if(radiation_intensity > 1)
radiation_intensity--
return TRUE // return 1 forces an update to all Nano uis attached to src
////////////////////////////////////////////////////////
if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) > 0)
if(selected_ui_target < 15)
selected_ui_target++
selected_ui_target_hex = selected_ui_target
switch(selected_ui_target)
if(10)
selected_ui_target_hex = "A"
if(11)
selected_ui_target_hex = "B"
if(12)
selected_ui_target_hex = "C"
if(13)
selected_ui_target_hex = "D"
if(14)
selected_ui_target_hex = "E"
if(15)
selected_ui_target_hex = "F"
else
selected_ui_target = 0
selected_ui_target_hex = 0
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["changeUITarget"] && text2num(href_list["changeUITarget"]) < 1)
if(selected_ui_target > 0)
selected_ui_target--
selected_ui_target_hex = selected_ui_target
switch(selected_ui_target)
if(10)
selected_ui_target_hex = "A"
if(11)
selected_ui_target_hex = "B"
if(12)
selected_ui_target_hex = "C"
if(13)
selected_ui_target_hex = "D"
if(14)
selected_ui_target_hex = "E"
else
selected_ui_target = 15
selected_ui_target_hex = "F"
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["selectUIBlock"] && href_list["selectUISubblock"]) // This chunk of code updates selected block / sub-block based on click
var/select_block = text2num(href_list["selectUIBlock"])
var/select_subblock = text2num(href_list["selectUISubblock"])
if((select_block <= DNA_UI_LENGTH) && (select_block >= 1))
selected_ui_block = select_block
if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1))
selected_ui_subblock = select_subblock
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["pulseUIRadiation"])
var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock)
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SSnanoui.update_uis(src) // update all UIs attached to src
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(!connected.occupant)
return TRUE
if(prob((80 + (radiation_duration / 2))))
var/radiation = (radiation_intensity + radiation_duration)
connected.occupant.apply_effect(radiation,IRRADIATE,0)
if(connected.radiation_check())
return TRUE
block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration)
connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block)
connected.occupant.UpdateAppearance()
else
var/radiation = ((radiation_intensity * 2) + radiation_duration)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return TRUE
if(prob(20 + radiation_intensity))
randmutb(connected.occupant)
domutcheck(connected.occupant, connected)
else
randmuti(connected.occupant)
connected.occupant.UpdateAppearance()
return TRUE // return 1 forces an update to all Nano uis attached to src
////////////////////////////////////////////////////////
if(href_list["injectRejuvenators"])
if(!connected.occupant)
return FALSE
var/inject_amount = round(text2num(href_list["injectRejuvenators"]), 5) // round to nearest 5
if(inject_amount < 0) // Since the user can actually type the commands himself, some sanity checking
inject_amount = 0
if(inject_amount > 50)
inject_amount = 50
connected.beaker.reagents.trans_to(connected.occupant, inject_amount)
connected.beaker.reagents.reaction(connected.occupant)
return TRUE // return 1 forces an update to all Nano uis attached to src
////////////////////////////////////////////////////////
if(href_list["selectSEBlock"] && href_list["selectSESubblock"]) // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes)
var/select_block = text2num(href_list["selectSEBlock"])
var/select_subblock = text2num(href_list["selectSESubblock"])
if((select_block <= DNA_SE_LENGTH) && (select_block >= 1))
selected_se_block = select_block
if((select_subblock <= DNA_BLOCK_SIZE) && (select_subblock >= 1))
selected_se_subblock = select_subblock
//testing("User selected block [selected_se_block] (sent [select_block]), subblock [selected_se_subblock] (sent [select_block]).")
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["pulseSERadiation"])
var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock)
//var/original_block=block
//testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...")
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SSnanoui.update_uis(src) // update all UIs attached to src
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(connected.occupant)
if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3)))))
var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return 1
var/real_SE_block=selected_se_block
block = miniscramble(block, radiation_intensity, radiation_duration)
if(prob(20))
if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2)
real_SE_block++
else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH)
real_SE_block--
//testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block)
domutcheck(connected.occupant, connected)
else
var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return 1
if(prob(80 - radiation_duration))
//testing("Random bad mut!")
randmutb(connected.occupant)
domutcheck(connected.occupant, connected)
else
randmuti(connected.occupant)
//testing("Random identity mut!")
connected.occupant.UpdateAppearance()
return TRUE // return 1 forces an update to all Nano uis attached to src
if(href_list["ejectBeaker"])
if(connected.beaker)
var/obj/item/reagent_containers/glass/B = connected.beaker
B.forceMove(connected.loc)
connected.beaker = null
if(tgui_act_modal(action, params))
return TRUE
if(href_list["ejectOccupant"])
connected.eject_occupant(usr)
return TRUE
// Transfer Buffer Management
if(href_list["bufferOption"])
var/bufferOption = href_list["bufferOption"]
// These bufferOptions do not require a bufferId
if(bufferOption == "wipeDisk")
if((isnull(disk)) || (disk.read_only))
//temphtml = "Invalid disk. Please try again."
return FALSE
disk.buf = null
//temphtml = "Data saved."
return TRUE
if(bufferOption == "ejectDisk")
if(!disk)
. = TRUE
switch(action)
if("selectMenuKey")
var/key = params["key"]
if(!(key in list(PAGE_UI, PAGE_SE, PAGE_BUFFER, PAGE_REJUVENATORS)))
return
disk.forceMove(get_turf(src))
disk = null
return TRUE
// All bufferOptions from here on require a bufferId
if(!href_list["bufferId"])
return FALSE
var/bufferId = text2num(href_list["bufferId"])
if(bufferId < 1 || bufferId > 3)
return FALSE // Not a valid buffer id
if(bufferOption == "saveUI")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_UI // DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name=connected.occupant.name
databuf.name = "Unique Identifier"
buffers[bufferId] = databuf
return TRUE
if(bufferOption == "saveUIAndUE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_UI|DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name=connected.occupant.dna.real_name
databuf.name = "Unique Identifier + Unique Enzymes"
buffers[bufferId] = databuf
return TRUE
if(bufferOption == "saveSE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_SE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name = connected.occupant.dna.real_name
databuf.name = "Structural Enzymes"
buffers[bufferId] = databuf
return TRUE
if(bufferOption == "clear")
buffers[bufferId] = new /datum/dna2/record()
return TRUE
if(bufferOption == "changeLabel")
var/datum/dna2/record/buf = buffers[bufferId]
var/text = sanitize(input(usr, "New Label:", "Edit Label", buf.name) as text|null)
buf.name = text
buffers[bufferId] = buf
return TRUE
if(bufferOption == "transfer")
if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna)
return TRUE
irradiating = 2
selected_menu_key = key
if("toggleLock")
if(connected && connected.occupant)
connected.locked = !(connected.locked)
if("pulseRadiation")
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SSnanoui.update_uis(src) // update all UIs attached to src
sleep(2 SECONDS)
SStgui.update_uis(src)
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
var/radiation = (rand(20,50) / connected.damage_coeff)
if(!connected.occupant)
return
var/radiation = (((radiation_intensity * 3) + radiation_duration * 3) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return TRUE
return
var/datum/dna2/record/buf = buffers[bufferId]
if((buf.types & DNA2_BUF_UI))
if((buf.types & DNA2_BUF_UE))
connected.occupant.real_name = buf.dna.real_name
connected.occupant.name = buf.dna.real_name
connected.occupant.UpdateAppearance(buf.dna.UI.Copy())
else if(buf.types & DNA2_BUF_SE)
connected.occupant.dna.SE = buf.dna.SE.Copy()
connected.occupant.dna.UpdateSE()
domutcheck(connected.occupant, connected)
return TRUE
if(bufferOption == "createInjector")
if(injector_ready && !waiting_for_user_input)
var/success = 1
var/obj/item/dnainjector/I = new /obj/item/dnainjector
var/datum/dna2/record/buf = buffers[bufferId]
buf = buf.copy()
if(href_list["createBlockInjector"])
waiting_for_user_input=1
var/list/selectedbuf
if(buf.types & DNA2_BUF_SE)
selectedbuf=buf.dna.SE
else
selectedbuf=buf.dna.UI
var/blk = input(usr,"Select Block","Block") as null|anything in all_dna_blocks(selectedbuf)
success = setInjectorBlock(I,blk,buf)
if(prob(95))
if(prob(75))
randmutb(connected.occupant)
else
I.buf = buf
waiting_for_user_input = 0
if(success)
I.forceMove(loc)
I.name += " ([buf.name])"
if(connected)
I.damage_coeff = connected.damage_coeff
injector_ready = FALSE
spawn(300)
injector_ready = TRUE
return TRUE
randmuti(connected.occupant)
else
if(prob(95))
randmutg(connected.occupant)
else
randmuti(connected.occupant)
if("radiationDuration")
radiation_duration = clamp(text2num(params["value"]), 1, 20)
if("radiationIntensity")
radiation_intensity = clamp(text2num(params["value"]), 1, 10)
////////////////////////////////////////////////////////
if("changeUITarget")
selected_ui_target = clamp(text2num(params["value"]), 1, 15)
selected_ui_target_hex = num2text(selected_ui_target, 1, 16)
if("selectUIBlock") // This chunk of code updates selected block / sub-block based on click
var/select_block = text2num(params["block"])
var/select_subblock = text2num(params["subblock"])
if(!select_block || !select_subblock)
return
if(bufferOption == "loadDisk")
if((isnull(disk)) || (!disk.buf))
//temphtml = "Invalid disk. Please try again."
return FALSE
selected_ui_block = clamp(select_block, 1, DNA_UI_LENGTH)
selected_ui_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
if("pulseUIRadiation")
var/block = connected.occupant.dna.GetUISubBlock(selected_ui_block, selected_ui_subblock)
buffers[bufferId] = disk.buf.copy()
//temphtml = "Data loaded."
return TRUE
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
if(bufferOption == "saveDisk")
if((isnull(disk)) || (disk.read_only))
//temphtml = "Invalid disk. Please try again."
return FALSE
SStgui.update_uis(src)
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
var/datum/dna2/record/buf = buffers[bufferId]
irradiating = 0
connected.locked = lock_state
disk.buf = buf.copy()
disk.name = "data disk - '[buf.dna.real_name]'"
//temphtml = "Data saved."
return TRUE
if(!connected.occupant)
return
if(prob((80 + (radiation_duration / 2))))
var/radiation = (radiation_intensity + radiation_duration)
connected.occupant.apply_effect(radiation,IRRADIATE,0)
if(connected.radiation_check())
return
block = miniscrambletarget(num2text(selected_ui_target), radiation_intensity, radiation_duration)
connected.occupant.dna.SetUISubBlock(selected_ui_block, selected_ui_subblock, block)
connected.occupant.UpdateAppearance()
else
var/radiation = ((radiation_intensity * 2) + radiation_duration)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return
if(prob(20 + radiation_intensity))
randmutb(connected.occupant)
domutcheck(connected.occupant, connected)
else
randmuti(connected.occupant)
connected.occupant.UpdateAppearance()
////////////////////////////////////////////////////////
if("injectRejuvenators")
if(!connected.occupant || !connected.beaker)
return
var/inject_amount = clamp(round(text2num(params["amount"]), 5), 0, 50) // round to nearest 5 and clamp to 0-50
if(!inject_amount)
return
connected.beaker.reagents.trans_to(connected.occupant, inject_amount)
connected.beaker.reagents.reaction(connected.occupant)
////////////////////////////////////////////////////////
if("selectSEBlock") // This chunk of code updates selected block / sub-block based on click (se stands for strutural enzymes)
var/select_block = text2num(params["block"])
var/select_subblock = text2num(params["subblock"])
if(!select_block || !select_subblock)
return
selected_se_block = clamp(select_block, 1, DNA_SE_LENGTH)
selected_se_subblock = clamp(select_subblock, 1, DNA_BLOCK_SIZE)
if("pulseSERadiation")
var/block = connected.occupant.dna.GetSESubBlock(selected_se_block, selected_se_subblock)
//var/original_block=block
//testing("Irradiating SE block [selected_se_block]:[selected_se_subblock] ([block])...")
irradiating = radiation_duration
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SStgui.update_uis(src)
sleep(10 * radiation_duration) // sleep for radiation_duration seconds
irradiating = 0
connected.locked = lock_state
if(connected.occupant)
if(prob((80 + ((radiation_duration / 2) + (connected.precision_coeff ** 3)))))
var/radiation = ((radiation_intensity + radiation_duration) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return 1
var/real_SE_block=selected_se_block
block = miniscramble(block, radiation_intensity, radiation_duration)
if(prob(20))
if(selected_se_block > 1 && selected_se_block < DNA_SE_LENGTH/2)
real_SE_block++
else if(selected_se_block > DNA_SE_LENGTH/2 && selected_se_block < DNA_SE_LENGTH)
real_SE_block--
//testing("Irradiated SE block [real_SE_block]:[selected_se_subblock] ([original_block] now [block]) [(real_SE_block!=selected_se_block) ? "(SHIFTED)":""]!")
connected.occupant.dna.SetSESubBlock(real_SE_block, selected_se_subblock, block)
domutcheck(connected.occupant, connected)
else
var/radiation = (((radiation_intensity * 2) + radiation_duration) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return
if(prob(80 - radiation_duration))
//testing("Random bad mut!")
randmutb(connected.occupant)
domutcheck(connected.occupant, connected)
else
randmuti(connected.occupant)
//testing("Random identity mut!")
connected.occupant.UpdateAppearance()
if("ejectBeaker")
if(connected.beaker)
var/obj/item/reagent_containers/glass/B = connected.beaker
B.forceMove(connected.loc)
connected.beaker = null
if("ejectOccupant")
connected.eject_occupant()
// Transfer Buffer Management
if("bufferOption")
var/bufferOption = params["option"]
var/bufferId = text2num(params["id"])
if(bufferId < 1 || bufferId > 3) // Not a valid buffer id
return
var/datum/dna2/record/buffer = buffers[bufferId]
switch(bufferOption)
if("saveUI")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_UI // DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name=connected.occupant.name
databuf.name = "Unique Identifier"
buffers[bufferId] = databuf
if("saveUIAndUE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_UI|DNA2_BUF_UE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name=connected.occupant.dna.real_name
databuf.name = "Unique Identifier + Unique Enzymes"
buffers[bufferId] = databuf
if("saveSE")
if(connected.occupant && connected.occupant.dna)
var/datum/dna2/record/databuf = new
databuf.types = DNA2_BUF_SE
databuf.dna = connected.occupant.dna.Clone()
if(ishuman(connected.occupant))
databuf.dna.real_name = connected.occupant.dna.real_name
databuf.name = "Structural Enzymes"
buffers[bufferId] = databuf
if("clear")
buffers[bufferId] = new /datum/dna2/record()
if("changeLabel")
tgui_modal_input(src, "changeBufferLabel", "Please enter the new buffer label:", null, list("id" = bufferId), buffer.name, TGUI_MODAL_INPUT_MAX_LENGTH_NAME)
if("transfer")
if(!connected.occupant || (NOCLONE in connected.occupant.mutations && connected.scan_level < 3) || !connected.occupant.dna)
return
irradiating = 2
var/lock_state = connected.locked
connected.locked = TRUE //lock it
SStgui.update_uis(src)
sleep(2 SECONDS)
irradiating = 0
connected.locked = lock_state
var/radiation = (rand(20,50) / connected.damage_coeff)
connected.occupant.apply_effect(radiation, IRRADIATE, 0)
if(connected.radiation_check())
return
var/datum/dna2/record/buf = buffers[bufferId]
if((buf.types & DNA2_BUF_UI))
if((buf.types & DNA2_BUF_UE))
connected.occupant.real_name = buf.dna.real_name
connected.occupant.name = buf.dna.real_name
connected.occupant.UpdateAppearance(buf.dna.UI.Copy())
else if(buf.types & DNA2_BUF_SE)
connected.occupant.dna.SE = buf.dna.SE.Copy()
connected.occupant.dna.UpdateSE()
domutcheck(connected.occupant, connected)
if("createInjector")
if(!injector_ready)
return
if(text2num(params["block"]) > 0)
var/list/choices = all_dna_blocks((buffer.types & DNA2_BUF_SE) ? buffer.dna.SE : buffer.dna.UI)
tgui_modal_choice(src, "createInjectorBlock", "Please select the block to create an injector from:", null, list("id" = bufferId), null, choices)
else
create_injector(bufferId, TRUE)
if("loadDisk")
if(isnull(disk) || disk.read_only)
return
buffers[bufferId] = disk.buf.copy()
if("saveDisk")
if(isnull(disk) || disk.read_only)
return
var/datum/dna2/record/buf = buffers[bufferId]
disk.buf = buf.copy()
disk.name = "data disk - '[buf.dna.real_name]'"
if("wipeDisk")
if(isnull(disk) || disk.read_only)
return
disk.buf = null
if("ejectDisk")
if(!disk)
return
disk.forceMove(get_turf(src))
disk = null
/**
* Creates a blank injector with the name of the buffer at the given buffer_id
*
* Arguments:
* * buffer_id - The ID of the buffer
* * copy_buffer - Whether the injector should copy the buffer contents
*/
/obj/machinery/computer/scan_consolenew/proc/create_injector(buffer_id, copy_buffer = FALSE)
if(buffer_id < 1 || buffer_id > length(buffers))
return
// Cooldown
injector_ready = FALSE
addtimer(CALLBACK(src, .proc/injector_cooldown_finish), 30 SECONDS)
// Create it
var/datum/dna2/record/buf = buffers[buffer_id]
var/obj/item/dnainjector/I = new()
I.forceMove(loc)
I.name += " ([buf.name])"
if(copy_buffer)
I.buf = buf.copy()
if(connected)
I.damage_coeff = connected.damage_coeff
return I
/**
* Called when the injector creation cooldown finishes
*/
/obj/machinery/computer/scan_consolenew/proc/injector_cooldown_finish()
injector_ready = TRUE
/**
* Called in tgui_act() to process modal actions
*
* Arguments:
* * action - The action passed by tgui
* * params - The params passed by tgui
*/
/obj/machinery/computer/scan_consolenew/proc/tgui_act_modal(action, params)
. = TRUE
var/id = params["id"] // The modal's ID
var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_ANSWER)
var/answer = params["answer"]
switch(id)
if("createInjectorBlock")
var/buffer_id = text2num(arguments["id"])
if(buffer_id < 1 || buffer_id > length(buffers))
return
var/datum/dna2/record/buf = buffers[buffer_id]
var/obj/item/dnainjector/I = create_injector(buffer_id)
setInjectorBlock(I, answer, buf.copy())
if("changeBufferLabel")
var/buffer_id = text2num(arguments["id"])
if(buffer_id < 1 || buffer_id > length(buffers))
return
var/datum/dna2/record/buf = buffers[buffer_id]
buf.name = answer
buffers[buffer_id] = buf
else
return FALSE
else
return FALSE
#undef PAGE_UI
#undef PAGE_SE
#undef PAGE_BUFFER
#undef PAGE_REJUVENATORS
/////////////////////////// DNA MACHINES
+23
View File
@@ -510,3 +510,26 @@ proc/get_all_job_icons() //For all existing HUD icons
return rankName
return "Unknown" //Return unknown if none of the above apply
proc/get_accesslist_static_data(num_min_region = REGION_GENERAL, num_max_region = REGION_COMMAND)
var/list/retval
for(var/i in num_min_region to num_max_region)
var/list/accesses = list()
var/list/available_accesses
if(i == REGION_CENTCOMM) // Override necessary, because get_region_accesses(REGION_CENTCOM) returns BOTH CC and crew accesses.
available_accesses = get_all_centcom_access()
else
available_accesses = get_region_accesses(i)
for(var/access in available_accesses)
var/access_desc = (i == REGION_CENTCOMM) ? get_centcom_access_desc(access) : get_access_desc(access)
if (access_desc)
accesses += list(list(
"desc" = replacetext(access_desc, "&nbsp", " "),
"ref" = access,
))
retval += list(list(
"name" = get_region_accesses_name(i),
"regid" = i,
"accesses" = accesses
))
return retval
+54 -43
View File
@@ -125,7 +125,7 @@
return attack_hand(user)
/obj/machinery/sleeper/attack_ghost(mob/user)
return attack_hand(user)
tgui_interact(user)
/obj/machinery/sleeper/attack_hand(mob/user)
if(stat & (NOPOWER|BROKEN))
@@ -135,17 +135,17 @@
to_chat(user, "<span class='notice'>Close the maintenance panel first.</span>")
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/sleeper/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/sleeper/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)
ui = new(user, src, ui_key, "sleeper.tmpl", "Sleeper", 550, 770)
ui = new(user, src, ui_key, "Sleeper", "Sleeper", 550, 775)
ui.open()
ui.set_auto_update(1)
/obj/machinery/sleeper/ui_data(mob/user, datum/topic_state/state)
/obj/machinery/sleeper/tgui_data(mob/user)
var/data[0]
data["amounts"] = amounts
data["hasOccupant"] = occupant ? 1 : 0
var/occupantData[0]
var/crisis = 0
@@ -210,9 +210,13 @@
if(beaker)
data["isBeakerLoaded"] = 1
if(beaker.reagents)
data["beakerMaxSpace"] = beaker.reagents.maximum_volume
data["beakerFreeSpace"] = round(beaker.reagents.maximum_volume - beaker.reagents.total_volume)
else
data["beakerMaxSpace"] = 0
data["beakerFreeSpace"] = 0
else
data["isBeakerLoaded"] = FALSE
var/chemicals[0]
for(var/re in possible_chems)
@@ -234,51 +238,52 @@
if(temp.id in occupant.reagents.overdose_list())
overdosing = 1
// Because I don't know how to do this on the nano side
pretty_amount = round(reagent_amount, 0.05)
chemicals.Add(list(list("title" = temp.name, "id" = temp.id, "commands" = list("chemical" = temp.id), "occ_amount" = reagent_amount, "pretty_amount" = pretty_amount, "injectable" = injectable, "overdosing" = overdosing, "od_warning" = caution)))
data["chemicals"] = chemicals
return data
/obj/machinery/sleeper/Topic(href, href_list)
if(!controls_inside && usr == occupant)
return 0
/obj/machinery/sleeper/tgui_act(action, params)
if(..())
return 1
return
if(!controls_inside && usr == occupant)
return
if(panel_open)
to_chat(usr, "<span class='notice'>Close the maintenance panel first.</span>")
return 0
return
if(stat & (NOPOWER|BROKEN))
return
if((usr.contents.Find(src) || ((get_dist(src, usr) <= 1) && istype(loc, /turf))) || (istype(usr, /mob/living/silicon/ai)))
if(href_list["chemical"])
if(occupant)
if(occupant.stat == DEAD)
to_chat(usr, "<span class='danger'>This person has no life for to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.</span>")
else if(occupant.health > min_health || (href_list["chemical"] in emergency_chems))
inject_chemical(usr,href_list["chemical"],text2num(href_list["amount"]))
else
to_chat(usr, "<span class='danger'>This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!</span>")
if(href_list["removebeaker"])
. = TRUE
switch(action)
if("chemical")
if(!occupant)
return
if(occupant.stat == DEAD)
to_chat(usr, "<span class='danger'>This person has no life to preserve anymore. Take [occupant.p_them()] to a department capable of reanimating them.</span>")
return
var/chemical = params["chemid"]
var/amount = text2num(params["amount"])
if(!length(chemical) || amount <= 0)
return
if(occupant.health > min_health || (chemical in emergency_chems))
inject_chemical(usr, chemical, amount)
else
to_chat(usr, "<span class='danger'>This person is not in good enough condition for sleepers to be effective! Use another means of treatment, such as cryogenics!</span>")
if("removebeaker")
remove_beaker()
if(href_list["togglefilter"])
if("togglefilter")
toggle_filter()
if(href_list["ejectify"])
if("ejectify")
eject()
if(href_list["auto_eject_dead_on"])
if("auto_eject_dead_on")
auto_eject_dead = TRUE
if(href_list["auto_eject_dead_off"])
if("auto_eject_dead_off")
auto_eject_dead = FALSE
add_fingerprint(usr)
return 1
else
return FALSE
add_fingerprint(usr)
/obj/machinery/sleeper/attackby(obj/item/I, mob/user, params)
if(istype(I, /obj/item/reagent_containers/glass))
@@ -290,6 +295,7 @@
beaker = I
I.forceMove(src)
user.visible_message("[user] adds \a [I] to [src]!", "You add \a [I] to [src]!")
SStgui.update_uis(src)
return
else
@@ -328,6 +334,7 @@
to_chat(M, "<span class='boldnotice'>You feel cool air surround you. You go numb as your senses turn inward.</span>")
add_fingerprint(user)
qdel(G)
SStgui.update_uis(src)
return
return ..()
@@ -374,9 +381,11 @@
occupant = null
updateUsrDialog()
update_icon()
SStgui.update_uis(src)
if(A == beaker)
beaker = null
updateUsrDialog()
SStgui.update_uis(src)
/obj/machinery/sleeper/emp_act(severity)
if(filtering)
@@ -394,7 +403,7 @@
qdel(src)
/obj/machinery/sleeper/proc/toggle_filter()
if(filtering)
if(filtering || !beaker)
filtering = 0
else
filtering = 1
@@ -410,29 +419,28 @@
// eject trash the occupant dropped
for(var/atom/movable/A in contents - component_parts - list(beaker))
A.forceMove(loc)
SStgui.update_uis(src)
/obj/machinery/sleeper/force_eject_occupant()
go_out()
/obj/machinery/sleeper/proc/inject_chemical(mob/living/user as mob, chemical, amount)
/obj/machinery/sleeper/proc/inject_chemical(mob/living/user, chemical, amount)
if(!(chemical in possible_chems))
to_chat(user, "<span class='notice'>The sleeper does not offer that chemical!</span>")
return
if(!(amount in amounts))
return
if(occupant)
if(occupant.reagents)
if(occupant.reagents.get_reagent_amount(chemical) + amount <= max_chem)
occupant.reagents.add_reagent(chemical, amount)
return
else
to_chat(user, "You can not inject any more of this chemical.")
return
else
to_chat(user, "The patient rejects the chemicals!")
return
else
to_chat(user, "There's no occupant in the sleeper!")
return
/obj/machinery/sleeper/verb/eject()
set name = "Eject Sleeper"
@@ -459,6 +467,7 @@
filtering = 0
beaker.forceMove(usr.loc)
beaker = null
SStgui.update_uis(src)
add_fingerprint(usr)
return
@@ -511,6 +520,7 @@
add_fingerprint(user)
if(user.pulling == L)
user.stop_pulling()
SStgui.update_uis(src)
return
return
@@ -547,6 +557,7 @@
for(var/obj/O in src)
qdel(O)
add_fingerprint(usr)
SStgui.update_uis(src)
return
return
+38 -30
View File
@@ -65,6 +65,7 @@
icon_state = "body_scanner_1"
add_fingerprint(user)
qdel(TYPECAST_YOUR_SHIT)
SStgui.update_uis(src)
return
return ..()
@@ -127,12 +128,13 @@
occupant = H
icon_state = "bodyscanner"
add_fingerprint(user)
SStgui.update_uis(src)
/obj/machinery/bodyscanner/attack_ai(user)
return attack_hand(user)
/obj/machinery/bodyscanner/attack_ghost(user)
return attack_hand(user)
tgui_interact(user)
/obj/machinery/bodyscanner/attack_hand(user)
if(stat & (NOPOWER|BROKEN))
@@ -145,7 +147,7 @@
to_chat(user, "<span class='notice'>Close the maintenance panel first.</span>")
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/bodyscanner/relaymove(mob/user)
if(user.incapacitated())
@@ -171,6 +173,7 @@
// eject trash the occupant dropped
for(var/atom/movable/A in contents - component_parts)
A.forceMove(loc)
SStgui.update_uis(src)
/obj/machinery/bodyscanner/force_eject_occupant()
go_out()
@@ -192,17 +195,16 @@
new /obj/effect/gibspawner/generic(get_turf(loc)) //I REPLACE YOUR TECHNOLOGY WITH FLESH!
qdel(src)
/obj/machinery/bodyscanner/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/bodyscanner/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)
ui = new(user, src, ui_key, "adv_med.tmpl", "Body Scanner", 690, 600)
ui = new(user, src, ui_key, "BodyScanner", "Body Scanner", 690, 600)
ui.open()
ui.set_auto_update(1)
/obj/machinery/bodyscanner/ui_data(mob/user, datum/topic_state/state)
/obj/machinery/bodyscanner/tgui_data(mob/user)
var/data[0]
data["occupied"] = occupant ? 1 : 0
data["occupied"] = occupant ? TRUE : FALSE
var/occupantData[0]
if(occupant)
@@ -236,9 +238,9 @@
occupantData["hasBorer"] = occupant.has_brain_worms()
var/bloodData[0]
bloodData["hasBlood"] = 0
bloodData["hasBlood"] = FALSE
if(!(NO_BLOOD in occupant.dna.species.species_traits))
bloodData["hasBlood"] = 1
bloodData["hasBlood"] = TRUE
bloodData["volume"] = occupant.blood_volume
bloodData["percent"] = round(((occupant.blood_volume / BLOOD_VOLUME_NORMAL)*100))
bloodData["pulse"] = occupant.get_pulse(GETPULSE_TOOL)
@@ -282,19 +284,19 @@
if(E.status & ORGAN_BROKEN)
organStatus["broken"] = E.broken_description
if(E.is_robotic())
organStatus["robotic"] = 1
organStatus["robotic"] = TRUE
if(E.status & ORGAN_SPLINTED)
organStatus["splinted"] = 1
organStatus["splinted"] = TRUE
if(E.status & ORGAN_DEAD)
organStatus["dead"] = 1
organStatus["dead"] = TRUE
organData["status"] = organStatus
if(istype(E, /obj/item/organ/external/chest) && occupant.is_lung_ruptured())
organData["lungRuptured"] = 1
organData["lungRuptured"] = TRUE
if(E.internal_bleeding)
organData["internalBleeding"] = 1
organData["internalBleeding"] = TRUE
extOrganData.Add(list(organData))
@@ -319,27 +321,33 @@
occupantData["blind"] = (BLINDNESS in occupant.mutations)
occupantData["colourblind"] = (COLOURBLIND in occupant.mutations)
occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations)
occupantData["nearsighted"] = (NEARSIGHTED in occupant.mutations)
data["occupant"] = occupantData
return data
/obj/machinery/bodyscanner/Topic(href, href_list)
/obj/machinery/bodyscanner/tgui_act(action, params)
if(..())
return 1
return
if(stat & (NOPOWER|BROKEN))
return
if(href_list["ejectify"])
eject()
if(href_list["print_p"])
visible_message("<span class='notice'>[src] rattles and prints out a sheet of paper.</span>")
var/obj/item/paper/P = new /obj/item/paper(loc)
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
P.info = "<CENTER><B>Body Scan - [href_list["name"]]</B></CENTER><BR>"
P.info += "<b>Time of scan:</b> [station_time_timestamp()]<br><br>"
P.info += "[generate_printing_text()]"
P.info += "<br><br><b>Notes:</b><br>"
P.name = "Body Scan - [href_list["name"]]"
. = TRUE
switch(action)
if("ejectify")
eject()
if("print_p")
visible_message("<span class='notice'>[src] rattles and prints out a sheet of paper.</span>")
var/obj/item/paper/P = new /obj/item/paper(loc)
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE)
var/name = occupant ? occupant.name : "Unknown"
P.info = "<CENTER><B>Body Scan - [name]</B></CENTER><BR>"
P.info += "<b>Time of scan:</b> [station_time_timestamp()]<br><br>"
P.info += "[generate_printing_text()]"
P.info += "<br><br><b>Notes:</b><br>"
P.name = "Body Scan - [name]"
else
return FALSE
/obj/machinery/bodyscanner/proc/generate_printing_text()
var/dat = ""
+128 -222
View File
@@ -11,6 +11,7 @@
list("name" = "\[SPECIAL\]", "icon" = "whiters")
)
possibleseccolor = list( // no point in having the N2O and "whiters" ones in these lists
list("name" = "\[None\]", "icon" = "none"),
list("name" = "\[N2\]", "icon" = "red-c"),
list("name" = "\[O2\]", "icon" = "blue-c"),
list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c"),
@@ -19,6 +20,7 @@
list("name" = "\[CAUTION\]", "icon" = "yellow-c")
)
possibletertcolor = list(
list("name" = "\[None\]", "icon" = "none"),
list("name" = "\[N2\]", "icon" = "red-c-1"),
list("name" = "\[O2\]", "icon" = "blue-c-1"),
list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-1"),
@@ -27,6 +29,7 @@
list("name" = "\[CAUTION\]", "icon" = "yellow-c-1")
)
possiblequartcolor = list(
list("name" = "\[None\]", "icon" = "none"),
list("name" = "\[N2\]", "icon" = "red-c-2"),
list("name" = "\[O2\]", "icon" = "blue-c-2"),
list("name" = "\[Toxin (Bio)\]", "icon" = "orange-c-2"),
@@ -34,12 +37,6 @@
list("name" = "\[Air\]", "icon" = "grey-c-2"),
list("name" = "\[CAUTION\]", "icon" = "yellow-c-2")
)
possibledecals = list( //var that stores all possible decals, used by ui
list("name" = "Low temperature canister", "icon" = "cold"),
list("name" = "High temperature canister", "icon" = "hot"),
list("name" = "Plasma containing canister", "icon" = "plasma")
)
GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
/obj/machinery/portable_atmospherics/canister
@@ -52,22 +49,17 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
max_integrity = 250
integrity_failure = 100
var/menu = 0
//used by nanoui: 0 = main menu, 1 = relabel
var/valve_open = 0
var/release_pressure = ONE_ATMOSPHERE
var/list/canister_color //variable that stores colours
var/list/decals //list that stores the decals
var/list/color_index // list which stores tgui color indexes for the recoloring options, to enable previously-set colors to show up right
//lists for check_change()
var/list/oldcolor
var/list/olddecals
//passed to the ui to render the color lists
var/list/colorcontainer
var/list/possibledecals
var/can_label = 1
var/filled = 0.5
@@ -82,18 +74,12 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
/obj/machinery/portable_atmospherics/canister/New()
..()
canister_color = list(
"prim" = "yellow",
"sec" = "none",
"ter" = "none",
"quart" = "none")
"prim" = "yellow",
"sec" = "none",
"ter" = "none",
"quart" = "none"
)
oldcolor = new /list()
decals = list("cold" = 0, "hot" = 0, "plasma" = 0)
colorcontainer = list()
possibledecals = list()
update_icon()
/obj/machinery/portable_atmospherics/canister/proc/init_data_vars()
//passed to the ui to render the color lists
colorcontainer = list(
"prim" = list(
"options" = GLOB.canister_icon_container.possiblemaincolor,
@@ -112,26 +98,8 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
"name" = "Quaternary color",
)
)
//var/anycolor used by the nanoUI, 0: no color applied. 1: color applied
for(var/C in colorcontainer)
if(C == "prim") continue
var/list/L = colorcontainer[C]
if(!(canister_color[C]) || (canister_color[C] == "none"))
L.Add(list("anycolor" = 0))
else
L.Add(list("anycolor" = 1))
colorcontainer[C] = L
possibledecals = list()
var/i
var/list/L = GLOB.canister_icon_container.possibledecals
for(i=1;i<=L.len;i++)
var/list/LL = L[i]
LL = LL.Copy() //make sure we don't edit the datum list
LL.Add(list("active" = decals[LL["icon"]])) //"active" used by nanoUI
possibledecals.Add(LL)
color_index = list()
update_icon()
/obj/machinery/portable_atmospherics/canister/proc/check_change()
var/old_flag = update_flag
@@ -155,10 +123,6 @@ GLOBAL_DATUM_INIT(canister_icon_container, /datum/canister_icons, new())
update_flag |= 64
oldcolor = canister_color.Copy()
if(list2params(olddecals) != list2params(decals))
update_flag |= 128
olddecals = decals.Copy()
if(update_flag == old_flag)
return 1
else
@@ -174,17 +138,16 @@ update_flag
16 = tank_pressure < 15*ONE_ATMOS
32 = tank_pressure go boom.
64 = colors
128 = decals
(note: colors and decals has to be applied every icon update)
(note: colors has to be applied every icon update)
*/
if(src.destroyed)
src.overlays = 0
src.icon_state = text("[]-1", src.canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever.
if(destroyed)
overlays = 0
icon_state = text("[]-1", canister_color["prim"])//yes, I KNOW the colours don't reflect when the can's borked, whatever.
return
if(icon_state != src.canister_color["prim"])
icon_state = src.canister_color["prim"]
if(icon_state != canister_color["prim"])
icon_state = canister_color["prim"]
if(check_change()) //Returns 1 if no change needed to icons.
return
@@ -192,14 +155,12 @@ update_flag
overlays.Cut()
for(var/C in canister_color)
if(C == "prim") continue
if(canister_color[C] == "none") continue
if(C == "prim")
continue
if(canister_color[C] == "none")
continue
overlays.Add(canister_color[C])
for(var/D in decals)
if(decals[D])
overlays.Add("decal-" + D)
if(update_flag & 1)
overlays += "can-open"
if(update_flag & 2)
@@ -213,35 +174,9 @@ update_flag
else if(update_flag & 32)
overlays += "can-o3"
update_flag &= ~196 //the flags 128 and 64 represent change, not states. As such, we have to reset them to be able to detect a change on the next go.
update_flag &= ~68 //the flag 64 represents change, not states. As such, we have to reset them to be able to detect a change on the next go.
return
//template modification exploit prevention, used in Topic()
/obj/machinery/portable_atmospherics/canister/proc/is_a_color(var/inputVar, var/checkColor = "all")
if(checkColor == "prim" || checkColor == "all")
for(var/list/L in GLOB.canister_icon_container.possiblemaincolor)
if(L["icon"] == inputVar)
return 1
if(checkColor == "sec" || checkColor == "all")
for(var/list/L in GLOB.canister_icon_container.possibleseccolor)
if(L["icon"] == inputVar)
return 1
if(checkColor == "ter" || checkColor == "all")
for(var/list/L in GLOB.canister_icon_container.possibletertcolor)
if(L["icon"] == inputVar)
return 1
if(checkColor == "quart" || checkColor == "all")
for(var/list/L in GLOB.canister_icon_container.possiblequartcolor)
if(L["icon"] == inputVar)
return 1
return 0
/obj/machinery/portable_atmospherics/canister/proc/is_a_decal(var/inputVar)
for(var/list/L in GLOB.canister_icon_container.possibledecals)
if(L["icon"] == inputVar)
return 1
return 0
/obj/machinery/portable_atmospherics/canister/temperature_expose(datum/gas_mixture/air, exposed_temperature, exposed_volume)
..()
if(exposed_temperature > temperature_resistance)
@@ -271,7 +206,7 @@ update_flag
stat |= BROKEN
density = FALSE
playsound(src.loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
playsound(loc, 'sound/effects/spray.ogg', 10, TRUE, -3)
update_icon()
if(holding)
@@ -297,7 +232,7 @@ update_flag
var/transfer_moles = 0
if((air_contents.temperature > 0) && (pressure_delta > 0))
transfer_moles = pressure_delta*environment.volume/(air_contents.temperature * R_IDEAL_GAS_EQUATION)
transfer_moles = pressure_delta * environment.volume / (air_contents.temperature * R_IDEAL_GAS_EQUATION)
//Actually transfer the gas
var/datum/gas_mixture/removed = air_contents.remove(transfer_moles)
@@ -307,7 +242,7 @@ update_flag
else
loc.assume_air(removed)
air_update_turf()
src.update_icon()
update_icon()
if(air_contents.return_pressure() < 1)
@@ -315,20 +250,20 @@ update_flag
else
can_label = 0
src.updateDialog()
updateDialog()
return
/obj/machinery/portable_atmospherics/canister/return_air()
return air_contents
/obj/machinery/portable_atmospherics/canister/proc/return_temperature()
var/datum/gas_mixture/GM = src.return_air()
var/datum/gas_mixture/GM = return_air()
if(GM && GM.volume>0)
return GM.temperature
return 0
/obj/machinery/portable_atmospherics/canister/proc/return_pressure()
var/datum/gas_mixture/GM = src.return_air()
var/datum/gas_mixture/GM = return_air()
if(GM && GM.volume>0)
return GM.return_pressure()
return 0
@@ -343,141 +278,113 @@ update_flag
else if(valve_open && holding)
investigate_log("[key_name(user)] started a transfer into [holding].<br>", "atmos")
/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user as mob)
src.add_hiddenprint(user)
return src.attack_hand(user)
/obj/machinery/portable_atmospherics/canister/attack_ai(var/mob/user)
add_hiddenprint(user)
return attack_hand(user)
/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user as mob)
return src.ui_interact(user)
/obj/machinery/portable_atmospherics/canister/attack_ghost(var/mob/user)
return tgui_interact(user)
/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user as mob)
return src.ui_interact(user)
/obj/machinery/portable_atmospherics/canister/attack_hand(var/mob/user)
return tgui_interact(user)
/obj/machinery/portable_atmospherics/canister/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1, var/datum/topic_state/state = GLOB.physical_state)
if(src.destroyed)
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/portable_atmospherics/canister/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_physical_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
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "canister.tmpl", "Canister", 480, 400, state = state)
// open the new ui window
ui = new(user, src, ui_key, "Canister", name, 600, 350, master_ui, state)
ui.open()
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/portable_atmospherics/canister/ui_data(mob/user, datum/topic_state/state)
init_data_vars() //set up var/colorcontainer and var/possibledecals
// this is the data which will be sent to the ui
var/data[0]
data["name"] = name
data["menu"] = menu ? 1 : 0
data["canLabel"] = can_label ? 1 : 0
data["canister_color"] = canister_color
data["colorContainer"] = colorcontainer.Copy()
colorcontainer.Cut()
data["possibleDecals"] = possibledecals.Copy()
possibledecals.Cut()
/obj/machinery/portable_atmospherics/canister/tgui_data()
var/data = list()
data["portConnected"] = connected_port ? 1 : 0
data["tankPressure"] = round(air_contents.return_pressure() ? air_contents.return_pressure() : 0)
data["releasePressure"] = round(release_pressure ? release_pressure : 0)
data["minReleasePressure"] = round(ONE_ATMOSPHERE/10)
data["maxReleasePressure"] = round(10*ONE_ATMOSPHERE)
data["defaultReleasePressure"] = ONE_ATMOSPHERE
data["minReleasePressure"] = round(ONE_ATMOSPHERE / 10)
data["maxReleasePressure"] = round(ONE_ATMOSPHERE * 10)
data["valveOpen"] = valve_open ? 1 : 0
data["name"] = name
data["canLabel"] = can_label ? 1 : 0
data["colorContainer"] = colorcontainer.Copy()
data["color_index"] = color_index
data["hasHoldingTank"] = holding ? 1 : 0
if(holding)
data["holdingTank"] = list("name" = holding.name, "tankPressure" = round(holding.air_contents.return_pressure()))
return data
/obj/machinery/portable_atmospherics/canister/Topic(href, href_list)
/obj/machinery/portable_atmospherics/canister/tgui_act(action, params)
if(..())
return 1
if(href_list["choice"] == "menu")
menu = text2num(href_list["mode_target"])
if(href_list["toggle"])
var/logmsg
if(valve_open)
if(holding)
logmsg = "Valve was <b>closed</b> by [key_name(usr)], stopping the transfer into the [holding]<br>"
else
logmsg = "Valve was <b>closed</b> by [key_name(usr)], stopping the transfer into the <font color='red'><b>air</b></font><br>"
else
if(holding)
logmsg = "Valve was <b>opened</b> by [key_name(usr)], starting the transfer into the [holding]<br>"
else
logmsg = "Valve was <b>opened</b> by [key_name(usr)], starting the transfer into the <font color='red'><b>air</b></font><br>"
if(air_contents.toxins > 0)
message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]")
if(air_contents.sleeping_agent > 0)
message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]")
investigate_log(logmsg, "atmos")
release_log += logmsg
valve_open = !valve_open
if(href_list["remove_tank"])
if(holding)
if(valve_open)
valve_open = 0
release_log += "Valve was <b>closed</b> by [key_name(usr)], stopping the transfer into the [holding]<br>"
holding.loc = loc
holding = null
if(href_list["pressure_adj"])
var/diff = text2num(href_list["pressure_adj"])
if(diff > 0)
release_pressure = min(10*ONE_ATMOSPHERE, release_pressure+diff)
else
release_pressure = max(ONE_ATMOSPHERE/10, release_pressure+diff)
if(href_list["rename"])
if(can_label)
var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null,1,MAX_NAME_LEN))
if(can_label) //Exploit prevention
if(T)
name = T
return
var/can_min_release_pressure = round(ONE_ATMOSPHERE / 10)
var/can_max_release_pressure = round(ONE_ATMOSPHERE * 10)
. = TRUE
switch(action)
if("relabel")
if(can_label)
var/T = sanitize(copytext(input("Choose canister label", "Name", name) as text|null, 1, MAX_NAME_LEN))
if(can_label) //Exploit prevention
if(T)
name = T
else
name = "canister"
else
name = "canister"
to_chat(usr, "<span class='warning'>As you attempted to rename it the pressure rose!</span>")
. = FALSE
if("pressure")
var/pressure = params["pressure"]
if(pressure == "reset")
pressure = ONE_ATMOSPHERE
else if(pressure == "min")
pressure = can_min_release_pressure
else if(pressure == "max")
pressure = can_max_release_pressure
else if(pressure == "input")
pressure = input("New release pressure ([can_min_release_pressure]-[can_max_release_pressure] kPa):", name, release_pressure) as num|null
if(isnull(pressure))
. = FALSE
else if(text2num(pressure) != null)
pressure = text2num(pressure)
if(.)
release_pressure = clamp(round(pressure), can_min_release_pressure, can_max_release_pressure)
investigate_log("was set to [release_pressure] kPa by [key_name(usr)].", "atmos")
if("valve")
var/logmsg
valve_open = !valve_open
if(valve_open)
logmsg = "Valve was <b>opened</b> by [key_name(usr)], starting a transfer into the [holding || "air"].<br>"
if(!holding)
logmsg = "Valve was <b>opened</b> by [key_name(usr)], starting a transfer into the [holding || "air"].<br>"
if(air_contents.toxins > 0)
message_admins("[key_name_admin(usr)] opened a canister that contains plasma in [get_area(src)]! (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
log_admin("[key_name(usr)] opened a canister that contains plasma at [get_area(src)]: [x], [y], [z]")
if(air_contents.sleeping_agent > 0)
message_admins("[key_name_admin(usr)] opened a canister that contains N2O in [get_area(src)]! (<A HREF='?_src_=holder;adminplayerobservecoodjump=1;X=[x];Y=[y];Z=[z]'>JMP</a>)")
log_admin("[key_name(usr)] opened a canister that contains N2O at [get_area(src)]: [x], [y], [z]")
else
to_chat(usr, "<span class='warning'>As you attempted to rename it the pressure rose!</span>")
if(href_list["choice"] == "Primary color")
if(is_a_color(href_list["icon"],"prim"))
canister_color["prim"] = href_list["icon"]
if(href_list["choice"] == "Secondary color")
if(href_list["icon"] == "none")
canister_color["sec"] = "none"
else if(is_a_color(href_list["icon"],"sec"))
canister_color["sec"] = href_list["icon"]
if(href_list["choice"] == "Tertiary color")
if(href_list["icon"] == "none")
canister_color["ter"] = "none"
else if(is_a_color(href_list["icon"],"ter"))
canister_color["ter"] = href_list["icon"]
if(href_list["choice"] == "Quaternary color")
if(href_list["icon"] == "none")
canister_color["quart"] = "none"
else if(is_a_color(href_list["icon"],"quart"))
canister_color["quart"] = href_list["icon"]
if(href_list["choice"] == "decals")
if(is_a_decal(href_list["icon"]))
decals[href_list["icon"]] = (decals[href_list["icon"]] == 0)
src.add_fingerprint(usr)
logmsg = "Valve was <b>closed</b> by [key_name(usr)], stopping the transfer into the [holding || "air"].<br>"
investigate_log(logmsg, "atmos")
release_log += logmsg
if("eject")
if(holding)
if(valve_open)
release_log += "[key_name(usr)] removed the [holding], leaving the valve open and transferring into the air<br>"
message_admins("[ADMIN_LOOKUPFLW(usr)] removed [holding] from [src] with valve still open at [ADMIN_VERBOSEJMP(src)] releasing contents into the <span class='boldannounce'>air</span>.")
investigate_log("[key_name(usr)] removed the [holding], leaving the valve open and transferring into the <span class='boldannounce'>air</span>.", "atmos")
replace_tank(usr, FALSE)
if("recolor")
if(can_label)
var/ctype = params["ctype"]
var/cnum = text2num(params["nc"])
if(isnull(colorcontainer[ctype]))
message_admins("[key_name_admin(usr)] passed an invalid ctype var to a canister.")
return
var/newcolor = sanitize_integer(cnum, 0, length(colorcontainer[ctype]["options"]))
color_index[ctype] = newcolor
newcolor++ // javascript starts arrays at 0, byond (for some reason) starts them at 1, this converts JS values to byond values
canister_color[ctype] = colorcontainer[ctype]["options"][newcolor]["icon"]
add_fingerprint(usr)
update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/toxins
name = "Canister \[Toxin (Plasma)\]"
@@ -513,19 +420,18 @@ update_flag
..()
canister_color["prim"] = "orange"
decals["plasma"] = 1
src.air_contents.toxins = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
air_contents.toxins = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
src.update_icon()
update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/oxygen/New()
..()
canister_color["prim"] = "blue"
src.air_contents.oxygen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
air_contents.oxygen = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
src.update_icon()
update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/sleeping_agent/New()
@@ -534,25 +440,25 @@ update_flag
canister_color["prim"] = "redws"
air_contents.sleeping_agent = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
src.update_icon()
update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/nitrogen/New()
..()
canister_color["prim"] = "red"
src.air_contents.nitrogen = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
air_contents.nitrogen = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
src.update_icon()
update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/carbon_dioxide/New()
..()
canister_color["prim"] = "black"
src.air_contents.carbon_dioxide = (src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
air_contents.carbon_dioxide = (maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
src.update_icon()
update_icon()
return 1
@@ -560,17 +466,17 @@ update_flag
..()
canister_color["prim"] = "grey"
src.air_contents.oxygen = (O2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
src.air_contents.nitrogen = (N2STANDARD*src.maximum_pressure*filled)*air_contents.volume/(R_IDEAL_GAS_EQUATION*air_contents.temperature)
air_contents.oxygen = (O2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
air_contents.nitrogen = (N2STANDARD * maximum_pressure * filled) * air_contents.volume / (R_IDEAL_GAS_EQUATION * air_contents.temperature)
src.update_icon()
update_icon()
return 1
/obj/machinery/portable_atmospherics/canister/custom_mix/New()
..()
canister_color["prim"] = "whiters"
src.update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD
update_icon() // Otherwise new canisters do not have their icon updated with the pressure light, likely want to add this to the canister class constructor, avoiding at current time to refrain from screwing up code for other canisters. --DZD
return 1
/obj/machinery/portable_atmospherics/canister/welder_act(mob/user, obj/item/I)
+40 -36
View File
@@ -40,8 +40,7 @@
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/operating/attack_hand(mob/user)
if(..(user))
@@ -52,16 +51,15 @@
add_fingerprint(user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/operating/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)//ui is mostly copy pasta from the sleeper ui
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/computer/operating/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)
ui = new(user, src, ui_key, "op_computer.tmpl", "Patient Monitor", 650, 455)
ui = new(user, src, ui_key, "OperatingComputer", "Patient Monitor", 650, 455, master_ui, state)
ui.open()
ui.set_auto_update(1)
/obj/machinery/computer/operating/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/computer/operating/tgui_data(mob/user)
var/data[0]
var/mob/living/carbon/human/occupant
if(table)
@@ -136,38 +134,43 @@
return data
/obj/machinery/computer/operating/Topic(href, href_list)
/obj/machinery/computer/operating/tgui_act(action, params)
if(..())
return 1
return
if(stat & (NOPOWER|BROKEN))
return
if((usr.contents.Find(src) || (in_range(src, usr) && istype(src.loc, /turf))) || (istype(usr, /mob/living/silicon)))
usr.set_machine(src)
if(href_list["verboseOn"])
verbose=1
if(href_list["verboseOff"])
verbose=0
if(href_list["healthOn"])
healthAnnounce=1
if(href_list["healthOff"])
healthAnnounce=0
if(href_list["critOn"])
crit=1
if(href_list["critOff"])
crit=0
if(href_list["oxyOn"])
oxy=1
if(href_list["oxyOff"])
oxy=0
if(href_list["oxy_adj"]!=0)
oxyAlarm=oxyAlarm+text2num(href_list["oxy_adj"])
if(href_list["choiceOn"])
choice=1
if(href_list["choiceOff"])
choice=0
if(href_list["health_adj"]!=0)
healthAlarm=healthAlarm+text2num(href_list["health_adj"])
return
. = TRUE
switch(action)
if("verboseOn")
verbose = TRUE
if("verboseOff")
verbose = FALSE
if("healthOn")
healthAnnounce = TRUE
if("healthOff")
healthAnnounce = FALSE
if("critOn")
crit = TRUE
if("critOff")
crit = FALSE
if("oxyOn")
oxy = TRUE
if("oxyOff")
oxy = FALSE
if("oxy_adj")
oxyAlarm = clamp(text2num(params["new"]), -100, 100)
if("choiceOn")
choice = TRUE
if("choiceOff")
choice = FALSE
if("health_adj")
healthAlarm = clamp(text2num(params["new"]), -100, 100)
else
return FALSE
/obj/machinery/computer/operating/process()
@@ -178,6 +181,7 @@
atom_say("New patient detected, loading stats")
victim = table.victim
atom_say("[victim.real_name], [victim.dna.blood_type] blood, [victim.stat ? "Non-Responsive" : "Awake"]")
SStgui.update_uis(src)
if(nextTick < world.time)
nextTick=world.time + OP_COMPUTER_COOLDOWN
if(crit && victim.health <= -50 )
@@ -233,7 +233,10 @@
/obj/item/circuitboard/brigcells
name = "Circuit board (Brig Cell Control)"
build_path = /obj/machinery/computer/brigcells
/obj/item/circuitboard/sm_monitor
name = "Circuit board (Supermatter Monitoring Console)"
build_path = /obj/machinery/computer/sm_monitor
origin_tech = "programming=2;powerstorage=2"
// RD console circuits, so that {de,re}constructing one of the special consoles doesn't ruin everything forever
/obj/item/circuitboard/rdconsole
+247 -199
View File
@@ -1,3 +1,6 @@
#define MENU_MAIN 1
#define MENU_RECORDS 2
/obj/machinery/computer/cloning
name = "cloning console"
icon = 'icons/obj/computer.dmi'
@@ -6,11 +9,11 @@
circuit = /obj/item/circuitboard/cloning
req_access = list(ACCESS_HEADS) //Only used for record deletion right now.
var/obj/machinery/dna_scannernew/scanner = null //Linked scanner. For scanning.
var/list/pods = list() //Linked cloning pods.
var/temp = ""
var/scantemp = "Scanner ready."
var/menu = 1 //Which menu screen to display
var/list/records = list()
var/list/pods = null //Linked cloning pods.
var/list/temp = null
var/list/scantemp = null
var/menu = MENU_MAIN //Which menu screen to display
var/list/records = null
var/datum/dna2/record/active_record = null
var/obj/item/disk/data/diskette = null //Mostly so the geneticist can steal everything.
var/loading = 0 // Nice loading text
@@ -24,6 +27,9 @@
/obj/machinery/computer/cloning/Initialize()
..()
pods = list()
records = list()
set_scan_temp("Scanner ready.", "good")
updatemodules()
/obj/machinery/computer/cloning/Destroy()
@@ -91,7 +97,7 @@
W.loc = src
src.diskette = W
to_chat(user, "You insert [W].")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
return
else if(istype(W, /obj/item/multitool))
var/obj/item/multitool/M = W
@@ -117,19 +123,21 @@
return
updatemodules()
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/cloning/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
/obj/machinery/computer/cloning/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)
if(stat & (NOPOWER|BROKEN))
return
// Set up the Nano UI
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
var/datum/asset/cloning/assets = get_asset_datum(/datum/asset/cloning)
assets.send(user)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "cloning_console.tmpl", "Cloning Console UI", 640, 520)
ui = new(user, src, ui_key, "CloningConsole", "Cloning Console", 640, 520)
ui.open()
/obj/machinery/computer/cloning/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/computer/cloning/tgui_data(mob/user)
var/data[0]
data["menu"] = src.menu
data["scanner"] = sanitize("[src.scanner]")
@@ -143,7 +151,18 @@
if(pod.efficiency > 5)
canpodautoprocess = 1
tempods.Add(list(list("pod" = "\ref[pod]", "name" = sanitize(capitalize(pod.name)), "biomass" = pod.biomass)))
var/status = "idle"
if(pod.mess)
status = "mess"
else if(pod.occupant && !(pod.stat & NOPOWER))
status = "cloning"
tempods.Add(list(list(
"pod" = "\ref[pod]",
"name" = sanitize(capitalize(pod.name)),
"biomass" = pod.biomass,
"status" = status,
"progress" = (pod.occupant && pod.occupant.stat != DEAD) ? pod.get_completion() : 0
)))
data["pods"] = tempods
data["loading"] = loading
@@ -168,188 +187,190 @@
temprecords.Add(list(list("record" = "\ref[R]", "realname" = sanitize(tempRealName))))
data["records"] = temprecords
if(src.menu == 3)
if(src.active_record)
data["activerecord"] = "\ref[src.active_record]"
var/obj/item/implant/health/H = null
if(src.active_record.implant)
H = locate(src.active_record.implant)
if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS)
data["podready"] = 1
else
data["podready"] = 0
if((H) && (istype(H)))
data["health"] = H.sensehealth()
data["realname"] = sanitize(src.active_record.dna.real_name)
data["unidentity"] = src.active_record.dna.uni_identity
data["strucenzymes"] = src.active_record.dna.struc_enzymes
if(selected_pod && (selected_pod in pods) && selected_pod.biomass >= CLONE_BIOMASS)
data["podready"] = 1
else
data["podready"] = 0
data["modal"] = tgui_modal_data(src)
return data
/obj/machinery/computer/cloning/Topic(href, href_list)
/obj/machinery/computer/cloning/tgui_act(action, params)
if(..())
return 1
if(loading)
return
if(stat & (NOPOWER|BROKEN))
return
if(href_list["scan"] && scanner && scanner.occupant)
scantemp = "Scanner ready."
loading = 1
spawn(20)
if(can_brainscan() && scan_mode)
scan_mob(scanner.occupant, scan_brain = 1)
else
scan_mob(scanner.occupant)
loading = 0
SSnanoui.update_uis(src)
if(href_list["task"])
switch(href_list["task"])
if("autoprocess")
autoprocess = 1
SSnanoui.update_uis(src)
if("stopautoprocess")
autoprocess = 0
SSnanoui.update_uis(src)
//No locking an open scanner.
else if((href_list["lock"]) && (!isnull(src.scanner)))
if((!src.scanner.locked) && (src.scanner.occupant))
src.scanner.locked = 1
else
src.scanner.locked = 0
else if(href_list["view_rec"])
src.active_record = locate(href_list["view_rec"])
if(istype(src.active_record,/datum/dna2/record))
if((isnull(src.active_record.ckey)))
qdel(src.active_record)
src.temp = "<span class=\"bad\">Error: Record corrupt.</span>"
else
src.menu = 3
else
src.active_record = null
src.temp = "<span class=\"bad\">Error: Record missing.</span>"
else if(href_list["del_rec"])
if((!src.active_record) || (src.menu < 3))
return
if(src.menu == 3) //If we are viewing a record, confirm deletion
src.temp = "Please confirm that you want to delete the record?"
src.menu = 4
else if(src.menu == 4)
var/obj/item/card/id/C = usr.get_active_hand()
if(istype(C)||istype(C, /obj/item/pda))
if(src.check_access(C))
src.records.Remove(src.active_record)
qdel(src.active_record)
src.temp = "Record deleted."
src.menu = 2
. = TRUE
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_ANSWER)
if(params["id"] == "del_rec" && active_record)
var/obj/item/card/id/C = usr.get_active_hand()
if(!istype(C) && !istype(C, /obj/item/pda))
set_temp("ID not in hand.", "danger")
return
if(check_access(C))
records.Remove(active_record)
qdel(active_record)
set_temp("Record deleted.", "success")
menu = MENU_RECORDS
else
src.temp = "<span class=\"bad\">Error: Access denied.</span>"
else if(href_list["disk"]) //Load or eject.
switch(href_list["disk"])
if("load")
if((isnull(src.diskette)) || isnull(src.diskette.buf))
src.temp = "<span class=\"bad\">Error: The disk's data could not be read.</span>"
SSnanoui.update_uis(src)
return
if(isnull(src.active_record))
src.temp = "<span class=\"bad\">Error: No active record was found.</span>"
src.menu = 1
SSnanoui.update_uis(src)
return
src.active_record = src.diskette.buf.copy()
src.temp = "Load successful."
if("eject")
if(!isnull(src.diskette))
src.diskette.loc = src.loc
src.diskette = null
else if(href_list["save_disk"]) //Save to disk!
if((isnull(src.diskette)) || (src.diskette.read_only) || (isnull(src.active_record)))
src.temp = "<span class=\"bad\">Error: The data could not be saved.</span>"
SSnanoui.update_uis(src)
set_temp("Access denied.", "danger")
return
// DNA2 makes things a little simpler.
src.diskette.buf=src.active_record.copy()
src.diskette.buf.types=0
switch(href_list["save_disk"]) //Save as Ui/Ui+Ue/Se
if("ui")
src.diskette.buf.types=DNA2_BUF_UI
if("ue")
src.diskette.buf.types=DNA2_BUF_UI|DNA2_BUF_UE
if("se")
src.diskette.buf.types=DNA2_BUF_SE
src.diskette.name = "data disk - '[src.active_record.dna.real_name]'"
src.temp = "Save \[[href_list["save_disk"]]\] successful."
switch(action)
if("scan")
if(!scanner || !scanner.occupant || loading)
return
set_scan_temp("Scanner ready.", "good")
loading = TRUE
else if(href_list["refresh"])
SSnanoui.update_uis(src)
else if(href_list["selectpod"])
var/obj/machinery/clonepod/selected = locate(href_list["selectpod"])
if(istype(selected) && (selected in pods))
selected_pod = selected
else if(href_list["clone"])
var/datum/dna2/record/C = locate(href_list["clone"])
//Look for that player! They better be dead!
if(istype(C))
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
if(!pods.len)
temp = "<span class=\"bad\">Error: No cloning pod detected.</span>"
else
var/obj/machinery/clonepod/pod = selected_pod
var/cloneresult
if(!selected_pod)
temp = "<span class=\"bad\">Error: No cloning pod selected.</span>"
else if(pod.occupant)
temp = "<span class=\"bad\">Error: The cloning pod is currently occupied.</span>"
else if(pod.biomass < CLONE_BIOMASS)
temp = "<span class=\"bad\">Error: Not enough biomass.</span>"
else if(pod.mess)
temp = "<span class=\"bad\">Error: The cloning pod is malfunctioning.</span>"
else if(!config.revival_cloning)
temp = "<span class=\"bad\">Error: Unable to initiate cloning cycle.</span>"
spawn(20)
if(can_brainscan() && scan_mode)
scan_mob(scanner.occupant, scan_brain = TRUE)
else
cloneresult = pod.growclone(C)
if(cloneresult)
if(cloneresult > 0)
temp = "Initiating cloning cycle..."
records.Remove(C)
qdel(C)
menu = 1
scan_mob(scanner.occupant)
loading = FALSE
SStgui.update_uis(src)
if("autoprocess")
autoprocess = text2num(params["on"]) > 0
if("lock")
if(isnull(scanner) || !scanner.occupant) //No locking an open scanner.
return
scanner.locked = !scanner.locked
if("view_rec")
var/ref = params["ref"]
if(!length(ref))
return
active_record = locate(ref)
if(istype(active_record))
if(isnull(active_record.ckey))
qdel(active_record)
set_temp("Error: Record corrupt.", "danger")
else
var/obj/item/implant/health/H = null
if(active_record.implant)
H = locate(active_record.implant)
var/list/payload = list(
activerecord = "\ref[active_record]",
health = (H && istype(H)) ? H.sensehealth() : "",
realname = sanitize(active_record.dna.real_name),
unidentity = active_record.dna.uni_identity,
strucenzymes = active_record.dna.struc_enzymes,
)
tgui_modal_message(src, action, "", null, payload)
else
active_record = null
set_temp("Error: Record missing.", "danger")
if("del_rec")
if(!active_record)
return
tgui_modal_boolean(src, action, "Please confirm that you want to delete the record by holding your ID and pressing Delete:", yes_text = "Delete", no_text = "Cancel")
if("disk") // Disk management.
if(!length(params["option"]))
return
switch(params["option"])
if("load")
if(isnull(diskette) || isnull(diskette.buf))
set_temp("Error: The disk's data could not be read.", "danger")
return
else if(isnull(active_record))
set_temp("Error: No active record was found.", "danger")
menu = MENU_MAIN
return
active_record = diskette.buf.copy()
set_temp("Successfully loaded from disk.", "success")
if("save")
if(isnull(diskette) || diskette.read_only || isnull(active_record))
set_temp("Error: The data could not be saved.", "danger")
return
// DNA2 makes things a little simpler.
var/types
switch(params["savetype"]) // Save as Ui/Ui+Ue/Se
if("ui")
types = DNA2_BUF_UI
if("ue")
types = DNA2_BUF_UI|DNA2_BUF_UE
if("se")
types = DNA2_BUF_SE
else
set_temp("Error: Invalid save format.", "danger")
return
diskette.buf = active_record.copy()
diskette.buf.types = types
diskette.name = "data disk - '[active_record.dna.real_name]'"
set_temp("Successfully saved to disk.", "success")
if("eject")
if(!isnull(diskette))
diskette.loc = loc
diskette = null
if("refresh")
SStgui.update_uis(src)
if("selectpod")
var/ref = params["ref"]
if(!length(ref))
return
var/obj/machinery/clonepod/selected = locate(ref)
if(istype(selected) && (selected in pods))
selected_pod = selected
if("clone")
var/ref = params["ref"]
if(!length(ref))
return
var/datum/dna2/record/C = locate(ref)
//Look for that player! They better be dead!
if(istype(C))
tgui_modal_clear(src)
//Can't clone without someone to clone. Or a pod. Or if the pod is busy. Or full of gibs.
if(!length(pods))
set_temp("Error: No cloning pod detected.", "danger")
else
var/obj/machinery/clonepod/pod = selected_pod
var/cloneresult
if(!selected_pod)
set_temp("Error: No cloning pod selected.", "danger")
else if(pod.occupant)
set_temp("Error: The cloning pod is currently occupied.", "danger")
else if(pod.biomass < CLONE_BIOMASS)
set_temp("Error: Not enough biomass.", "danger")
else if(pod.mess)
set_temp("Error: The cloning pod is malfunctioning.", "danger")
else if(!config.revival_cloning)
set_temp("Error: Unable to initiate cloning cycle.", "danger")
else
temp = "[C.name] => <font class='bad'>Initialisation failure.</font>"
cloneresult = pod.growclone(C)
if(cloneresult)
set_temp("Initiating cloning cycle...", "success")
records.Remove(C)
qdel(C)
menu = MENU_MAIN
else
set_temp("Error: Initialisation failure.", "danger")
else
set_temp("Error: Data corruption.", "danger")
if("menu")
menu = clamp(text2num(params["num"]), MENU_MAIN, MENU_RECORDS)
if("toggle_mode")
if(loading)
return
if(can_brainscan())
scan_mode = !scan_mode
else
scan_mode = FALSE
if("eject")
if(usr.incapacitated() || !scanner || loading)
return
scanner.eject_occupant(usr)
scanner.add_fingerprint(usr)
if("cleartemp")
temp = null
else
temp = "<span class=\"bad\">Error: Data corruption.</span>"
else if(href_list["menu"])
src.menu = text2num(href_list["menu"])
temp = ""
scantemp = "Scanner ready."
else if(href_list["toggle_mode"])
if(can_brainscan())
scan_mode = !scan_mode
else
scan_mode = 0
return FALSE
src.add_fingerprint(usr)
SSnanoui.update_uis(src)
return
/obj/machinery/computer/cloning/proc/scan_mob(mob/living/carbon/human/subject as mob, var/scan_brain = 0)
if(stat & NOPOWER)
@@ -360,46 +381,46 @@
return
if(isnull(subject) || (!(ishuman(subject))) || (!subject.dna))
if(isalien(subject))
scantemp = "<span class=\"bad\">Error: Xenomorphs are not scannable.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Xenomorphs are not scannable.", "bad")
SStgui.update_uis(src)
return
// can add more conditions for specific non-human messages here
else
scantemp = "<span class=\"bad\">Error: Subject species is not scannable.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Subject species is not scannable.", "bad")
SStgui.update_uis(src)
return
if(subject.get_int_organ(/obj/item/organ/internal/brain))
var/obj/item/organ/internal/brain/Brn = subject.get_int_organ(/obj/item/organ/internal/brain)
if(istype(Brn))
if(NO_SCAN in Brn.dna.species.species_traits)
scantemp = "<span class=\"bad\">Error: [Brn.dna.species.name_plural] are not scannable.</span>"
SSnanoui.update_uis(src)
set_scan_temp("[Brn.dna.species.name_plural] are not scannable.", "bad")
SStgui.update_uis(src)
return
if(!subject.get_int_organ(/obj/item/organ/internal/brain))
scantemp = "<span class=\"bad\">Error: No brain detected in subject.</span>"
SSnanoui.update_uis(src)
set_scan_temp("No brain detected in subject.", "bad")
SStgui.update_uis(src)
return
if(subject.suiciding)
scantemp = "<span class=\"bad\">Error: Subject has committed suicide and is not scannable.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Subject has committed suicide and is not scannable.", "bad")
SStgui.update_uis(src)
return
if((!subject.ckey) || (!subject.client))
scantemp = "<span class=\"bad\">Error: Subject's brain is not responding. Further attempts after a short delay may succeed.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Subject's brain is not responding. Further attempts after a short delay may succeed.", "bad")
SStgui.update_uis(src)
return
if((NOCLONE in subject.mutations) && src.scanner.scan_level < 2)
scantemp = "<span class=\"bad\">Error: Subject has incompatible genetic mutations.</span>"
SSnanoui.update_uis(src)
set_scan_temp("Subject has incompatible genetic mutations.", "bad")
SStgui.update_uis(src)
return
if(!isnull(find_record(subject.ckey)))
scantemp = "Subject already in database."
SSnanoui.update_uis(src)
set_scan_temp("Subject already in database.")
SStgui.update_uis(src)
return
for(var/obj/machinery/clonepod/pod in pods)
if(pod.occupant && pod.clonemind == subject.mind)
scantemp = "Subject already getting cloned."
SSnanoui.update_uis(src)
set_scan_temp("Subject already getting cloned.")
SStgui.update_uis(src)
return
subject.dna.check_integrity()
@@ -434,8 +455,8 @@
R.mind = "\ref[subject.mind]"
src.records += R
scantemp = "Subject successfully scanned. " + extra_info
SSnanoui.update_uis(src)
set_scan_temp("Subject successfully scanned. [extra_info]", "good")
SStgui.update_uis(src)
//Find a specific record by key.
/obj/machinery/computer/cloning/proc/find_record(var/find_key)
@@ -451,3 +472,30 @@
/obj/machinery/computer/cloning/proc/can_brainscan()
return (scanner && scanner.scan_level > 3)
/**
* Sets a temporary message to display to the user
*
* Arguments:
* * text - Text to display, null/empty to clear the message from the UI
* * style - The style of the message: (color name), info, success, warning, danger
*/
/obj/machinery/computer/cloning/proc/set_temp(text = "", style = "info", update_now = FALSE)
temp = list(text = text, style = style)
if(update_now)
SStgui.update_uis(src)
/**
* Sets a temporary scan message to display to the user
*
* Arguments:
* * text - Text to display, null/empty to clear the message from the UI
* * color - The color of the message: (color name)
*/
/obj/machinery/computer/cloning/proc/set_scan_temp(text = "", color = "", update_now = FALSE)
scantemp = list(text = text, color = color)
if(update_now)
SStgui.update_uis(src)
#undef MENU_MAIN
#undef MENU_RECORDS
+331 -392
View File
@@ -1,10 +1,12 @@
#define MED_DATA_MAIN 1 // Main menu
#define MED_DATA_R_LIST 2 // Record list
#define MED_DATA_MAINT 3 // Records maintenance
#define MED_DATA_RECORD 4 // Record
#define MED_DATA_V_DATA 5 // Virus database
#define MED_DATA_MEDBOT 6 // Medbot monitor
#define FIELD(N, V, E) list(field = N, value = V, edit = E)
#define MED_FIELD(N, V, E, LB) list(field = N, value = V, edit = E, line_break = LB)
/obj/machinery/computer/med_data //TODO:SANITY
name = "medical records console"
desc = "This can be used to check medical records."
@@ -18,11 +20,45 @@
var/screen = null
var/datum/data/record/active1 = null
var/datum/data/record/active2 = null
var/temp = null
var/list/temp = null
var/printing = null
// The below are used to make modal generation more convenient
var/static/list/field_edit_questions
var/static/list/field_edit_choices
light_color = LIGHT_COLOR_DARKBLUE
/obj/machinery/computer/med_data/Initialize()
..()
field_edit_questions = list(
// General
"sex" = "Please select new sex:",
"age" = "Please input new age:",
"fingerprint" = "Please input new fingerprint hash:",
"p_stat" = "Please select new physical status:",
"m_stat" = "Please select new mental status:",
// Medical
"blood_type" = "Please select new blood type:",
"b_dna" = "Please input new DNA:",
"mi_dis" = "Please input new minor disabilities:",
"mi_dis_d" = "Please summarize minor disabilities:",
"ma_dis" = "Please input new major disabilities:",
"ma_dis_d" = "Please summarize major disabilities:",
"alg" = "Please input new allergies:",
"alg_d" = "Please summarize allergies:",
"cdi" = "Please input new current diseases:",
"cdi_d" = "Please summarize current diseases:",
"notes" = "Please input new important notes:",
)
field_edit_choices = list(
// General
"sex" = list("Male", "Female"),
"p_stat" = list("*Deceased*", "*SSD*", "Active", "Physically Unfit", "Disabled"),
"m_stat" = list("*Insane*", "*Unstable*", "*Watch*", "Stable"),
// Medical
"blood_type" = list("A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"),
)
/obj/machinery/computer/med_data/Destroy()
active1 = null
active2 = null
@@ -33,7 +69,7 @@
usr.drop_item()
O.forceMove(src)
scan = O
ui_interact(user)
tgui_interact(user)
return
return ..()
@@ -44,20 +80,25 @@
to_chat(user, "<span class='danger'>Unable to establish a connection</span>: You're too far away from the station!")
return
add_fingerprint(user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/computer/med_data/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)
/obj/machinery/computer/med_data/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)
ui = new(user, src, ui_key, "med_data.tmpl", name, 800, 380)
ui = new(user, src, ui_key, "MedicalRecords", "Medical Records", 800, 380, master_ui, state)
ui.open()
ui.set_autoupdate(FALSE)
/obj/machinery/computer/med_data/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/computer/med_data/tgui_data(mob/user)
var/data[0]
data["temp"] = temp
data["scan"] = scan ? scan.name : null
data["authenticated"] = authenticated
data["rank"] = rank
data["screen"] = screen
data["printing"] = printing
data["isAI"] = isAI(user)
data["isRobot"] = isrobot(user)
if(authenticated)
switch(screen)
if(MED_DATA_R_LIST)
@@ -72,17 +113,17 @@
if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
var/list/fields = list()
general["fields"] = fields
fields[++fields.len] = list("field" = "Name:", "value" = active1.fields["name"], "edit" = null)
fields[++fields.len] = list("field" = "ID:", "value" = active1.fields["id"], "edit" = null)
fields[++fields.len] = list("field" = "Sex:", "value" = active1.fields["sex"], "edit" = "sex")
fields[++fields.len] = list("field" = "Age:", "value" = active1.fields["age"], "edit" = "age")
fields[++fields.len] = list("field" = "Fingerprint:", "value" = active1.fields["fingerprint"], "edit" = "fingerprint")
fields[++fields.len] = list("field" = "Physical Status:", "value" = active1.fields["p_stat"], "edit" = "p_stat")
fields[++fields.len] = list("field" = "Mental Status:", "value" = active1.fields["m_stat"], "edit" = "m_stat")
fields[++fields.len] = FIELD("Name", active1.fields["name"], null)
fields[++fields.len] = FIELD("ID", active1.fields["id"], null)
fields[++fields.len] = FIELD("Sex", active1.fields["sex"], "sex")
fields[++fields.len] = FIELD("Age", active1.fields["age"], "age")
fields[++fields.len] = FIELD("Fingerprint", active1.fields["fingerprint"], "fingerprint")
fields[++fields.len] = FIELD("Physical Status", active1.fields["p_stat"], "p_stat")
fields[++fields.len] = FIELD("Mental Status", active1.fields["m_stat"], "m_stat")
var/list/photos = list()
general["photos"] = photos
photos[++photos.len] = list("photo" = active1.fields["photo-south"])
photos[++photos.len] = list("photo" = active1.fields["photo-west"])
photos[++photos.len] = active1.fields["photo-south"]
photos[++photos.len] = active1.fields["photo-west"]
general["has_photos"] = (active1.fields["photo-south"] || active1.fields["photo-west"] ? 1 : 0)
general["empty"] = 0
else
@@ -93,17 +134,17 @@
if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2))
var/list/fields = list()
medical["fields"] = fields
fields[++fields.len] = list("field" = "Blood Type:", "value" = active2.fields["blood_type"], "edit" = "blood_type", "line_break" = 0)
fields[++fields.len] = list("field" = "DNA:", "value" = active2.fields["b_dna"], "edit" = "b_dna", "line_break" = 1)
fields[++fields.len] = list("field" = "Minor Disabilities:", "value" = active2.fields["mi_dis"], "edit" = "mi_dis", "line_break" = 0)
fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["mi_dis_d"], "edit" = "mi_dis_d", "line_break" = 1)
fields[++fields.len] = list("field" = "Major Disabilities:", "value" = active2.fields["ma_dis"], "edit" = "ma_dis", "line_break" = 0)
fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["ma_dis_d"], "edit" = "ma_dis_d", "line_break" = 1)
fields[++fields.len] = list("field" = "Allergies:", "value" = active2.fields["alg"], "edit" = "alg", "line_break" = 0)
fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["alg_d"], "edit" = "alg_d", "line_break" = 1)
fields[++fields.len] = list("field" = "Current Diseases:", "value" = active2.fields["cdi"], "edit" = "cdi", "line_break" = 0)
fields[++fields.len] = list("field" = "Details:", "value" = active2.fields["cdi_d"], "edit" = "cdi_d", "line_break" = 1)
fields[++fields.len] = list("field" = "Important Notes:", "value" = active2.fields["notes"], "edit" = "notes", "line_break" = 0)
fields[++fields.len] = MED_FIELD("Blood Type", active2.fields["blood_type"], "blood_type", FALSE)
fields[++fields.len] = MED_FIELD("DNA", active2.fields["b_dna"], "b_dna", TRUE)
fields[++fields.len] = MED_FIELD("Minor Disabilities", active2.fields["mi_dis"], "mi_dis", FALSE)
fields[++fields.len] = MED_FIELD("Details", active2.fields["mi_dis_d"], "mi_dis_d", TRUE)
fields[++fields.len] = MED_FIELD("Major Disabilities", active2.fields["ma_dis"], "ma_dis", FALSE)
fields[++fields.len] = MED_FIELD("Details", active2.fields["ma_dis_d"], "ma_dis_d", TRUE)
fields[++fields.len] = MED_FIELD("Allergies", active2.fields["alg"], "alg", FALSE)
fields[++fields.len] = MED_FIELD("Details", active2.fields["alg_d"], "alg_d", TRUE)
fields[++fields.len] = MED_FIELD("Current Diseases", active2.fields["cdi"], "cdi", FALSE)
fields[++fields.len] = MED_FIELD("Details", active2.fields["cdi_d"], "cdi_d", TRUE)
fields[++fields.len] = MED_FIELD("Important Notes", active2.fields["notes"], "notes", TRUE)
if(!active2.fields["comments"] || !islist(active2.fields["comments"]))
active2.fields["comments"] = list()
medical["comments"] = active2.fields["comments"]
@@ -127,7 +168,9 @@
var/turf/T = get_turf(M)
if(T)
var/medbot = list()
var/area/A = get_area(T)
medbot["name"] = M.name
medbot["area"] = A.name
medbot["x"] = T.x
medbot["y"] = T.y
medbot["on"] = M.on
@@ -138,395 +181,290 @@
else
medbot["use_beaker"] = 0
data["medbots"] += list(medbot)
data["modal"] = tgui_modal_data(src)
return data
/obj/machinery/computer/med_data/Topic(href, href_list)
/obj/machinery/computer/med_data/tgui_act(action, params)
if(..())
return 1
return
if(stat & (NOPOWER|BROKEN))
return
if(!GLOB.data_core.general.Find(active1))
active1 = null
if(!GLOB.data_core.medical.Find(active2))
active2 = null
if(href_list["temp"])
temp = null
. = TRUE
if(tgui_act_modal(action, params))
return
if(href_list["temp_action"])
if(href_list["temp_action"])
var/temp_href = splittext(href_list["temp_action"], "=")
switch(temp_href[1])
if("del_all2")
for(var/datum/data/record/R in GLOB.data_core.medical)
qdel(R)
setTemp("<h3>All records deleted.</h3>")
if("p_stat")
if(active1)
switch(temp_href[2])
if("deceased")
active1.fields["p_stat"] = "*Deceased*"
if("ssd")
active1.fields["p_stat"] = "*SSD*"
if("active")
active1.fields["p_stat"] = "Active"
if("unfit")
active1.fields["p_stat"] = "Physically Unfit"
if("disabled")
active1.fields["p_stat"] = "Disabled"
if("m_stat")
if(active1)
switch(temp_href[2])
if("insane")
active1.fields["m_stat"] = "*Insane*"
if("unstable")
active1.fields["m_stat"] = "*Unstable*"
if("watch")
active1.fields["m_stat"] = "*Watch*"
if("stable")
active1.fields["m_stat"] = "Stable"
if("blood_type")
if(active2)
switch(temp_href[2])
if("an")
active2.fields["blood_type"] = "A-"
if("bn")
active2.fields["blood_type"] = "B-"
if("abn")
active2.fields["blood_type"] = "AB-"
if("on")
active2.fields["blood_type"] = "O-"
if("ap")
active2.fields["blood_type"] = "A+"
if("bp")
active2.fields["blood_type"] = "B+"
if("abp")
active2.fields["blood_type"] = "AB+"
if("op")
active2.fields["blood_type"] = "O+"
if("del_r2")
QDEL_NULL(active2)
if(href_list["scan"])
if(scan)
scan.forceMove(loc)
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
switch(action)
if("cleartemp")
temp = null
if("scan")
if(scan)
scan.forceMove(loc)
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
else
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/card/id))
usr.drop_item()
I.forceMove(src)
scan = I
if("login")
var/login_type = text2num(params["login_type"])
if(login_type == LOGIN_TYPE_NORMAL && istype(scan))
if(check_access(scan))
authenticated = scan.registered_name
rank = scan.assignment
else if(login_type == LOGIN_TYPE_AI && isAI(usr))
authenticated = usr.name
rank = "AI"
else if(login_type == LOGIN_TYPE_ROBOT && isrobot(usr))
authenticated = usr.name
var/mob/living/silicon/robot/R = usr
rank = "[R.modtype] [R.braintype]"
if(authenticated)
active1 = null
active2 = null
screen = MED_DATA_R_LIST
else
var/obj/item/I = usr.get_active_hand()
if(istype(I, /obj/item/card/id))
usr.drop_item()
I.forceMove(src)
scan = I
. = FALSE
if(href_list["login"])
if(isAI(usr))
authenticated = usr.name
rank = "AI"
else if(isrobot(usr))
authenticated = usr.name
var/mob/living/silicon/robot/R = usr
rank = "[R.modtype] [R.braintype]"
else if(istype(scan, /obj/item/card/id))
if(check_access(scan))
authenticated = scan.registered_name
rank = scan.assignment
if(authenticated)
active1 = null
active2 = null
screen = MED_DATA_MAIN
if(.)
return
if(authenticated)
if(href_list["logout"])
authenticated = null
screen = null
active1 = null
active2 = null
. = TRUE
switch(action)
if("logout")
if(scan)
scan.forceMove(loc)
if(ishuman(usr) && !usr.get_active_hand())
usr.put_in_hands(scan)
scan = null
authenticated = null
screen = null
active1 = null
active2 = null
if("screen")
screen = clamp(text2num(params["screen"]) || 0, MED_DATA_R_LIST, MED_DATA_MEDBOT)
active1 = null
active2 = null
if("vir")
var/type = text2path(params["vir"] || "")
if(!ispath(type, /datum/disease))
return
if(href_list["screen"])
screen = text2num(href_list["screen"])
if(screen < 1)
screen = MED_DATA_MAIN
var/datum/disease/D = new type(0)
var/list/payload = list(
name = D.name,
max_stages = D.max_stages,
spread_text = D.spread_text,
cure = D.cure_text || "None",
desc = D.desc,
severity = D.severity
);
tgui_modal_message(src, "virus", "", null, payload)
qdel(D)
if("del_all")
for(var/datum/data/record/R in GLOB.data_core.medical)
qdel(R)
set_temp("All medical records deleted.")
if("del_r")
if(active2)
set_temp("Medical record deleted.")
qdel(active2)
if("d_rec")
var/datum/data/record/general_record = locate(params["d_rec"] || "")
if(!GLOB.data_core.general.Find(general_record))
set_temp("Record not found.", "danger")
return
active1 = null
active2 = null
var/datum/data/record/medical_record
for(var/datum/data/record/M in GLOB.data_core.medical)
if(M.fields["name"] == general_record.fields["name"] && M.fields["id"] == general_record.fields["id"])
medical_record = M
break
if(href_list["vir"])
var/type = href_list["vir"]
var/datum/disease/D = new type(0)
var/afs = ""
for(var/mob/M in D.viable_mobtypes)
afs += "[initial(M.name)];"
var/severity = D.severity
switch(severity)
if("Harmful", "Minor")
severity = "<span class='good'>[severity]</span>"
if("Medium")
severity = "<span class='average'>[severity]</span>"
if("Dangerous!")
severity = "<span class='bad'>[severity]</span>"
if("BIOHAZARD THREAT!")
severity = "<h4><span class='bad'>[severity]</span></h4>"
setTemp({"<b>Name:</b> [D.name]
<BR><b>Number of stages:</b> [D.max_stages]
<BR><b>Spread:</b> [D.spread_text] Transmission
<BR><b>Possible Cure:</b> [(D.cure_text||"none")]
<BR><b>Affected Lifeforms:</b>[afs]<BR>
<BR><b>Notes:</b> [D.desc]<BR>
<BR><b>Severity:</b> [severity]"})
qdel(D)
if(href_list["del_all"])
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_all2=1")
buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null)
setTemp("<h3>Are you sure you wish to delete all records?</h3>", buttons)
if(href_list["field"])
if(..())
return 1
var/a1 = active1
var/a2 = active2
switch(href_list["field"])
if("fingerprint")
if(istype(active1, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please input fingerprint hash:", "Med. records", active1.fields["fingerprint"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active1 != a1)
return 1
active1.fields["fingerprint"] = t1
if("sex")
if(istype(active1, /datum/data/record))
if(active1.fields["sex"] == "Male")
active1.fields["sex"] = "Female"
else
active1.fields["sex"] = "Male"
if("age")
if(istype(active1, /datum/data/record))
var/t1 = input("Please input age:", "Med. records", active1.fields["age"], null) as num
if(!t1 || ..() || active1 != a1)
return 1
active1.fields["age"] = t1
if("mi_dis")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please input minor disabilities list:", "Med. records", active2.fields["mi_dis"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["mi_dis"] = t1
if("mi_dis_d")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please summarize minor dis.:", "Med. records", active2.fields["mi_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["mi_dis_d"] = t1
if("ma_dis")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please input major diabilities list:", "Med. records", active2.fields["ma_dis"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["ma_dis"] = t1
if("ma_dis_d")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please summarize major dis.:", "Med. records", active2.fields["ma_dis_d"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["ma_dis_d"] = t1
if("alg")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please state allergies:", "Med. records", active2.fields["alg"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["alg"] = t1
if("alg_d")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please summarize allergies:", "Med. records", active2.fields["alg_d"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["alg_d"] = t1
if("cdi")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please state diseases:", "Med. records", active2.fields["cdi"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["cdi"] = t1
if("cdi_d")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please summarize diseases:", "Med. records", active2.fields["cdi_d"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["cdi_d"] = t1
if("notes")
if(istype(active2, /datum/data/record))
var/t1 = copytext(html_encode(trim(input("Please summarize notes:", "Med. records", html_decode(active2.fields["notes"]), null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["notes"] = t1
if("p_stat")
if(istype(active1, /datum/data/record))
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "*Deceased*", "icon" = "stethoscope", "href" = "p_stat=deceased", "status" = (active1.fields["p_stat"] == "*Deceased*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "*SSD*", "icon" = "stethoscope", "href" = "p_stat=ssd", "status" = (active1.fields["p_stat"] == "*SSD*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "Active", "icon" = "stethoscope", "href" = "p_stat=active", "status" = (active1.fields["p_stat"] == "Active" ? "selected" : null))
buttons[++buttons.len] = list("name" = "Physically Unfit", "icon" = "stethoscope", "href" = "p_stat=unfit", "status" = (active1.fields["p_stat"] == "Physically Unfit" ? "selected" : null))
buttons[++buttons.len] = list("name" = "Disabled", "icon" = "stethoscope", "href" = "p_stat=disabled", "status" = (active1.fields["p_stat"] == "Disabled" ? "selected" : null))
setTemp("<h3>Physical Condition</h3>", buttons)
if("m_stat")
if(istype(active1, /datum/data/record))
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "*Insane*", "icon" = "stethoscope", "href" = "m_stat=insane", "status" = (active1.fields["m_stat"] == "*Insane*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "*Unstable*", "icon" = "stethoscope", "href" = "m_stat=unstable", "status" = (active1.fields["m_stat"] == "*Unstable*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "*Watch*", "icon" = "stethoscope", "href" = "m_stat=watch", "status" = (active1.fields["m_stat"] == "*Watch*" ? "selected" : null))
buttons[++buttons.len] = list("name" = "Stable", "icon" = "stethoscope", "href" = "m_stat=stable", "status" = (active1.fields["m_stat"] == "Stable" ? "selected" : null))
setTemp("<h3>Mental Condition</h3>", buttons)
if("blood_type")
if(istype(active2, /datum/data/record))
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "A-", "icon" = "tint", "href" = "blood_type=an", "status" = (active2.fields["blood_type"] == "A-" ? "selected" : null))
buttons[++buttons.len] = list("name" = "A+", "icon" = "tint", "href" = "blood_type=ap", "status" = (active2.fields["blood_type"] == "A+" ? "selected" : null))
buttons[++buttons.len] = list("name" = "B-", "icon" = "tint", "href" = "blood_type=bn", "status" = (active2.fields["blood_type"] == "B-" ? "selected" : null))
buttons[++buttons.len] = list("name" = "B+", "icon" = "tint", "href" = "blood_type=bp", "status" = (active2.fields["blood_type"] == "B+" ? "selected" : null))
buttons[++buttons.len] = list("name" = "AB-", "icon" = "tint", "href" = "blood_type=abn", "status" = (active2.fields["blood_type"] == "AB-" ? "selected" : null))
buttons[++buttons.len] = list("name" = "AB+", "icon" = "tint", "href" = "blood_type=abp", "status" = (active2.fields["blood_type"] == "AB+" ? "selected" : null))
buttons[++buttons.len] = list("name" = "O-", "icon" = "tint", "href" = "blood_type=on", "status" = (active2.fields["blood_type"] == "O-" ? "selected" : null))
buttons[++buttons.len] = list("name" = "O+", "icon" = "tint", "href" = "blood_type=op", "status" = (active2.fields["blood_type"] == "O+" ? "selected" : null))
setTemp("<h3>Blood Type</h3>", buttons)
if("b_dna")
if(istype(active2, /datum/data/record))
var/t1 = copytext(trim(sanitize(input("Please input DNA hash:", "Med. records", active2.fields["b_dna"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["b_dna"] = t1
if("vir_name")
var/datum/data/record/v = locate(href_list["edit_vir"])
if(v)
var/t1 = copytext(trim(sanitize(input("Please input pathogen name:", "VirusDB", v.fields["name"], null) as text)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active1 != a1)
return 1
v.fields["name"] = t1
if("vir_desc")
var/datum/data/record/v = locate(href_list["edit_vir"])
if(v)
var/t1 = copytext(trim(sanitize(input("Please input information about pathogen:", "VirusDB", v.fields["description"], null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active1 != a1)
return 1
v.fields["description"] = t1
if(href_list["del_r"])
if(active2)
var/list/buttons = list()
buttons[++buttons.len] = list("name" = "Yes", "icon" = "check", "href" = "del_r2=1", "status" = null)
buttons[++buttons.len] = list("name" = "No", "icon" = "times", "href" = null, "status" = null)
setTemp("<h3>Are you sure you wish to delete the record (Medical Portion Only)?</h3>", buttons)
if(href_list["d_rec"])
var/datum/data/record/R = locate(href_list["d_rec"])
var/datum/data/record/M = locate(href_list["d_rec"])
if(!GLOB.data_core.general.Find(R))
setTemp("<h3 class='bad'>Record not found!</h3>")
return 1
for(var/datum/data/record/E in GLOB.data_core.medical)
if(E.fields["name"] == R.fields["name"] && E.fields["id"] == R.fields["id"])
M = E
active1 = R
active2 = M
screen = MED_DATA_RECORD
if(href_list["new"])
if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record))
var/datum/data/record/R = new /datum/data/record()
R.fields["name"] = active1.fields["name"]
R.fields["id"] = active1.fields["id"]
R.name = "Medical Record #[R.fields["id"]]"
R.fields["blood_type"] = "Unknown"
R.fields["b_dna"] = "Unknown"
R.fields["mi_dis"] = "None"
R.fields["mi_dis_d"] = "No minor disabilities have been declared."
R.fields["ma_dis"] = "None"
R.fields["ma_dis_d"] = "No major disabilities have been diagnosed."
R.fields["alg"] = "None"
R.fields["alg_d"] = "No allergies have been detected in this patient."
R.fields["cdi"] = "None"
R.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
R.fields["notes"] = "No notes."
GLOB.data_core.medical += R
active2 = R
active1 = general_record
active2 = medical_record
screen = MED_DATA_RECORD
if(href_list["add_c"])
if(!istype(active2, /datum/data/record))
return 1
var/a2 = active2
var/t1 = copytext(trim(sanitize(input("Add Comment:", "Med. records", null, null) as message)), 1, MAX_MESSAGE_LEN)
if(!t1 || ..() || active2 != a2)
return 1
active2.fields["comments"] += "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]<BR>[t1]"
if(href_list["del_c"])
var/index = min(max(text2num(href_list["del_c"]) + 1, 1), length(active2.fields["comments"]))
if(istype(active2, /datum/data/record) && active2.fields["comments"][index])
active2.fields["comments"] -= active2.fields["comments"][index]
if(href_list["search"])
var/t1 = clean_input("Search String: (Name, DNA, or ID)", "Med. records", null, null)
if(!t1 || ..())
return 1
active1 = null
active2 = null
t1 = lowertext(t1)
for(var/datum/data/record/R in GLOB.data_core.medical)
if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))
if("new")
if(istype(active1, /datum/data/record) && !istype(active2, /datum/data/record))
var/datum/data/record/R = new /datum/data/record()
R.fields["name"] = active1.fields["name"]
R.fields["id"] = active1.fields["id"]
R.name = "Medical Record #[R.fields["id"]]"
R.fields["blood_type"] = "Unknown"
R.fields["b_dna"] = "Unknown"
R.fields["mi_dis"] = "None"
R.fields["mi_dis_d"] = "No minor disabilities have been declared."
R.fields["ma_dis"] = "None"
R.fields["ma_dis_d"] = "No major disabilities have been diagnosed."
R.fields["alg"] = "None"
R.fields["alg_d"] = "No allergies have been detected in this patient."
R.fields["cdi"] = "None"
R.fields["cdi_d"] = "No diseases have been diagnosed at the moment."
R.fields["notes"] = "No notes."
GLOB.data_core.medical += R
active2 = R
if(!active2)
setTemp("<h3 class='bad'>Could not locate record [t1].</h3>")
else
screen = MED_DATA_RECORD
set_temp("Medical record created.", "success")
if("del_c")
var/index = text2num(params["del_c"] || "")
if(!index || !istype(active2, /datum/data/record))
return
var/list/comments = active2.fields["comments"]
index = clamp(index, 1, length(comments))
if(comments[index])
comments.Cut(index, index + 1)
if("search")
active1 = null
active2 = null
var/t1 = lowertext(params["t1"] || "")
if(!length(t1))
return
for(var/datum/data/record/R in GLOB.data_core.medical)
if(t1 == lowertext(R.fields["name"]) || t1 == lowertext(R.fields["id"]) || t1 == lowertext(R.fields["b_dna"]))
active2 = R
break
if(!active2)
set_temp("Medical record not found. You must enter the person's exact name, ID or DNA.", "danger")
return
for(var/datum/data/record/E in GLOB.data_core.general)
if(E.fields["name"] == active2.fields["name"] && E.fields["id"] == active2.fields["id"])
active1 = E
break
screen = MED_DATA_RECORD
if("print_p")
if(!printing)
printing = TRUE
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, TRUE)
SStgui.update_uis(src)
addtimer(CALLBACK(src, .proc/print_finish), 5 SECONDS)
else
return FALSE
if(href_list["print_p"])
if(!printing)
printing = 1
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
sleep(50)
var/obj/item/paper/P = new /obj/item/paper(loc)
P.info = "<CENTER><B>Medical Record</B></CENTER><BR>"
if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
<BR>\nSex: [active1.fields["sex"]]
<BR>\nAge: [active1.fields["age"]]
<BR>\nFingerprint: [active1.fields["fingerprint"]]
<BR>\nPhysical Status: [active1.fields["p_stat"]]
<BR>\nMental Status: [active1.fields["m_stat"]]<BR>"}
/**
* Called in tgui_act() to process modal actions
*
* Arguments:
* * action - The action passed by tgui
* * params - The params passed by tgui
*/
/obj/machinery/computer/med_data/proc/tgui_act_modal(action, params)
. = TRUE
var/id = params["id"] // The modal's ID
var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_OPEN)
switch(id)
if("edit")
var/field = arguments["field"]
if(!length(field) || !field_edit_questions[field])
return
var/question = field_edit_questions[field]
var/choices = field_edit_choices[field]
if(length(choices))
tgui_modal_choice(src, id, question, arguments = arguments, value = arguments["value"], choices = choices)
else
tgui_modal_input(src, id, question, arguments = arguments, value = arguments["value"])
if("add_c")
tgui_modal_input(src, id, "Please enter your message:")
else
P.info += "<B>General Record Lost!</B><BR>"
if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2))
P.info += {"<BR>\n<CENTER><B>Medical Data</B></CENTER>
<BR>\nBlood Type: [active2.fields["blood_type"]]
<BR>\nDNA: [active2.fields["b_dna"]]<BR>\n
<BR>\nMinor Disabilities: [active2.fields["mi_dis"]]
<BR>\nDetails: [active2.fields["mi_dis_d"]]<BR>\n
<BR>\nMajor Disabilities: [active2.fields["ma_dis"]]
<BR>\nDetails: [active2.fields["ma_dis_d"]]<BR>\n
<BR>\nAllergies: [active2.fields["alg"]]
<BR>\nDetails: [active2.fields["alg_d"]]<BR>\n
<BR>\nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section)
<BR>\nDetails: [active2.fields["cdi_d"]]<BR>\n
<BR>\nImportant Notes:
<BR>\n\t[active2.fields["notes"]]<BR>\n
<BR>\n
<CENTER><B>Comments/Log</B></CENTER><BR>"}
for(var/c in active2.fields["comments"])
P.info += "[c]<BR>"
else
P.info += "<B>Medical Record Lost!</B><BR>"
P.info += "</TT>"
P.name = "paper- 'Medical Record: [active1.fields["name"]]'"
printing = 0
return 1
return FALSE
if(TGUI_MODAL_ANSWER)
var/answer = params["answer"]
switch(id)
if("edit")
var/field = arguments["field"]
if(!length(field) || !field_edit_questions[field])
return
var/list/choices = field_edit_choices[field]
if(length(choices) && !(answer in choices))
return
/obj/machinery/computer/med_data/proc/setTemp(text, list/buttons = list())
temp = list("text" = text, "buttons" = buttons, "has_buttons" = buttons.len > 0)
if(field == "age")
var/new_age = text2num(answer)
if(new_age < AGE_MIN || new_age > AGE_MAX)
set_temp("Invalid age. It must be between [AGE_MIN] and [AGE_MAX].", "danger")
return
answer = new_age
if(istype(active2) && (field in active2.fields))
active2.fields[field] = answer
else if(istype(active1) && (field in active1.fields))
active1.fields[field] = answer
if("add_c")
if(!length(answer) || !istype(active2) || !length(authenticated))
return
active2.fields["comments"] += list(list(
header = "Made by [authenticated] ([rank]) on [GLOB.current_date_string] [station_time_timestamp()]",
text = answer
))
else
return FALSE
else
return FALSE
/**
* Called when the print timer finishes
*/
/obj/machinery/computer/med_data/proc/print_finish()
var/obj/item/paper/P = new /obj/item/paper(loc)
P.info = "<center></b>Medical Record</b></center><br>"
if(istype(active1, /datum/data/record) && GLOB.data_core.general.Find(active1))
P.info += {"Name: [active1.fields["name"]] ID: [active1.fields["id"]]
<br>\nSex: [active1.fields["sex"]]
<br>\nAge: [active1.fields["age"]]
<br>\nFingerprint: [active1.fields["fingerprint"]]
<br>\nPhysical Status: [active1.fields["p_stat"]]
<br>\nMental Status: [active1.fields["m_stat"]]<br>"}
else
P.info += "</b>General Record Lost!</b><br>"
if(istype(active2, /datum/data/record) && GLOB.data_core.medical.Find(active2))
P.info += {"<br>\n<center></b>Medical Data</b></center>
<br>\nBlood Type: [active2.fields["blood_type"]]
<br>\nDNA: [active2.fields["b_dna"]]<br>\n
<br>\nMinor Disabilities: [active2.fields["mi_dis"]]
<br>\nDetails: [active2.fields["mi_dis_d"]]<br>\n
<br>\nMajor Disabilities: [active2.fields["ma_dis"]]
<br>\nDetails: [active2.fields["ma_dis_d"]]<br>\n
<br>\nAllergies: [active2.fields["alg"]]
<br>\nDetails: [active2.fields["alg_d"]]<br>\n
<br>\nCurrent Diseases: [active2.fields["cdi"]] (per disease info placed in log/comment section)
<br>\nDetails: [active2.fields["cdi_d"]]<br>\n
<br>\nImportant Notes:
<br>\n\t[active2.fields["notes"]]<br>\n
<br>\n
<center></b>Comments/Log</b></center><br>"}
for(var/c in active2.fields["comments"])
P.info += "[c]<br>"
else
P.info += "</b>Medical Record Lost!</b><br>"
P.info += "</tt>"
P.name = "paper - 'Medical Record: [active1.fields["name"]]'"
printing = FALSE
SStgui.update_uis(src)
/**
* Sets a temporary message to display to the user
*
* Arguments:
* * text - Text to display, null/empty to clear the message from the UI
* * style - The style of the message: (color name), info, success, warning, danger, virus
*/
/obj/machinery/computer/med_data/proc/set_temp(text = "", style = "info", update_now = FALSE)
temp = list(text = text, style = style)
if(update_now)
SStgui.update_uis(src)
/obj/machinery/computer/med_data/emp_act(severity)
if(stat & (BROKEN|NOPOWER))
@@ -564,9 +502,10 @@
icon_screen = "medlaptop"
density = 0
#undef MED_DATA_MAIN
#undef MED_DATA_R_LIST
#undef MED_DATA_MAINT
#undef MED_DATA_RECORD
#undef MED_DATA_V_DATA
#undef MED_DATA_MEDBOT
#undef FIELD
#undef MED_FIELD
+147
View File
@@ -0,0 +1,147 @@
/obj/machinery/computer/sm_monitor
name = "supermatter monitoring console"
desc = "Used to monitor supermatter shards."
icon_keyboard = "power_key"
icon_screen = "smmon_0"
circuit = /obj/item/circuitboard/sm_monitor
light_color = LIGHT_COLOR_YELLOW
/// Cache-list of all supermatter shards
var/list/supermatters
/// Last status of the active supermatter for caching purposes
var/last_status
/// Reference to the active shard
var/obj/machinery/power/supermatter_shard/active
/obj/machinery/computer/sm_monitor/Destroy()
active = null
return ..()
/obj/machinery/computer/sm_monitor/attack_ai(mob/user)
attack_hand(user)
/obj/machinery/computer/sm_monitor/attack_hand(mob/user)
add_fingerprint(user)
if(stat & (BROKEN|NOPOWER))
return
tgui_interact(user)
/obj/machinery/computer/sm_monitor/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)
ui = new(user, src, ui_key, "SupermatterMonitor", name, 600, 325, master_ui, state)
ui.open()
/obj/machinery/computer/sm_monitor/tgui_data(mob/user)
var/list/data = list()
if(istype(active))
var/turf/T = get_turf(active)
// If we somehow delam during this proc, handle it somewhat
if(!T)
active = null
refresh()
return
var/datum/gas_mixture/air = T.return_air()
if(!air)
active = null
return
data["active"] = TRUE
data["SM_integrity"] = active.get_integrity()
data["SM_power"] = active.power
data["SM_ambienttemp"] = air.temperature
data["SM_ambientpressure"] = air.return_pressure()
//data["SM_EPR"] = round((air.total_moles / air.group_multiplier) / 23.1, 0.01)
var/other_moles = air.total_trace_moles()
var/TM = air.total_moles()
if(TM)
data["SM_gas_O2"] = round(100*air.oxygen/TM, 0.01)
data["SM_gas_CO2"] = round(100*air.carbon_dioxide/TM, 0.01)
data["SM_gas_N2"] = round(100*air.nitrogen/TM, 0.01)
data["SM_gas_PL"] = round(100*air.toxins/TM, 0.01)
if(other_moles)
data["SM_gas_OTHER"] = round(100 * other_moles / TM, 0.01)
else
data["SM_gas_OTHER"] = 0
else
data["SM_gas_O2"] = 0
data["SM_gas_CO2"] = 0
data["SM_gas_N2"] = 0
data["SM_gas_PH"] = 0
data["SM_gas_OTHER"] = 0
else
var/list/SMS = list()
for(var/I in supermatters)
var/obj/machinery/power/supermatter_shard/S = I
var/area/A = get_area(S)
if(!A)
continue
SMS.Add(list(list(
"area_name" = A.name,
"integrity" = S.get_integrity(),
"uid" = S.UID()
)))
data["active"] = FALSE
data["supermatters"] = SMS
return data
/**
* Supermatter List Refresher
*
* This proc loops through the list of supermatters in the atmos SS and adds them to this console's cache list
*/
/obj/machinery/computer/sm_monitor/proc/refresh()
supermatters = list()
var/turf/T = get_turf(tgui_host()) // Get the TGUI host incase this ever turned into a supermatter monitoring module for AIs to use or something
if(!T)
return
for(var/obj/machinery/power/supermatter_shard/S in SSair.atmos_machinery)
// Delaminating, not within coverage, not on a tile.
if(!(is_station_level(S.z) || is_mining_level(S.z) || atoms_share_level(S, T) || !istype(S.loc, /turf/simulated/)))
continue
supermatters.Add(S)
if(!(active in supermatters))
active = null
/obj/machinery/computer/sm_monitor/process()
if(stat & (NOPOWER|BROKEN))
return FALSE
if(active)
var/new_status = active.get_status()
if(last_status != new_status)
last_status = new_status
if(last_status == SUPERMATTER_ERROR)
last_status = SUPERMATTER_INACTIVE
icon_screen = "smmon_[last_status]"
update_icon()
return TRUE
/obj/machinery/computer/sm_monitor/tgui_act(action, params)
if(..())
return
if(stat & (BROKEN|NOPOWER))
return
. = TRUE
switch(action)
if("refresh")
refresh()
if("view")
var/newuid = params["view"]
for(var/obj/machinery/power/supermatter_shard/S in supermatters)
if(S.UID() == newuid)
active = S
break
if("back")
active = null
+48 -67
View File
@@ -103,7 +103,7 @@
beaker.forceMove(drop_location())
beaker = null
/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O as mob|obj, mob/living/user as mob)
/obj/machinery/atmospherics/unary/cryo_cell/MouseDrop_T(atom/movable/O, mob/living/user)
if(O.loc == user) //no you can't pull things out of your ass
return
if(user.incapacitated()) //are you cuffed, dying, lying, stunned or other
@@ -140,6 +140,7 @@
add_attack_logs(user, L, "put into a cryo cell at [COORD(src)].", ATKLOG_ALL)
if(user.pulling == L)
user.stop_pulling()
SStgui.update_uis(src)
/obj/machinery/atmospherics/unary/cryo_cell/process()
..()
@@ -177,14 +178,14 @@
return FALSE
/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user as mob)
/obj/machinery/atmospherics/unary/cryo_cell/relaymove(mob/user)
if(user.stat)
return
go_out()
return
/obj/machinery/atmospherics/unary/cryo_cell/attack_ghost(mob/user)
return attack_hand(user)
tgui_interact(user)
/obj/machinery/atmospherics/unary/cryo_cell/attack_hand(mob/user)
if(user == occupant)
@@ -194,36 +195,18 @@
to_chat(usr, "<span class='boldnotice'>Close the maintenance panel first.</span>")
return
ui_interact(user)
tgui_interact(user)
/**
* The ui_interact proc is used to open and update Nano UIs
* If ui_interact is not used then the UI will not update correctly
* ui_interact is currently defined for /atom/movable (which is inherited by /obj and /mob)
*
* @param user /mob The mob who is interacting with this ui
* @param ui_key string A string key to use for this ui. Allows for multiple unique uis on one obj/mob (defaut value "main")
* @param ui /datum/nanoui This parameter is passed by the nanoui process() proc when updating an open ui
*
* @return nothing
*/
/obj/machinery/atmospherics/unary/cryo_cell/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
// 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/atmospherics/unary/cryo_cell/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
// for a list of parameters and their descriptions see the code docs in \code\modules\nano\nanoui.dm
ui = new(user, src, ui_key, "cryo.tmpl", "Cryo Cell Control System", 520, 480)
// open the new ui window
ui = new(user, src, ui_key, "Cryo", "Cryo Cell", 520, 490)
ui.open()
// auto update every Master Controller tick
ui.set_auto_update(1)
/obj/machinery/atmospherics/unary/cryo_cell/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/atmospherics/unary/cryo_cell/tgui_data(mob/user)
var/data[0]
data["isOperating"] = on
data["hasOccupant"] = occupant ? 1 : 0
data["hasOccupant"] = occupant ? TRUE : FALSE
var/occupantData[0]
if(occupant)
@@ -246,7 +229,7 @@
else if(air_contents.temperature > TCRYO)
data["cellTemperatureStatus"] = "average"
data["isBeakerLoaded"] = beaker ? 1 : 0
data["isBeakerLoaded"] = beaker ? TRUE : FALSE
data["beakerLabel"] = null
data["beakerVolume"] = 0
if(beaker)
@@ -259,48 +242,44 @@
data["auto_eject_dead"] = (auto_eject_prefs & AUTO_EJECT_DEAD) ? TRUE : FALSE
return data
/obj/machinery/atmospherics/unary/cryo_cell/Topic(href, href_list)
if(usr == occupant)
return 0 // don't update UIs attached to this object
/obj/machinery/atmospherics/unary/cryo_cell/tgui_act(action, params)
if(..() || usr == occupant)
return
if(stat & (NOPOWER|BROKEN))
return
if(..())
return 0 // don't update UIs attached to this object
if(href_list["switchOn"])
on = TRUE
update_icon()
if(href_list["switchOff"])
on = FALSE
update_icon()
if(href_list["auto_eject_healthy_on"])
auto_eject_prefs |= AUTO_EJECT_HEALTHY
if(href_list["auto_eject_healthy_off"])
auto_eject_prefs &= ~AUTO_EJECT_HEALTHY
if(href_list["auto_eject_dead_on"])
auto_eject_prefs |= AUTO_EJECT_DEAD
if(href_list["auto_eject_dead_off"])
auto_eject_prefs &= ~AUTO_EJECT_DEAD
if(href_list["ejectBeaker"])
if(beaker)
. = TRUE
switch(action)
if("switchOn")
on = TRUE
update_icon()
if("switchOff")
on = FALSE
update_icon()
if("auto_eject_healthy_on")
auto_eject_prefs |= AUTO_EJECT_HEALTHY
if("auto_eject_healthy_off")
auto_eject_prefs &= ~AUTO_EJECT_HEALTHY
if("auto_eject_dead_on")
auto_eject_prefs |= AUTO_EJECT_DEAD
if("auto_eject_dead_off")
auto_eject_prefs &= ~AUTO_EJECT_DEAD
if("ejectBeaker")
if(!beaker)
return
beaker.forceMove(get_step(loc, SOUTH))
beaker = null
if(href_list["ejectOccupant"])
if(!occupant || isslime(usr) || ispAI(usr))
return 0 // don't update UIs attached to this object
add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL)
go_out()
if("ejectOccupant")
if(!occupant || isslime(usr) || ispAI(usr))
return
add_attack_logs(usr, occupant, "ejected from cryo cell at [COORD(src)]", ATKLOG_ALL)
go_out()
else
return FALSE
add_fingerprint(usr)
return 1 // update UIs attached to this object
/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G as obj, var/mob/user as mob, params)
/obj/machinery/atmospherics/unary/cryo_cell/attackby(var/obj/item/G, var/mob/user, params)
if(istype(G, /obj/item/reagent_containers/glass))
var/obj/item/reagent_containers/B = G
if(beaker)
@@ -313,6 +292,7 @@
beaker = B
add_attack_logs(user, null, "Added [B] containing [B.reagents.log_list()] to a cryo cell at [COORD(src)]")
user.visible_message("[user] adds \a [B] to [src]!", "You add \a [B] to [src]!")
SStgui.update_uis(src)
return
if(exchange_parts(user, G))
@@ -357,7 +337,7 @@
return
if(occupant)
var/image/pickle = image(occupant.icon, occupant.icon_state)
var/mutable_appearance/pickle = mutable_appearance(occupant.icon, occupant.icon_state)
pickle.overlays = occupant.overlays
pickle.pixel_y = 22
@@ -455,8 +435,9 @@
playsound(loc, 'sound/machines/ding.ogg', 50, 1)
if(AUTO_EJECT_DEAD)
playsound(loc, 'sound/machines/buzz-sigh.ogg', 40)
SStgui.update_uis(src)
/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M as mob)
/obj/machinery/atmospherics/unary/cryo_cell/proc/put_mob(mob/living/carbon/M)
if(!istype(M))
to_chat(usr, "<span class='danger'>The cryo cell cannot handle such a lifeform!</span>")
return
@@ -530,7 +511,7 @@
/datum/data/function/proc/reset()
return
/datum/data/function/proc/r_input(href, href_list, mob/user as mob)
/datum/data/function/proc/r_input(href, href_list, mob/user)
return
/datum/data/function/proc/display()
+171 -142
View File
@@ -38,6 +38,11 @@
#define AIRLOCK_DAMAGE_DEFLECTION_N 21 // Normal airlock damage deflection
#define AIRLOCK_DAMAGE_DEFLECTION_R 30 // Reinforced airlock damage deflection
#define TGUI_GREEN 2
#define TGUI_ORANGE 1
#define TGUI_RED 0
GLOBAL_LIST_EMPTY(airlock_overlays)
/obj/machinery/door/airlock
@@ -54,7 +59,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
normalspeed = 1
siemens_strength = 1
var/security_level = 0 //How much are wires secured
var/aiControlDisabled = FALSE //If TRUE, AI control is disabled until the AI hacks back in and disables the lock. If 2, the AI has bypassed the lock. If -1, the control is enabled but the AI had bypassed it earlier, so if it is disabled again the AI would have no trouble getting back in.
var/aiControlDisabled = AICONTROLDISABLED_OFF
var/hackProof = FALSE // if TRUE, this door can't be hacked by the AI
var/electrified_until = 0 // World time when the door is no longer electrified. -1 if it is permanently electrified until someone fixes it.
var/main_power_lost_until = 0 //World time when main power is restored.
@@ -65,7 +70,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
var/spawnPowerRestoreRunning = 0
var/lights = TRUE // bolt lights show by default
var/datum/wires/airlock/wires
var/aiDisabledIdScanner = 0
var/aiDisabledIdScanner = FALSE
var/aiHacking = 0
var/obj/machinery/door/airlock/closeOther
var/closeOtherId
@@ -96,7 +101,7 @@ GLOBAL_LIST_EMPTY(airlock_overlays)
var/doorDeni = 'sound/machines/deniedbeep.ogg' // i'm thinkin' Deni's
var/boltUp = 'sound/machines/boltsup.ogg'
var/boltDown = 'sound/machines/boltsdown.ogg'
var/is_special = 0
var/is_special = FALSE
/obj/machinery/door/airlock/welded
welded = TRUE
@@ -174,7 +179,7 @@ About the new airlock wires panel:
spawn (10)
justzap = 0
return
else /*if(justzap)*/
else
return
else if(user.hallucination > 50 && prob(10) && !operating)
if(user.electrocute_act(50, src, 1, illusion = TRUE)) // We'll just go with a flat 50 damage, instead of doing powernet checks
@@ -190,10 +195,10 @@ About the new airlock wires panel:
return 0
/obj/machinery/door/airlock/proc/canAIControl()
return ((aiControlDisabled!=1) && (!isAllPowerLoss()))
return ((aiControlDisabled != AICONTROLDISABLED_ON) && (!isAllPowerLoss()))
/obj/machinery/door/airlock/proc/canAIHack()
return ((aiControlDisabled==1) && (!hackProof) && (!isAllPowerLoss()))
return ((aiControlDisabled == AICONTROLDISABLED_ON) && (!hackProof) && (!isAllPowerLoss()))
/obj/machinery/door/airlock/proc/arePowerSystemsOn()
if(stat & (NOPOWER|BROKEN))
@@ -561,38 +566,58 @@ About the new airlock wires panel:
/obj/machinery/door/airlock/attack_ghost(mob/user)
if(panel_open)
wires.Interact(user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/door/airlock/attack_ai(mob/user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/door/airlock/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
/obj/machinery/door/airlock/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, "door_control.tmpl", "Door Controls - [src]", 600, 375)
ui = new(user, src, ui_key, "AiAirlock", name, 600, 400, master_ui, state)
ui.open()
ui.set_auto_update(1)
/obj/machinery/door/airlock/ui_data(mob/user, datum/topic_state/state = GLOB.default_state)
var/data[0]
data["main_power_loss"] = round(main_power_lost_until > 0 ? max(main_power_lost_until - world.time, 0) / 10 : main_power_lost_until, 1)
data["backup_power_loss"] = round(backup_power_lost_until > 0 ? max(backup_power_lost_until - world.time, 0) / 10 : backup_power_lost_until, 1)
data["electrified"] = round(electrified_until > 0 ? max(electrified_until - world.time, 0) / 10 : electrified_until, 1)
data["open"] = !density
/obj/machinery/door/airlock/tgui_data(mob/user)
var/list/data = list()
var/commands[0]
commands[++commands.len] = list("name" = "IdScan", "command"= "idscan", "active" = !aiDisabledIdScanner,"enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1)
commands[++commands.len] = list("name" = "Bolts", "command"= "bolts", "active" = !locked, "enabled" = "Raised", "disabled" = "Dropped", "danger" = 0, "act" = 0)
commands[++commands.len] = list("name" = "Bolt Lights", "command"= "lights", "active" = lights, "enabled" = "Enabled", "disabled" = "Disable", "danger" = 0, "act" = 1)
commands[++commands.len] = list("name" = "Safeties", "command"= "safeties", "active" = safe, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0)
commands[++commands.len] = list("name" = "Timing", "command"= "timing", "active" = normalspeed, "enabled" = "Nominal", "disabled" = "Overridden", "danger" = 1, "act" = 0)
commands[++commands.len] = list("name" = "Door State", "command"= "open", "active" = density, "enabled" = "Closed", "disabled" = "Opened", "danger" = 0, "act" = 0)
commands[++commands.len] = list("name" = "Emergency Access","command"= "emergency", "active" = !emergency, "enabled" = "Disabled", "disabled" = "Enabled", "danger" = 0, "act" = 0)
var/list/power = list()
power["main"] = main_power_lost_until ? TGUI_RED : TGUI_GREEN
power["main_timeleft"] = max(main_power_lost_until - world.time, 0) / 10
power["backup"] = backup_power_lost_until ? TGUI_RED : TGUI_GREEN
power["backup_timeleft"] = max(backup_power_lost_until - world.time, 0) / 10
data["power"] = power
if(electrified_until == -1)
data["shock"] = TGUI_RED
else if(electrified_until > 0)
data["shock"] = TGUI_ORANGE
else
data["shock"] = TGUI_GREEN
data["commands"] = commands
data["shock_timeleft"] = max(electrified_until - world.time, 0) / 10
data["id_scanner"] = !aiDisabledIdScanner
data["emergency"] = emergency // access
data["locked"] = locked // bolted
data["lights"] = lights // bolt lights
data["safe"] = safe // safeties
data["speed"] = normalspeed // safe speed
data["welded"] = welded // welded
data["opened"] = !density // opened
var/list/wire = list()
wire["main_power"] = !wires.is_cut(WIRE_MAIN_POWER1)
wire["backup_power"] = !wires.is_cut(WIRE_BACKUP_POWER1)
wire["shock"] = !wires.is_cut(WIRE_ELECTRIFY)
wire["id_scanner"] = !wires.is_cut(WIRE_IDSCAN)
wire["bolts"] = !wires.is_cut(WIRE_DOOR_BOLTS)
wire["lights"] = !wires.is_cut(WIRE_BOLT_LIGHT)
wire["safe"] = !wires.is_cut(WIRE_SAFETY)
wire["timing"] = !wires.is_cut(WIRE_SPEED)
data["wires"] = wire
return data
/obj/machinery/door/airlock/proc/hack(mob/user)
set waitfor = 0
if(!aiHacking)
@@ -632,7 +657,7 @@ About the new airlock wires panel:
to_chat(user, "Transfer complete. Forcing airlock to execute program.")
sleep(50)
//disable blocked control
aiControlDisabled = 2
aiControlDisabled = AICONTROLDISABLED_BYPASS
to_chat(user, "Receiving control information from airlock.")
sleep(10)
//bring up airlock dialog
@@ -728,125 +753,125 @@ About the new airlock wires panel:
else
try_to_activate_door(user)
/obj/machinery/door/airlock/CanUseTopic(mob/user)
if(!issilicon(user) && !isobserver(user))
return STATUS_CLOSE
if(emagged)
to_chat(user, "<span class='warning'>Unable to interface: Internal error.</span>")
return STATUS_CLOSE
if(!canAIControl() && !isobserver(user))
if(canAIHack(user))
hack(user)
else
if(isAllPowerLoss()) //don't really like how this gets checked a second time, but not sure how else to do it.
to_chat(user, "<span class='warning'>Unable to interface: Connection timed out.</span>")
else
to_chat(user, "<span class='warning'>Unable to interface: Connection refused.</span>")
return STATUS_CLOSE
return ..()
/obj/machinery/door/airlock/Topic(href, href_list, nowindow = 0)
/obj/machinery/door/airlock/tgui_act(action, params)
if(..())
return 1
var/activate = text2num(href_list["activate"])
switch(href_list["command"])
if("idscan")
if(wires.is_cut(WIRE_IDSCAN))
to_chat(usr, "The IdScan wire has been cut - IdScan feature permanently disabled.")
else if(activate && aiDisabledIdScanner)
aiDisabledIdScanner = 0
to_chat(usr, "IdScan feature has been enabled.")
else if(!activate && !aiDisabledIdScanner)
aiDisabledIdScanner = 1
to_chat(usr, "IdScan feature has been disabled.")
if("main_power")
return
if(!issilicon(usr) && !usr.can_admin_interact())
to_chat(usr, "<span class='warning'>Access denied. Only silicons may use this interface.</span>")
return
if(issilicon(usr) && emagged)
to_chat(usr, "<span class='warning'>Unable to interface: Internal error.</span>")
return
if(!canAIControl() && !isobserver(usr))
if(canAIHack(usr))
hack(usr)
else
if(isAllPowerLoss())
to_chat(usr, "<span class='warning'>Unable to interface: Connection timed out.</span>")
else
to_chat(usr, "<span class='warning'>Unable to interface: Connection refused.</span>")
return
. = TRUE
switch(action)
if("disrupt-main")
if(!main_power_lost_until)
loseMainPower()
update_icon()
if("backup_power")
else
to_chat(usr, "<span class='warning'>Main power is already offline.</span>")
. = FALSE
if("disrupt-backup")
if(!backup_power_lost_until)
loseBackupPower()
update_icon()
if("bolts")
if(wires.is_cut(WIRE_DOOR_BOLTS))
to_chat(usr, "The door bolt control wire has been cut - Door bolts permanently dropped.")
else if(activate && lock())
to_chat(usr, "The door bolts have been dropped.")
else if(!activate && unlock())
to_chat(usr, "The door bolts have been raised.")
if("electrify_temporary")
if(activate && wires.is_cut(WIRE_ELECTRIFY))
to_chat(usr, text("The electrification wire is cut - Door permanently electrified."))
else if(!activate && electrified_until != 0)
to_chat(usr, "The door is now un-electrified.")
electrify(0)
else if(activate) //electrify door for 30 seconds
shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
usr.create_attack_log("<font color='red'>Electrified the [name] at [x] [y] [z]</font>")
add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
to_chat(usr, "The door is now electrified for thirty seconds.")
electrify(30)
if("electrify_permanently")
if(wires.is_cut(WIRE_ELECTRIFY))
to_chat(usr, text("The electrification wire is cut - Cannot electrify the door."))
else if(!activate && electrified_until != 0)
to_chat(usr, "The door is now un-electrified.")
electrify(0)
else if(activate)
shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
usr.create_attack_log("<font color='red'>Electrified the [name] at [x] [y] [z]</font>")
add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
to_chat(usr, "The door is now electrified.")
electrify(-1)
if("open")
if(welded)
to_chat(usr, text("The airlock has been welded shut!"))
else if(locked)
to_chat(usr, text("The door bolts are down!"))
else if(activate && density)
open()
else if(!activate && !density)
close()
if("safeties")
// Safeties! We don't need no stinking safeties!
if(wires.is_cut(WIRE_SAFETY))
to_chat(usr, text("The safety wire is cut - Cannot secure the door."))
else if(activate && safe)
safe = 0
else if(!activate && !safe)
safe = 1
if("timing")
// Door speed control
if(wires.is_cut(WIRE_SPEED))
to_chat(usr, text("The timing wire is cut - Cannot alter timing."))
else if(activate && normalspeed)
normalspeed = 0
else if(!activate && !normalspeed)
normalspeed = 1
if("lights")
// Bolt lights
if(wires.is_cut(WIRE_BOLT_LIGHT))
to_chat(usr, "The bolt lights wire has been cut - The door bolt lights are permanently disabled.")
else if(!activate && lights)
lights = 0
to_chat(usr, "The door bolt lights have been disabled.")
else if(activate && !lights)
lights = 1
to_chat(usr, "The door bolt lights have been enabled.")
update_icon()
if("emergency")
// Emergency access
if(emergency)
emergency = 0
to_chat(usr, "Emergency access has been disabled.")
else
emergency = 1
to_chat(usr, "Emergency access has been enabled.")
to_chat(usr, "<span class='warning'>Backup power is already offline.</span>")
if("shock-restore")
to_chat(usr, "<span class='notice'>The door is now un-electrified.</span>")
electrify(0)
if("shock-temp")
if(wires.is_cut(WIRE_ELECTRIFY))
to_chat(usr, "<span class='warning'>The electrification wire is cut - Door permanently electrified.</span>")
. = FALSE
else
//electrify door for 30 seconds
shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
usr.create_attack_log("<font color='red'>Electrified the [name] at [x] [y] [z]</font>")
add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
to_chat(usr, "<span class='notice'>The door is now electrified for thirty seconds.</span>")
electrify(30)
if("shock-perm")
if(wires.is_cut(WIRE_ELECTRIFY))
to_chat(usr, "<span class='warning'>The electrification wire is cut - Cannot electrify the door.</span>")
. = FALSE
else
shockedby += text("\[[time_stamp()]\][usr](ckey:[usr.ckey])")
usr.create_attack_log("<font color='red'>Electrified the [name] at [x] [y] [z]</font>")
add_attack_logs(usr, src, "Electrified", ATKLOG_ALL)
to_chat(usr, "<span class='notice'>The door is now electrified.</span>")
electrify(-1)
if("idscan-toggle")
if(wires.is_cut(WIRE_IDSCAN))
to_chat(usr, "<span class='warning'>The IdScan wire has been cut - IdScan feature permanently disabled.</span>")
. = FALSE
else if(aiDisabledIdScanner)
aiDisabledIdScanner = FALSE
to_chat(usr, "<span class='notice'>IdScan feature has been enabled.</span>")
else
aiDisabledIdScanner = TRUE
to_chat(usr, "<span class='notice'>IdScan feature has been disabled.</span>")
if("emergency-toggle")
emergency = !emergency
if(emergency)
to_chat(usr, "<span class='notice'>Emergency access has been enabled.</span>")
else
to_chat(usr, "<span class='notice'>Emergency access has been disabled.</span>")
update_icon()
return 1
if("bolt-toggle")
if(wires.is_cut(WIRE_DOOR_BOLTS))
to_chat(usr, "<span class='warning'>The door bolt control wire has been cut - Door bolts permanently dropped.</span>")
else if(lock())
to_chat(usr, "<span class='notice'>The door bolts have been dropped.</span>")
else if(unlock())
to_chat(usr, "<span class='notice'>The door bolts have been raised.</span>")
if("light-toggle")
if(wires.is_cut(WIRE_BOLT_LIGHT))
to_chat(usr, "<span class='warning'>The bolt lights wire has been cut - The door bolt lights are permanently disabled.</span>")
else if(lights)
lights = FALSE
to_chat(usr, "<span class='notice'>The door bolt lights have been disabled.</span>")
else if(!lights)
lights = TRUE
to_chat(usr, "<span class='notice'>The door bolt lights have been enabled.</span>")
update_icon()
if("safe-toggle")
if(wires.is_cut(WIRE_SAFETY))
to_chat(usr, "<span class='warning'>The safety wire is cut - Cannot secure the door.</span>")
else if(safe)
safe = 0
to_chat(usr, "<span class='notice'>The door safeties have been disabled.</span>")
else
safe = 1
to_chat(usr, "<span class='notice'>The door safeties have been enabled.</span>")
if("speed-toggle")
if(wires.is_cut(WIRE_SPEED))
to_chat(usr, "<span class='warning'>The timing wire is cut - Cannot alter timing.</span>")
else if(normalspeed)
normalspeed = 0
else
normalspeed = 1
if("open-close")
if(welded)
to_chat(usr, "<span class='warning'>The airlock has been welded shut!</span>")
else if(locked)
to_chat(usr, "<span class='warning'>The door bolts are down!</span>")
else if(density)
open()
else
close()
else
. = FALSE
/obj/machinery/door/airlock/attackby(obj/item/C, mob/user, params)
add_fingerprint(user)
@@ -1055,7 +1080,7 @@ About the new airlock wires panel:
return
return TRUE
/obj/machinery/door/airlock/try_to_crowbar(mob/living/user, obj/item/I) //*scream
/obj/machinery/door/airlock/try_to_crowbar(mob/living/user, obj/item/I)
if(operating)
return
if(istype(I, /obj/item/twohanded/fireaxe)) //let's make this more specific //FUCK YOU
@@ -1399,10 +1424,10 @@ About the new airlock wires panel:
qdel(src)
/obj/machinery/door/airlock/proc/ai_control_callback()
if(aiControlDisabled == 1)
aiControlDisabled = 0
else if(aiControlDisabled == 2)
aiControlDisabled = -1
if(aiControlDisabled == AICONTROLDISABLED_ON)
aiControlDisabled = AICONTROLDISABLED_OFF
else if(aiControlDisabled == AICONTROLDISABLED_BYPASS)
aiControlDisabled = AICONTROLDISABLED_PERMA
#undef AIRLOCK_CLOSED
#undef AIRLOCK_CLOSING
@@ -1423,3 +1448,7 @@ About the new airlock wires panel:
#undef AIRLOCK_INTEGRITY_MULTIPLIER
#undef AIRLOCK_DAMAGE_DEFLECTION_N
#undef AIRLOCK_DAMAGE_DEFLECTION_R
#undef TGUI_GREEN
#undef TGUI_ORANGE
#undef TGUI_RED
+8 -8
View File
@@ -365,14 +365,14 @@
assemblytype = /obj/structure/door_assembly/door_assembly_vault
security_level = 6
hackProof = TRUE
aiControlDisabled = TRUE
aiControlDisabled = AICONTROLDISABLED_ON
/obj/machinery/door/airlock/hatch/gamma
name = "gamma level hatch"
hackProof = 1
aiControlDisabled = 1
hackProof = TRUE
aiControlDisabled = AICONTROLDISABLED_ON
resistance_flags = FIRE_PROOF | ACID_PROOF
is_special = 1
is_special = TRUE
/obj/machinery/door/airlock/hatch/gamma/attackby(obj/C, mob/user, params)
if(!issilicon(user))
@@ -432,8 +432,8 @@
/obj/machinery/door/airlock/highsecurity/red
name = "secure armory airlock"
hackProof = 1
aiControlDisabled = 1
hackProof = TRUE
aiControlDisabled = AICONTROLDISABLED_ON
/obj/machinery/door/airlock/highsecurity/red/attackby(obj/C, mob/user, params)
if(!issilicon(user))
@@ -487,7 +487,7 @@
damage_deflection = 30
explosion_block = 3
hackProof = TRUE
aiControlDisabled = 1
aiControlDisabled = AICONTROLDISABLED_ON
normal_integrity = 700
security_level = 1
paintable = FALSE
@@ -504,7 +504,7 @@
assemblytype = /obj/structure/door_assembly/door_assembly_cult
damage_deflection = 10
hackProof = TRUE
aiControlDisabled = TRUE
aiControlDisabled = AICONTROLDISABLED_ON
paintable = FALSE
var/openingoverlaytype = /obj/effect/temp_visual/cult/door
var/friendly = FALSE
+16 -36
View File
@@ -6,6 +6,14 @@
// Reasonable defaults, in case someone manually spawns us
var/lasercolor = "r" //Something to do with lasertag turrets, blame Sieve for not adding a comment.
installation = /obj/item/gun/energy/laser/tag/red
targetting_is_configurable = FALSE
lethal_is_configurable = FALSE
shot_delay = 30
iconholder = 1
has_cover = FALSE
always_up = TRUE
raised = TRUE
req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE)
/obj/machinery/porta_turret/tag/red
@@ -18,43 +26,15 @@
icon_state = "[lasercolor]grey_target_prism"
/obj/machinery/porta_turret/tag/weapon_setup(var/obj/item/gun/energy/E)
switch(E.type)
if(/obj/item/gun/energy/laser/tag/blue)
eprojectile = /obj/item/gun/energy/laser/tag/blue
lasercolor = "b"
req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE)
check_arrest = 0
check_records = 0
check_weapons = 1
check_access = 0
check_anomalies = 0
shot_delay = 30
return
if(/obj/item/gun/energy/laser/tag/red)
eprojectile = /obj/item/gun/energy/laser/tag/red
lasercolor = "r"
req_access = list(ACCESS_MAINT_TUNNELS, ACCESS_THEATRE)
check_arrest = 0
check_records = 0
check_weapons = 1
check_access = 0
check_anomalies = 0
shot_delay = 30
iconholder = 1
/obj/machinery/porta_turret/tag/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, "turret_control.tmpl", "Turret Controls", 500, 300)
ui.open()
ui.set_auto_update(1)
/obj/machinery/porta_turret/tag/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
var/data[0]
data["access"] = !isLocked(user)
data["locked"] = locked
data["enabled"] = enabled
data["is_lethal"] = 0
/obj/machinery/porta_turret/tag/tgui_data(mob/user)
var/list/data = list(
"locked" = isLocked(user), // does the current user have access?
"on" = enabled, // is turret turned on?
"lethal" = FALSE,
"lethal_is_configurable" = lethal_is_configurable
)
return data
/obj/machinery/porta_turret/tag/update_icon()
+234 -225
View File
@@ -7,18 +7,18 @@
name = "turret"
icon = 'icons/obj/turrets.dmi'
icon_state = "turretCover"
anchored = 1
density = 0
anchored = TRUE
density = FALSE
use_power = IDLE_POWER_USE //this turret uses and requires power
idle_power_usage = 50 //when inactive, this turret takes up constant 50 Equipment power
active_power_usage = 300 //when active, this turret takes up constant 300 Equipment power
power_channel = EQUIP //drains power from the EQUIPMENT channel
armor = list(melee = 50, bullet = 30, laser = 30, energy = 30, bomb = 30, bio = 0, rad = 0, fire = 90, acid = 90)
var/raised = 0 //if the turret cover is "open" and the turret is raised
var/raising= 0 //if the turret is currently opening or closing its cover
var/raised = FALSE //if the turret cover is "open" and the turret is raised
var/raising= FALSE //if the turret is currently opening or closing its cover
var/health = 80 //the turret's health
var/locked = 1 //if the turret's behaviour control access is locked
var/controllock = 0 //if the turret responds to control panels
var/locked = TRUE //if the turret's behaviour control access is locked
var/controllock = FALSE //if the turret responds to control panels. TRUE = does NOT respond
var/installation = /obj/item/gun/energy/gun/turret //the type of weapon installed
var/gun_charge = 0 //the charge of the gun inserted
@@ -31,68 +31,49 @@
var/last_fired = 0 //1: if the turret is cooling down from a shot, 0: turret is ready to fire
var/shot_delay = 15 //1.5 seconds between each shot
var/check_arrest = 1 //checks if the perp is set to arrest
var/check_records = 1 //checks if a security record exists at all
var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have
var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements
var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos)
var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg
var/ailock = 0 // AI cannot use this
var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI
var/check_arrest = TRUE //checks if the perp is set to arrest
var/check_records = TRUE //checks if a security record exists at all
var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have
var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements
var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos)
var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg
var/check_borgs = FALSE //if TRUE, target all cyborgs.
var/ailock = FALSE // if TRUE, AI cannot use this
var/attacked = 0 //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!)
var/attacked = FALSE //if set to 1, the turret gets pissed off and shoots at people nearby (unless they have sec access!)
var/enabled = 1 //determines if the turret is on
var/lethal = 0 //whether in lethal or stun mode
var/disabled = 0
var/enabled = TRUE //determines if the turret is on
var/lethal = FALSE //whether in lethal or stun mode
var/lethal_is_configurable = TRUE // if false, its lethal setting cannot be changed
var/disabled = FALSE
var/shot_sound //what sound should play when the turret fires
var/eshot_sound //what sound should play when the emagged turret fires
var/datum/effect_system/spark_spread/spark_system //the spark system, used for generating... sparks?
var/wrenching = 0
var/wrenching = FALSE
var/last_target //last target fired at, prevents turrets from erratically firing at all valid targets in range
var/screen = 0 // Screen 0: main control, screen 1: access levels
var/one_access = 0 // Determines if access control is set to req_one_access or req_access
var/one_access = FALSE // Determines if access control is set to req_one_access or req_access
var/region_min = REGION_GENERAL
var/region_max = REGION_COMMAND
var/syndicate = 0 //is the turret a syndicate turret?
var/syndicate = FALSE //is the turret a syndicate turret?
var/faction = ""
var/emp_vulnerable = 1 // Can be empd
var/emp_vulnerable = TRUE // Can be empd
var/scan_range = 7
var/always_up = 0 //Will stay active
var/has_cover = 1 //Hides the cover
var/always_up = FALSE //Will stay active
var/has_cover = TRUE //Hides the cover
/obj/machinery/porta_turret/centcom
name = "Centcom Turret"
enabled = 0
ailock = 1
check_synth = 0
check_access = 1
check_arrest = 1
check_records = 1
check_weapons = 1
check_anomalies = 1
/obj/machinery/porta_turret/centcom/pulse
name = "Pulse Turret"
health = 200
enabled = 1
lethal = 1
req_access = list(ACCESS_CENT_COMMANDER)
installation = /obj/item/gun/energy/pulse/turret
/obj/machinery/porta_turret/stationary
ailock = 1
lethal = 1
installation = /obj/item/gun/energy/laser
/obj/machinery/porta_turret/Initialize(mapload)
. = ..()
if(req_access && req_access.len)
req_access.Cut()
req_one_access = list(ACCESS_SECURITY, ACCESS_HEADS)
one_access = 1
one_access = TRUE
//Sets up a spark system
spark_system = new /datum/effect_system/spark_spread
@@ -110,7 +91,7 @@
if(req_one_access && req_one_access.len)
req_one_access.Cut()
req_access = list(ACCESS_CENT_SPECOPS)
one_access = 0
one_access = FALSE
/obj/machinery/porta_turret/proc/setup()
var/obj/item/gun/energy/E = new installation //All energy-based weapons are applicable
@@ -185,152 +166,151 @@ GLOBAL_LIST_EMPTY(turret_icons)
else
icon_state = "turretCover"
/obj/machinery/porta_turret/proc/isLocked(mob/user)
if(ailock && (isrobot(user) || isAI(user)))
to_chat(user, "<span class='notice'>There seems to be a firewall preventing you from accessing this device.</span>")
return 1
if(locked && !(isrobot(user) || isAI(user) || isobserver(user)))
to_chat(user, "<span class='notice'>Access denied.</span>")
return 1
return 0
/obj/machinery/porta_turret/attack_ai(mob/user)
if(isLocked(user))
return
ui_interact(user)
/obj/machinery/porta_turret/attack_ghost(mob/user)
ui_interact(user)
/obj/machinery/porta_turret/attack_hand(mob/user)
if(isLocked(user))
return
ui_interact(user)
/obj/machinery/porta_turret/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
ui = SSnanoui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "turret_control.tmpl", "Turret Controls", 500, 320)
ui.open()
ui.set_auto_update(1)
/obj/machinery/porta_turret/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
var/data[0]
data["access"] = !isLocked(user)
data["screen"] = screen
data["locked"] = locked
data["enabled"] = enabled
data["lethal_control"] = !syndicate ? 1 : 0
data["lethal"] = lethal
if(data["access"] && !syndicate)
var/settings[0]
settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth)
settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons)
settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records)
settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest)
settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access)
settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies)
data["settings"] = settings
if(!syndicate)
data["one_access"] = one_access
var/accesses[0]
var/list/access_list = get_all_accesses()
for(var/access in access_list)
var/name = get_access_desc(access)
var/active
if(one_access)
active = (access in req_one_access)
else
active = (access in req_access)
accesses[++accesses.len] = list("name" = name, "active" = active, "number" = access)
data["accesses"] = accesses
return data
/obj/machinery/porta_turret/proc/HasController()
var/area/A = get_area(src)
return A && A.turret_controls.len > 0
/obj/machinery/porta_turret/CanUseTopic(var/mob/user)
/obj/machinery/porta_turret/proc/access_is_configurable()
return targetting_is_configurable && !HasController()
/obj/machinery/porta_turret/proc/isLocked(mob/user)
if(HasController())
to_chat(user, "<span class='notice'>Turrets can only be controlled using the assigned turret controller.</span>")
return STATUS_CLOSE
return TRUE
if(isrobot(user) || isAI(user))
if(ailock)
to_chat(user, "<span class='notice'>There seems to be a firewall preventing you from accessing this device.</span>")
return TRUE
else
return FALSE
if(isobserver(user))
if(user.can_admin_interact())
return FALSE
else
return TRUE
if(locked)
return TRUE
return FALSE
if(isLocked(user))
return STATUS_CLOSE
/obj/machinery/porta_turret/attack_ai(mob/user)
tgui_interact(user)
if(!anchored)
to_chat(usr, "<span class='notice'>\The [src] has to be secured first!</span>")
return STATUS_CLOSE
/obj/machinery/porta_turret/attack_ghost(mob/user)
tgui_interact(user)
return ..()
/obj/machinery/porta_turret/attack_hand(mob/user)
tgui_interact(user)
/obj/machinery/porta_turret/Topic(href, href_list, var/nowindow = 0)
if(..())
return 1
if(href_list["command"] && href_list["value"])
var/value = text2num(href_list["value"])
if(href_list["command"] == "enable")
enabled = value
else if(syndicate)
return 1
else if(href_list["command"] == "screen")
screen = value
else if(href_list["command"] == "lethal")
lethal = value
else if(href_list["command"] == "check_synth")
check_synth = value
else if(href_list["command"] == "check_weapons")
check_weapons = value
else if(href_list["command"] == "check_records")
check_records = value
else if(href_list["command"] == "check_arrest")
check_arrest = value
else if(href_list["command"] == "check_access")
check_access = value
else if(href_list["command"] == "check_anomalies")
check_anomalies = value
if(!syndicate)
if(href_list["one_access"])
toggle_one_access(href_list["one_access"])
if(href_list["access"])
toggle_access(href_list["access"])
return 1
/obj/machinery/porta_turret/proc/toggle_one_access(var/access)
one_access = text2num(access)
if(one_access == 1)
req_one_access = req_access.Copy()
req_access.Cut()
else if(one_access == 0)
req_access = req_one_access.Copy()
req_one_access.Cut()
/obj/machinery/porta_turret/proc/toggle_access(var/access)
var/required = text2num(access)
if(!(required in get_all_accesses()))
/obj/machinery/porta_turret/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(HasController())
to_chat(user, "<span class='notice'>[src] can only be controlled using the assigned turret controller.</span>")
return
if(!anchored)
to_chat(user, "<span class='notice'>[src] has to be secured first!</span>")
return
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "PortableTurret", name, 500, access_is_configurable() ? 800 : 400)
ui.open()
if(one_access)
if((required in req_one_access))
req_one_access -= required
else
req_one_access += required
else
if((required in req_access))
req_access -= required
else
req_access += required
/obj/machinery/porta_turret/tgui_data(mob/user)
var/list/data = list(
"locked" = isLocked(user), // does the current user have access?
"on" = enabled,
"targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up
"lethal" = lethal,
"lethal_is_configurable" = lethal_is_configurable,
"check_weapons" = check_weapons,
"neutralize_noaccess" = check_access,
"one_access" = one_access,
"selectedAccess" = one_access ? req_one_access : req_access,
"access_is_configurable" = access_is_configurable(),
"neutralize_norecord" = check_records,
"neutralize_criminals" = check_arrest,
"neutralize_all" = check_synth,
"neutralize_unidentified" = check_anomalies,
"neutralize_cyborgs" = check_borgs
)
return data
/obj/machinery/porta_turret/tgui_static_data(mob/user)
var/list/data = list()
data["regions"] = get_accesslist_static_data(region_min, region_max)
return data
/obj/machinery/porta_turret/tgui_act(action, params)
if (..())
return
if(isLocked(usr))
return
. = TRUE
switch(action)
if("power")
enabled = !enabled
if("lethal")
if(lethal_is_configurable)
lethal = !lethal
if(targetting_is_configurable)
switch(action)
if("authweapon")
check_weapons = !check_weapons
if("authaccess")
check_access = !check_access
if("authnorecord")
check_records = !check_records
if("autharrest")
check_arrest = !check_arrest
if("authxeno")
check_anomalies = !check_anomalies
if("authsynth")
check_synth = !check_synth
if("authborgs")
check_borgs = !check_borgs
if("set")
var/access = text2num(params["access"])
if(one_access)
if(!(access in req_one_access))
req_one_access += access
else
req_one_access -= access
else
if(!(access in req_access))
req_access += access
else
req_access -= access
if(access_is_configurable())
switch(action)
if("grant_region")
var/region = text2num(params["region"])
if(isnull(region))
return
if(one_access)
req_one_access |= get_region_accesses(region)
else
req_access |= get_region_accesses(region)
if("deny_region")
var/region = text2num(params["region"])
if(isnull(region))
return
if(one_access)
req_one_access -= get_region_accesses(region)
else
req_access -= get_region_accesses(region)
if("clear_all")
if(one_access)
req_one_access = list()
else
req_access = list()
if("grant_all")
if(one_access)
req_one_access = get_all_accesses()
else
req_access = get_all_accesses()
if("one_access")
if(one_access)
req_one_access = list()
else
req_access = list()
one_access = !one_access
/obj/machinery/porta_turret/power_change()
if(powered() || !use_power)
@@ -379,23 +359,25 @@ GLOBAL_LIST_EMPTY(turret_icons)
"<span class='notice'>You begin [anchored ? "un" : ""]securing the turret.</span>" \
)
wrenching = 1
wrenching = TRUE
if(do_after(user, 50 * I.toolspeed, target = src))
//This code handles moving the turret around. After all, it's a portable turret!
if(!anchored)
playsound(loc, I.usesound, 100, 1)
anchored = 1
anchored = TRUE
update_icon()
to_chat(user, "<span class='notice'>You secure the exterior bolts on the turret.</span>")
else if(anchored)
playsound(loc, I.usesound, 100, 1)
anchored = 0
anchored = FALSE
to_chat(user, "<span class='notice'>You unsecure the exterior bolts on the turret.</span>")
update_icon()
wrenching = 0
wrenching = FALSE
else if(istype(I, /obj/item/card/id) || istype(I, /obj/item/pda))
if(allowed(user))
if(HasController())
to_chat(user, "<span class='notice'>Turrets regulated by a nearby turret controller are not unlockable.</span>")
else if(allowed(user))
locked = !locked
to_chat(user, "<span class='notice'>Controls are now [locked ? "locked" : "unlocked"].</span>")
updateUsrDialog()
@@ -409,9 +391,9 @@ GLOBAL_LIST_EMPTY(turret_icons)
playsound(src.loc, 'sound/weapons/smash.ogg', 60, 1)
if(I.force * 0.5 > 1) //if the force of impact dealt at least 1 damage, the turret gets pissed off
if(!attacked && !emagged)
attacked = 1
attacked = TRUE
spawn(60)
attacked = 0
attacked = FALSE
..()
@@ -445,12 +427,12 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(user)
to_chat(user, "<span class='warning'>You short out [src]'s threat assessment circuits.</span>")
visible_message("[src] hums oddly...")
emagged = 1
emagged = TRUE
iconholder = 1
controllock = 1
enabled = 0 //turns off the turret temporarily
controllock = TRUE
enabled = FALSE //turns off the turret temporarily
sleep(60) //6 seconds for the traitor to gtfo of the area before the turret decides to ruin his shit
enabled = 1 //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
enabled = TRUE //turns it back on. The cover popUp() popDown() are automatically called in process(), no need to define it here
/obj/machinery/porta_turret/take_damage(force)
if(!raised && !raising)
@@ -470,9 +452,9 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(enabled)
if(!attacked && !emagged)
attacked = 1
attacked = TRUE
spawn(60)
attacked = 0
attacked = FALSE
..()
@@ -489,12 +471,12 @@ GLOBAL_LIST_EMPTY(turret_icons)
check_access = prob(20) // check_access is a pretty big deal, so it's least likely to get turned on
check_anomalies = prob(50)
if(prob(5))
emagged = 1
emagged = TRUE
enabled=0
spawn(rand(60,600))
spawn(rand(60, 600))
if(!enabled)
enabled=1
enabled = TRUE
..()
@@ -585,8 +567,8 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(get_turf(L) == get_turf(src))
return TURRET_NOT_TARGET
if(!emagged && !syndicate && (issilicon(L) || isbot(L))) // Don't target silica
return TURRET_NOT_TARGET
if(!emagged && !syndicate && (issilicon(L) || isbot(L)))
return (check_borgs && isrobot(L)) ? TURRET_PRIORITY_TARGET : TURRET_NOT_TARGET
if(L.stat && !emagged) //if the perp is dead/dying, no need to bother really
return TURRET_NOT_TARGET //move onto next potential victim!
@@ -643,7 +625,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
return
if(stat & BROKEN)
return
set_raised_raising(raised, 1)
set_raised_raising(raised, TRUE)
playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1)
update_icon()
@@ -653,7 +635,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
sleep(10)
qdel(flick_holder)
set_raised_raising(1, 0)
set_raised_raising(TRUE, FALSE)
update_icon()
/obj/machinery/porta_turret/proc/popDown() //pops the turret down
@@ -664,7 +646,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
return
if(stat & BROKEN)
return
set_raised_raising(raised, 1)
set_raised_raising(raised, TRUE)
playsound(get_turf(src), 'sound/effects/turret/open.wav', 60, 1)
update_icon()
@@ -674,7 +656,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
sleep(10)
qdel(flick_holder)
set_raised_raising(0, 0)
set_raised_raising(FALSE, FALSE)
update_icon()
/obj/machinery/porta_turret/on_assess_perp(mob/living/carbon/human/perp)
@@ -739,6 +721,27 @@ GLOBAL_LIST_EMPTY(turret_icons)
A.throw_at(target, scan_range, 1)
return A
/obj/machinery/porta_turret/centcom
name = "Centcom Turret"
enabled = FALSE
ailock = TRUE
check_synth = FALSE
check_access = TRUE
check_arrest = TRUE
check_records = TRUE
check_weapons = TRUE
check_anomalies = TRUE
region_max = REGION_CENTCOMM // Non-turretcontrolled turrets at CC can have their access customized to check for CC accesses.
/obj/machinery/porta_turret/centcom/pulse
name = "Pulse Turret"
health = 200
enabled = TRUE
lethal = TRUE
lethal_is_configurable = FALSE
req_access = list(ACCESS_CENT_COMMANDER)
installation = /obj/item/gun/energy/pulse/turret
/datum/turret_checks
var/enabled
var/lethal
@@ -748,6 +751,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
var/check_arrest
var/check_weapons
var/check_anomalies
var/check_borgs
var/ailock
/obj/machinery/porta_turret/proc/setState(var/datum/turret_checks/TC)
@@ -763,6 +767,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
check_arrest = TC.check_arrest
check_weapons = TC.check_weapons
check_anomalies = TC.check_anomalies
check_borgs = TC.check_borgs
ailock = TC.ailock
power_change()
@@ -791,7 +796,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(istype(I, /obj/item/wrench) && !anchored)
playsound(loc, I.usesound, 100, 1)
to_chat(user, "<span class='notice'>You secure the external bolts.</span>")
anchored = 1
anchored = TRUE
build_step = 1
return
@@ -816,7 +821,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
else if(istype(I, /obj/item/wrench))
playsound(loc, I.usesound, 75, 1)
to_chat(user, "<span class='notice'>You unfasten the external bolts.</span>")
anchored = 0
anchored = FALSE
build_step = 0
return
@@ -841,8 +846,10 @@ GLOBAL_LIST_EMPTY(turret_icons)
gun_charge = E.cell.charge //the gun's charge is stored in gun_charge
to_chat(user, "<span class='notice'>You add [I] to the turret.</span>")
if(istype(installation, /obj/item/gun/energy/laser/tag/blue) || istype(installation, /obj/item/gun/energy/laser/tag/red))
target_type = /obj/machinery/porta_turret/tag
if(istype(E, /obj/item/gun/energy/laser/tag/blue))
target_type = /obj/machinery/porta_turret/tag/blue
else if(istype(E, /obj/item/gun/energy/laser/tag/red))
target_type = /obj/machinery/porta_turret/tag/red
else
target_type = /obj/machinery/porta_turret
@@ -934,7 +941,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
Turret.name = finish_name
Turret.installation = installation
Turret.gun_charge = gun_charge
Turret.enabled = 0
Turret.enabled = FALSE
Turret.setup()
qdel(src)
@@ -981,25 +988,27 @@ GLOBAL_LIST_EMPTY(turret_icons)
var/icon_state_active = "syndieturret1"
var/icon_state_destroyed = "syndieturret2"
syndicate = 1
syndicate = TRUE
installation = null
always_up = 1
always_up = TRUE
use_power = NO_POWER_USE
has_cover = 0
raised = 1
has_cover = FALSE
raised = TRUE
scan_range = 9
faction = "syndicate"
emp_vulnerable = 0
emp_vulnerable = FALSE
lethal = 1
check_arrest = 0
check_records = 0
check_weapons = 0
check_access = 0
check_anomalies = 1
check_synth = 1
ailock = 1
lethal = TRUE
lethal_is_configurable = FALSE
targetting_is_configurable = FALSE
check_arrest = FALSE
check_records = FALSE
check_weapons = FALSE
check_access = FALSE
check_anomalies = TRUE
check_synth = TRUE
ailock = TRUE
var/area/syndicate_depot/core/depotarea
/obj/machinery/porta_turret/syndicate/die()
@@ -1018,7 +1027,7 @@ GLOBAL_LIST_EMPTY(turret_icons)
if(req_one_access && req_one_access.len)
req_one_access.Cut()
req_access = list(ACCESS_SYNDICATE)
one_access = 0
one_access = FALSE
/obj/machinery/porta_turret/syndicate/update_icon()
if(stat & BROKEN)
+44 -39
View File
@@ -12,29 +12,26 @@
var/resultlvl = null
/obj/machinery/slot_machine/attack_hand(mob/user as mob)
tgui_interact(user)
/obj/machinery/slot_machine/tgui_interact(mob/user, ui_key = "main", datum/tgui/ui = null, force_open = 0, 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, "SlotMachine", name, 350, 200, master_ui, state)
ui.open()
/obj/machinery/slot_machine/tgui_data(mob/user)
var/list/data = list()
// Get account
account = user.get_worn_id_account()
if(!account)
if(istype(user.get_active_hand(), /obj/item/card/id))
account = get_card_account(user.get_active_hand())
else
account = null
ui_interact(user)
/obj/machinery/slot_machine/wrench_act(mob/user, obj/item/I)
. = TRUE
if(!I.tool_use_check(user, 0))
return
default_unfasten_wrench(user, I)
/obj/machinery/slot_machine/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, "slotmachine.tmpl", name, 350, 200)
ui.open()
ui.set_auto_update(1)
/obj/machinery/slot_machine/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
var/data[0]
// Send data
data["working"] = working
data["money"] = account ? account.money : null
data["plays"] = plays
@@ -42,22 +39,23 @@
data["resultlvl"] = resultlvl
return data
/obj/machinery/slot_machine/Topic(href, href_list)
/obj/machinery/slot_machine/tgui_act(action, params)
if(..())
return
add_fingerprint(usr)
if(href_list["ops"])
if(text2num(href_list["ops"])) // Play
if(working)
return
if(!account || account.money < 10)
return
if(!account.charge(10, null, "Bet", "Slot Machine", "Slot Machine"))
return
plays++
working = 1
icon_state = "slots-on"
playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
addtimer(CALLBACK(src, .proc/spin_slots, usr.name), 25)
if(action == "spin")
if(working)
return
if(!account || account.money < 10)
return
if(!account.charge(10, null, "Bet", "Slot Machine", "Slot Machine"))
return
plays++
working = TRUE
icon_state = "slots-on"
playsound(src.loc, 'sound/machines/ding.ogg', 50, 1)
addtimer(CALLBACK(src, .proc/spin_slots, usr.name), 25)
/obj/machinery/slot_machine/proc/spin_slots(userName)
switch(rand(1,4050))
@@ -65,44 +63,45 @@
atom_say("JACKPOT! [userName] has won a MILLION CREDITS!")
GLOB.event_announcement.Announce("Congratulations to [userName] on winning the Jackpot of ONE MILLION CREDITS!", "Jackpot Winner")
result = "JACKPOT! You win one million credits!"
resultlvl = "highlight"
resultlvl = "teal"
win_money(1000000, 'sound/goonstation/misc/airraid_loop.ogg')
if(2 to 5) // .07%
atom_say("Big Winner! [userName] has won a hundred thousand credits!")
GLOB.event_announcement.Announce("Congratulations to [userName] on winning a hundred thousand credits!", "Big Winner")
result = "Big Winner! You win a hundred thousand credits!"
resultlvl = "good"
resultlvl = "green"
win_money(100000, 'sound/goonstation/misc/klaxon.ogg')
if(6 to 50) // 1.08%
atom_say("Big Winner! [userName] has won ten thousand credits!")
result = "You win ten thousand credits!"
resultlvl = "good"
resultlvl = "green"
win_money(10000, 'sound/goonstation/misc/klaxon.ogg')
if(51 to 100) // 1.21%
atom_say("Winner! [userName] has won a thousand credits!")
result = "You win a thousand credits!"
resultlvl = "good"
resultlvl = "green"
win_money(1000, 'sound/goonstation/misc/bell.ogg')
if(101 to 200) // 2.44%
atom_say("Winner! [userName] has won a hundred credits!")
result = "You win a hundred credits!"
resultlvl = "good"
resultlvl = "green"
win_money(100, 'sound/goonstation/misc/bell.ogg')
if(201 to 300) // 2.44%
atom_say("Winner! [userName] has won fifty credits!")
result = "You win fifty credits!"
resultlvl = "good"
resultlvl = "green"
win_money(50)
if(301 to 1000) // 17.26%
atom_say("Winner! [userName] has won ten credits!")
result = "You win ten credits!"
resultlvl = "good"
resultlvl = "green"
win_money(10)
else // 75.31%
result = "<span class='warning'>No luck!</span>"
resultlvl = "average"
working = 0
result = "No luck!"
resultlvl = "orange"
working = FALSE
icon_state = "slots-off"
SStgui.update_uis(src) // Push a UI update
/obj/machinery/slot_machine/proc/win_money(amt, sound='sound/machines/ping.ogg')
if(sound)
@@ -110,3 +109,9 @@
if(!account)
return
account.credit(amt, "Slot Winnings", "Slot Machine", account.owner_name)
/obj/machinery/slot_machine/wrench_act(mob/user, obj/item/I)
. = TRUE
if(!I.tool_use_check(user, 0))
return
default_unfasten_wrench(user, I)
+102 -102
View File
@@ -11,51 +11,56 @@
desc = "Used to control a room's automated defenses."
icon = 'icons/obj/machines/turret_control.dmi'
icon_state = "control_standby"
anchored = 1
density = 0
anchored = TRUE
density = FALSE
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
var/enabled = 0
var/lethal = 0
var/locked = 1
var/enabled = FALSE
var/lethal = FALSE
var/lethal_is_configurable = TRUE
var/locked = TRUE
var/area/control_area //can be area name, path or nothing.
var/check_arrest = 1 //checks if the perp is set to arrest
var/check_records = 1 //checks if a security record exists at all
var/check_weapons = 0 //checks if it can shoot people that have a weapon they aren't authorized to have
var/check_access = 1 //if this is active, the turret shoots everything that does not meet the access requirements
var/check_anomalies = 1 //checks if it can shoot at unidentified lifeforms (ie xenos)
var/check_synth = 0 //if active, will shoot at anything not an AI or cyborg
var/ailock = 0 //Silicons cannot use this
var/targetting_is_configurable = TRUE // if false, you cannot change who this turret attacks via its UI
var/check_arrest = TRUE //checks if the perp is set to arrest
var/check_records = TRUE //checks if a security record exists at all
var/check_weapons = FALSE //checks if it can shoot people that have a weapon they aren't authorized to have
var/check_access = TRUE //if this is active, the turret shoots everything that does not meet the access requirements
var/check_anomalies = TRUE //checks if it can shoot at unidentified lifeforms (ie xenos)
var/check_synth = FALSE //if active, will shoot at anything not an AI or cyborg
var/check_borgs = FALSE //if TRUE, target all cyborgs.
var/ailock = FALSE //Silicons cannot use this
var/syndicate = 0
var/syndicate = FALSE
var/faction = "" // Turret controls can only access turrets that are in the same faction
req_access = list(ACCESS_AI_UPLOAD)
/obj/machinery/turretid/stun
enabled = 1
enabled = TRUE
icon_state = "control_stun"
/obj/machinery/turretid/lethal
enabled = 1
lethal = 1
enabled = TRUE
lethal = TRUE
icon_state = "control_kill"
/obj/machinery/turretid/syndicate
enabled = 1
lethal = 1
enabled = TRUE
lethal = TRUE
lethal_is_configurable = FALSE
targetting_is_configurable = FALSE
icon_state = "control_kill"
lethal = 1
check_arrest = 0
check_records = 0
check_weapons = 0
check_access = 0
check_anomalies = 1
check_synth = 1
ailock = 1
check_arrest = FALSE
check_records = FALSE
check_weapons = FALSE
check_access = FALSE
check_anomalies = TRUE
check_synth = TRUE
check_borgs = FALSE
ailock = TRUE
syndicate = 1
syndicate = TRUE
faction = "syndicate"
req_access = list(ACCESS_SYNDICATE_LEADER)
@@ -87,21 +92,23 @@
return
/obj/machinery/turretid/proc/isLocked(mob/user)
if(ailock && (isrobot(user) || isAI(user)))
to_chat(user, "<span class='notice'>There seems to be a firewall preventing you from accessing this device.</span>")
return 1
if(isrobot(user) || isAI(user))
if(ailock)
to_chat(user, "<span class='notice'>There seems to be a firewall preventing you from accessing this device.</span>")
return TRUE
else
return FALSE
if(locked && !(isrobot(user) || isAI(user) || isobserver(user)))
to_chat(user, "<span class='notice'>Access denied.</span>")
return 1
if(isobserver(user))
if(user.can_admin_interact())
return FALSE
else
return TRUE
return 0
if(locked)
return TRUE
/obj/machinery/turretid/CanUseTopic(mob/user)
if(isLocked(user))
return STATUS_CLOSE
return ..()
return FALSE
/obj/machinery/turretid/attackby(obj/item/W, mob/user)
if(stat & BROKEN)
@@ -120,89 +127,82 @@
/obj/machinery/turretid/emag_act(user as mob)
if(!emagged)
to_chat(user, "<span class='danger'>You short out the turret controls' access analysis module.</span>")
emagged = 1
locked = 0
ailock = 0
emagged = TRUE
locked = FALSE
ailock = FALSE
return
/obj/machinery/turretid/attack_ai(mob/user as mob)
if(isLocked(user))
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/turretid/attack_ghost(mob/user as mob)
ui_interact(user)
tgui_interact(user)
/obj/machinery/turretid/attack_hand(mob/user as mob)
if(isLocked(user))
return
tgui_interact(user)
ui_interact(user)
/obj/machinery/turretid/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)
/obj/machinery/turretid/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, "turret_control.tmpl", "Turret Controls", 500, 350)
ui = new(user, src, ui_key, "PortableTurret", name, 500, 400)
ui.open()
ui.set_auto_update(1)
/obj/machinery/turretid/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
var/data[0]
data["access"] = !isLocked(user)
data["locked"] = locked
data["enabled"] = enabled
data["lethal_control"] = !syndicate ? 1 : 0
data["lethal"] = lethal
if(data["access"] && !syndicate)
var/settings[0]
settings[++settings.len] = list("category" = "Neutralize All Non-Synthetics", "setting" = "check_synth", "value" = check_synth)
settings[++settings.len] = list("category" = "Check Weapon Authorization", "setting" = "check_weapons", "value" = check_weapons)
settings[++settings.len] = list("category" = "Check Security Records", "setting" = "check_records", "value" = check_records)
settings[++settings.len] = list("category" = "Check Arrest Status", "setting" = "check_arrest", "value" = check_arrest)
settings[++settings.len] = list("category" = "Check Access Authorization", "setting" = "check_access", "value" = check_access)
settings[++settings.len] = list("category" = "Check Misc. Lifeforms", "setting" = "check_anomalies", "value" = check_anomalies)
data["settings"] = settings
/obj/machinery/turretid/tgui_data(mob/user)
var/list/data = list(
"locked" = isLocked(user), // does the current user have access?
"on" = enabled,
"targetting_is_configurable" = targetting_is_configurable, // If false, targetting settings don't show up
"lethal" = lethal,
"lethal_is_configurable" = lethal_is_configurable,
"check_weapons" = check_weapons,
"neutralize_noaccess" = check_access,
"one_access" = FALSE,
"selectedAccess" = list(),
"access_is_configurable" = FALSE,
"neutralize_norecord" = check_records,
"neutralize_criminals" = check_arrest,
"neutralize_all" = check_synth,
"neutralize_unidentified" = check_anomalies,
"neutralize_cyborgs" = check_borgs
)
return data
/obj/machinery/turretid/Topic(href, href_list, var/nowindow = 0)
if(..())
return 1
/obj/machinery/turretid/tgui_act(action, params)
if (..())
return
if(isLocked(usr))
return 1
if(href_list["command"] && href_list["value"])
var/value = text2num(href_list["value"])
if(href_list["command"] == "enable")
enabled = value
else if(syndicate)
return 1
else if(href_list["command"] == "lethal")
lethal = value
else if(href_list["command"] == "check_synth")
check_synth = value
else if(href_list["command"] == "check_weapons")
check_weapons = value
else if(href_list["command"] == "check_records")
check_records = value
else if(href_list["command"] == "check_arrest")
check_arrest = value
else if(href_list["command"] == "check_access")
check_access = value
else if(href_list["command"] == "check_anomalies")
check_anomalies = value
updateTurrets()
return 1
return
. = TRUE
switch(action)
if("power")
enabled = !enabled
if("lethal")
if(lethal_is_configurable)
lethal = !lethal
if(targetting_is_configurable)
switch(action)
if("authweapon")
check_weapons = !check_weapons
if("authaccess")
check_access = !check_access
if("authnorecord")
check_records = !check_records
if("autharrest")
check_arrest = !check_arrest
if("authxeno")
check_anomalies = !check_anomalies
if("authsynth")
check_synth = !check_synth
if("authborgs")
check_borgs = !check_borgs
updateTurrets()
/obj/machinery/turretid/proc/updateTurrets()
var/datum/turret_checks/TC = new
TC.enabled = enabled
TC.lethal = lethal
TC.check_synth = check_synth
TC.check_borgs = check_borgs
TC.check_access = check_access
TC.check_records = check_records
TC.check_arrest = check_arrest
-1
View File
@@ -1372,7 +1372,6 @@
/obj/item/clothing/glasses/gglasses = 1,
/obj/item/clothing/shoes/jackboots = 1,
/obj/item/clothing/under/schoolgirl = 1,
/obj/item/clothing/head/kitty = 1,
/obj/item/clothing/under/blackskirt = 1,
/obj/item/clothing/suit/toggle/owlwings = 1,
/obj/item/clothing/under/owl = 1,
@@ -372,6 +372,15 @@
new /obj/item/ammo_box/magazine/m12g/buckshot(src)
new /obj/item/ammo_box/magazine/m12g/dragon(src)
/obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags
desc = "A large duffelbag, containing three types of extended drum magazines."
/obj/item/storage/backpack/duffel/syndie/ammo/shotgunXLmags/New()
..()
new /obj/item/ammo_box/magazine/m12g/XtrLrg(src)
new /obj/item/ammo_box/magazine/m12g/XtrLrg/buckshot(src)
new /obj/item/ammo_box/magazine/m12g/XtrLrg/dragon(src)
/obj/item/storage/backpack/duffel/mining_conscript/
name = "mining conscription kit"
desc = "A kit containing everything a crewmember needs to support a shaft miner in the field."
-341
View File
@@ -1,341 +0,0 @@
/datum/song
var/name = "Untitled"
var/list/lines = new()
var/tempo = 5 // delay between notes
var/playing = 0 // if we're playing
var/help = 0 // if help is open
var/repeat = 0 // number of times remaining to repeat
var/max_repeat = 10 // maximum times we can repeat
var/instrumentDir = "piano" // the folder with the sounds
var/instrumentExt = "ogg" // the file extension
var/obj/instrumentObj = null // the associated obj playing the sound
/datum/song/New(dir, obj, ext = "ogg")
tempo = sanitize_tempo(tempo)
instrumentDir = dir
instrumentObj = obj
instrumentExt = ext
/datum/song/Destroy()
instrumentObj = null
return ..()
// note is a number from 1-7 for A-G
// acc is either "b", "n", or "#"
// oct is 1-8 (or 9 for C)
/datum/song/proc/playnote(note, acc as text, oct)
// 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/soundfile = "sound/instruments/[instrumentDir]/[ascii2text(note+64)][acc][oct].[instrumentExt]"
soundfile = file(soundfile)
// make sure the note exists
if(!fexists(soundfile))
return
// and play
var/turf/source = get_turf(instrumentObj)
var/sound/music_played = sound(soundfile)
for(var/A in hearers(15, source))
var/mob/M = A
if(!M.client || !(M.client.prefs.sound & SOUND_INSTRUMENTS))
continue
M.playsound_local(source, null, 100, falloff = 5, S = music_played)
/datum/song/proc/shouldStopPlaying(mob/user)
if(instrumentObj)
//if(!user.canUseTopic(instrumentObj))
//return 1
return !instrumentObj.anchored // add special cases to stop in subclasses
else
return 1
/datum/song/proc/playsong(mob/user)
while(repeat >= 0)
var/cur_oct[7]
var/cur_acc[7]
for(var/i = 1 to 7)
cur_oct[i] = 3
cur_acc[i] = "n"
for(var/line in lines)
for(var/beat in splittext(lowertext(line), ","))
var/list/notes = splittext(beat, "/")
for(var/note in splittext(notes[1], "-"))
if(!playing || shouldStopPlaying(user)) //If the instrument is playing, or special case
playing = 0
return
if(length(note) == 0)
continue
var/cur_note = text2ascii(note) - 96
if(cur_note < 1 || cur_note > 7)
continue
for(var/i=2 to length(note))
var/ni = copytext(note,i,i+1)
if(!text2num(ni))
if(ni == "#" || ni == "b" || ni == "n")
cur_acc[cur_note] = ni
else if(ni == "s")
cur_acc[cur_note] = "#" // so shift is never required
else
cur_oct[cur_note] = text2num(ni)
playnote(cur_note, cur_acc[cur_note], cur_oct[cur_note])
if(notes.len >= 2 && text2num(notes[2]))
sleep(sanitize_tempo(tempo / text2num(notes[2])))
else
sleep(tempo)
repeat--
playing = 0
repeat = 0
/datum/song/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(!instrumentObj)
return
ui = SSnanoui.try_update_ui(user, instrumentObj, ui_key, ui, force_open)
if(!ui)
ui = new(user, instrumentObj, ui_key, "song.tmpl", instrumentObj.name, 700, 500)
ui.open()
ui.set_auto_update(1)
/datum/song/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
var/data[0]
data["lines"] = lines
data["tempo"] = tempo
data["playing"] = playing
data["help"] = help
data["repeat"] = repeat
data["maxRepeat"] = max_repeat
data["minTempo"] = world.tick_lag
data["maxTempo"] = 600
return data
/datum/song/Topic(href, href_list)
if(!in_range(instrumentObj, usr) || (issilicon(usr) && instrumentObj.loc != usr) || !isliving(usr) || usr.incapacitated())
usr << browse(null, "window=instrument")
usr.unset_machine()
return 1
instrumentObj.add_fingerprint(usr)
if(href_list["newsong"])
playing = 0
lines = new()
tempo = sanitize_tempo(5) // default 120 BPM
name = ""
SSnanoui.update_uis(src)
else if(href_list["import"])
playing = 0
var/t = ""
do
t = html_encode(input(usr, "Please paste the entire song, formatted:", text("[]", name), t) as message)
if(!in_range(instrumentObj, usr))
return
if(length(t) >= 12000)
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(t) > 12000)
//split into lines
spawn()
lines = splittext(t, "\n")
if(lines.len == 0)
return 1
if(copytext(lines[1],1,6) == "BPM: ")
tempo = sanitize_tempo(600 / text2num(copytext(lines[1],6)))
lines.Cut(1,2)
else
tempo = sanitize_tempo(5) // default 120 BPM
if(lines.len > 200)
to_chat(usr, "Too many lines!")
lines.Cut(201)
var/linenum = 1
for(var/l in lines)
if(length(l) > 200)
to_chat(usr, "Line [linenum] too long!")
lines.Remove(l)
else
linenum++
SSnanoui.update_uis(src)
else if(href_list["help"])
help = !help
SSnanoui.update_uis(src)
if(href_list["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 += round(text2num(href_list["repeat"]))
if(repeat < 0)
repeat = 0
if(repeat > max_repeat)
repeat = max_repeat
SSnanoui.update_uis(src)
else if(href_list["tempo"])
tempo = sanitize_tempo(tempo + text2num(href_list["tempo"]) * world.tick_lag)
SSnanoui.update_uis(src)
else if(href_list["play"])
if(playing)
return
playing = 1
spawn()
playsong(usr)
SSnanoui.update_uis(src)
else if(href_list["insertline"])
var/num = round(text2num(href_list["insertline"]))
if(num < 1 || num > lines.len + 1)
return
var/newline = html_encode(input("Enter your line: ", instrumentObj.name) as text|null)
if(!newline || !in_range(instrumentObj, usr))
return
if(lines.len > 200)
return
if(length(newline) > 200)
newline = copytext(newline, 1, 200)
lines.Insert(num, newline)
SSnanoui.update_uis(src)
else if(href_list["deleteline"])
var/num = round(text2num(href_list["deleteline"]))
if(num > lines.len || num < 1)
return
lines.Cut(num, num + 1)
SSnanoui.update_uis(src)
else if(href_list["modifyline"])
var/num = round(text2num(href_list["modifyline"]))
var/content = html_encode(input("Enter your line: ", instrumentObj.name, lines[num]) as text|null)
if(!content || !in_range(instrumentObj, usr))
return
if(length(content) > 200)
content = copytext(content, 1, 200)
if(num > lines.len || num < 1)
return
lines[num] = content
SSnanoui.update_uis(src)
else if(href_list["stop"])
playing = 0
SSnanoui.update_uis(src)
/datum/song/proc/sanitize_tempo(new_tempo)
new_tempo = abs(new_tempo)
return max(round(new_tempo, world.tick_lag), world.tick_lag)
// subclass for handheld instruments, like violin
/datum/song/handheld
/datum/song/handheld/shouldStopPlaying()
if(instrumentObj)
return !isliving(instrumentObj.loc)
else
return 1
//////////////////////////////////////////////////////////////////////////
/obj/structure/piano
name = "space minimoog"
icon = 'icons/obj/musician.dmi'
icon_state = "minimoog"
anchored = 1
density = 1
var/datum/song/song
/obj/structure/piano/New()
..()
song = new("piano", src)
if(prob(50))
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"
/obj/structure/piano/Destroy()
QDEL_NULL(song)
return ..()
/obj/structure/piano/Initialize()
if(song)
song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded
..()
/obj/structure/piano/attack_hand(mob/user as mob)
ui_interact(user)
/obj/structure/piano/ui_interact(mob/user, ui_key = "main", var/datum/nanoui/ui = null, var/force_open = 1)
if(!isliving(user) || user.incapacitated() || !anchored)
return
song.ui_interact(user, ui_key, ui, force_open)
/obj/structure/piano/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
return song.ui_data(user, ui_key, state)
/obj/structure/piano/Topic(href, href_list)
song.Topic(href, href_list)
/obj/structure/piano/wrench_act(mob/user, obj/item/I)
. = TRUE
if(!I.tool_use_check(user, 0))
return
if(!anchored && !isinspace())
WRENCH_ANCHOR_MESSAGE
if(!I.use_tool(src, user, 20, volume = I.tool_volume))
return
user.visible_message( \
"[user] tightens [src]'s casters.", \
"<span class='notice'> You have tightened [src]'s casters. Now it can be played again.</span>", \
"You hear ratchet.")
anchored = TRUE
else if(anchored)
to_chat(user, "<span class='notice'> You begin to loosen [src]'s casters...</span>")
if(!I.use_tool(src, user, 40, volume = I.tool_volume))
return
user.visible_message( \
"[user] loosens [src]'s casters.", \
"<span class='notice'> You have loosened [src]. Now it can be pulled somewhere else.</span>", \
"You hear ratchet.")
anchored = FALSE
else
to_chat(user, "<span class='warning'>[src] needs to be bolted to the floor!</span>")
+13 -12
View File
@@ -4,12 +4,13 @@
return
var/turf/turf_source = get_turf(source)
if(!turf_source)
return
if(!SSsounds.channel_list) // Not ready yet
return
//allocate a channel if necessary now so its the same for everyone
channel = channel || open_sound_channel()
channel = channel || SSsounds.random_available_channel()
// Looping through the player list has the added bonus of working for mobs inside containers
var/sound/S = sound(get_sfx(soundin))
@@ -33,7 +34,7 @@
if(distance <= maxdistance)
M.playsound_local(turf_source, soundin, vol, vary, frequency, falloff, channel, pressure_affected, S)
/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S)
/mob/proc/playsound_local(turf/turf_source, soundin, vol as num, vary, frequency, falloff, channel = 0, pressure_affected = TRUE, sound/S, distance_multiplier = 1)
if(!client || !can_hear())
return
@@ -41,7 +42,7 @@
S = sound(get_sfx(soundin))
S.wait = 0 //No queue
S.channel = channel || open_sound_channel()
S.channel = channel || SSsounds.random_available_channel()
S.volume = vol
if(vary)
@@ -55,6 +56,7 @@
//sound volume falloff with distance
var/distance = get_dist(T, turf_source)
distance *= distance_multiplier
S.volume -= max(distance - world.view, 0) * 2 //multiplicative falloff to add on top of natural audio falloff.
@@ -81,9 +83,9 @@
return //No sound
var/dx = turf_source.x - T.x // Hearing from the right/left
S.x = dx
S.x = dx * distance_multiplier
var/dz = turf_source.y - T.y // Hearing from infront/behind
S.z = dz
S.z = dz * distance_multiplier
// The y value is for above your head, but there is no ceiling in 2d spessmens.
S.y = 1
S.falloff = (falloff ? falloff : FALLOFF_SOUNDS)
@@ -98,15 +100,14 @@
var/mob/M = m
M.playsound_local(M, null, volume, vary, frequency, falloff, channel, pressure_affected, S)
/proc/open_sound_channel()
var/static/next_channel = 1 //loop through the available 1024 - (the ones we reserve) channels and pray that its not still being used
. = ++next_channel
if(next_channel > CHANNEL_HIGHEST_AVAILABLE)
next_channel = 1
/mob/proc/stop_sound_channel(chan)
SEND_SOUND(src, sound(null, repeat = 0, wait = 0, channel = chan))
/mob/proc/set_sound_channel_volume(channel, volume)
var/sound/S = sound(null, FALSE, FALSE, channel, volume)
S.status = SOUND_UPDATE
SEND_SOUND(src, S)
/client/proc/playtitlemusic()
if(!SSticker || !SSticker.login_music || config.disable_lobby_music)
return
+14
View File
@@ -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
-18
View File
@@ -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()
@@ -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
+26
View File
@@ -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')
+19
View File
@@ -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')
+36
View File
@@ -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')
+86
View File
@@ -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)
@@ -1,55 +1,10 @@
//copy pasta of the space piano, don't hurt me -Pete
/obj/item/instrument
name = "generic instrument"
icon = 'icons/obj/musician.dmi'
lefthand_file = 'icons/mob/inhands/equipment/instruments_lefthand.dmi'
righthand_file = 'icons/mob/inhands/equipment/instruments_righthand.dmi'
resistance_flags = FLAMMABLE
max_integrity = 100
var/datum/song/handheld/song
var/instrumentId = "generic"
var/instrumentExt = "mid"
/obj/item/instrument/New()
song = new(instrumentId, src, instrumentExt)
..()
/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/Initialize(mapload)
song.tempo = song.sanitize_tempo(song.tempo) // tick_lag isn't set when the map is loaded
..()
/obj/item/instrument/attack_self(mob/user)
ui_interact(user)
/obj/item/instrument/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
if(!isliving(user) || user.incapacitated())
return
song.ui_interact(user, ui_key, ui, force_open)
/obj/item/instrument/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
return song.ui_data(user, ui_key, state)
/obj/item/instrument/Topic(href, href_list)
song.Topic(href, href_list)
/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"
instrumentExt = "ogg"
force = 10
hitsound = "swing_hit"
instrumentId = "violin"
allowed_instrument_ids = "violin"
/obj/item/instrument/violin/golden
name = "golden violin"
@@ -63,87 +18,146 @@
desc = "An advanced electronic synthesizer that can be used as various instruments."
icon_state = "synth"
item_state = "synth"
instrumentId = "piano"
instrumentExt = "ogg"
var/static/list/insTypes = list("accordion" = "mid", "glockenspiel" = "mid", "guitar" = "ogg", "eguitar" = "ogg", "harmonica" = "mid", "piano" = "ogg", "recorder" = "mid", "saxophone" = "mid", "trombone" = "mid", "violin" = "ogg", "xylophone" = "mid")
actions_types = list(/datum/action/item_action/synthswitch)
allowed_instrument_ids = "piano"
/obj/item/instrument/piano_synth/proc/changeInstrument(name = "piano")
song.instrumentDir = name
song.instrumentExt = insTypes[name]
/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"
instrumentExt = "ogg"
force = 10
attack_verb = list("played metal on", "serenaded", "crashed", "smashed")
hitsound = 'sound/effects/guitarsmash.ogg'
instrumentId = "guitar"
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"
instrumentExt = "ogg"
force = 12
attack_verb = list("played metal on", "shredded", "crashed", "smashed")
hitsound = 'sound/weapons/stringsmash.ogg'
instrumentId = "eguitar"
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"
instrumentId = "glockenspiel"
allowed_instrument_ids = "glockenspiel"
/obj/item/instrument/accordion
name = "accordion"
desc = "Pun-Pun not included."
icon_state = "accordion"
item_state = "accordion"
instrumentId = "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"
instrumentId = "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"
instrumentId = "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"
instrumentId = "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"
instrumentId = "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."
desc = "A percussion instrument with a bright tone."
icon_state = "xylophone"
item_state = "xylophone"
instrumentId = "xylophone"
allowed_instrument_ids = "bikehorn"
/obj/item/instrument/bikehorn
name = "gilded bike horn"
@@ -153,14 +167,14 @@
lefthand_file = 'icons/mob/inhands/items_lefthand.dmi'
righthand_file = 'icons/mob/inhands/items_righthand.dmi'
attack_verb = list("beautifully honks")
instrumentId = "bikehorn"
instrumentExt = "ogg"
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
@@ -168,7 +182,7 @@
/obj/item/stack/cable_coil = 6,
/obj/item/stack/tape_roll = 5)
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
time = 80
time = 8 SECONDS
category = CAT_MISC
/datum/crafting_recipe/guitar
@@ -178,7 +192,7 @@
/obj/item/stack/cable_coil = 6,
/obj/item/stack/tape_roll = 5)
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
time = 80
time = 8 SECONDS
category = CAT_MISC
/datum/crafting_recipe/eguitar
@@ -188,5 +202,15 @@
/obj/item/stack/cable_coil = 6,
/obj/item/stack/tape_roll = 5)
tools = list(TOOL_SCREWDRIVER, TOOL_WIRECUTTER)
time = 80
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"
+43
View File
@@ -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')
+56
View File
@@ -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')
+410
View File
@@ -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)
+179
View File
@@ -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)
+19
View File
@@ -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')
@@ -74,7 +74,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, 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
@@ -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>")
+113 -162
View File
@@ -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
@@ -213,7 +218,7 @@
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
@@ -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
@@ -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
@@ -862,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)) || \
@@ -873,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())
@@ -905,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
@@ -1033,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())
@@ -1080,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()
@@ -1137,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]")
@@ -1257,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
@@ -1383,3 +1330,7 @@
updateDialog()
#undef APC_UPDATE_ICON_COOLDOWN
#undef APC_EXTERNAL_POWER_NOTCONNECTED
#undef APC_EXTERNAL_POWER_NOENERGY
#undef APC_EXTERNAL_POWER_GOOD
@@ -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
View File
@@ -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)
@@ -383,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)"
@@ -414,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
@@ -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()
@@ -24,6 +24,7 @@
var/list/hacked_reagents = list("toxin")
var/hack_message = "You disable the safety safeguards, enabling the \"Mad Scientist\" mode."
var/unhack_message = "You re-enable the safety safeguards, enabling the \"NT Standard\" mode."
var/is_drink = FALSE
/obj/machinery/chem_dispenser/get_cell()
return cell
@@ -120,7 +121,6 @@
var/usedpower = cell.give(recharge_amount)
if(usedpower)
use_power(15 * recharge_amount)
SSnanoui.update_uis(src) // update all UIs attached to src
recharge_counter = 0
return
recharge_counter++
@@ -131,7 +131,6 @@
else
spawn(rand(0, 15))
stat |= NOPOWER
SSnanoui.update_uis(src) // update all UIs attached to src
/obj/machinery/chem_dispenser/ex_act(severity)
if(severity < 3)
@@ -145,19 +144,17 @@
beaker = null
overlays.Cut()
/obj/machinery/chem_dispenser/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = 1)
/obj/machinery/chem_dispenser/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 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, "chem_dispenser.tmpl", ui_title, 390, 655)
// open the new ui window
ui = new(user, src, ui_key, "ChemDispenser", ui_title, 390, 655)
ui.open()
/obj/machinery/chem_dispenser/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/chem_dispenser/tgui_data(mob/user)
var/data[0]
data["glass"] = is_drink
data["amount"] = amount
data["energy"] = cell.charge ? cell.charge * powerefficiency : "0" //To prevent NaN in the UI.
data["maxEnergy"] = cell.maxcharge * powerefficiency
@@ -187,63 +184,59 @@
return data
/obj/machinery/chem_dispenser/Topic(href, href_list)
/obj/machinery/chem_dispenser/tgui_act(actions, params)
if(..())
return TRUE
return
if(stat & (NOPOWER|BROKEN))
return
if(href_list["amount"])
amount = round(text2num(href_list["amount"]), 1) // round to nearest 1
if(amount < 0) // Since the user can actually type the commands himself, some sanity checking
amount = 0
if(amount > 100)
amount = 100
if(href_list["dispense"])
if(!is_operational() || QDELETED(cell))
return
if(beaker && dispensable_reagents.Find(href_list["dispense"]))
. = TRUE
switch(actions)
if("amount")
amount = clamp(round(text2num(params["amount"]), 1), 0, 50) // round to nearest 1 and clamp to 0 - 50
if("dispense")
if(!is_operational() || QDELETED(cell))
return
if(!beaker || !dispensable_reagents.Find(params["reagent"]))
return
var/datum/reagents/R = beaker.reagents
var/free = R.maximum_volume - R.total_volume
var/actual = min(amount, (cell.charge * powerefficiency) * 10, free)
if(!cell.use(actual / powerefficiency))
atom_say("Not enough energy to complete operation!")
return
R.add_reagent(href_list["dispense"], actual)
R.add_reagent(params["reagent"], actual)
overlays.Cut()
if(!icon_beaker)
icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
icon_beaker = mutable_appearance('icons/obj/chemical.dmi', "disp_beaker") //randomize beaker overlay position.
icon_beaker.pixel_x = rand(-10, 5)
overlays += icon_beaker
if(href_list["remove"])
if(beaker)
if(href_list["removeamount"])
var/amount = text2num(href_list["removeamount"])
if(isnum(amount) && (amount > 0))
var/datum/reagents/R = beaker.reagents
var/id = href_list["remove"]
R.remove_reagent(id, amount)
else if(isnum(amount) && (amount == -1)) //Isolate instead
var/datum/reagents/R = beaker.reagents
var/id = href_list["remove"]
R.isolate_reagent(id)
if(href_list["ejectBeaker"])
if(beaker)
if("remove")
var/amount = text2num(params["amount"])
if(!beaker || !amount)
return
var/datum/reagents/R = beaker.reagents
var/id = params["reagent"]
if(amount > 0)
R.remove_reagent(id, amount)
else if(amount == -1) //Isolate instead
R.isolate_reagent(id)
if("ejectBeaker")
if(!beaker)
return
beaker.forceMove(loc)
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
overlays.Cut()
else
return FALSE
add_fingerprint(usr)
return TRUE // update UIs attached to this object
/obj/machinery/chem_dispenser/attackby(obj/item/I, mob/user, params)
if(exchange_parts(user, I))
SSnanoui.update_uis(src)
SStgui.update_uis(src)
return
if(isrobot(user))
@@ -263,9 +256,9 @@
beaker = I
I.forceMove(src)
to_chat(user, "<span class='notice'>You set [I] on the machine.</span>")
SSnanoui.update_uis(src) // update all UIs attached to src
SStgui.update_uis(src) // update all UIs attached to src
if(!icon_beaker)
icon_beaker = image('icons/obj/chemical.dmi', src, "disp_beaker") //randomize beaker overlay position.
icon_beaker = mutable_appearance('icons/obj/chemical.dmi', "disp_beaker") //randomize beaker overlay position.
icon_beaker.pixel_x = rand(-10, 5)
overlays += icon_beaker
return
@@ -299,8 +292,7 @@
to_chat(user, unhack_message)
dispensable_reagents -= hacked_reagents
hackedcheck = FALSE
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/chem_dispenser/screwdriver_act(mob/user, obj/item/I)
if(default_deconstruction_screwdriver(user, "[initial(icon_state)]-o", "[initial(icon_state)]", I))
@@ -321,14 +313,14 @@
return attack_hand(user)
/obj/machinery/chem_dispenser/attack_ghost(mob/user)
if(user.can_admin_interact())
return attack_hand(user)
if(stat & BROKEN)
return
tgui_interact(user)
/obj/machinery/chem_dispenser/attack_hand(mob/user)
if(stat & BROKEN)
return
ui_interact(user)
tgui_interact(user)
/obj/machinery/chem_dispenser/soda
icon_state = "soda_dispenser"
@@ -342,6 +334,7 @@
hacked_reagents = list("thirteenloko")
hack_message = "You change the mode from 'McNano' to 'Pizza King'."
unhack_message = "You change the mode from 'Pizza King' to 'McNano'."
is_drink = TRUE
/obj/machinery/chem_dispenser/soda/New()
..()
@@ -377,6 +370,7 @@
hacked_reagents = list("goldschlager", "patron", "absinthe", "ethanol", "nothing", "sake")
hack_message = "You disable the 'nanotrasen-are-cheap-bastards' lock, enabling hidden and very expensive boozes."
unhack_message = "You re-enable the 'nanotrasen-are-cheap-bastards' lock, disabling hidden and very expensive boozes."
is_drink = TRUE
/obj/machinery/chem_dispenser/beer/New()
..()
@@ -1,15 +1,19 @@
/obj/machinery/chem_heater
name = "chemical heater"
density = 1
anchored = 1
density = TRUE
anchored = TRUE
icon = 'icons/obj/chemical.dmi'
icon_state = "mixer0b"
use_power = IDLE_POWER_USE
idle_power_usage = 40
resistance_flags = FIRE_PROOF | ACID_PROOF
resistance_flags = FIRE_PROOF|ACID_PROOF
var/obj/item/reagent_containers/beaker = null
var/desired_temp = T0C
var/on = FALSE
/// Whether this should auto-eject the beaker once done heating/cooling.
var/auto_eject = FALSE
/// The higher this number, the faster reagents will heat/cool.
var/speed_increase = 0
/obj/machinery/chem_heater/New()
..()
@@ -19,35 +23,36 @@
component_parts += new /obj/item/stack/sheet/glass(null)
RefreshParts()
/obj/machinery/chem_heater/RefreshParts()
speed_increase = initial(speed_increase)
for(var/obj/item/stock_parts/micro_laser/M in component_parts)
speed_increase += 5 * (M.rating - 1)
/obj/machinery/chem_heater/process()
..()
if(stat & NOPOWER)
if(stat & (NOPOWER|BROKEN))
return
var/state_change = FALSE
if(on)
if(beaker)
if(!beaker.reagents.total_volume)
on = FALSE
SSnanoui.update_uis(src)
return
beaker.reagents.temperature_reagents(desired_temp)
beaker.reagents.temperature_reagents(desired_temp)
if(abs(beaker.reagents.chem_temp - desired_temp) <= 3)
beaker.reagents.temperature_reagents(desired_temp, max(1, 35 - speed_increase))
if(round(beaker.reagents.chem_temp) == round(desired_temp))
playsound(loc, 'sound/machines/ding.ogg', 50, 1)
on = FALSE
state_change = TRUE
if(state_change)
SSnanoui.update_uis(src)
if(auto_eject)
eject_beaker()
/obj/machinery/chem_heater/proc/eject_beaker(mob/user)
if(beaker)
beaker.forceMove(get_turf(src))
if(Adjacent(user) && !issilicon(user))
if(user && Adjacent(user) && !issilicon(user))
user.put_in_hands(beaker)
beaker = null
icon_state = "mixer0b"
on = FALSE
SSnanoui.update_uis(src)
SStgui.update_uis(src)
/obj/machinery/chem_heater/power_change()
if(powered())
@@ -55,7 +60,6 @@
else
spawn(rand(0, 15))
stat |= NOPOWER
SSnanoui.update_uis(src)
/obj/machinery/chem_heater/attackby(obj/item/I, mob/user)
if(isrobot(user))
@@ -71,7 +75,7 @@
I.forceMove(src)
to_chat(user, "<span class='notice'>You add the beaker to the machine!</span>")
icon_state = "mixer1b"
SSnanoui.update_uis(src)
SStgui.update_uis(src)
return
if(exchange_parts(user, I))
@@ -95,62 +99,62 @@
default_deconstruction_crowbar(user, I)
/obj/machinery/chem_heater/attack_hand(mob/user)
ui_interact(user)
tgui_interact(user)
/obj/machinery/chem_heater/attack_ghost(mob/user)
if(user.can_admin_interact())
return attack_hand(user)
tgui_interact(user)
/obj/machinery/chem_heater/attack_ai(mob/user)
add_hiddenprint(user)
return attack_hand(user)
/obj/machinery/chem_heater/Topic(href, href_list)
/obj/machinery/chem_heater/tgui_act(action, params)
if(..())
return FALSE
return
if(stat & (NOPOWER|BROKEN))
return
if(href_list["toggle_on"])
if(!beaker.reagents.total_volume)
return FALSE
on = !on
. = 1
if(href_list["adjust_temperature"])
var/val = href_list["adjust_temperature"]
if(isnum(val))
desired_temp = clamp(desired_temp+val, 0, 1000)
else if(val == "input")
var/target = input("Please input the target temperature", name) as num
desired_temp = clamp(target, 0, 1000)
. = TRUE
switch(action)
if("toggle_on")
on = !on
if("adjust_temperature")
desired_temp = clamp(text2num(params["target"]), 0, 1000)
if("eject_beaker")
eject_beaker(usr)
. = FALSE
if("toggle_autoeject")
auto_eject = !auto_eject
else
return FALSE
. = 1
add_fingerprint(usr)
if(href_list["eject_beaker"])
eject_beaker(usr)
. = 0 //updated in eject_beaker() already
/obj/machinery/chem_heater/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null)
/obj/machinery/chem_heater/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)
if(user.stat || user.restrained())
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)
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "chem_heater.tmpl", "ChemHeater", 350, 270)
ui = new(user, src, ui_key, "ChemHeater", "Chemical Heater", 350, 270, master_ui, state)
ui.open()
/obj/machinery/chem_heater/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/chem_heater/tgui_data(mob/user)
var/data[0]
var/cur_temp = beaker ? beaker.reagents.chem_temp : null
data["targetTemp"] = desired_temp
data["targetTempReached"] = FALSE
data["autoEject"] = auto_eject
data["isActive"] = on
data["isBeakerLoaded"] = beaker ? 1 : 0
data["isBeakerLoaded"] = beaker ? TRUE : FALSE
data["currentTemp"] = beaker ? beaker.reagents.chem_temp : null
data["currentTemp"] = cur_temp
data["beakerCurrentVolume"] = beaker ? beaker.reagents.total_volume : null
data["beakerMaxVolume"] = beaker ? beaker.volume : null
if(cur_temp)
data["targetTempReached"] = round(cur_temp) == round(desired_temp)
//copy-pasted from chem dispenser
var/beakerContents[0]
if(beaker)
@@ -1,3 +1,9 @@
#define MAX_PILL_SPRITE 20 //max icon state of the pill sprites
#define MAX_MULTI_AMOUNT 20 // Max number of pills/patches that can be made at once
#define MAX_UNITS_PER_PILL 100 // Max amount of units in a pill
#define MAX_UNITS_PER_PATCH 30 // Max amount of units in a patch
#define MAX_CUSTOM_NAME_LEN 64 // Max length of a custom pill/condiment/whatever
/obj/machinery/chem_master
name = "\improper ChemMaster 3000"
density = TRUE
@@ -14,10 +20,12 @@
var/useramount = 30 // Last used amount
var/pillamount = 10
var/patchamount = 10
var/bottlesprite = "bottle"
var/pillsprite = "1"
var/bottlesprite = 1
var/pillsprite = 1
var/client/has_sprites = list()
var/printing = FALSE
var/static/list/pill_bottle_wrappers
var/static/list/bottle_styles
/obj/machinery/chem_master/New()
..()
@@ -94,7 +102,7 @@
beaker = I
I.forceMove(src)
to_chat(user, "<span class='notice'>You add the beaker to the machine!</span>")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
update_icon()
else if(istype(I, /obj/item/storage/pill_bottle))
@@ -109,14 +117,10 @@
loaded_pill_bottle = I
I.forceMove(src)
to_chat(user, "<span class='notice'>You add [I] into the dispenser slot!</span>")
SSnanoui.update_uis(src)
SStgui.update_uis(src)
else
return ..()
/obj/machinery/chem_master/crowbar_act(mob/user, obj/item/I)
if(!panel_open)
return
@@ -142,319 +146,127 @@
power_change()
return TRUE
/obj/machinery/chem_master/Topic(href, href_list)
/obj/machinery/chem_master/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state)
if(..())
return
if(stat & (NOPOWER|BROKEN))
return
if(tgui_act_modal(action, params, ui, state))
return TRUE
add_fingerprint(usr)
usr.set_machine(src)
if(href_list["ejectp"])
if(loaded_pill_bottle)
loaded_pill_bottle.forceMove(loc)
loaded_pill_bottle = null
else if(href_list["change_pillbottle"])
if(loaded_pill_bottle)
var/list/wrappers = list("Default wrapper", "Red wrapper", "Green wrapper", "Pale green wrapper", "Blue wrapper", "Light blue wrapper", "Teal wrapper", "Yellow wrapper", "Orange wrapper", "Pink wrapper", "Brown wrapper")
var/chosen = input(usr, "Select a pillbottle wrapper", "Pillbottle wrapper", wrappers[1]) as null|anything in wrappers
if(!chosen)
. = TRUE
switch(action)
if("toggle")
mode = !mode
if("ejectp")
if(loaded_pill_bottle)
loaded_pill_bottle.forceMove(loc)
loaded_pill_bottle = null
if("print")
if(printing || condi)
return
var/color
switch(chosen)
if("Default wrapper")
loaded_pill_bottle.cut_overlays()
return
if("Red wrapper")
color = COLOR_RED
if("Green wrapper")
color = COLOR_GREEN
if("Pink wrapper")
color = COLOR_PINK
if("Teal wrapper")
color = COLOR_TEAL
if("Blue wrapper")
color = COLOR_BLUE
if("Brown wrapper")
color = COLOR_MAROON
if("Light blue wrapper")
color = COLOR_CYAN_BLUE
if("Yellow wrapper")
color = COLOR_YELLOW
if("Pale green wrapper")
color = COLOR_PALE_BTL_GREEN
if("Orange wrapper")
color = COLOR_ORANGE
loaded_pill_bottle.wrapper_color = color
loaded_pill_bottle.apply_wrap()
else if(href_list["close"])
usr << browse(null, "window=chem_master")
onclose(usr, "chem_master")
usr.unset_machine()
return
if(href_list["print_p"])
if(!printing)
var/idx = text2num(params["idx"]) || 0
var/from_beaker = text2num(params["beaker"]) || FALSE
var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list
if(idx < 1 || idx > length(reagent_list))
return
var/datum/reagent/R = reagent_list[idx]
printing = TRUE
visible_message("<span class='notice'>[src] rattles and prints out a sheet of paper.</span>")
playsound(loc, 'sound/goonstation/machines/printer_dotmatrix.ogg', 50, 1)
var/obj/item/paper/P = new /obj/item/paper(loc)
P.info = "<CENTER><B>Chemical Analysis</B></CENTER><BR>"
P.info = "<center><b>Chemical Analysis</b></center><br>"
P.info += "<b>Time of analysis:</b> [station_time_timestamp()]<br><br>"
P.info += "<b>Chemical name:</b> [href_list["name"]]<br>"
if(href_list["name"] == "Blood")
var/datum/reagents/R = beaker.reagents
var/datum/reagent/blood/G
for(var/datum/reagent/F in R.reagent_list)
if(F.name == href_list["name"])
G = F
break
var/B = G.data["blood_type"]
var/C = G.data["blood_DNA"]
P.info += "<b>Description:</b><br>Blood Type: [B]<br>DNA: [C]"
P.info += "<b>Chemical name:</b> [R.name]<br>"
if(istype(R, /datum/reagent/blood))
var/datum/reagent/blood/B = R
P.info += "<b>Description:</b> N/A<br><b>Blood Type:</b> [B.data["blood_type"]]<br><b>DNA:</b> [B.data["blood_DNA"]]"
else
P.info += "<b>Description:</b> [href_list["desc"]]"
P.info += "<b>Description:</b> [R.description]"
P.info += "<br><br><b>Notes:</b><br>"
P.name = "Chemical Analysis - [href_list["name"]]"
printing = FALSE
P.name = "Chemical Analysis - [R.name]"
spawn(50)
printing = FALSE
else
. = FALSE
if(beaker)
var/datum/reagents/R = beaker.reagents
if(href_list["analyze"])
var/dat = ""
if(!condi)
if(href_list["name"] == "Blood")
var/datum/reagent/blood/G
for(var/datum/reagent/F in R.reagent_list)
if(F.name == href_list["name"])
G = F
break
var/A = G.name
var/B = G.data["blood_type"]
var/C = G.data["blood_DNA"]
dat += "<TITLE>Chemmaster 3000</TITLE>Chemical infos:<BR><BR>Name:<BR>[A]<BR><BR>Description:<BR>Blood Type: [B]<br>DNA: [C]"
else
dat += "<TITLE>Chemmaster 3000</TITLE>Chemical infos:<BR><BR>Name:<BR>[href_list["name"]]<BR><BR>Description:<BR>[href_list["desc"]]"
dat += "<BR><BR><A href='?src=[UID()];print_p=1;desc=[href_list["desc"]];name=[href_list["name"]]'>(Print Analysis)</A><BR>"
dat += "<A href='?src=[UID()];main=1'>(Back)</A>"
if(. || !beaker)
return
. = TRUE
var/datum/reagents/R = beaker.reagents
switch(action)
if("add")
var/id = params["id"]
var/amount = text2num(params["amount"])
if(!id || !amount)
return
R.trans_id_to(src, id, amount)
if("remove")
var/id = params["id"]
var/amount = text2num(params["amount"])
if(!id || !amount)
return
if(mode)
reagents.trans_id_to(beaker, id, amount)
else
dat += "<TITLE>Condimaster 3000</TITLE>Condiment infos:<BR><BR>Name:<BR>[href_list["name"]]<BR><BR>Description:<BR>[href_list["desc"]]<BR><BR><BR><A href='?src=[UID()];main=1'>(Back)</A>"
usr << browse(dat, "window=chem_master;size=575x500")
return
else if(href_list["add"])
if(href_list["amount"])
var/id = href_list["add"]
var/amount = text2num(href_list["amount"])
R.trans_id_to(src, id, amount)
else if(href_list["addcustom"])
var/id = href_list["addcustom"]
useramount = input("Select the amount to transfer.", 30, useramount) as num
useramount = isgoodnumber(useramount)
Topic(null, list("amount" = "[useramount]", "add" = "[id]"))
else if(href_list["remove"])
if(href_list["amount"])
var/id = href_list["remove"]
var/amount = text2num(href_list["amount"])
if(mode)
reagents.trans_id_to(beaker, id, amount)
else
reagents.remove_reagent(id, amount)
else if(href_list["removecustom"])
var/id = href_list["removecustom"]
useramount = input("Select the amount to transfer.", 30, useramount) as num
useramount = isgoodnumber(useramount)
Topic(null, list("amount" = "[useramount]", "remove" = "[id]"))
else if(href_list["toggle"])
mode = !mode
else if(href_list["main"])
attack_hand(usr)
return
else if(href_list["eject"])
if(beaker)
beaker.forceMove(get_turf(src))
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
reagents.clear_reagents()
update_icon()
else if(href_list["createpill"] || href_list["createpill_multiple"])
if(!condi)
var/count = 1
if(href_list["createpill_multiple"])
count = input("Select the number of pills to make.", 10, pillamount) as num|null
if(count == null)
return
count = isgoodnumber(count)
if(count > 20)
count = 20 //Pevent people from creating huge stacks of pills easily. Maybe move the number to defines?
if(count <= 0)
return
var/amount_per_pill = reagents.total_volume / count
if(amount_per_pill > 100)
amount_per_pill = 100
var/name = clean_input("Name:","Name your pill!","[reagents.get_master_reagent_name()] ([amount_per_pill]u)")
if(!name)
return
name = reject_bad_text(name)
while(count--)
if(reagents.total_volume <= 0)
to_chat(usr, "<span class='notice'>Not enough reagents to create these pills!</span>")
return
var/obj/item/reagent_containers/food/pill/P = new/obj/item/reagent_containers/food/pill(loc)
if(!name)
name = reagents.get_master_reagent_name()
P.name = "[name] pill"
P.pixel_x = rand(-7, 7) //random position
P.pixel_y = rand(-7, 7)
P.icon_state = "pill"+pillsprite
reagents.trans_to(P, amount_per_pill)
if(loaded_pill_bottle && loaded_pill_bottle.type == /obj/item/storage/pill_bottle)
if(loaded_pill_bottle.contents.len < loaded_pill_bottle.storage_slots)
P.forceMove(loaded_pill_bottle)
updateUsrDialog()
else
var/name = clean_input("Name:", "Name your bag!", reagents.get_master_reagent_name())
if(!name)
return
name = reject_bad_text(name)
var/obj/item/reagent_containers/food/condiment/pack/P = new/obj/item/reagent_containers/food/condiment/pack(loc)
if(!name) name = reagents.get_master_reagent_name()
P.originalname = name
P.name = "[name] pack"
P.desc = "A small condiment pack. The label says it contains [name]."
reagents.trans_to(P, 10)
else if(href_list["createpatch"] || href_list["createpatch_multiple"])
if(!condi)
var/count = 1
if(href_list["createpatch_multiple"])
count = input("Select the number of patches to make.", 10, patchamount) as num|null
if(count == null)
return
count = isgoodnumber(count)
if(!count || count <= 0)
return
if(count > 20)
count = 20 //Pevent people from creating huge stacks of patches easily. Maybe move the number to defines?
var/amount_per_patch = reagents.total_volume/count
if(amount_per_patch > 30)
amount_per_patch = 30
var/name = clean_input("Name:", "Name your patch!", "[reagents.get_master_reagent_name()] ([amount_per_patch]u)")
if(!name)
return
name = reject_bad_text(name)
var/is_medical_patch = chemical_safety_check(reagents)
while(count--)
var/obj/item/reagent_containers/food/pill/patch/P = new/obj/item/reagent_containers/food/pill/patch(loc)
if(!name) name = reagents.get_master_reagent_name()
P.name = "[name] patch"
P.pixel_x = rand(-7, 7) //random position
P.pixel_y = rand(-7, 7)
reagents.trans_to(P,amount_per_patch)
if(is_medical_patch)
P.instant_application = TRUE
P.icon_state = "bandaid_med"
if(loaded_pill_bottle && loaded_pill_bottle.type == /obj/item/storage/pill_bottle/patch_pack)
if(loaded_pill_bottle.contents.len < loaded_pill_bottle.storage_slots)
P.forceMove(loaded_pill_bottle)
updateUsrDialog()
else if(href_list["createbottle"])
if(!condi)
var/name = clean_input("Name:", "Name your bottle!", reagents.get_master_reagent_name())
if(!name)
return
name = reject_bad_text(name)
var/obj/item/reagent_containers/glass/bottle/reagent/P = new/obj/item/reagent_containers/glass/bottle/reagent(loc)
if(!name)
name = reagents.get_master_reagent_name()
P.name = "[name] bottle"
P.pixel_x = rand(-7, 7) //random position
P.pixel_y = rand(-7, 7)
P.icon_state = bottlesprite
reagents.trans_to(P, 50)
else
var/obj/item/reagent_containers/food/condiment/P = new/obj/item/reagent_containers/food/condiment(loc)
reagents.trans_to(P, 50)
else if(href_list["change_pill"])
#define MAX_PILL_SPRITE 20 //max icon state of the pill sprites
var/dat = "<table>"
var/j = 0
for(var/i = 1 to MAX_PILL_SPRITE)
j++
if(j == 1)
dat += "<tr>"
dat += "<td><a href=\"?src=[UID()]&pill_sprite=[i]\"><img src=\"pill[i].png\" /></a></td>"
if(j == 5)
dat += "</tr>"
j = 0
dat += "</table>"
usr << browse(dat, "window=chem_master_iconsel;size=225x193")
return
else if(href_list["change_bottle"])
var/dat = "<table>"
var/j = 0
for(var/i in list("bottle", "small_bottle", "wide_bottle", "round_bottle", "reagent_bottle"))
j++
if(j == 1)
dat += "<tr>"
dat += "<td><a href=\"?src=[UID()]&bottle_sprite=[i]\"><img src=\"[i].png\" /></a></td>"
if(j == 5)
dat += "</tr>"
j = 0
dat += "</table>"
usr << browse(dat, "window=chem_master_iconsel;size=225x193")
return
else if(href_list["pill_sprite"])
pillsprite = href_list["pill_sprite"]
usr << browse(null, "window=chem_master_iconsel")
else if(href_list["bottle_sprite"])
bottlesprite = href_list["bottle_sprite"]
usr << browse(null, "window=chem_master_iconsel")
SSnanoui.update_uis(src)
reagents.remove_reagent(id, amount)
if("eject")
if(!beaker)
return
beaker.forceMove(get_turf(src))
if(Adjacent(usr) && !issilicon(usr))
usr.put_in_hands(beaker)
beaker = null
reagents.clear_reagents()
update_icon()
if("create_condi_bottle")
if(!condi || !reagents.total_volume)
return
var/obj/item/reagent_containers/food/condiment/P = new(loc)
reagents.trans_to(P, 50)
else
return FALSE
/obj/machinery/chem_master/attack_ai(mob/user)
return attack_hand(user)
/obj/machinery/chem_master/attack_ghost(mob/user)
if(user.can_admin_interact())
return attack_hand(user)
tgui_interact(user)
/obj/machinery/chem_master/attack_hand(mob/user)
if(..())
return TRUE
ui_interact(user)
tgui_interact(user)
/obj/machinery/chem_master/ui_interact(mob/user, ui_key = "main", datum/nanoui/ui = null, force_open = TRUE)
/obj/machinery/chem_master/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)
var/datum/asset/chem_master/assets = get_asset_datum(/datum/asset/chem_master)
assets.send(user)
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)
ui = new(user, src, ui_key, "chem_master.tmpl", name, 575, 500)
ui = new(user, src, ui_key, "ChemMaster", name, 575, 500)
ui.open()
/obj/machinery/chem_master/ui_data(mob/user, ui_key = "main", datum/topic_state/state = GLOB.default_state)
/obj/machinery/chem_master/tgui_data(mob/user)
var/data[0]
data["condi"] = condi
data["loaded_pill_bottle"] = (loaded_pill_bottle ? 1 : 0)
data["loaded_pill_bottle"] = loaded_pill_bottle ? TRUE : FALSE
if(loaded_pill_bottle)
data["loaded_pill_bottle_name"] = loaded_pill_bottle.name
data["loaded_pill_bottle_contents_len"] = loaded_pill_bottle.contents.len
data["loaded_pill_bottle_storage_slots"] = loaded_pill_bottle.storage_slots
data["beaker"] = (beaker ? 1 : 0)
data["beaker"] = beaker ? TRUE : FALSE
if(beaker)
var/list/beaker_reagents_list = list()
data["beaker_reagents"] = beaker_reagents_list
@@ -464,13 +276,255 @@
data["buffer_reagents"] = buffer_reagents_list
for(var/datum/reagent/R in reagents.reagent_list)
buffer_reagents_list[++buffer_reagents_list.len] = list("name" = R.name, "volume" = R.volume, "id" = R.id, "description" = R.description)
else
data["beaker_reagents"] = list()
data["buffer_reagents"] = list()
data["pillsprite"] = pillsprite
data["bottlesprite"] = bottlesprite
data["mode"] = mode
data["printing"] = printing
// Transfer modal information if there is one
data["modal"] = tgui_modal_data(src)
return data
/**
* Called in tgui_act() to process modal actions
*
* Arguments:
* * action - The action passed by tgui
* * params - The params passed by tgui
*/
/obj/machinery/chem_master/proc/tgui_act_modal(action, params, datum/tgui/ui, datum/tgui_state/state)
. = TRUE
var/id = params["id"] // The modal's ID
var/list/arguments = istext(params["arguments"]) ? json_decode(params["arguments"]) : params["arguments"]
switch(tgui_modal_act(src, action, params))
if(TGUI_MODAL_OPEN)
switch(id)
if("analyze")
var/idx = text2num(arguments["idx"]) || 0
var/from_beaker = text2num(arguments["beaker"]) || FALSE
var/reagent_list = from_beaker ? beaker.reagents.reagent_list : reagents.reagent_list
if(idx < 1 || idx > length(reagent_list))
return
var/datum/reagent/R = reagent_list[idx]
var/list/result = list("idx" = idx, "name" = R.name, "desc" = R.description)
if(!condi && istype(R, /datum/reagent/blood))
var/datum/reagent/blood/B = R
result["blood_type"] = B.data["blood_type"]
result["blood_dna"] = B.data["blood_DNA"]
arguments["analysis"] = result
tgui_modal_message(src, id, "", null, arguments)
if("change_pill_bottle_style")
if(!loaded_pill_bottle)
return
if(!pill_bottle_wrappers)
pill_bottle_wrappers = list(
"CLEAR" = "Default",
COLOR_RED = "Red",
COLOR_GREEN = "Green",
COLOR_PALE_BTL_GREEN = "Pale green",
COLOR_BLUE = "Blue",
COLOR_CYAN_BLUE = "Light blue",
COLOR_TEAL = "Teal",
COLOR_YELLOW = "Yellow",
COLOR_ORANGE = "Orange",
COLOR_PINK = "Pink",
COLOR_MAROON = "Brown"
)
var/current = pill_bottle_wrappers[loaded_pill_bottle.wrapper_color] || "Default"
tgui_modal_choice(src, id, "Please select a pill bottle wrapper:", null, arguments, current, pill_bottle_wrappers)
if("addcustom")
if(!beaker || !beaker.reagents.total_volume)
return
tgui_modal_input(src, id, "Please enter the amount to transfer to buffer:", null, arguments, useramount)
if("removecustom")
if(!reagents.total_volume)
return
tgui_modal_input(src, id, "Please enter the amount to transfer to [mode ? "beaker" : "disposal"]:", null, arguments, useramount)
if("create_condi_pack")
if(!condi || !reagents.total_volume)
return
tgui_modal_input(src, id, "Please name your new condiment pack:", null, arguments, reagents.get_master_reagent_name(), MAX_CUSTOM_NAME_LEN)
if("create_pill")
if(condi || !reagents.total_volume)
return
var/num = round(text2num(arguments["num"] || 1))
if(!num)
return
arguments["num"] = num
var/amount_per_pill = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PILL)
var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_pill]u)"
var/pills_text = num == 1 ? "new pill" : "[num] new pills"
tgui_modal_input(src, id, "Please name your [pills_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN)
if("create_pill_multiple")
if(condi || !reagents.total_volume)
return
tgui_modal_input(src, id, "Please enter the amount of pills to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5)
if("change_pill_style")
var/list/choices = list()
for(var/i = 1 to MAX_PILL_SPRITE)
choices += "pill[i].png"
tgui_modal_bento(src, id, "Please select the new style for pills:", null, arguments, pillsprite, choices)
if("create_patch")
if(condi || !reagents.total_volume)
return
var/num = round(text2num(arguments["num"] || 1))
if(!num)
return
arguments["num"] = num
var/amount_per_patch = clamp(reagents.total_volume / num, 0, MAX_UNITS_PER_PATCH)
var/default_name = "[reagents.get_master_reagent_name()] ([amount_per_patch]u)"
var/patches_text = num == 1 ? "new patch" : "[num] new patches"
tgui_modal_input(src, id, "Please name your [patches_text]:", null, arguments, default_name, MAX_CUSTOM_NAME_LEN)
if("create_patch_multiple")
if(condi || !reagents.total_volume)
return
tgui_modal_input(src, id, "Please enter the amount of patches to make (max [MAX_MULTI_AMOUNT] at a time):", null, arguments, pillamount, 5)
if("create_bottle")
if(condi || !reagents.total_volume)
return
tgui_modal_input(src, id, "Please name your bottle:", null, arguments, reagents.get_master_reagent_name(), MAX_CUSTOM_NAME_LEN)
if("change_bottle_style")
if(!bottle_styles)
bottle_styles = list("bottle", "small_bottle", "wide_bottle", "round_bottle", "reagent_bottle")
var/list/bottle_styles_png = list()
for(var/style in bottle_styles)
bottle_styles_png += "[style].png"
tgui_modal_bento(src, id, "Please select the new style for bottles:", null, arguments, bottlesprite, bottle_styles_png)
else
return FALSE
if(TGUI_MODAL_ANSWER)
var/answer = params["answer"]
switch(id)
if("change_pill_bottle_style")
if(!pill_bottle_wrappers || !loaded_pill_bottle) // wat?
return
var/color = "CLEAR"
for(var/col in pill_bottle_wrappers)
var/col_name = pill_bottle_wrappers[col]
if(col_name == answer)
color = col
break
if(length(color) && color != "CLEAR")
loaded_pill_bottle.wrapper_color = color
loaded_pill_bottle.apply_wrap()
else
loaded_pill_bottle.wrapper_color = null
loaded_pill_bottle.cut_overlays()
if("addcustom")
var/amount = isgoodnumber(text2num(answer))
if(!amount || !arguments["id"])
return
tgui_act("add", list("id" = arguments["id"], "amount" = amount), ui, state)
if("removecustom")
var/amount = isgoodnumber(text2num(answer))
if(!amount || !arguments["id"])
return
tgui_act("remove", list("id" = arguments["id"], "amount" = amount), ui, state)
if("create_condi_pack")
if(!condi || !reagents.total_volume)
return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/obj/item/reagent_containers/food/condiment/pack/P = new(loc)
P.originalname = answer
P.name = "[answer] pack"
P.desc = "A small condiment pack. The label says it contains [answer]."
reagents.trans_to(P, 10)
if("create_pill")
if(condi || !reagents.total_volume)
return
var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
if(!count)
return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/amount_per_pill = clamp(reagents.total_volume / count, 0, MAX_UNITS_PER_PILL)
while(count--)
if(reagents.total_volume <= 0)
to_chat(usr, "<span class='notice'>Not enough reagents to create these pills!</span>")
return
var/obj/item/reagent_containers/food/pill/P = new(loc)
P.name = "[answer] pill"
P.pixel_x = rand(-7, 7) // Random position
P.pixel_y = rand(-7, 7)
P.icon_state = "pill[pillsprite]"
reagents.trans_to(P, amount_per_pill)
// Load the pills in the bottle if there's one loaded
if(istype(loaded_pill_bottle) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots)
P.forceMove(loaded_pill_bottle)
if("create_pill_multiple")
if(condi || !reagents.total_volume)
return
tgui_act("modal_open", list("id" = "create_pill", "arguments" = list("num" = answer)), ui, state)
if("change_pill_style")
var/new_style = clamp(text2num(answer) || 0, 0, MAX_PILL_SPRITE)
if(!new_style)
return
pillsprite = new_style
if("create_patch")
if(condi || !reagents.total_volume)
return
var/count = clamp(round(text2num(arguments["num"]) || 0), 0, MAX_MULTI_AMOUNT)
if(!count)
return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/amount_per_patch = clamp(reagents.total_volume / count, 0, MAX_UNITS_PER_PATCH)
var/is_medical_patch = chemical_safety_check(reagents)
while(count--)
if(reagents.total_volume <= 0)
to_chat(usr, "<span class='notice'>Not enough reagents to create these patches!</span>")
return
var/obj/item/reagent_containers/food/pill/patch/P = new(loc)
P.name = "[answer] patch"
P.pixel_x = rand(-7, 7) // random position
P.pixel_y = rand(-7, 7)
reagents.trans_to(P, amount_per_patch)
if(is_medical_patch)
P.instant_application = TRUE
P.icon_state = "bandaid_med"
// Load the patches in the bottle if there's one loaded
if(istype(loaded_pill_bottle, /obj/item/storage/pill_bottle/patch_pack) && length(loaded_pill_bottle.contents) < loaded_pill_bottle.storage_slots)
P.forceMove(loaded_pill_bottle)
if("create_patch_multiple")
if(condi || !reagents.total_volume)
return
tgui_act("modal_open", list("id" = "create_patch", "arguments" = list("num" = answer)), ui, state)
if("create_bottle")
if(condi || !reagents.total_volume)
return
if(!length(answer))
answer = reagents.get_master_reagent_name()
var/obj/item/reagent_containers/glass/bottle/reagent/P = new(loc)
P.name = "[answer] bottle"
P.pixel_x = rand(-7, 7) // random position
P.pixel_y = rand(-7, 7)
P.icon_state = length(bottle_styles) && bottle_styles[bottlesprite] || "bottle"
reagents.trans_to(P, 50)
if("change_bottle_style")
if(!bottle_styles)
return
var/new_sprite = text2num(answer) || 1
if(new_sprite < 1 || new_sprite > length(bottle_styles))
return
bottlesprite = new_sprite
else
return FALSE
else
return FALSE
/obj/machinery/chem_master/proc/isgoodnumber(num)
if(isnum(num))
if(num > 200)
@@ -481,7 +535,7 @@
num = round(num)
return num
else
return 0
return FALSE
/obj/machinery/chem_master/proc/chemical_safety_check(datum/reagents/R)
var/all_safe = TRUE
@@ -503,3 +557,9 @@
component_parts += new /obj/item/reagent_containers/glass/beaker(null)
component_parts += new /obj/item/reagent_containers/glass/beaker(null)
RefreshParts()
#undef MAX_PILL_SPRITE
#undef MAX_MULTI_AMOUNT
#undef MAX_UNITS_PER_PILL
#undef MAX_UNITS_PER_PATCH
#undef MAX_CUSTOM_NAME_LEN
@@ -272,6 +272,16 @@
build_path = /obj/item/circuitboard/solar_control
category = list("Computer Boards")
/datum/design/sm_monitor
name = "Console Board (Supermatter Monitoring)"
desc = "Allows for the construction of circuit boards used to build a supermatter monitoring console"
id = "sm_monitor"
req_tech = list("programming" = 2, "powerstorage" = 2)
build_type = IMPRINTER
materials = list(MAT_GLASS = 1000)
build_path = /obj/item/circuitboard/sm_monitor
category = list("Computer Boards")
/datum/design/spacepodlocator
name = "Console Board (Spacepod Locator)"
desc = "Allows for the construction of circuit boards used to build a space-pod locating console"
+1 -1
View File
@@ -296,7 +296,7 @@
return
var/sound/S = sound(mysound)
S.wait = 0 //No queue
S.channel = open_sound_channel()
S.channel = SSsounds.random_available_channel()
S.volume = 50
for(var/mob/M in passengers | pilot)
M << S
+6
View File
@@ -111,6 +111,12 @@
typepath = /obj/item/instrument/eguitar
cost = 500
/datum/storeitem/banjo
name = "Banjo"
desc = "It's pretty much just a drum with a neck and strings."
typepath = /obj/item/instrument/banjo
cost = 500
/datum/storeitem/piano_synth
name = "Piano Synthesizer"
desc = "An advanced electronic synthesizer that can emulate various instruments."
+370
View File
@@ -0,0 +1,370 @@
/**
* tgui modals
*
* Allows creation of modals within tgui.
*/
GLOBAL_LIST(tgui_modals)
/**
* Call this from a proc that is called in tgui_act() to process modal actions
*
* Example: /obj/machinery/chem_master/proc/tgui_act_modal
* You can then switch based on the return value and show different
* modals depending on the answer.
* Arguments:
* * source - The source datum
* * action - The called action
* * params - The params to the action
*/
/datum/proc/tgui_modal_act(datum/source = src, action = "", params)
ASSERT(istype(source))
. = null
switch(action)
if("modal_open") // Params: id, arguments
return TGUI_MODAL_OPEN
if("modal_answer") // Params: id, answer, arguments
params["answer"] = tgui_modal_preprocess_answer(source, params["answer"])
if(tgui_modal_answer(source, params["id"], params["answer"])) // If there's a current modal with a delegate that returned TRUE, no need to continue
. = TGUI_MODAL_DELEGATE
else
. = TGUI_MODAL_ANSWER
tgui_modal_clear(source)
if("modal_close") // Params: id
tgui_modal_clear(source)
return TGUI_MODAL_CLOSE
/**
* Call this from tgui_data() to return modal information if needed
* Arguments:
* * source - The source datum
*/
/datum/proc/tgui_modal_data(datum/source = src)
ASSERT(istype(source))
var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID())
if(!current)
return null
return current.to_data()
/**
* Clears the current modal for a given datum
*
* Arguments:
* * source - The source datum
*/
/datum/proc/tgui_modal_clear(datum/source = src)
ASSERT(istype(source))
LAZYINITLIST(GLOB.tgui_modals)
var/datum/tgui_modal/previous = GLOB.tgui_modals[source.UID()]
if(!previous)
return FALSE
for(var/i in 1 to length(GLOB.tgui_modals))
var/key = GLOB.tgui_modals[i]
if(previous == GLOB.tgui_modals[key])
GLOB.tgui_modals.Cut(i, i + 1)
break
SStgui.update_uis(source)
return TRUE
/**
* Opens a message TGUI modal
*
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when closed
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
*/
/datum/proc/tgui_modal_message(datum/source = src, id, text = "Default modal message", delegate, arguments)
ASSERT(length(id))
var/datum/tgui_modal/modal = new(id, text, delegate, arguments)
return tgui_modal_new(source, modal)
/**
* Opens a text input TGUI modal
*
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when submitted
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
* * value - The default value of the input
* * max_length - The maximum char length of the input
*/
/datum/proc/tgui_modal_input(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", max_length = TGUI_MODAL_INPUT_MAX_LENGTH)
ASSERT(length(id))
ASSERT(max_length > 0)
var/datum/tgui_modal/input/modal = new(id, text, delegate, arguments, value, max_length)
return tgui_modal_new(source, modal)
/**
* Opens a dropdown input TGUI modal
*
* Internally checks if the answer is in the list of choices.
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when submitted
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
* * value - The default value of the dropdown
* * choices - The list of available choices in the dropdown
*/
/datum/proc/tgui_modal_choice(datum/source = src, id, text = "Default modal message", delegate, arguments, value = "", choices)
ASSERT(length(id))
var/datum/tgui_modal/input/choice/modal = new(id, text, delegate, arguments, value, choices)
return tgui_modal_new(source, modal)
/**
* Opens a bento input TGUI modal
*
* Internally checks if the answer is in the list of choices.
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when submitted
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
* * value - The default value of the bento
* * choices - The list of available choices in the bento
*/
/datum/proc/tgui_modal_bento(datum/source = src, id, text = "Default modal message", delegate, arguments, value, choices)
ASSERT(length(id))
var/datum/tgui_modal/input/bento/modal = new(id, text, delegate, arguments, value, choices)
return tgui_modal_new(source, modal)
/**
* Opens a yes/no TGUI modal
*
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * text - The text to display above the answers
* * delegate - The proc to call when "Yes" is pressed
* * delegate_no - The proc to call when "No" is pressed
* * arguments - List of arguments passed to and from JS (mostly useful for chaining modals)
* * yes_text - The text to show in the "Yes" button
* * no_text - The text to show in the "No" button
*/
/datum/proc/tgui_modal_boolean(datum/source = src, id, text = "Default modal message", delegate, delegate_no, arguments, yes_text = "Yes", no_text = "No")
ASSERT(length(id))
var/datum/tgui_modal/boolean/modal = new(id, text, delegate, delegate_no, arguments, yes_text, no_text)
return tgui_modal_new(source, modal)
/**
* Registers a given modal to a source. Private.
*
* Arguments:
* * source - The source datum
* * modal - The datum/tgui_modal to register
* * replace_previous - Whether any modal currently assigned to source should be replaced
* * instant_update - Whether the changes should reflect immediately
*/
/datum/proc/tgui_modal_new(datum/source = src, datum/tgui_modal/modal = null, replace_previous = TRUE, instant_update = TRUE)
ASSERT(istype(source))
ASSERT(istype(modal))
var/datum/tgui_modal/previous = LAZYACCESS(GLOB.tgui_modals, source.UID())
if(previous && !replace_previous)
return FALSE
modal.owning_source = source
// Previous one should get GC'd
LAZYSET(GLOB.tgui_modals, source.UID(), modal)
if(instant_update)
SStgui.update_uis(source)
return TRUE
/**
* Calls the source's currently assigned modal's (if there is one) on_answer() proc. Private.
*
* Arguments:
* * source - The source datum
* * id - The ID of the modal
* * answer - The provided answer
*/
/datum/proc/tgui_modal_answer(datum/source = src, id, answer = "")
ASSERT(istype(source))
var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID())
if(!current)
return FALSE
return current.on_answer(answer)
/**
* Passes an answer from JS through the modal's proc.
*
* Used namely for cutting the text short if it's longer
* than an input modal's max_length.
* Arguments:
* * source - The source datum
* * answer - The provided answer
*/
/datum/proc/tgui_modal_preprocess_answer(datum/source = src, answer = "")
ASSERT(istype(source))
var/datum/tgui_modal/current = LAZYACCESS(GLOB.tgui_modals, source.UID())
if(!current)
return answer
return current.preprocess_answer(answer)
/**
* Modal datum (contains base information for a modal)
*/
/datum/tgui_modal
var/datum/owning_source
var/id
var/text
var/delegate
var/list/arguments
var/modal_type = "message"
/datum/tgui_modal/New(id, text, delegate, list/arguments)
src.id = id
src.text = text
src.delegate = delegate
src.arguments = arguments
/**
* Called when it's time to pre-process the answer before using it
*
* Arguments:
* * answer - The answer, a nullable text
*/
/datum/tgui_modal/proc/preprocess_answer(answer)
return reject_bad_text(answer, TGUI_MODAL_INPUT_MAX_LENGTH) // bleh
/**
* Called when a modal receives an answer
*
* Arguments:
* * answer - The answer, a nullable text
*/
/datum/tgui_modal/proc/on_answer(answer)
if(delegate)
return call(owning_source, delegate)(answer, arguments)
return FALSE
/**
* Creates a list that describes a modal visually to be passed to JS
*/
/datum/tgui_modal/proc/to_data()
. = list()
.["id"] = id
.["text"] = text
.["args"] = arguments || list()
.["type"] = modal_type
/**
* Input modal - has a text entry that can be used to enter an answer
*/
/datum/tgui_modal/input
modal_type = "input"
var/value
var/max_length
/datum/tgui_modal/input/New(id, text, delegate, list/arguments, value, max_length)
..(id, text, delegate, arguments)
src.value = value
src.max_length = max_length
/datum/tgui_modal/input/preprocess_answer(answer)
. = ..(answer)
if(length(answer) > max_length)
. = copytext(., 1, max_length + 1)
/datum/tgui_modal/input/to_data()
. = ..()
.["value"] = value
/**
* Choice modal - has a dropdown menu that can be used to select an answer
*/
/datum/tgui_modal/input/choice
modal_type = "choice"
var/choices
/datum/tgui_modal/input/choice/New(id, text, delegate, list/arguments, value, choices)
..(id, text, delegate, arguments, value, TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in dropdowns, but whatever
src.choices = choices
/datum/tgui_modal/input/choice/on_answer(answer)
if(answer in choices) // Make sure the answer is actually in our choices!
return ..(answer, arguments)
return FALSE
/datum/tgui_modal/input/choice/to_data()
. = ..()
.["choices"] = choices
/**
* Bento modal - Similar to choice, it displays the choices in a grid of images
*
* The returned answer is the index of the choice.
*/
/datum/tgui_modal/input/bento
modal_type = "bento"
var/choices
/datum/tgui_modal/input/bento/New(id, text, delegate, list/arguments, value, choices)
..(id, text, delegate, arguments, text2num(value), TGUI_MODAL_INPUT_MAX_LENGTH) // Max length doesn't really matter in here, but whatever
src.choices = choices
/datum/tgui_modal/input/bento/preprocess_answer(answer)
return text2num(answer) || 0
/datum/tgui_modal/input/bento/on_answer(answer)
if(answer >= 1 && answer <= length(choices)) // Make sure the answer index is actually in our indexes!
return ..(answer, arguments)
return FALSE
/datum/tgui_modal/input/bento/to_data()
. = ..()
.["choices"] = choices
/**
* Boolean modal - has yes/no buttons that do different actions depending on which is pressed
*/
/datum/tgui_modal/boolean
modal_type = "boolean"
var/delegate_no
var/yes_text
var/no_text
/datum/tgui_modal/boolean/New(id, text, delegate, delegate_no, list/arguments, yes_text, no_text)
..(id, text, delegate, arguments)
src.delegate_no = delegate_no
src.yes_text = yes_text
src.no_text = no_text
/datum/tgui_modal/boolean/preprocess_answer(answer)
return text2num(answer) || FALSE
/datum/tgui_modal/boolean/on_answer(answer)
if(answer)
return ..(answer, arguments)
else if(delegate_no)
return call(owning_source, delegate_no)(arguments)
return FALSE
/datum/tgui_modal/boolean/to_data()
. = ..()
.["yes_text"] = yes_text
.["no_text"] = no_text
Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 51 KiB

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 23 KiB

-231
View File
@@ -1,231 +0,0 @@
<!--
Title: Body Scanner UI
Used In File(s): \code\game\machinery\adv_med.dm
-->
{{if !data.occupied}}
<h3>No occupant detected.</h3>
{{else}}
<h4><b>Occupant Data:</b></h4>
<div class="item">
<div class="itemLabelNarrow">
Name:
</div>
<div class="itemContent">
{{:data.occupant.name}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Health:
</div>
{{:helper.displayBar(data.occupant.health, -100, 100, (data.occupant.health >= 50) ? 'good' : (data.occupant.health >= 0) ? 'average' : 'bad')}}
<div class="itemContent" style="width: 60px">
{{:helper.smoothRound(data.occupant.health)}}%
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Status:
</div>
<div class="itemContent">
{{if data.occupant.stat==0}}
<span class="good"> Alive </span>
{{else data.occupant.stat==1}}
<span class="average"> Critical </span>
{{else}}
<span class="bad"> Dead </span>
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Temperature:
</div>
<div class="itemContent">
{{:helper.smoothRound(data.occupant.bodyTempC)}}&deg;C, {{:helper.smoothRound(data.occupant.bodyTempF)}}&deg;F
</div>
</div>
{{if data.occupant.hasBorer}}
<span class="bad">
Large growth detected in frontal lobe, possibly cancerous. Surgical removal is recommended. </span> <br>
{{/if}}
{{if data.occupant.blind}}
<span class="average">Cataracts detected.</span><br>
{{/if}}
{{if data.occupant.colourblind}}
<span class="average">Photoreceptor abnormalities detected.</span><br>
{{/if}}
{{if data.occupant.nearsighted}}
<span class="average">Retinal misalignment detected.</span> <br>
{{/if}}
<br>
{{:helper.link('Print', 'print', {'print_p' : 1, 'name' : data.occupant.name})}}
{{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectify' : 1}, data.occupied ? null : 'disabled')}}
<br> <br>
<b>Damage: </b>
<table> <tbody>
<tr>
<td> Brute Damage: </td>
<td> Brain Damage: </td>
</tr>
<tr>
<td> {{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth+100, (data.occupant.bruteLoss < 15) ? 'notgood' : (data.occupant.bruteLoss < 40) ? 'average' : 'bad', data.occupant.bruteLoss) }} </td>
<td> {{:helper.displayBar(data.occupant.brainLoss, 0, data.occupant.maxHealth+100, (data.occupant.brainLoss < 15) ? 'notgood' : (data.occupant.brainLoss < 40) ? 'average' : 'bad', data.occupant.brainLoss)}} </td>
</tr>
<tr>
<td> Resp. Damage: </td>
<td> Radiation Level: </td>
</tr>
<tr>
<td> {{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth+100, (data.occupant.oxyLoss < 15) ? 'notgood' : (data.occupant.oxyLoss < 40) ? 'average' : 'bad', data.occupant.oxyLoss)}} </td>
<td> {{:helper.displayBar(data.occupant.radLoss, 0, data.occupant.maxHealth+100, (data.occupant.radLoss < 15) ? 'notgood' : (data.occupant.radLoss < 40) ? 'average' : 'bad', data.occupant.radLoss)}} </td>
</tr>
<tr>
<td> Toxin Damage: </td>
<td> Genetic Damage: </td>
</tr>
<tr>
<td> {{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth+100, (data.occupant.toxLoss < 15) ? 'notgood' : (data.occupant.toxLoss < 40) ? 'average' : 'bad', data.occupant.toxLoss)}} </td>
<td> {{:helper.displayBar(data.occupant.cloneLoss, 0, data.occupant.maxHealth+100, (data.occupant.cloneLoss < 15) ? 'notgood' : (data.occupant.cloneLoss < 40) ? 'average' : 'bad', data.occupant.cloneLoss)}} </td>
</tr>
<tr>
<td> Burn Severity: </td>
<td> Paralysis %: ({{:helper.smoothRound(data.occupant.paralysisSeconds)}} seconds left) </td>
</tr>
<tr>
<td> {{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth+100, (data.occupant.fireLoss < 15) ? 'notgood' : (data.occupant.fireLoss < 40) ? 'average' : 'bad', data.occupant.fireLoss)}} </td>
<td> {{:helper.displayBar(data.occupant.paralysis, 0, 100, (data.occupant.paralysis < 25) ? 'notgood' : (data.occupant.paralysis < 50) ? 'average' : 'bad', data.occupant.paralysis)}} </td>
</tr>
</tbody>
</table>
{{if data.occupant.blood.hasBlood}}
<br>
<b>Blood:</b>
<div class="line">
<div class="statusLabel">Pulse:</div> <div class="statusValue">{{:data.occupant.blood.pulse}} bpm</div>
</div>
<div class="line">
<div class="statusLabel">Blood Level:</div>
{{:helper.displayBar(data.occupant.blood.bloodLevel, 0, data.occupant.blood.bloodMax, (data.occupant.blood.percent <= 60) ? 'bad' : (data.occupant.blood.percent <= 90) ? 'average' : 'good' )}}
<div class="statusValue"> {{:helper.smoothRound(data.occupant.blood.percent)}}%, {{:helper.smoothRound(data.occupant.blood.bloodLevel)}}cl </div>
</div>
{{/if}}
<br>
{{if data.occupant.hasVirus}}
<span class="bad">
Viral pathogen detected in blood stream.
</span> <br>
{{/if}}
{{if data.occupant.implant_len}}
<br>
<b>Implants</b>
<table style="width: 30%">
<tbody class="striped">
{{for data.occupant.implant}}
<tr><td>{{:value.name}}</td></tr>
{{/for}}
</tbody>
</table>
<br>
{{/if}}
<b>External Organs</b>
<table> <thead>
<tr>
<th style="width: 25%"> Name </th>
<th style='width: 15%'> Total damage </th>
<th style="width: 15%"> Brute damage </th>
<th style="width: 15%"> Burn damage </th>
<th style="width: 30%"> Injuries </th>
</tr>
</thead>
<tbody class="striped">
{{for data.occupant.extOrgan}}
<tr>
<td> {{:value.name}} </td>
<td> {{:helper.displayBar(value.totalLoss, 0, value.maxHealth, (value.totalLoss < value.bruised) ? 'notgood' : (value.totalLoss < value.broken) ? 'average' : 'bad', value.totalLoss) }} </td>
<td> {{if value.bruteLoss> 0}} <span class='average'> {{else}} <span> {{/if}} {{:helper.smoothRound(value.bruteLoss)}} </span> </td>
<td> {{if value.fireLoss> 0}} <span class='average'> {{else}} <span> {{/if}} {{:helper.smoothRound(value.fireLoss)}} </span> </td>
<td>
<span class='average'>
{{if value.internalBleeding}} internal bleeding <br> {{/if}}
{{if value.lungRuptured}} lung ruptured <br> {{/if}}
{{if value.status.broken}} {{:value.status.broken}} <br> {{/if}}
{{if value.germ_level > 100}}
{{if value.germ_level < 300}}
mild infection <br>
{{else value.germ_level < 400}}
mild infection+ <br>
{{else value.germ_level < 500}}
mild infection++ <br>
{{else value.germ_level < 700}}
acute infection <br>
{{else value.germ_level < 800}}
acute infection+ <br>
{{else value.germ_level < 900}}
acute infection++ <br>
{{else value.germ_level >= 900}}
septic <br>
{{/if}}
{{/if}}
{{if value.open}} open incision <br> {{/if}}
</span>
{{if value.status.splinted}} splinted <br> {{/if}}
{{if value.status.robotic}} robotic <br> {{/if}}
{{if value.status.dead}} <span class='bad'>DEAD</span> <br {{/if}}
{{if value.shrapnel_len}}
{{for value.shrapnel : shrapValue:shrapindex}}
{{:shrapValue.known ? shrapValue.name : "unknown object"}} <br>
{{/for}}
{{/if}}
</td>
</tr>
{{/for}}
</tbody>
</table>
<br>
<b>Internal Organs</b>
<table> <thead>
<tr>
<th width=25%> Name </th>
<th width=35%> Brute damage </th>
<th widht=40%> Injuries </th>
</tr>
</thead>
<tbody class="striped">
{{for data.occupant.intOrgan}}
<tr>
<td> {{:value.name}} <br> </td>
<td> {{:helper.displayBar(value.damage, 0, value.maxHealth, (value.damage < value.bruised) ? 'notgood' : (value.damage < value.broken) ? 'average' : 'bad', value.damage) }} </td>
<td>
<span class="average">
{{if value.germ_level > 100}}
{{if value.germ_level < 300}}
mild infection <br>
{{else value.germ_level < 400}}
mild infection+ <br>
{{else value.germ_level < 500}}
mild infection++ <br>
{{else value.germ_level < 700}}
acute Infection <br>
{{else value.germ_level < 800}}
acute Infection+ <br>
{{else value.germ_level < 900}}
acute Infection++ <br>
{{else value.germ_level >= 900}}
septic <br>
{{/if}}
{{/if}}
</span>
{{if value.robotic == 1}} robotic <br> {{/if}}
{{if value.robotic == 2}} assisted <br> {{/if}}
{{if value.dead}} <span class='bad'>DEAD</span> <br> {{/if}}
</td>
</tr>
{{/for}}
</tbody>
</table>
{{/if}}
-197
View File
@@ -1,197 +0,0 @@
<div class='notice'>
{{if data.siliconUser}}
<div class="itemContentSmall">
Interface Lock:
</div>
<div class="itemContentFull">
{{:helper.link('Engaged', 'lock', {'toggleaccess' : 1}, data.siliconLock ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'toggleaccess' : 1}, data.malfStatus >= 2 ? 'linkOff' : (data.siliconLock ? null : 'selected'))}}
</div>
<div class="clearBoth"></div>
{{else}}
{{if data.locked}}
Swipe an ID card to unlock this interface
{{else}}
Swipe an ID card to lock this interface
{{/if}}
{{/if}}
</div>
<div style="min-width: 480px">
<h3>Power Status</h3>
<div class="item">
<div class="itemLabel">
Main Breaker:
</div>
<div class="itemContent">
{{if data.locked && !data.siliconUser}}
{{if data.isOperating}}
<span class='good'>On</span>
{{else}}
<span class='bad'>Off</span>
{{/if}}
{{else}}
{{:helper.link('On', 'power-off', {'breaker' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'breaker' : 1}, data.isOperating ? null : 'selected')}}
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabel">
External Power:
</div>
<div class="itemContent">
{{if data.externalPower == 2}}
<span class='good'>Good</span>
{{else data.externalPower == 1}}
<span class='average'>Low</span>
{{else}}
<span class='bad'>None</span>
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Power Cell:
</div>
{{if data.powerCellStatus == null}}
<div class="itemContent bad">
Power cell removed.
</div>
{{else}}
{{:helper.displayBar(data.powerCellStatus, 0, 100, (data.powerCellStatus >= 50) ? 'good' : (data.powerCellStatus >= 25) ? 'average' : 'bad')}}
<div class="itemContent" style="width: 60px">
{{:helper.smoothRound(data.powerCellStatus, 1)}}%
</div>
{{/if}}
</div>
{{if data.powerCellStatus != null}}
<div class="item">
<div class="itemLabel">
Charge Mode:
</div>
<div class="itemContent">
{{if data.locked && !data.siliconUser}}
{{if data.chargeMode}}
<span class='good'>Auto</span>
{{else}}
<span class='bad'>Off</span>
{{/if}}
{{else}}
{{:helper.link('Auto', 'refresh', {'cmode' : 1}, data.chargeMode ? 'selected' : null)}}{{:helper.link('Off', 'close', {'cmode' : 1}, data.chargeMode ? null : 'selected')}}
{{/if}}
&nbsp;
{{if data.chargingStatus > 1}}
[<span class='good'>Fully Charged</span>]
{{else data.chargingStatus == 1}}
[<span class='average'>Charging</span>]
{{else}}
[<span class='bad'>Not Charging</span>]
{{/if}}
</div>
</div>
{{/if}}
<h3>Power Channels</h3>
{{for data.powerChannels}}
<div class="item">
<div class="itemLabel">
{{:value.title}}:
</div>
<div class="itemContent" style="width: 70px; text-align: right">
{{:helper.smoothRound(value.powerLoad)}} W
</div>
<div class="itemContent" style="width: 105px">
&nbsp;&nbsp;
{{if value.status <= 1}}
<span class='bad'>Off</span>
{{else value.status >= 2}}
<span class='good'>On</span>
{{/if}}
{{if data.locked}}
{{if value.status == 1 || value.status == 3}}
&nbsp;&nbsp;Auto
{{else}}
&nbsp;&nbsp;Manual
{{/if}}
{{/if}}
</div>
{{if !data.locked || data.siliconUser}}
<div class="itemContentFull">
{{:helper.link('Auto', 'refresh', value.topicParams.auto, (value.status == 1 || value.status == 3) ? 'selected' : null)}}
{{:helper.link('On', 'power-off', value.topicParams.on, (value.status == 2) ? 'selected' : null)}}
{{:helper.link('Off', 'close', value.topicParams.off, (value.status == 0) ? 'selected' : null)}}
</div>
{{/if}}
</div>
{{/for}}
<div class="item" style="font-weight: bold">
<div class="itemLabel">
Total Load:
</div>
<div class="itemContent" style="width: 70px; text-align: right">
{{:helper.smoothRound(data.totalLoad)}} W
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel">
Cover Lock:
</div>
<div class="itemContent">
{{if data.locked && !data.siliconUser}}
{{if data.coverLocked}}
<span class='good'>Engaged</span>
{{else}}
<span class='bad'>Disengaged</span>
{{/if}}
{{else}}
{{:helper.link('Engaged', 'lock', {'lock' : 1}, data.coverLocked ? 'selected' : null)}}{{:helper.link('Disengaged', 'unlock', {'lock' : 1}, data.coverLocked ? null : 'selected')}}
{{/if}}
</div>
</div>
{{if data.siliconUser}}
<h3>System Overrides</h3>
<div class="item">
{{:helper.link('Overload Lighting Circuit', 'lightbulb-o', {'overload' : 1})}}
{{if data.malfStatus == 1}}
{{:helper.link('Override Programming', 'file-text', {'malfhack' : 1})}}
{{else data.malfStatus == 2}}
{{:helper.link('Shunt Core Processes', 'arrow-down', {'occupyapc' : 1})}}
{{else data.malfStatus == 3}}
{{:helper.link('Return to Main Core', 'arrow-left', {'deoccupyapc' : 1})}}
{{else data.malfStatus == 4}}
{{:helper.link('Shunt Core Processes', 'arrow-down', {'occupyapc' : 1}, 'linkOff')}}
{{/if}}
</div>
{{/if}}
<div class="item">
<div class="itemLabel">
Night Shift Lighting:
</div>
<div class="itemContent">
{{if data.locked}}
{{if data.nightshiftLights}}
<span class='good'>On</span>
{{else}}
<span class='bad'>Off</span>
{{/if}}
{{else}}
{{:helper.link('Enabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? 'selected' : null)}}{{:helper.link('Disabled', 'lightbulb-o', {'toggle_nightshift' : 1}, data.nightshiftLights ? null : 'selected')}}
{{/if}}
</div>
</div>
</div>
-158
View File
@@ -1,158 +0,0 @@
{{if data.menu == 0}}
<h3>Tank Status</h3>
<div class="item">
<div class="itemLabel">
Tank Label:
</div>
<div class="itemContent">
<div style="float: left; width: 180px;">{{:data.name}}</div> {{:helper.link('Relabel', 'pencil', {'choice' : 'menu', 'mode_target' : 1}, data.canLabel ? null : 'disabled')}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Tank Pressure:
</div>
<div class="itemContent">
{{:helper.smoothRound(data.tankPressure)}} kPa
</div>
</div>
<div class="item">
<div class="itemLabel">
Port Status:
</div>
<div class="itemContent">
{{:data.portConnected ? '<span class="good">Connected</span>' : '<span class="average">Disconnected</span>'}}
</div>
</div>
<h3>Holding Tank Status</h3>
{{if data.hasHoldingTank}}
<div class="item">
<div class="itemLabel">
Tank Label:
</div>
<div class="itemContent">
<div style="float: left; width: 180px;">{{:data.holdingTank.name}}</div> {{:helper.link('Eject', 'eject', {'remove_tank' : 1})}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Tank Pressure:
</div>
<div class="itemContent">
{{:helper.smoothRound(data.holdingTank.tankPressure)}} kPa
</div>
</div>
{{else}}
<div class="item"><span class="average"><i>No holding tank inserted.</i></span></div>
<div class="item">&nbsp;</div>
{{/if}}
<h3>Release Valve Status</h3>
<div class="item">
<div class="itemLabel">
Release Pressure:
</div>
<div class="itemContent">
{{:helper.displayBar(data.releasePressure, data.minReleasePressure, data.maxReleasePressure)}}
<div style="clear: both; padding-top: 4px;">
{{:helper.link('-', null, {'pressure_adj' : -1000}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}}
{{:helper.link('-', null, {'pressure_adj' : -100}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}}
{{:helper.link('-', null, {'pressure_adj' : -10}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}}
{{:helper.link('-', null, {'pressure_adj' : -1}, (data.releasePressure > data.minReleasePressure) ? null : 'disabled')}}
<div style="float: left; width: 80px; text-align: center;">&nbsp;{{:data.releasePressure}} kPa&nbsp;</div>
{{:helper.link('+', null, {'pressure_adj' : 1}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}}
{{:helper.link('+', null, {'pressure_adj' : 10}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}}
{{:helper.link('+', null, {'pressure_adj' : 100}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}}
{{:helper.link('+', null, {'pressure_adj' : 1000}, (data.releasePressure < data.maxReleasePressure) ? null : 'disabled')}}
</div>
</div>
</div>
<div class="item">
<div class="itemLabel">
Release Valve:
</div>
<div class="itemContent">
{{:helper.link('Open', 'unlock', {'toggle' : 1}, data.valveOpen ? 'selected' : null)}}{{:helper.link('Close', 'lock', {'toggle' : 1}, data.valveOpen ? null : 'selected')}}
</div>
</div>
{{else}}
{{:helper.link('Go back', 'home', {'choice' : 'menu', 'mode_target' : 0}, null)}}
<h3>Relabel</h3>
<div class="itemLabel">
Name:
</div>
<div class="itemContent">
<div style="float: left; width: 180px;">{{:data.name}}</div> {{:helper.link('Rename', 'pencil', {'rename' : 1}, data.canLabel ? null : 'disabled')}}
</div>
<h3>Colors</h3>
<div class="item" style='width: 100%'>
<div style='float: left; width: 175px; min-height: 210px'>
<div class='average'><b>{{:data.colorContainer.prim.name}}</b></div>
{{for data.colorContainer.prim.options}}
<div class='itemContentWide'>
{{if value.icon == data.canister_color.prim}}
{{:helper.link(value.name, '', {'choice' : data.colorContainer.prim.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}}
{{else}}
{{:helper.link(value.name, '', {'choice' : data.colorContainer.prim.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}}
{{/if}}
</div>
{{/for}}
</div>
<div style='float: left; width: 175px; min-height: 210px'>
<div class='average'><b>{{:data.colorContainer.sec.name}}</b></div>
{{:helper.link("None", '', {'choice' : data.colorContainer.sec.name, 'icon' : "none"}, data.canLabel ? null : 'disabled', data.colorContainer.sec.anycolor ? null : 'selected')}}
{{for data.colorContainer.sec.options}}
<div class='itemContentWide'>
{{if value.icon == data.canister_color.sec}}
{{:helper.link(value.name, '', {'choice' : data.colorContainer.sec.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}}
{{else}}
{{:helper.link(value.name, '', {'choice' : data.colorContainer.sec.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}}
{{/if}}
</div>
{{/for}}
</div>
<div style='float: left; width: 175px; min-height: 210px'>
<div class='average'><b>{{:data.colorContainer.ter.name}}</b></div>
{{:helper.link("None", '', {'choice' : data.colorContainer.ter.name, 'icon' : "none"}, data.canLabel ? null : 'disabled', data.colorContainer.ter.anycolor ? null : 'selected')}}
{{for data.colorContainer.ter.options}}
<div class='itemContentWide'>
{{if value.icon == data.canister_color.ter}}
{{:helper.link(value.name, '', {'choice' : data.colorContainer.ter.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}}
{{else}}
{{:helper.link(value.name, '', {'choice' : data.colorContainer.ter.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}}
{{/if}}
</div>
{{/for}}
</div>
<div style='float: left; width: 175px; min-height: 210px'>
<div class='average'><b>{{:data.colorContainer.quart.name}}</b></div>
{{:helper.link("None", '', {'choice' : data.colorContainer.quart.name, 'icon' : "none"}, data.canLabel ? null : 'disabled', data.colorContainer.quart.anycolor ? null : 'selected')}}
{{for data.colorContainer.quart.options}}
<div class='itemContentWide'>
{{if value.icon == data.canister_color.quart}}
{{:helper.link(value.name, '', {'choice' : data.colorContainer.quart.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', 'selected')}}
{{else}}
{{:helper.link(value.name, '', {'choice' : data.colorContainer.quart.name, 'icon' : value.icon}, data.canLabel ? null : 'disabled', null)}}
{{/if}}
</div>
{{/for}}
</div>
</div>
<h3>Decals</h3>
<div style='float: left; width: 175px; min-height: 110px'>
<div class='average'><b>Pick and choose:</b></div>
<div class='itemContentWide'>
{{for data.possibleDecals}}
{{:helper.link(value.name, '', {'choice' : 'decals', 'icon' : value.icon}, data.canLabel ? null : 'disabled', value.active ? 'selected' : null)}}
{{/for}}
</div>
</div>
{{/if}}
-93
View File
@@ -1,93 +0,0 @@
<!--
Title: Chem Dispenser 5000 UI
Used In File(s): \code\modules\reagents\Chemistry-Machinery.dm
-->
<div class="item">
<div class="itemLabel">
Energy:
</div>
<div class="itemContent">
{{:helper.displayBar(data.energy, 0, data.maxEnergy, 'good', data.energy + ' Units')}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Dispense:
</div>
<div class="itemContent">
{{:helper.link('1', 'gear', {'amount' : 1}, (data.amount == 1) ? 'selected' : null)}}
{{:helper.link('5', 'gear', {'amount' : 5}, (data.amount == 5) ? 'selected' : null)}}
{{:helper.link('10', 'gear', {'amount' : 10}, (data.amount == 10) ? 'selected' : null)}}
{{:helper.link('20', 'gear', {'amount' : 20}, (data.amount == 20) ? 'selected' : null)}}
{{:helper.link('30', 'gear', {'amount' : 30}, (data.amount == 30) ? 'selected' : null)}}
{{:helper.link('50', 'gear', {'amount' : 50}, (data.amount == 50) ? 'selected' : null)}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel" style="width: 100%;">
{{if data.glass}}
Drink Dispenser
{{else}}
Chemical Dispenser
{{/if}}
</div>
</div>
<div class="item">
<div class="itemContentWide" style="width: 100%;">
{{for data.chemicals}}
{{:helper.link(value.title, 'arrow-circle-down', value.commands, null, data.glass ? 'fixedLeftWide' : 'fixedLeft')}}
{{/for}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel">
{{if data.glass}}
Glass
{{else}}
Beaker
{{/if}} Contents
</div>
<div class="itemContent">
{{:helper.link(data.glass ? 'Eject Glass' : 'Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}}
</div>
</div>
<div class="statusDisplay" style="min-height: 180px;">
<div class="item">
<div class="itemContent" style="width: 100%;">
{{if data.isBeakerLoaded}}
{{if data.beakerCurrentVolume}}
<span class="floatRight" style="width: 115px;"><b>Dispose:</b></span>
{{/if}}
<b>Volume:&nbsp;{{:data.beakerCurrentVolume}}&nbsp;/&nbsp;{{:data.beakerMaxVolume}}</b><br>
{{for data.beakerContents}}
<div style="min-height: 22px;">
<div class="floatLeft">
<span class="highlight">
{{:value.volume}} units of {{:value.name}}
</span>
</div>
<div class="floatRight">
{{:helper.link('Isolate', null, {'remove' : value.id, 'removeamount' : -1})}}
{{:helper.link('1', null, {'remove' : value.id, 'removeamount' : 1})}}
{{:helper.link('5', null, {'remove' : value.id, 'removeamount' : 5})}}
{{:helper.link('10', null, {'remove' : value.id, 'removeamount' : 10})}}
{{:helper.link('ALL', null, {'remove' : value.id, 'removeamount' : value.volume})}}
</div>
</div>
<div style="height:2px"></div> <!-- This is necessary to prevent the link margins from hitting each other and causing weird things to happen. It's essentialy a smaller <br> tag. -->
{{empty}}
<span class="bad">{{if data.glass}}Glass{{else}}Beaker{{/if}} is empty
</span>
{{/for}}
{{else}}
<span class="average"><i>
No {{if data.glass}}Glass{{else}}Beaker{{/if}} loaded
</i></span>
{{/if}}
</div>
</div>
</div>
-47
View File
@@ -1,47 +0,0 @@
<!--
Title: ChemHeater UI
Used In File(s): \code\modules\reagents\newchem\chem_heater.dm
-->
<div class="item">
<div class="itemLabel">
Status:
</div>
<div class="itemContent">
{{:helper.link(data.isActive ? 'On' : 'Off', 'power-off', {'toggle_on' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Target:
</div>
<div class="itemContent">
{{:helper.link(data.targetTemp + 'K', 'gear', {'adjust_temperature' : 'input'}, null)}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Beaker
</div>
<div class="itemContent">
{{:helper.link('Eject', 'eject', {'eject_beaker' : 1}, data.isBeakerLoaded ? null : 'disabled', 'floatRight')}}
</div>
</div>
<div class="statusDisplay">
<div style="height: 110px; overflow: auto;">
<div class="item">
<div class="itemContent">
<div style="width: 100%;">
{{if data.isBeakerLoaded}}
<b>Volume:&nbsp;{{:helper.smoothRound(data.beakerCurrentVolume)}}&nbsp;/&nbsp;{{:data.beakerMaxVolume}}</b><br>
<b>Temperature:&nbsp;{{:helper.smoothRound(data.currentTemp)}}&nbsp;Kelvin</b><br>
{{for data.beakerContents}}
<span class="highlight">{{:value.volume}} units of {{:value.name}}</span><br>
{{/for}}
{{else}}
<span class="average"><i>No beaker loaded</i></span>
{{/if}}
</div>
</div>
</div>
</div>
</div>
-106
View File
@@ -1,106 +0,0 @@
{{if data.beaker}}
<div class='item'>{{:helper.link('Eject Beaker and Clear Buffer', 'eject', {'eject': 1})}}</div>
<div class='statusDisplay'>
<h3>Add to buffer:</h3>
{{for data.beaker_reagents}}
<div class='line'>
<div class='itemLabel'>{{:value.name}}, {{:value.volume}} units</div>
{{:helper.link('Analyze', 'search', {'analyze': 1, 'desc': value.description, 'name': value.name})}}
{{:helper.link('1', 'plus', {'add': value.id, 'amount': 1})}}
{{:helper.link('5', 'plus', {'add': value.id, 'amount': 5})}}
{{:helper.link('10', 'plus', {'add': value.id, 'amount': 10})}}
{{:helper.link('All', 'plus', {'add': value.id, 'amount': value.volume})}}
{{:helper.link('Custom', 'plus', {'addcustom': value.id})}}
</div>
{{/for}}
</div>
<div class='statusDisplay'>
<h3>
<div class='item'>
Transfer to {{:helper.link(data.mode ? 'beaker' : 'disposal', 'arrows-h', {'toggle': 1})}}
</div>
</h3>
{{for data.buffer_reagents}}
<div class='line'>
<div class='itemLabel'>{{:value.name}}, {{:value.volume}} units</div>
{{:helper.link('Analyze', 'search', {'analyze': 1, 'desc': value.description, 'name': value.name})}}
{{:helper.link('1', 'plus', {'remove': value.id, 'amount': 1})}}
{{:helper.link('5', 'plus', {'remove': value.id, 'amount': 5})}}
{{:helper.link('10', 'plus', {'remove': value.id, 'amount': 10})}}
{{:helper.link('All', 'plus', {'remove': value.id, 'amount': value.volume})}}
{{:helper.link('Custom', 'plus', {'removecustom': value.id})}}
</div>
{{/for}}
</div>
<div class='statusDisplay'>
<h3>Containers:</h3>
<div class='item'>
{{if data.condi}}
{{:helper.link('Create pack (10u max)', 'arrow-right', {'createpill': 1})}}
{{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}}
{{else}}
<table>
<tr>
<td>
{{:helper.link('Create pill (100u max)', 'arrow-right', {'createpill': 1})}}
</td>
<td>
{{:helper.link('Multiple', 'arrow-right', {'createpill_multiple': 1})}}
</td>
<td>
{{:helper.link('Style', 'pencil', {'change_pill': 1})}}
</td>
<td>
<img src='pill{{:data.pillsprite}}.png'>
</td>
</tr>
<tr>
<td>
{{:helper.link('Create patch (30u max)', 'arrow-right', {'createpatch': 1})}}
</td>
<td>
{{:helper.link('Multiple', 'arrow-right', {'createpatch_multiple': 1})}}
</td>
</tr>
<tr>
<td>
{{:helper.link('Create bottle (50u max)', 'arrow-right', {'createbottle': 1})}}
</td>
<td>
{{:helper.link('Style', 'pencil', {'change_bottle': 1})}}
</td>
<td>
<img src='{{:data.bottlesprite}}.png'>
</td>
</tr>
</table>
{{/if}}
</div>
</div>
{{else}}
<span class='item'>No beaker loaded</span><br>
{{/if}}
<div class='statusDisplay'>
<span class='item'>
{{if data.loaded_pill_bottle}}
<table>
<tr>
<td>
{{:helper.link('Pill Bottle (' + data.loaded_pill_bottle_contents_len + '/' + data.loaded_pill_bottle_storage_slots + ')', 'eject', {'ejectp': 1})}}
</td>
<td>
{{:helper.link('Change Wrapper', 'pencil', {'change_pillbottle': 1})}}
</td>
</tr>
</table>
{{else}}
<table>
<tr>
<td>
{{:helper.link('No Pill Bottle', 'eject', {}, 'disabled')}}
</td>
</tr>
</table>
{{/if}}
</span>
</div>
-148
View File
@@ -1,148 +0,0 @@
<!--
Title: Cloning Console UI
Used In File(s): \code\game\machinery\computer\cloning.dm
-->
<div class="block">
<div class="item">
<div class="itemLabelNarrow">Menu</div>
<div class="itemContentWide">
{{:helper.link('Main', 'home', {'menu' : 1}, data.menu == 1 ? 'selected' : '')}}{{:helper.link('Records', 'folder-open', {'menu' : 2}, data.menu == 2 ? 'selected' : '')}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Refresh</div>
<div class="itemContentWide">
{{:helper.link('Refresh', 'refresh', {'refresh' : 1})}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Autoprocessing</div>
<div class="itemContentWide">
{{if data.autoallowed}}
{{:helper.link('Enabled', 'check', {'task' : 'autoprocess'}, data.autoprocess ? 'selected' : '')}}{{:helper.link('Disabled', 'close', {'task' : 'stopautoprocess'}, !data.autoprocess ? 'selected' : '')}}
{{else}}
{{:helper.link('Enabled', 'check', null, 'disabled')}}{{:helper.link('Disabled', 'close', null, 'selected')}}
{{/if}}
</div>
</div>
{{if data.disk}}
<div class="item">
<div class="itemLabelNarrow">Disk</div>
<div class="itemContentWide">{{:helper.link('Eject', 'eject', {'disk' : 'eject'})}}</div>
</div>
{{/if}}
</div>
{{if data.menu == 1}}
<h2>Modules</h2>
<div class="item">
<div class="itemLabelNarrow">Scanner</div>
<div class="itemContentWide{{if data.scanner}} good{{else}} bad{{/if}}">
{{if data.scanner}} Found!{{else}} Error: Not detected!{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Pods</div>
<div class="itemContentWide{{if data.numberofpods}} good{{else}} bad{{/if}}">
{{if data.numberofpods}} Found {{:data.numberofpods}}!{{else}} Error: None detected!{{/if}}
</div>
</div>
{{if data.scanner}}
<h2>Scanner Functions</h2>
{{if data.loading}}
<b>Scanning...</b>
{{else}}
<b>{{:data.scantemp}}</b>
{{/if}}
<div class="item">
<div class="itemLabelNarrow">Scan Occupant</div>
<div class="itemContentWide">{{:helper.link('Scan', 'search', {'scan' : 1}, !data.occupant ? 'disabled' : '')}}</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Scanner Lock</div>
<div class="itemContentWide">{{if data.occupant}}{{:helper.link('Engaged', 'lock', {'lock' : 1}, data.locked ? 'selected' : '')}}{{else}} {{:helper.link('Engaged', 'lock', null, 'disabled')}}{{/if}}{{:helper.link('Disengaged', 'unlock', {'lock' : 1}, !data.locked ? 'selected' : '')}}</div>
</div>
{{if data.can_brainscan}}
<div class="item">
<div class="itemLabelNarrow">Scan Mode</div>
<div class="itemContentWide">
{{:helper.link('Brain', null, {'toggle_mode' : 1}, !data.scan_mode ? '' : 'selected')}}{{:helper.link('Body', null, {'toggle_mode' : 1}, data.scan_mode ? '' : 'selected')}}
</div>
</div>
{{/if}}
{{/if}}
{{if data.numberofpods}}
<h2>Pods</h2>
{{for data.pods}}
<b>{{:value.name}}</b></br>
<div class="item">
<div class="itemLabelNarrow">Biomass</div>
<div class="itemContentWide{{if value.biomass > 0}} good{{else}} bad{{/if}}">
{{:value.biomass}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">Select</div>
<div class="itemContentWide">
{{:helper.link('Select', 'check', {'selectpod' : value.pod}, value.pod == data.selected_pod ? 'selected' : '')}}
</div>
</div>
{{/for}}
{{/if}}
{{/if}}
{{if data.menu == 2}}
<h2>Current Records</h2>
{{if data.temp}}
<div class="item">
{{:data.temp}}
</div>
{{/if}}
{{for data.records}}
<div>{{:helper.link(value.realname, 'arrow-circle-down', {'view_rec' : value.record})}}</div>
{{empty}}
No records found!
{{/for}}
{{/if}}
{{if data.menu == 3}}
<h2>Selected Record</h2>
{{if data.temp}}
<div class="item">
{{:data.temp}}
</div>
{{/if}}
{{if !data.activerecord}}
<div class="bad">Error: Record not found!</div>
{{else}}
<div class="item">
<b>Real Name:</b> {{:data.realname}}<br />
<b>Health:</b> {{:data.health}} (<span class="oxyloss_light">OXY</span> - <span class="burn">BURN</span> - <span class="toxin_light">TOX</span> - <span class="brute">BRUTE</span>)
</div>
<div class="item">
<b>UI:</b> {{:data.unidentity}}<br />
<b>SE:</b> <div style="word-wrap: break-word;">{{:data.strucenzymes}}</div>
</div>
{{if data.disk}}
<div class="item">
<div class="itemLabelNarrow">Disk</div>
<div class="itemContentWide">{{:helper.link('Load from Disk', 'floppy-o', {'disk' : 'load'})}}{{:helper.link('Save UI + UE', 'arrow-circle-up', {'save_disk' : 'ue'})}}{{:helper.link('Save UI', 'arrow-circle-up', {'save_disk' : 'ui'})}}{{:helper.link('Save SE', 'arrow-circle-up', {'save_disk' : 'se'})}}</div>
</div>
{{/if}}
<div class="item">
<div class="itemLabelNarrow">Actions</div>
<div class="itemContentWide">{{:helper.link('Clone', 'arrow-right', {'clone' : data.activerecord}, !data.podready ? 'disabled' : '')}}{{:helper.link('Delete', 'trash', {'del_rec' : 1})}}</div>
</div>
{{/if}}
{{/if}}
{{if data.menu == 4}}
<h2>Confirm Record Deletion</h2>
{{if data.temp}}
<div class="item">
{{:data.temp}}
</div>
{{/if}}
<div class="item">
{{:helper.link('Scan Card and Confirm', 'trash', {'del_rec' : 1})}}{{:helper.link('Cancel', 'close', {'menu' : 3})}}
</div>
{{/if}}
-117
View File
@@ -1,117 +0,0 @@
<!--
Title: Cryo Cell Status UI
Used In File(s): \code\game\machinery\cryo.dm
-->
<h3>Cryo Cell Status</h3>
<div class="statusDisplay">
{{if !data.hasOccupant}}
<div class="line">Cell Unoccupied</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;<i class="fa fa-arrow-right"></i>&nbsp;
{{if data.occupant.stat == 0}}
<span class="good">Conscious</span>
{{else data.occupant.stat == 1}}
<span class="average">Unconscious</span>
{{else}}
<span class="bad">DEAD</span>
{{/if}}
</div>
{{if data.occupant.stat < 2}}
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel"><i class="fa fa-arrow-right"></i> Brute Damage:</div>
{{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.bruteLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel"><i class="fa fa-arrow-right"></i> Resp. Damage:</div>
{{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.oxyLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel"><i class="fa fa-arrow-right"></i> Toxin Damage:</div>
{{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.toxLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel"><i class="fa fa-arrow-right"></i> Burn Severity:</div>
{{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.fireLoss)}}</div>
</div>
{{/if}}
{{/if}}
<br>
<hr>
<div class="line">
<div class="statusLabel">Cell Temperature:</div><div class="statusValue">
<span class="{{:data.cellTemperatureStatus}}">{{:helper.smoothRound(data.cellTemperature)}} K</span>
</div>
</div>
</div>
<h3>Cryo Cell Operation</h3>
<div class="item">
<div class="itemLabel">
Cryo Cell Status:
</div>
<div class="itemContent" style="width: 40%;">
{{:helper.link('On', 'power-off', {'switchOn' : 1}, data.isOperating ? 'selected' : null)}}{{:helper.link('Off', 'close', {'switchOff' : 1}, data.isOperating ? null : 'selected')}}
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectOccupant' : 1}, data.hasOccupant ? null : 'disabled')}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel">
Beaker:
</div>
<div class="itemContent" style="width: 40%;">
{{if data.isBeakerLoaded}}
{{:data.beakerLabel ? data.beakerLabel : '<span class="average">No label</span>'}}<br>
{{if data.beakerVolume}}
<span class="highlight">{{:helper.smoothRound(data.beakerVolume)}} units remaining</span><br>
{{else}}
<span class="bad">Beaker is empty</span>
{{/if}}
{{else}}
<span class="average"><i>No beaker loaded</i></span>
{{/if}}
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel" style="width: 70%">
Auto-eject healthy patients:
</div>
<div class="itemContent" style="width: 30%;">
{{:helper.link('On', 'power-off', {'auto_eject_healthy_on' : 1}, data.auto_eject_healthy ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_healthy_off' : 0}, data.auto_eject_healthy ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="itemLabel" style="width: 70%">
Auto-eject dead patients:
</div>
<div class="itemContent" style="width: 30%;">
{{:helper.link('On', 'power-off', {'auto_eject_dead_on' : 1}, data.auto_eject_dead ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_dead_off' : 0}, data.auto_eject_dead ? null : 'selected')}}
</div>
</div>
-313
View File
@@ -1,313 +0,0 @@
<!--
Title: DNA Modifier UI
Used In File(s): D:\Development\SS13-BS12\code\game\dna\dna_modifier.dm
-->
<h3>Status</h3>
<div class="statusDisplay">
{{if !data.hasOccupant}}
<div class="line">Cell Unoccupied</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;=>&nbsp;
{{if data.occupant.stat == 0}}
<span class="good">Conscious</span>
{{else data.occupant.stat == 1}}
<span class="average">Unconscious</span>
{{else}}
<span class="bad">DEAD</span>
{{/if}}
</div>
{{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}}
<div class="notice">
The occupant's DNA structure is ruined beyond recognition, please insert a subject with an intact DNA structure.
</div>
{{else data.occupant.stat < 2}}
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel">Radiation:</div>
{{:helper.displayBar(data.occupant.radiationLevel, 0, 100, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.radiationLevel)}}</div>
</div>
<div class="line">
<div class="statusLabel">Unique Enzymes:</div>
<div class="statusValue">{{:data.occupant.uniqueEnzymes ? data.occupant.uniqueEnzymes : '<span class="bad">Unknown</span>'}}</div>
</div>
{{/if}}
{{/if}}
<div class="clearBoth"></div>
</div>
<h3>Operations</h3>
{{if !data.occupant.isViableSubject || !data.occupant.uniqueIdentity || !data.occupant.structuralEnzymes}}
<div class="notice">
No operation possible on this subject
</div>
{{else}}
<div class="item">
{{:helper.link('Modify U.I.', 'link', {'selectMenuKey' : 'ui'}, data.selectedMenuKey == 'ui' ? 'selected' : null)}}
{{:helper.link('Modify S.E.', 'link', {'selectMenuKey' : 'se'}, data.selectedMenuKey == 'se' ? 'selected' : null)}}
{{:helper.link('Transfer Buffers', 'floppy-o', {'selectMenuKey' : 'buffer'}, data.selectedMenuKey == 'buffer' ? 'selected' : null)}}
{{:helper.link('Rejuvenators', 'plus', {'selectMenuKey' : 'rejuvenators'}, data.selectedMenuKey == 'rejuvenators' ? 'selected' : null)}}
</div>
<div class="item">&nbsp;</div>
{{if !data.selectedMenuKey || data.selectedMenuKey == 'ui'}}
<h3>Modify Unique Identifier</h3>
{{:helper.displayDNABlocks(data.occupant.uniqueIdentity, data.selectedUIBlock, data.selectedUISubBlock, data.dnaBlockSize, 'UI')}}
<div class="clearBoth"></div>
<div class="item">
<div class="itemLabelNarrow">
Target:
</div>
<div class="itemContentWide">
{{:helper.link('-', null, {'changeUITarget' : 0}, (data.selectedUITarget > 0) ? null : 'disabled')}}
<div class="statusValue">&nbsp;{{:data.selectedUITargetHex}}&nbsp;</div>
{{:helper.link('+', null, {'changeUITarget' : 1}, (data.selectedUITarget < 15) ? null : 'disabled')}}
</div>
</div>
<div class="item">
<div class="itemContentWide">
{{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseUIRadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}}
</div>
</div>
{{else data.selectedMenuKey == 'se'}}
<h3>Modify Structural Enzymes</h3>
{{:helper.displayDNABlocks(data.occupant.structuralEnzymes, data.selectedSEBlock, data.selectedSESubBlock, data.dnaBlockSize, 'SE')}}
<div class="clearBoth"></div>
<div class="item">
<div class="itemContentWide">
{{:helper.link('Irradiate Block', 'exclamation-circle', {'pulseSERadiation' : 1}, !data.occupant.isViableSubject ? 'disabled' : null)}}
</div>
</div>
{{else data.selectedMenuKey == 'buffer'}}
<h3>Transfer Buffers</h3>
{{for data.buffers}}
<h4>Buffer {{:(index + 1)}}</h4>
<div class="itemGroup">
<div class="item">
<div class="itemLabelNarrow">
Load Data:
</div>
<div class="itemContentWide">
{{:helper.link('Subject U.I.', 'link', {'bufferOption' : 'saveUI', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}}
{{:helper.link('Subject U.I. + U.E.', 'link', {'bufferOption' : 'saveUIAndUE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}}
{{:helper.link('Subject S.E.', 'link', {'bufferOption' : 'saveSE', 'bufferId' : (index + 1)}, !data.hasOccupant ? 'disabled' : null)}}
{{:helper.link('From Disk', 'floppy-o', {'bufferOption' : 'loadDisk', 'bufferId' : (index + 1)}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}}
</div>
</div>
{{if value.data}}
<div class="item">
<div class="itemLabelNarrow">
Label:
</div>
<div class="itemContentWide">
{{:helper.link(value.label, 'file', {'bufferOption' : 'changeLabel', 'bufferId' : (index + 1)})}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Subject:
</div>
<div class="itemContentWide">
{{:value.owner ? value.owner : '<span class="average">Unknown</span>'}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Stored Data:
</div>
<div class="itemContentWide">
{{:value.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}}
{{:value.ue ? ' + Unique Enzymes' : ''}}
</div>
</div>
{{else}}
<div class="item">
<div class="itemContentWide">
<span class="highlight">This buffer is empty.</span>
</div>
</div>
{{/if}}
<div class="item">
<div class="itemLabelNarrow">
Options:
</div>
<div class="itemContentWide">
{{:helper.link('Clear', 'trash', {'bufferOption' : 'clear', 'bufferId' : (index + 1)}, !value.data ? 'disabled' : null)}}
{{:helper.link('Injector', data.isInjectorReady ? 'pencil' : 'clock-o', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1)}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}}
{{:helper.link('Block Injector', data.isInjectorReady ? 'pencil' : 'clock-o', {'bufferOption' : 'createInjector', 'bufferId' : (index + 1), 'createBlockInjector' : 1}, (!data.isInjectorReady || !value.data) ? 'disabled' : null)}}
{{:helper.link('Transfer', 'exclamation-circle', {'bufferOption' : 'transfer', 'bufferId' : (index + 1)}, (!data.hasOccupant || !value.data) ? 'disabled' : null)}}
{{:helper.link('Save To Disk', 'floppy-o', {'bufferOption' : 'saveDisk', 'bufferId' : (index + 1)}, (!value.data || !data.hasDisk) ? 'disabled' : null)}}
</div>
</div>
</div>
{{/for}}
<h4>Data Disk</h4>
<div class="itemGroup">
{{if data.hasDisk}}
{{if data.disk.data}}
<div class="item">
<div class="itemLabelNarrow">
Label:
</div>
<div class="itemContentWide">
{{:data.disk.label ? data.disk.label : 'No Label'}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Subject:
</div>
<div class="itemContentWide">
{{:data.disk.owner ? data.disk.owner : '<span class="average">Unknown</span>'}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Stored Data:
</div>
<div class="itemContentWide">
{{:data.disk.data == 'ui' ? 'Unique Identifiers' : 'Structural Enzymes'}}
{{:data.disk.ue ? ' + Unique Enzymes' : ''}}
</div>
</div>
{{else}}
<div class="item">
<div class="itemContentWide">
<span class="average">Disk is blank.</span>
</div>
</div>
{{/if}}
{{else}}
<div class="item">
<div class="itemContentWide">
<span class="highlight">No disk inserted.</span>
</div>
</div>
{{/if}}
<div class="item">
<div class="itemLabelNarrow">
Options:
</div>
<div class="itemContentWide">
{{:helper.link('Wipe Disk', 'trash', {'bufferOption' : 'wipeDisk'}, (!data.hasDisk || !data.disk.data) ? 'disabled' : null)}}
{{:helper.link('Eject Disk', 'eject', {'bufferOption' : 'ejectDisk'}, !data.hasDisk ? 'disabled' : null)}}
</div>
</div>
</div>
{{else data.selectedMenuKey == 'rejuvenators'}}
<h3>Rejuvenators</h3>
<div class="item">
<div class="itemLabelNarrow">
Inject:
</div>
<div class="itemContentWide">
{{:helper.link('5', 'pencil', {'injectRejuvenators' : 5}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
{{:helper.link('10', 'pencil', {'injectRejuvenators' : 10}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
{{:helper.link('20', 'pencil', {'injectRejuvenators' : 20}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
{{:helper.link('30', 'pencil', {'injectRejuvenators' : 30}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
{{:helper.link('50', 'pencil', {'injectRejuvenators' : 50}, (!data.hasOccupant || !data.beakerVolume) ? 'disabled' : null)}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabelNarrow">
Beaker:
</div>
<div class="itemContentWide" style="width: 40%;">
{{if data.isBeakerLoaded}}
{{:data.beakerLabel ? data.beakerLabel : '<span class="average">No label</span>'}}<br>
{{if data.beakerVolume}}
<span class="highlight">{{:data.beakerVolume}} units remaining</span><br>
{{else}}
<span class="bad">Beaker is empty</span>
{{/if}}
{{else}}
<span class="average"><i>No beaker loaded</i></span>
{{/if}}
</div>
<div class="itemContentWide" style="width: 26%;">
{{:helper.link('Eject Beaker', 'eject', {'ejectBeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
</div>
</div>
{{/if}}
<div class="item">&nbsp;</div>
{{if !data.selectedMenuKey || data.selectedMenuKey == 'ui' || data.selectedMenuKey == 'se'}}
<h3>Radiation Emitter Settings</h3>
<div class="item">
<div class="itemLabelNarrow">
Intensity:
</div>
<div class="itemContentWide">
{{:helper.link('-', null, {'radiationIntensity' : 0}, (data.radiationIntensity > 1) ? null : 'disabled')}}
<div class="statusValue">&nbsp;{{:data.radiationIntensity}}&nbsp;</div>
{{:helper.link('+', null, {'radiationIntensity' : 1}, (data.radiationIntensity < 10) ? null : 'disabled')}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Duration:
</div>
<div class="itemContentWide">
{{:helper.link('-', null, {'radiationDuration' : 0}, (data.radiationDuration > 2) ? null : 'disabled')}}
<div class="statusValue">&nbsp;{{:data.radiationDuration}}&nbsp;</div>
{{:helper.link('+', null, {'radiationDuration' : 1}, (data.radiationDuration < 20) ? null : 'disabled')}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
&nbsp;
</div>
<div class="itemContentWide">
{{:helper.link('Pulse Radiation', 'exclamation-circle', {'pulseRadiation' : 1}, !data.hasOccupant ? 'disabled' : null)}}
</div>
</div>
{{/if}}
{{/if}}
<div class="item">&nbsp;</div>
<hr>
<div class="item">
<div class="itemLabelNarrow">
Occupant:
</div>
<div class="itemContentWide">
{{:helper.link('Eject Occupant', 'eject', {'ejectOccupant' : 1}, data.locked || !data.hasOccupant || data.irradiating ? 'disabled' : null)}}
</div>
</div>
<div class="item">
<div class="itemLabelNarrow">
Door Lock:
</div>
<div class="itemContentWide">
{{:helper.link('Engaged', 'lock', {'toggleLock' : 1}, data.locked ? 'selected' : !data.hasOccupant ? 'disabled' : null)}}
{{:helper.link('Disengaged', 'unlock', {'toggleLock' : 1}, !data.locked ? 'selected' : data.irradiating ? 'disabled' : null)}}
</div>
</div>
{{if data.irradiating}}
<div class="mask">
<div class="maskContent">
<h1>Irradiating Subject</h1>
<h3>For {{:data.irradiating}} seconds.</h3>
</div>
</div>
{{/if}}
-69
View File
@@ -1,69 +0,0 @@
<style type="text/css">
.fixedCell {
width: 78px;
}
.fixedCellWide {
width: 120px;
}
.link, .linkOn, .linkOff, .selected, .disabled, .yellowButton, .redButton {
min-width: 70px;
margin: 0px 2px 0px 0px;
}
</style>
<div class="item">
<div class="itemLabelWider">
Main power is
{{if data.main_power_loss == 0}}
online
{{else data.main_power_loss == -1}}
offline
{{else}}
offline for {{:data.main_power_loss}} second{{:data.main_power_loss == 1 ? '' : 's'}}
{{/if}}.
</div>
<div class="itemContentNarrow">
{{:helper.link(data.main_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'main_power'}, data.main_power_loss == 0 ? null : 'disabled', data.main_power_loss == 0 ? 'redButton' : null)}}
</div>
</div>
<div class="item">
<div class="itemLabelWider">
Backup power is
{{if data.backup_power_loss == 0}}
online
{{else data.backup_power_loss == -1}}
offline
{{else}}
offline for {{:data.backup_power_loss}} second{{:data.backup_power_loss == 1 ? '' : 's'}}
{{/if}}.
</div>
<div class="itemContentNarrow">
{{:helper.link(data.backup_power_loss ? 'Disabled' : 'Disable', null, {'command' : 'backup_power'}, data.backup_power_loss == 0 ? null : 'disabled', data.backup_power_loss == 0 ? 'redButton' : null)}}
</div>
</div>
<div class="item">
<div class="itemLabelWidest">
Electrified Status:
</div>
<div class="itemContentWidest">
{{:helper.link('Offline' , null, {'command' : 'electrify_permanently', 'activate' : "0" }, data.electrified == 0 ? 'selected' : null)}}
{{:helper.link(data.electrified <= 0 ? 'Temporary (30s)' : 'Temporary (' + data.electrified +'s)', null, {'command' : 'electrify_temporary', 'activate' : "1"}, null, data.electrified > 0 ? 'redButton' : null)}}
{{:helper.link('Permanent', null, {'command' : 'electrify_permanently', 'activate' : "1"}, data.electrified == -1 ? 'redButton' : null)}}
</div>
</div>
<br>
<table>
{{for data.commands}}
<tr>
<td class='itemLabel fixedCellWide'>{{:value.name}}:</td>
<td class='fixedCell'>{{:helper.link(value.enabled, null, {'command' : value.command, 'activate' : value.act ? 1 : 0}, value.active ? 'selected' : null)}}</td>
<td class='fixedCell'>{{:helper.link(value.disabled, null, {'command' : value.command, 'activate' : value.act ? 0 : 1}, !value.active ? (value.danger ? 'redButton' : 'selected') : null)}}</td>
</tr>
{{/for}}
</table>
-171
View File
@@ -1,171 +0,0 @@
<!--
Title: Medical Records UI
Used In File(s): \code\game\machinery\computer\medical.dm
-->
<!--
#define MED_DATA_MAIN 1 // Main menu
#define MED_DATA_R_LIST 2 // Record list
#define MED_DATA_MAINT 3 // Records maintenance
#define MED_DATA_RECORD 4 // Record
#define MED_DATA_V_DATA 5 // Virus database
#define MED_DATA_MEDBOT 6 // Medbot monitor
-->
<div class="item">
<div class="itemLabelNarrow">
<b>Confirm Identity:</b>
</div>
<div class="itemContent">
{{:helper.link(data.scan ? data.scan : "----------", 'eject', {'scan' : 1}, null, data.scan ? 'itemContentWide' : 'fixedLeft')}}
</div>
</div>
<hr>
{{if data.authenticated}}
{{if data.screen == 1}} <!-- MED_DATA_MAIN -->
<h3>Menu</h3>
<div class="item">
<div class="line">{{:helper.link('Search Records', 'search', {'search' : 1})}}</div>
<div class="line">{{:helper.link('List Records', 'list', {'screen' : 2})}}</div>
<div class="line">{{:helper.link('Virus Database', 'database', {'screen' : 5})}}</div>
<div class="line">{{:helper.link('Medbot Tracking', 'plus-square', {'screen' : 6})}}</div>
<div class="line">{{:helper.link('Record Maintenance', 'wrench', {'screen' : 3})}}</div>
<div class="line">{{:helper.link('Logout', 'lock', {'logout' : 1})}}</div>
</div>
{{else data.screen == 2}} <!-- MED_DATA_R_LIST -->
<h3><center>Record List</center></h3>
{{for data.records}}
<div class="line">{{:helper.link(value.id + ': ' + value.name, 'user', {'d_rec' : value.ref})}}</div>
{{/for}}
<br><hr>
<div class="line">{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}</div>
{{else data.screen == 3}} <!-- MED_DATA_MAINT -->
<h3>Records Maintenance</h3>
<div class="line">{{:helper.link('Backup To Disk', 'download', {'back' : 1}, 'disabled')}}</div>
<div class="line">{{:helper.link('Upload From disk', 'upload', {'u_load' : 1}, 'disabled')}}</div>
<div class="line">{{:helper.link('Delete All Records', 'trash', {'del_all' : 1})}}</div>
<div class="line">{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}</div>
{{else data.screen == 4}} <!-- MED_DATA_RECORD -->
<h3><center>Medical Record</center></h3>
<h4><center>General Data</center></h4>
{{if data.general.empty}}
<span class="bad"><center>General Record Lost!</center></span>
{{else}}
<div class="statusDisplayRecords">
<table style='width:100%'><tr>
<td valign='top' style='margin-right: 300px;'>
{{for data.general.fields}}
<div class="item">
<div class="itemLabel">{{:value.field}}</div>
<div class="itemContent">{{:helper.link(value.value, value.edit ? 'pencil' : 'user-times', {'field' : value.edit}, value.edit ? null : 'disabled')}}</div>
</div>
{{/for}}
</td>
<td valign='middle' align='center' style='width: 300px;'>
{{if data.general.has_photos}}
{{for data.general.photos}}
{{if value.photo}}
<img src={{:value.photo}} height=96 width=96 border=5>
{{/if}}
{{/for}}
{{/if}}
</td>
</tr></table>
</div>
{{/if}}
<h4><center>Medical Data</center></h4>
{{if data.medical.empty}}
<span class="bad"><center>Medical Record Lost!</center></span>
<div style="text-align: center;">
<div style="display: inline-block;">{{:helper.link('New Record', 'plus', {'new' : 1})}}</div>
</div>
<h3><center>Menu</center></h3>
{{else}}
{{for data.medical.fields}}
<div class="item">
<div class="itemLabel">{{:value.field}}</div>
<div class="itemContent">{{:helper.link(value.value, 'pencil', {'field' : value.edit})}}</div>
</div>
{{if value.line_break}}
<br>
{{/if}}
{{/for}}
<h4><center>Comments/Log</center></h4>
{{for data.medical.comments}}
<div class="line">{{:value}}</div>
<div class="line">{{:helper.link('Remove entry', 'trash', {'del_c' : index})}}</div>
<br><hr>
{{/for}}
<div style="text-align: center;">
<div style="display: inline-block;">{{:helper.link('Add Entry', 'plus', {'add_c' : 1})}}</div>
</div>
<h3><center>Menu</center></h3>
<div class="line">{{:helper.link('Delete Record (Medical Only)', 'trash', {'del_r' : 1})}}</div>
{{/if}}
<div class="line">{{:helper.link('Print Record', 'print', {'print_p' : 1})}}</div>
<div class="line">{{:helper.link('Back', 'arrow-left', {'screen' : 2})}}</div>
{{else data.screen == 5}} <!-- MED_DATA_V_DATA -->
<h3><center>Virus Database</center></h3>
{{for data.virus}}
<div class="line">{{:helper.link(value.name, 'arrow-right', {'vir' : value.D})}}</div>
{{/for}}
<br><hr>
{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}
{{else data.screen == 6}} <!-- MED_DATA_MEDBOT -->
<h3><center>Medical Robot Monitor</center></h3>
{{for data.medbots}}
<h4>{{:value.name}}</h4>
<div class="item">
<div class="itemLabel">Location:</div>
<div class="itemContent">{{:value.x}}, {{:value.y}}</div>
</div>
<div class="item">
<div class="itemLabel">Status:</div>
<div class="itemContent">
{{:value.on ? "<span class='good'>Online</span>" : "<span class='average'>Offline</span>"}}. {{if value.use_beaker}}Reservoir: [{{:value.total_volume}}/{{:value.maximum_volume}}]{{else}}Using internal synthesizer.{{/if}}
</div>
</div>
<hr>
{{empty}}
<h4 class="bad"><center>None detected!</center></h4>
{{/for}}
<div class="line">{{:helper.link('Back', 'arrow-left', {'screen' : 1})}}</div>
{{/if}}
{{else}}
<h3>Menu</h3>
{{:helper.link('Login', 'unlock', {'login' : 1})}}
{{/if}}
{{if data.temp}}
<div class="mask" style="display: table;">
<div class="maskContent" style="margin: 0px; vertical-align: middle; align:center; display: table-cell;">
{{if data.temp.notice}}
<div class="notice"><center>{{:data.temp.text}}</center></div>
{{else}}
<center>{{:data.temp.text}}</center>
{{/if}}
{{if data.temp.has_buttons}}
<div class="line">
<div style="display: inline-block;">
<div style="width: 500px; display: flex; display: -webkit-flex; flex-wrap: wrap; -webkit-flex-wrap: wrap; justify-content: center; -webkit-justify-content: center;">
{{for data.temp.buttons}}
{{:helper.link(value.name, value.icon, {'temp' : 1, 'temp_action' : value.href}, value.status)}}
{{/for}}
</div>
</div>
</div>
{{/if}}
<div style="display: inline-block;">
<div class="item">{{:helper.link('Clear screen', 'home', {'temp' : 1})}}</div>
</div>
</div>
</div>
{{/if}}
-215
View File
@@ -1,215 +0,0 @@
<!--
Title: Operating Computer UI
Used In File(s): \code\game\machinery\computer\Operating.dm
-->
<h3>Patient Monitor</h3>
<div class="statusDisplay">
{{if data.choice==0}}
{{if !data.hasOccupant}}
<div class="line">No Patient Detected</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;=>&nbsp;
{{if data.occupant.stat == 0}}
<span class="good">Conscious</span>
{{else data.occupant.stat == 1}}
<span class="average">Unconscious</span>
{{else}}
<span class="bad">DEAD</span>
{{/if}}
</div>
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= data.occupant.maxHealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else data.occupant.health > 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}}
{{else data.occupant.health >= data.minhealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel">Brute Damage:</div>
{{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.bruteLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Resp. Damage:</div>
{{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.oxyLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Toxin Damage:</div>
{{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.toxLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Burn Severity:</div>
{{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.fireLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">Patient Temperature:</div>
{{if data.occupant.temperatureSuitability == -3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 0}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{/if}}
</div>
{{if data.occupant.hasBlood}}
<br>
<div class="line">
<div class="statusLabel">Blood Level:</div>
{{if data.occupant.bloodPercent <= 60}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else data.occupant.bloodPercent <= 90}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{/if}}
<div class="line">
<div class="statusLabel">Blood Type:</div> <div class="statusValue">{{:data.occupant.bloodType}}</div>
</div>
</div>
<div class="line">
<div class="statusLabel">Pulse:</div> <div class="statusValue">{{:data.occupant.pulse}} bpm</div>
</div>
{{/if}}
{{if data.occupant.inSurgery}}
<div class="line">
<div class="statusLabel">Initiated Surgery Procedure:</div> <div class="statusValue">{{:data.occupant.surgeryName}}</div>
<br>
<div class="statusLabel">Next Step:</div> <div class="statusValue">{{:data.occupant.stepName}}</div>
</div>
{{/if}}
<br>
<br>
{{/if}}
<div class="item">
<div class="itemLabelWide">
{{:helper.link('Options', 'gear', {'choiceOn' : 1})}}
</div>
</div>
{{else}}
<div class="item">
<div class="statusLabel">
Loudspeaker:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'verboseOn' : 1}, data.verbose ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'verboseOff' : 1}, data.verbose ? null : 'selected')}}
</div>
</div>
<br>
<div class="item">
<div class="statusLabel">
Health Announcer:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'healthOn' : 1}, data.health ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'healthOff' : 1}, data.health ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="line">
<div class="statusLabel">Health Announcer Threshold:</div>
{{:helper.link('-', null, {'health_adj' : -100}, (data.healthAlarm >= 0) ? null : 'disabled')}}
{{:helper.link('-', null, {'health_adj' : -10}, (data.healthAlarm >= -90) ? null : 'disabled')}}
{{:helper.link('-', null, {'health_adj' : -1}, (data.healthAlarm > -100) ? null : 'disabled')}}
{{if data.healthAlarm >= 100}}
{{:helper.displayBar(data.healthAlarm, 0, 100, 'good')}}
{{else data.healthAlarm > 0}}
{{:helper.displayBar(data.healthAlarm, 0, 100, 'average')}}
{{else data.healthAlarm >= -100}}
{{:helper.displayBar(data.healthAlarm, 0, -100, 'average alignRight')}}
{{else}}
{{:helper.displayBar(data.healthAlarm, 0, -100, 'bad alignRight')}}
{{/if}}
{{:helper.link('+', null, {'health_adj' : 1}, (data.healthAlarm < 100) ? null : 'disabled')}}
{{:helper.link('+', null, {'health_adj' : 10}, (data.healthAlarm <= 90) ? null : 'disabled')}}
{{:helper.link('+', null, {'health_adj' : 100}, (data.healthAlarm <= 0) ? null : 'disabled')}}
<div style="float: left; width: 80px; text-align: center;">&nbsp;{{: data.healthAlarm }} &nbsp;</div>
</div>
</div>
<br>
<div class="item">
<div class="statusLabel">
Oxygen Alarm:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'oxyOn' : 1}, data.oxy ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'oxyOff' : 1}, data.oxy ? null : 'selected')}}
</div>
</div>
<div class="item">
<div class="line">
<div class="statusLabel">Oxygen Alert Threshold:</div>
{{:helper.link('-', null, {'oxy_adj' : -100}, (data.oxyAlarm >= 100) ? null : 'disabled')}}
{{:helper.link('-', null, {'oxy_adj' : -10}, (data.oxyAlarm >= 10) ? null : 'disabled')}}
{{:helper.link('-', null, {'oxy_adj' : -1}, (data.oxyAlarm >= 0) ? null : 'disabled')}}
{{:helper.displayBar( data.oxyAlarm, 0, 200, 'good')}}
{{:helper.link('+', null, {'oxy_adj' : 1}, (data.oxyAlarm < 200) ? null : 'disabled')}}
{{:helper.link('+', null, {'oxy_adj' : 10}, (data.oxyAlarm <= 190) ? null : 'disabled')}}
{{:helper.link('+', null, {'oxy_adj' : 100}, (data.oxyAlarm <= 100) ? null : 'disabled')}}
<div style="float: left; width: 80px; text-align: center;">&nbsp;{{: data.oxyAlarm }} &nbsp;</div>
</div>
</div>
<br>
<div class="item">
<div class="statusLabel">
Critical Alert:
</div>
<div class="itemContentNarrow" style="float: left">
{{:helper.link('On', 'power-off', {'critOn' : 1}, data.crit ? 'selected' : null)}}
{{:helper.link('Off', 'close', {'critOff' : 1}, data.crit ? null : 'selected')}}
</div>
</div>
<br>
<div class="item">
<div class="itemLabelWide">
{{:helper.link('Return', 'arrow-left', {'choiceOff' : 1})}}
</div>
</div>
{{/if}}
</div>
-211
View File
@@ -1,211 +0,0 @@
<!--
Title: Sleeper Status UI
Used In File(s): \code\game\machinery\Sleeper.dm
-->
<h3>Sleeper Status</h3>
<div class="statusDisplay">
{{if !data.hasOccupant}}
<div class="line">Sleeper Unoccupied</div>
{{else}}
<div class="line">
{{:data.occupant.name}}&nbsp;=>&nbsp;
{{if data.occupant.stat == 0}}
<span class="good">Conscious</span>
{{else data.occupant.stat == 1}}
<span class="average">Unconscious</span>
{{else}}
<span class="bad">DEAD</span>
{{/if}}
</div>
<div class="line">
<div class="statusLabel">Health:</div>
{{if data.occupant.health >= data.occupant.maxHealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'good')}}
{{else data.occupant.health > 0}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.maxHealth, 'average')}}
{{else data.occupant.health >= data.minhealth}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'average alignRight')}}
{{else}}
{{:helper.displayBar(data.occupant.health, 0, data.occupant.minHealth, 'bad alignRight')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.health)}}</div>
</div>
<div class="line">
<div class="statusLabel">=&gt; Brute Damage:</div>
{{:helper.displayBar(data.occupant.bruteLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.bruteLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">=&gt; Resp. Damage:</div>
{{:helper.displayBar(data.occupant.oxyLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.oxyLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">=&gt; Toxin Damage:</div>
{{:helper.displayBar(data.occupant.toxLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.toxLoss)}}</div>
</div>
<div class="line">
<div class="statusLabel">=&gt; Burn Severity:</div>
{{:helper.displayBar(data.occupant.fireLoss, 0, data.occupant.maxHealth, 'bad')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.fireLoss)}}</div>
</div>
<br>
<div class="line">
<!--
<div class="statusLabel">Patient Temperature:</div>
{{if data.occupant.temperatureSuitability == -3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'cold3')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'cold2')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'cold1')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 0}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'hot1')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'hot2')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'hot3')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{/if}}
If you can get the changing temperature bars to work right, congratulations
-->
<div class="statusLabel">Patient Temperature:</div>
{{if data.occupant.temperatureSuitability == -3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == -1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 0}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'good')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 1}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 2}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'average')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{else data.occupant.temperatureSuitability == 3}}
{{:helper.displayBar(data.occupant.bodyTemperature, 0, data.occupant.maxTemp, 'bad')}}
<div class="statusValue bad">{{:helper.smoothRound(data.occupant.btCelsius)}}&deg;C, {{:helper.smoothRound(data.occupant.btFaren)}}&deg;F</div>
{{/if}}
</div>
{{if data.occupant.hasBlood}}
<br>
<div class="line">
<div class="statusLabel">Pulse:</div> <div class="statusValue">{{:data.occupant.pulse}} bpm</div>
</div>
<div class="line">
<div class="statusLabel">Blood Level:</div>
{{if data.occupant.bloodPercent <= 60}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'bad')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else data.occupant.bloodPercent <= 90}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'average')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{else}}
{{:helper.displayBar(data.occupant.bloodLevel, 0, data.occupant.bloodMax, 'good')}}
<div class="statusValue">
{{:helper.smoothRound(data.occupant.bloodPercent)}}%, {{:helper.smoothRound(data.occupant.bloodLevel)}}cl
</div>
{{/if}}
</div>
{{/if}}
{{/if}}
</div>
<h3>Sleeper Operation</h3>
<div class="item">
<div class="itemLabel">
Sleeper Status:
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Occupant', 'arrow-circle-o-down', {'ejectify' : 1}, data.hasOccupant ? null : 'disabled')}}
</div>
</div>
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel">
Dialysis Beaker:
</div>
<div class="itemContent" style="width: 40%;">
{{if data.isBeakerLoaded}}
<span class="highlight">{{:helper.smoothRound(data.beakerFreeSpace)}} units of space remaining</span><br>
{{else}}
<span class="average"><i>No Dialysis Output Beaker Loaded</i></span>
{{/if}}
</div>
<div class="itemContent" style="width: 26%;">
{{:helper.link('Eject Beaker', 'eject', {'removebeaker' : 1}, data.isBeakerLoaded ? null : 'disabled')}}
{{if data.isBeakerLoaded}}
<div class="line">
<div class="itemContent">
{{:helper.link('On', 'power-off', {'togglefilter' : 1}, data.occupant.hasBlood ? (data.dialysis ? 'selected' : null) : 'disabled')}}{{:helper.link('Off', 'close', {'togglefilter' : 1}, data.dialysis ? null : 'selected')}}
</div>
</div>
{{/if}}
</div>
</div>
<div class="item">
<div class="itemLabel">
Chemicals:
</div>
{{for data.chemicals}}
<div class="line"><b>{{:value.title}}</b></div>
<div class="line">
{{if value.overdosing}}
{{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'bad')}}
{{else value.od_warning}}
{{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'average')}}
{{else}}
{{:helper.displayBar(value.occ_amount, 0, data.maxchem, 'good')}}
{{/if}}
<div class="statusValue">{{:helper.smoothRound(value.pretty_amount)}}/{{:data.maxchem}}</div>
<br>
</div>
<div class="line">
<div class="itemContent">
{{:helper.link('5', 'gear', {'chemical' : value.id, 'amount' : 5}, (!value.injectable || ((value.occ_amount + 5) > data.maxchem)) ? 'disabled' : null)}}
{{:helper.link('10', 'gear', {'chemical' : value.id, 'amount' : 10}, (!value.injectable || ((value.occ_amount + 10) > data.maxchem)) ? 'disabled' : null)}}
</div>
</div>
{{/for}}
<div class="item">&nbsp;</div>
<div class="item">
<div class="itemLabel" style="width: 35%">
Auto-eject dead patients:
</div>
<div class="itemContent" style="width: 30%">
{{:helper.link('On', 'power-off', {'auto_eject_dead_on' : 1}, data.auto_eject_dead ? 'selected' : null)}}{{:helper.link('Off', 'close', {'auto_eject_dead_off' : 0}, data.auto_eject_dead ? null : 'selected')}}
</div>
</div>
</div>
-34
View File
@@ -1,34 +0,0 @@
<!--
Title: Slot Machine UI
Used In File(s): /code/game/machinery/slotmachine.dm
-->
{{if data.money != null}}
<div class="line">
{{:data.plays}} players have tried their luck today!
</div>
<div class="line">
<div class="statusLabel">Credits Remaining:</div>
{{:helper.string("<div class='statusValue {0}'>{1}</div>", data.money >= 10 ? "" : "bad", helper.smoothRound(data.money))}}
</div>
<div class="item">
<div class="statusLabel">
Ten credits to play!
</div>
<div class="statusValue">
{{:helper.link('SPIN!', 'refresh', {'ops' : 1}, data.money >= 10 && !data.working ? null : 'disabled')}}
</div>
</div>
{{if data.result}}
<div class="line {{:data.resultlvl}}">
{{:data.result}}
</div>
{{/if}}
{{if data.working}}
<div class="notice">Spinning!</div>
{{/if}}
{{else}}
<div class="notice">
Could not scan your card or could not find account!<br>
Please wear or hold your ID and try again.
</div>
{{/if}}
-89
View File
@@ -1,89 +0,0 @@
<!--
Title: Instrument UI
Used In File(s): \code\game\objects\structures\musician.dm
-->
<div class="block">
<div class="item">
<div class="itemLabel"><b>Playback</b></div>
<div class="itemContent">
{{:helper.link('New', 'file', {'newsong': 1})}}
{{:helper.link('Import', 'folder-open', {'import': 1})}}
{{:helper.link('Play', 'play', {'play': 1}, data.lines.length > 0 ? (data.playing ? 'selected' : null) : 'disabled')}}
{{:helper.link('Stop', 'stop', {'stop': 1}, !data.playing ? 'selected' : null)}}
</div>
</div>
<div class="item">
<div class="itemLabel">Repeat Song:</div>
{{:helper.link('-', null, {'repeat' : -10}, data.repeat > 0 && !data.playing ? null : 'disabled')}}
{{:helper.link('-', null, {'repeat' : -1}, data.repeat > 0 && !data.playing ? null : 'disabled')}}
<span style="float: left; width: 80px; text-align: center;">{{:data.repeat}}</span>
{{:helper.link('+', null, {'repeat' : 1}, data.repeat < data.maxRepeat && !data.playing ? null : 'disabled')}}
{{:helper.link('+', null, {'repeat' : 10}, data.repeat < data.maxRepeat && !data.playing ? null : 'disabled')}}
</div>
<div class="item">
<div class="itemLabel">Tempo:</div>
{{:helper.link('-', null, {'tempo' : 10}, data.tempo < data.maxTempo ? null : 'disabled')}}
{{:helper.link('-', null, {'tempo' : 1}, data.tempo < data.maxTempo ? null : 'disabled')}}
<span style="float: left; width: 80px; text-align: center;">{{:helper.round(600 / data.tempo)}} BPM</span>
{{:helper.link('+', null, {'tempo' : -1}, data.tempo > data.minTempo ? null : 'disabled')}}
{{:helper.link('+', null, {'tempo' : -10}, data.tempo > data.minTempo ? null : 'disabled')}}
</div>
</div>
<h3>Editor</h3>
<div class="striped">
{{for data.lines}}
<div class="line" style="padding:4px">
<div class="floatLeft highlight">{{:index + 1}}:&nbsp;</div>
<div class="floatRight">
{{:helper.link('Edit', 'pencil', {'modifyline': index+1}, data.playing ? 'disabled' : null)}}
{{:helper.link('Insert', 'arrow-up', {'insertline': index+1}, data.playing ? 'disabled' : null)}}
{{:helper.link('Delete', 'trash', {'deleteline': index+1}, data.playing ? 'disabled' : null)}}
</div>
<div>{{:value}}</div>
</div>
{{/for}}
</div>
<div class="floatRight">
{{:helper.link('Add Line', 'arrow-down', {'insertline': data.lines.length + 1}, data.playing ? 'disabled' : null)}}
</div>
<div class="clearBoth"></div>
<hr>
<div class="item">
<div class="itemLabelNarrow">
{{:helper.link('Help', data.help ? 'close' : 'question', {'help': 1})}}
</div>
<div class="itemContent">
{{if data.help}}
<p>
Lines are a series of chords, separated by commas <span class="highlight">(,)</span>, each with notes seperated by hyphens <span class="highlight">(-)</span>.
<br>
Every note in a chord will play together, with the chord timed by the <span class="highlight">tempo</span> as defined above.
</p>
<p>
Notes are played by the <span class="good">names of the note</span>, and optionally, the <span class="average">accidental</span>, and/or the <span class="bad">octave number</span>.
<br>
By default, every note is <span class="average">natural</span> and in <span class="bad">octave 3</span>. Defining a different state for either is remembered for each <span class="good">note</span>.
<ul>
<li><span class="highlight">Example:</span> <i>C,D,E,F,G,A,B</i> will play a <span class="good">C</span> <span class="average">major</span> scale.</li>
<li>After a note has an <span class="average">accidental</span> or <span class="bad">octave</span> placed, it will be remembered: <i>C,C4,C#,C3</i> is <i>C3,C4,C4#,C3#</i></li>
</ul>
</p>
<p>
<span class="highlight">Chords</span> can be played simply by seperating each note with a hyphon: <i>A-C#,Cn-E,E-G#,Gn-B</i>.<br>
A <span class="highlight">pause</span> may be denoted by an empty chord: <i>C,E,,C,G</i>.
<br>
To make a chord be a different time, end it with /x, where the chord length will be length defined by <span class="highlight">tempo / x</span>, <span class="highlight">eg:</span> <i>C,G/2,E/4</i>.
</p>
<p>
Combined, an example line is: <i>E-E4/4,F#/2,G#/8,B/8,E3-E4/4</i>.
<ul>
<li>Lines may be up to 50 characters.</li>
<li>A song may only contain up to 50 lines.</li>
</ul>
</p>
{{/if}}
</div>
</div>
-72
View File
@@ -1,72 +0,0 @@
<div class="item">
Behaviour controls are {{:data.locked ? "locked" : "unlocked"}}.
</div>
<br>
{{if data.access}}
{{if !data.screen}}
<div class="item">
<div class="itemLabelWide">
Turret Status:
</div>
<div class="itemContentNarrow">
{{:helper.link('Enabled', null, {'command' : 'enable', 'value' : 1}, data.enabled ?'redButton' : null)}}
{{:helper.link('Disabled',null, {'command' : 'enable', 'value' : 0}, !data.enabled ? 'selected' : null)}}
</div>
</div>
{{if data.accesses}}
<div class="item">
<div class="itemLabelWide">
Required Access:
</div>
<div class="itemContentNarrow">
{{:helper.link('Modify', null, {'command' : 'screen', 'value' : 1})}}
</div>
</div>
{{/if}}
{{if data.lethal_control}}
<div class="item">
<div class="itemLabelWide">
Lethal Mode:
</div>
<div class="itemContentNarrow">
{{:helper.link('On', null, {'command' : 'lethal', 'value' : 1}, data.lethal ?'redButton' : null)}}
{{:helper.link('Off',null, {'command' : 'lethal', 'value' : 0}, !data.lethal ? 'selected' : null)}}
</div>
</div>
{{/if}}
{{for data.settings}}
<div class="item">
<div class="itemLabelWide">
{{:value.category}}
</div>
<div class="itemContentNarrow">
{{:helper.link('On', null, {'command' : value.setting, 'value' : 1}, value.value ? 'selected' : null)}}
{{:helper.link('Off',null, {'command' : value.setting, 'value' : 0}, !value.value ? 'selected' : null)}}
</div>
</div>
{{/for}}
{{else}}
<div class="item">
<div class="itemLabelWide">
Turret Settings:
</div>
<div class="itemContentNarrow">
{{:helper.link('Modify', null, {'command' : 'screen', 'value' : 0})}}
</div>
</div>
<div class="item">
<div class="itemLabelWide">
Access Type:
</div>
<div class="itemContentNarrow">
{{:helper.link('All', null, {'one_access' : 0}, !data.one_access ? 'selected' : null)}}
{{:helper.link('One', null, {'one_access' : 1}, data.one_access ? 'selected' : null)}}
</div>
</div>
<h3>Select Access</h3>
{{for data.accesses}}
{{:helper.link(value.name, null, {'access' : value.number}, null, value.active ? 'selected' : null)}}
{{/for}}
{{/if}}
{{/if}}
+26 -2
View File
@@ -40,6 +40,7 @@
#include "code\__DEFINES\genetics.dm"
#include "code\__DEFINES\hud.dm"
#include "code\__DEFINES\hydroponics.dm"
#include "code\__DEFINES\instruments.dm"
#include "code\__DEFINES\inventory.dm"
#include "code\__DEFINES\is_helpers.dm"
#include "code\__DEFINES\job.dm"
@@ -71,6 +72,7 @@
#include "code\__DEFINES\station_goals.dm"
#include "code\__DEFINES\status_effects.dm"
#include "code\__DEFINES\subsystems.dm"
#include "code\__DEFINES\tgui.dm"
#include "code\__DEFINES\tools.dm"
#include "code\__DEFINES\typeids.dm"
#include "code\__DEFINES\vv.dm"
@@ -239,6 +241,7 @@
#include "code\controllers\subsystem\parallax.dm"
#include "code\controllers\subsystem\radio.dm"
#include "code\controllers\subsystem\shuttles.dm"
#include "code\controllers\subsystem\sounds.dm"
#include "code\controllers\subsystem\spacedrift.dm"
#include "code\controllers\subsystem\statistics.dm"
#include "code\controllers\subsystem\sun.dm"
@@ -251,6 +254,7 @@
#include "code\controllers\subsystem\weather.dm"
#include "code\controllers\subsystem\processing\dcs.dm"
#include "code\controllers\subsystem\processing\fastprocess.dm"
#include "code\controllers\subsystem\processing\instruments.dm"
#include "code\controllers\subsystem\processing\obj.dm"
#include "code\controllers\subsystem\processing\processing.dm"
#include "code\controllers\subsystem\tickets\mentor_tickets.dm"
@@ -305,6 +309,7 @@
#include "code\datums\components\paintable.dm"
#include "code\datums\components\slippery.dm"
#include "code\datums\components\spawner.dm"
#include "code\datums\components\spooky.dm"
#include "code\datums\components\squeak.dm"
#include "code\datums\components\swarming.dm"
#include "code\datums\diseases\_disease.dm"
@@ -729,6 +734,7 @@
#include "code\game\machinery\computer\salvage_ship.dm"
#include "code\game\machinery\computer\security.dm"
#include "code\game\machinery\computer\skills.dm"
#include "code\game\machinery\computer\sm_monitor.dm"
#include "code\game\machinery\computer\specops_shuttle.dm"
#include "code\game\machinery\computer\station_alert.dm"
#include "code\game\machinery\computer\store.dm"
@@ -878,7 +884,6 @@
#include "code\game\objects\items\devices\flashlight.dm"
#include "code\game\objects\items\devices\floor_painter.dm"
#include "code\game\objects\items\devices\handheld_defib.dm"
#include "code\game\objects\items\devices\instruments.dm"
#include "code\game\objects\items\devices\laserpointer.dm"
#include "code\game\objects\items\devices\lightreplacer.dm"
#include "code\game\objects\items\devices\machineprototype.dm"
@@ -1083,7 +1088,6 @@
#include "code\game\objects\structures\misc.dm"
#include "code\game\objects\structures\mop_bucket.dm"
#include "code\game\objects\structures\morgue.dm"
#include "code\game\objects\structures\musician.dm"
#include "code\game\objects\structures\noticeboard.dm"
#include "code\game\objects\structures\plasticflaps.dm"
#include "code\game\objects\structures\reflector.dm"
@@ -1603,6 +1607,25 @@
#include "code\modules\hydroponics\grown\tobacco.dm"
#include "code\modules\hydroponics\grown\tomato.dm"
#include "code\modules\hydroponics\grown\towercap.dm"
#include "code\modules\instruments\_instrument_data.dm"
#include "code\modules\instruments\_instrument_key.dm"
#include "code\modules\instruments\brass.dm"
#include "code\modules\instruments\chromatic_percussion.dm"
#include "code\modules\instruments\fun.dm"
#include "code\modules\instruments\guitar.dm"
#include "code\modules\instruments\hardcoded.dm"
#include "code\modules\instruments\organ.dm"
#include "code\modules\instruments\piano.dm"
#include "code\modules\instruments\synth_tones.dm"
#include "code\modules\instruments\objs\items\_instrument.dm"
#include "code\modules\instruments\objs\items\headphones.dm"
#include "code\modules\instruments\objs\items\instruments.dm"
#include "code\modules\instruments\objs\structures\_musician.dm"
#include "code\modules\instruments\objs\structures\piano.dm"
#include "code\modules\instruments\songs\_song.dm"
#include "code\modules\instruments\songs\_song_ui.dm"
#include "code\modules\instruments\songs\play_legacy.dm"
#include "code\modules\instruments\songs\play_synthesized.dm"
#include "code\modules\karma\karma.dm"
#include "code\modules\keybindings\bindings_admin.dm"
#include "code\modules\keybindings\bindings_ai.dm"
@@ -2433,6 +2456,7 @@
#include "code\modules\telesci\telepad.dm"
#include "code\modules\telesci\telesci_computer.dm"
#include "code\modules\tgui\external.dm"
#include "code\modules\tgui\modal.dm"
#include "code\modules\tgui\states.dm"
#include "code\modules\tgui\tgui.dm"
#include "code\modules\tgui\modules\_base.dm"
Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More