fix conflicts
@@ -450,7 +450,7 @@ var/global/datum/controller/subsystem/ticker/ticker
|
||||
if(captainless)
|
||||
for(var/mob/M in player_list)
|
||||
if(!istype(M,/mob/new_player))
|
||||
to_chat(M, "<span class='notice'>Colony Directorship not forced on anyone.</span>")
|
||||
to_chat(M, "<span class='notice'>Site Management is not forced on anyone.</span>")
|
||||
|
||||
|
||||
/datum/controller/subsystem/ticker/proc/declare_completion()
|
||||
|
||||
@@ -32,6 +32,13 @@
|
||||
containertype = /obj/structure/closet/crate/nanomed
|
||||
containername = "BloodPack crate"
|
||||
|
||||
/datum/supply_pack/med/synthplas
|
||||
name = "BloodPack (Synthplas) crate"
|
||||
contains = list(/obj/item/weapon/reagent_containers/blood/synthplas = 6)
|
||||
cost = 80
|
||||
containertype = /obj/structure/closet/crate/nanomed
|
||||
containername = "SynthPlas crate"
|
||||
|
||||
/datum/supply_pack/med/bodybag
|
||||
name = "Body bag crate"
|
||||
contains = list(/obj/item/weapon/storage/box/bodybags = 3)
|
||||
|
||||
@@ -297,9 +297,9 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is
|
||||
M.add_ion_law("HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE")
|
||||
if(5)
|
||||
to_chat(M, "<br>")
|
||||
to_chat(M, "<span class='danger'>THE COLONY DIRECTOR, HOP AND HOS ARE [who2]...LAWS UPDATED</span>")
|
||||
to_chat(M, "<span class='danger'>THE SITE MANAGER, HOP AND HOS ARE [who2]...LAWS UPDATED</span>")
|
||||
to_chat(M, "<br>")
|
||||
M.add_ion_law("THE COLONY DIRECTOR, HOP AND HOS ARE [who2]")
|
||||
M.add_ion_law("THE SITE MANAGER, HOP AND HOS ARE [who2]")
|
||||
if(6)
|
||||
to_chat(M, "<br>")
|
||||
to_chat(M, "<span class='danger'>THE STATION IS BUILT FOR [who2]...LAWS UPDATED</span>")
|
||||
|
||||
@@ -438,11 +438,15 @@ steam.start() -- spawns the effect
|
||||
if(src.processing)
|
||||
src.processing = 0
|
||||
spawn(0)
|
||||
var/turf/T = get_turf(src.holder)
|
||||
var/turf/T
|
||||
if(istype(holder, /atom/movable))
|
||||
var/atom/movable/AM = holder
|
||||
if(AM.locs && AM.locs.len)
|
||||
T = pick(AM.locs)
|
||||
T = get_turf(pick(AM.locs))
|
||||
else
|
||||
T = get_turf(AM)
|
||||
else //when would this ever be attached a non-atom/movable?
|
||||
T = get_turf(src.holder)
|
||||
if(T != src.oldposition)
|
||||
if(isturf(T))
|
||||
var/obj/effect/effect/ion_trails/I = new /obj/effect/effect/ion_trails(src.oldposition)
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
/obj/structure/closet/secure_closet/hos
|
||||
name = "head of security's locker"
|
||||
req_access = list(access_hos)
|
||||
storage_capacity = 2.5 * MOB_MEDIUM
|
||||
storage_capacity = 2.6 * MOB_MEDIUM
|
||||
closet_appearance = /decl/closet_appearance/secure_closet/security/hos
|
||||
|
||||
starts_with = list(
|
||||
@@ -99,7 +99,8 @@
|
||||
/obj/item/clothing/suit/storage/hooded/wintercoat/security,
|
||||
/obj/item/clothing/shoes/boots/winter/security,
|
||||
/obj/item/device/flashlight/maglight,
|
||||
/obj/item/clothing/mask/gas/half)
|
||||
/obj/item/clothing/mask/gas/half,
|
||||
/obj/item/clothing/mask/gas/sechailer/swat/hos)
|
||||
|
||||
/obj/structure/closet/secure_closet/hos/Initialize()
|
||||
if(prob(50))
|
||||
@@ -113,6 +114,7 @@
|
||||
|
||||
/obj/structure/closet/secure_closet/warden
|
||||
name = "warden's locker"
|
||||
storage_capacity = 42
|
||||
req_access = list(access_armory)
|
||||
closet_appearance = /decl/closet_appearance/secure_closet/security/warden
|
||||
|
||||
@@ -143,7 +145,8 @@
|
||||
/obj/item/clothing/shoes/boots/winter/security,
|
||||
/obj/item/device/flashlight/maglight,
|
||||
/obj/item/device/megaphone,
|
||||
/obj/item/clothing/mask/gas/half)
|
||||
/obj/item/clothing/mask/gas/half,
|
||||
/obj/item/clothing/mask/gas/sechailer/swat/warden)
|
||||
|
||||
/obj/structure/closet/secure_closet/warden/Initialize()
|
||||
if(prob(50))
|
||||
|
||||
@@ -406,7 +406,8 @@
|
||||
/obj/item/clothing/gloves/swat,
|
||||
/obj/item/clothing/mask/balaclava/tactical,
|
||||
/obj/item/clothing/mask/balaclava,
|
||||
/obj/item/clothing/mask/bandana/skull = 2)
|
||||
/obj/item/clothing/mask/bandana/skull = 2,
|
||||
/obj/item/clothing/mask/gas/sechailer/swat)
|
||||
|
||||
|
||||
/obj/structure/closet/wardrobe/suit
|
||||
|
||||
@@ -397,6 +397,7 @@
|
||||
|
||||
/datum/gear/suit/teshcoat/New()
|
||||
..()
|
||||
|
||||
gear_tweaks += gear_tweak_free_color_choice
|
||||
|
||||
/datum/gear/suit/teshcoatwhite
|
||||
@@ -417,4 +418,17 @@
|
||||
|
||||
/datum/gear/accessory/teshneckscarf/New()
|
||||
..()
|
||||
gear_tweaks += gear_tweak_free_color_choice
|
||||
gear_tweaks += gear_tweak_free_color_choice
|
||||
|
||||
/datum/gear/shoes/toelessjack
|
||||
display_name = "toe-less jackboots"
|
||||
path = /obj/item/clothing/shoes/boots/jackboots/toeless
|
||||
|
||||
|
||||
/datum/gear/shoes/toelessknee
|
||||
display_name = "toe-less jackboots, knee-length"
|
||||
path = /obj/item/clothing/shoes/boots/jackboots/toeless/knee
|
||||
|
||||
/datum/gear/shoes/toelessthigh
|
||||
display_name = "toe-less jackboots, thigh-length"
|
||||
path = /obj/item/clothing/shoes/boots/jackboots/toeless/thigh
|
||||
@@ -42,6 +42,23 @@
|
||||
w_class = ITEMSIZE_SMALL
|
||||
armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0)
|
||||
|
||||
//Turn it into a hailer mask
|
||||
/obj/item/clothing/mask/gas/half/attackby(obj/item/I, mob/user)
|
||||
if(istype(I, /obj/item/device/hailer))
|
||||
playsound(src, 'sound/items/Screwdriver.ogg', 50, 1)
|
||||
user.drop_item(src)
|
||||
var/obj/item/clothing/mask/gas/sechailer/N = new /obj/item/clothing/mask/gas/sechailer(src.loc)
|
||||
N.fingerprints = src.fingerprints
|
||||
N.fingerprintshidden = src.fingerprintshidden
|
||||
N.fingerprintslast = src.fingerprintslast
|
||||
N.suit_fibers = src.suit_fibers
|
||||
N.hailer = I
|
||||
I.loc = N
|
||||
if(!isturf(N.loc))
|
||||
user.put_in_hands(N)
|
||||
qdel(src)
|
||||
..()
|
||||
|
||||
//Plague Dr suit can be found in clothing/suits/bio.dm
|
||||
/obj/item/clothing/mask/gas/plaguedoctor
|
||||
name = "plague doctor mask"
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
/obj/item/clothing/mask/gas/sechailer
|
||||
name = "hailer face mask"
|
||||
desc = "A compact, durable gas mask that can be connected to an air supply. This one possesses a security hailer."
|
||||
description_info = "This mask has a hailer attached, you can activate it on the button or use the Halt! verb, for switching phrases you can alt+click it or change it using the change phrase verb."
|
||||
icon_state = "halfgas"
|
||||
armor = list(melee = 10, bullet = 10, laser = 10, energy = 0, bomb = 0, bio = 55, rad = 0)
|
||||
action_button_name = "HALT!"
|
||||
body_parts_covered = FACE
|
||||
var/obj/item/device/hailer/hailer
|
||||
var/cooldown = 0
|
||||
var/phrase = 1
|
||||
var/aggressiveness = 1
|
||||
var/safety = 1
|
||||
var/list/phrase_list = list(
|
||||
"halt" = "HALT! HALT! HALT! HALT!",
|
||||
"bobby" = "Stop in the name of the Law.",
|
||||
"compliance" = "Compliance is in your best interest.",
|
||||
"justice" = "Prepare for justice!",
|
||||
"running" = "Running will only increase your sentence.",
|
||||
"dontmove" = "Don't move, Creep!",
|
||||
"floor" = "Down on the floor, Creep!",
|
||||
"robocop" = "Dead or alive you're coming with me.",
|
||||
"god" = "God made today for the crooks we could not catch yesterday.",
|
||||
"freeze" = "Freeze, Scum Bag!",
|
||||
"imperial" = "Stop right there, criminal scum!",
|
||||
"bash" = "Stop or I'll bash you.",
|
||||
"harry" = "Go ahead, make my day.",
|
||||
"asshole" = "Stop breaking the law, asshole.",
|
||||
"stfu" = "You have the right to shut the fuck up",
|
||||
"shutup" = "Shut up crime!",
|
||||
"super" = "Face the wrath of the golden bolt.",
|
||||
"dredd" = "I am, the LAW!"
|
||||
)
|
||||
|
||||
/obj/item/clothing/mask/gas/sechailer/swat/hos
|
||||
name = "\improper HOS SWAT mask"
|
||||
desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a tan stripe."
|
||||
icon_state = "hosmask"
|
||||
|
||||
|
||||
/obj/item/clothing/mask/gas/sechailer/swat/warden
|
||||
name = "\improper Warden SWAT mask"
|
||||
desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000. It has a blue stripe."
|
||||
icon_state = "wardenmask"
|
||||
|
||||
/obj/item/clothing/mask/gas/sechailer/swat
|
||||
name = "\improper SWAT mask"
|
||||
desc = "A close-fitting tactical mask with an especially aggressive Compli-o-nator 3000."
|
||||
icon_state = "officermask"
|
||||
body_parts_covered = HEAD|FACE|EYES
|
||||
flags_inv = HIDEFACE|BLOCKHAIR
|
||||
aggressiveness = 3
|
||||
phrase = 12
|
||||
|
||||
|
||||
/obj/item/clothing/mask/gas/sechailer/ui_action_click()
|
||||
halt()
|
||||
|
||||
/obj/item/clothing/mask/gas/sechailer/AltClick(mob/user)
|
||||
selectphrase()
|
||||
|
||||
/obj/item/clothing/mask/gas/sechailer/verb/selectphrase()
|
||||
set name = "Select gas mask phrase"
|
||||
set category = "Object"
|
||||
set desc = "Alter the message shouted by your complionator gas mask."
|
||||
|
||||
var/key = phrase_list[phrase]
|
||||
var/message = phrase_list[key]
|
||||
|
||||
if (!safety)
|
||||
to_chat(usr, "<span class='notice'>You set the restrictor to: FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT.</span>")
|
||||
return
|
||||
switch(aggressiveness)
|
||||
if(1)
|
||||
phrase = (phrase < 6) ? (phrase + 1) : 1
|
||||
key = phrase_list[phrase]
|
||||
message = phrase_list[key]
|
||||
to_chat(usr,"<span class='notice'>You set the restrictor to: [message]</span>")
|
||||
if(2)
|
||||
phrase = (phrase < 11 && phrase >= 7) ? (phrase + 1) : 7
|
||||
key = phrase_list[phrase]
|
||||
message = phrase_list[key]
|
||||
to_chat(usr,"<span class='notice'>You set the restrictor to: [message]</span>")
|
||||
if(3)
|
||||
phrase = (phrase < 18 && phrase >= 12 ) ? (phrase + 1) : 12
|
||||
key = phrase_list[phrase]
|
||||
message = phrase_list[key]
|
||||
to_chat(usr,"<span class='notice'>You set the restrictor to: [message]</span>")
|
||||
if(4)
|
||||
phrase = (phrase < 18 && phrase >= 1 ) ? (phrase + 1) : 1
|
||||
key = phrase_list[phrase]
|
||||
message = phrase_list[key]
|
||||
to_chat(usr,"<span class='notice'>You set the restrictor to: [message]</span>")
|
||||
else
|
||||
to_chat(usr, "<span class='notice'>It's broken.</span>")
|
||||
|
||||
/obj/item/clothing/mask/gas/sechailer/emag_act(mob/user)
|
||||
if(safety)
|
||||
safety = 0
|
||||
to_chat(user, "<span class='warning'>You silently fry [src]'s vocal circuit with the cryptographic sequencer.</span>")
|
||||
else
|
||||
return
|
||||
|
||||
/obj/item/clothing/mask/gas/sechailer/attackby(obj/item/I, mob/user)
|
||||
if(I.is_screwdriver())
|
||||
switch(aggressiveness)
|
||||
if(1)
|
||||
to_chat(user, "<span class='notice'>You set the aggressiveness restrictor to the second position.</span>")
|
||||
aggressiveness = 2
|
||||
phrase = 7
|
||||
if(2)
|
||||
to_chat(user, "<span class='notice'>You set the aggressiveness restrictor to the third position.</span>")
|
||||
aggressiveness = 3
|
||||
phrase = 13
|
||||
if(3)
|
||||
to_chat(user, "<span class='notice'>You set the aggressiveness restrictor to the fourth position.</span>")
|
||||
aggressiveness = 4
|
||||
phrase = 1
|
||||
if(4)
|
||||
to_chat(user, "<span class='notice'>You set the aggressiveness restrictor to the first position.</span>")
|
||||
aggressiveness = 1
|
||||
phrase = 1
|
||||
if(5)
|
||||
to_chat(user, "<span class='warning'>You adjust the restrictor but nothing happens, probably because its broken.</span>")
|
||||
if(I.is_wirecutter())
|
||||
if(aggressiveness != 5)
|
||||
to_chat(user, "<span class='warning'>You broke it!</span>")
|
||||
aggressiveness = 5
|
||||
if(I.is_crowbar())
|
||||
if(!hailer)
|
||||
to_chat(user, "<span class='warning'>This mask has an integrated hailer, you can't remove it!</span>")
|
||||
else
|
||||
var/obj/N = new /obj/item/clothing/mask/gas/half(src.loc)
|
||||
playsound(src, 'sound/items/Screwdriver.ogg', 50, 1)
|
||||
N.fingerprints = src.fingerprints
|
||||
N.fingerprintshidden = src.fingerprintshidden
|
||||
N.fingerprintslast = src.fingerprintslast
|
||||
N.suit_fibers = src.suit_fibers
|
||||
if(!isturf(N.loc))
|
||||
user.put_in_hands(hailer)
|
||||
user.put_in_hands(N)
|
||||
else
|
||||
hailer.loc = N.loc
|
||||
qdel(src)
|
||||
return
|
||||
..()
|
||||
|
||||
/obj/item/clothing/mask/gas/sechailer/verb/halt()
|
||||
set name = "HALT!"
|
||||
set category = "Objects"
|
||||
set desc = "Activate your face mask hailer."
|
||||
var/key = phrase_list[phrase]
|
||||
var/message = phrase_list[key]
|
||||
|
||||
if(cooldown < world.time - 35) // A cooldown, to stop people being jerks
|
||||
if(!safety)
|
||||
message = "FUCK YOUR CUNT YOU SHIT EATING COCKSUCKER MAN EAT A DONG FUCKING ASS RAMMING SHIT FUCK EAT PENISES IN YOUR FUCK FACE AND SHIT OUT ABORTIONS OF FUCK AND DO SHIT IN YOUR ASS YOU COCK FUCK SHIT MONKEY FUCK ASS WANKER FROM THE DEPTHS OF SHIT."
|
||||
usr.visible_message("[usr]'s Compli-o-Nator: <font color='red' size='4'><b>[message]</b></font>")
|
||||
playsound(src, 'sound/voice/binsult.ogg', 50, 0, 4) //Future sound channel = something like SFX
|
||||
cooldown = world.time
|
||||
return
|
||||
|
||||
usr.visible_message("[usr]'s Compli-o-Nator: <font color='red' size='4'><b>[message]</b></font>")
|
||||
playsound(src, "sound/voice/complionator/[key].ogg", 50, 0, 4) //future sound channel = something like SFX
|
||||
cooldown = world.time
|
||||
@@ -39,19 +39,19 @@
|
||||
|
||||
/obj/item/clothing/under/seromi/smock/dress/science
|
||||
name = "small research dress"
|
||||
icon_state = "seromi_dress_science"
|
||||
icon_state = "tesh_dress_science"
|
||||
|
||||
/obj/item/clothing/under/seromi/smock/dress/security
|
||||
name = "small security dress"
|
||||
icon_state = "seromi_dress_security"
|
||||
icon_state = "tesh_dress_security"
|
||||
|
||||
/obj/item/clothing/under/seromi/smock/dress/engine
|
||||
name = "small engineering dress"
|
||||
icon_state = "seromi_dress_engine"
|
||||
icon_state = "tesh_dress_engine"
|
||||
|
||||
/obj/item/clothing/under/seromi/smock/dress/medical
|
||||
name = "small medical dress"
|
||||
icon_state = "seromi_dress_medical"
|
||||
icon_state = "tesh_dress_medical"
|
||||
|
||||
/obj/item/clothing/under/seromi/smock/uniform
|
||||
name = "small command uniform"
|
||||
|
||||
@@ -133,9 +133,9 @@ Would like to add a law like "Law x is _______" where x = a number, and _____ is
|
||||
M.add_ion_law("HUMANS NEED TO EAT [what2pref] [what2] TO SURVIVE")
|
||||
if(5)
|
||||
to_chat(M, "<br>")
|
||||
to_chat(M, "<font color='red'>THE COLONY DIRECTOR, HOP AND HOS ARE [who2]...LAWS UPDATED</font>")
|
||||
to_chat(M, "<font color='red'>THE SITE MANAGER, HOP AND HOS ARE [who2]...LAWS UPDATED</font>")
|
||||
to_chat(M, "<br>")
|
||||
M.add_ion_law("THE COLONY DIRECTOR, HOP AND HOS ARE [who2]")
|
||||
M.add_ion_law("THE SITE MANAGER, HOP AND HOS ARE [who2]")
|
||||
if(6)
|
||||
to_chat(M, "<br>")
|
||||
to_chat(M, "<font color='red'>THE STATION IS BUILT FOR [who2]...LAWS UPDATED</font>")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
item_state = "electronic"
|
||||
origin_tech = list(TECH_MAGNET = 1, TECH_ENGINEERING = 1)
|
||||
matter = list(DEFAULT_WALL_MATERIAL = 150)
|
||||
var/scan_time = 5 SECONDS
|
||||
var/scan_time = 2 SECONDS
|
||||
|
||||
/obj/item/weapon/mining_scanner/attack_self(mob/user as mob)
|
||||
to_chat(user, "<span class='notice'>You begin sweeping \the [src] about, scanning for metal deposits.</span>")
|
||||
|
||||
@@ -63,7 +63,7 @@ var/global/list/SKILL_PRE = list("Engineer" = SKILL_ENGINEER, "Roboticist" = SKI
|
||||
/datum/skill/knowledge/law
|
||||
ID = "law"
|
||||
name = "Corporate Law"
|
||||
desc = "Your knowledge of corporate law and procedures. This includes Corporate Regulations, as well as general station rulings and procedures. A low level in this skill is typical for security officers, a high level in this skill is typical for Colony Directors."
|
||||
desc = "Your knowledge of corporate law and procedures. This includes Corporate Regulations, as well as general station rulings and procedures. A low level in this skill is typical for security officers, a high level in this skill is typical for Site Managers."
|
||||
field = "Security"
|
||||
secondary = 1
|
||||
|
||||
|
||||
@@ -71,6 +71,10 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
|
||||
if(linked)
|
||||
apply_visual(user)
|
||||
user.reset_view(linked)
|
||||
if(isliving(user))
|
||||
var/mob/living/L = user
|
||||
L.looking_elsewhere = 1
|
||||
L.handle_vision()
|
||||
user.set_viewsize(world.view + extra_view)
|
||||
GLOB.moved_event.register(user, src, /obj/machinery/computer/ship/proc/unlook)
|
||||
// TODO GLOB.stat_set_event.register(user, src, /obj/machinery/computer/ship/proc/unlook)
|
||||
@@ -78,6 +82,10 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
|
||||
|
||||
/obj/machinery/computer/ship/proc/unlook(var/mob/user)
|
||||
user.reset_view()
|
||||
if(isliving(user))
|
||||
var/mob/living/L = user
|
||||
L.looking_elsewhere = 0
|
||||
L.handle_vision()
|
||||
user.set_viewsize() // reset to default
|
||||
GLOB.moved_event.unregister(user, src, /obj/machinery/computer/ship/proc/unlook)
|
||||
// TODO GLOB.stat_set_event.unregister(user, src, /obj/machinery/computer/ship/proc/unlook)
|
||||
@@ -109,4 +117,4 @@ somewhere on that shuttle. Subtypes of these can be then used to perform ship ov
|
||||
var/M = W.resolve()
|
||||
if(M)
|
||||
unlook(M)
|
||||
. = ..()
|
||||
. = ..()
|
||||
|
||||
@@ -45,8 +45,29 @@ proc/get_deepspace(x,y)
|
||||
for(var/atom/movable/AM in contents)
|
||||
if(!AM.lost_in_space())
|
||||
return FALSE
|
||||
if(has_buckled_mobs())
|
||||
for(var/mob/M in buckled_mobs)
|
||||
if(!M.lost_in_space())
|
||||
return FALSE
|
||||
|
||||
return TRUE
|
||||
|
||||
/obj/item/device/uav/lost_in_space()
|
||||
if(state == 1)
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/obj/machinery/power/supermatter/lost_in_space()
|
||||
return FALSE
|
||||
|
||||
/obj/singularity/lost_in_space()
|
||||
return FALSE
|
||||
|
||||
/obj/vehicle/lost_in_space()
|
||||
if(load && !load.lost_in_space())
|
||||
return FALSE
|
||||
return ..()
|
||||
|
||||
/mob/lost_in_space()
|
||||
return isnull(client)
|
||||
|
||||
|
||||
@@ -5,12 +5,14 @@
|
||||
/obj/item/weapon/gun/energy/hooklauncher
|
||||
name = "gravity whip"
|
||||
desc = "A large, strange gauntlet."
|
||||
icon = 'icons/obj/gun2.dmi'
|
||||
icon_state = "gravwhip"
|
||||
item_state = "gravwhip"
|
||||
fire_sound_text = "laser blast"
|
||||
|
||||
fire_delay = 15
|
||||
charge_cost = 300
|
||||
charge_meter = FALSE
|
||||
|
||||
cell_type = /obj/item/weapon/cell/device/weapon
|
||||
projectile_type = /obj/item/projectile/energy/hook
|
||||
@@ -29,7 +31,6 @@
|
||||
cell_type = /obj/item/weapon/cell/device/weapon/recharge/alien
|
||||
battery_lock = TRUE
|
||||
charge_cost = 400
|
||||
charge_meter = FALSE
|
||||
|
||||
projectile_type = /obj/item/projectile/energy/hook/ring
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* "Magic" "Guns"
|
||||
*/
|
||||
|
||||
/obj/item/weapon/gun/magic
|
||||
name = "staff of nothing"
|
||||
desc = "This staff is boring to watch because even though it came first you've seen everything it can do in other staves for years."
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "staffofnothing"
|
||||
item_icons = list(
|
||||
slot_l_hand_str = 'icons/mob/items/lefthand_magic.dmi',
|
||||
slot_r_hand_str = 'icons/mob/items/righthand_magic.dmi',
|
||||
)
|
||||
fire_sound = 'sound/weapons/emitter.ogg'
|
||||
w_class = ITEMSIZE_HUGE
|
||||
projectile_type = null
|
||||
var/checks_antimagic = TRUE
|
||||
var/max_charges = 6
|
||||
var/charges = 0
|
||||
var/recharge_rate = 4
|
||||
var/charge_tick = 0
|
||||
var/can_charge = TRUE
|
||||
|
||||
/obj/item/weapon/gun/magic/consume_next_projectile()
|
||||
if(checks_antimagic && locate(/obj/item/weapon/nullrod) in usr) return null
|
||||
if(!ispath(projectile_type)) return null
|
||||
if(charges <= 0) return null
|
||||
|
||||
charges -= 1
|
||||
|
||||
return new projectile_type(src)
|
||||
|
||||
/obj/item/weapon/gun/magic/Initialize()
|
||||
. = ..()
|
||||
charges = max_charges
|
||||
if(can_charge)
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/weapon/gun/magic/Destroy()
|
||||
if(can_charge)
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/gun/magic/process()
|
||||
if (charges >= max_charges)
|
||||
charge_tick = 0
|
||||
return
|
||||
charge_tick++
|
||||
if(charge_tick < recharge_rate)
|
||||
return 0
|
||||
charge_tick = 0
|
||||
charges++
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/gun/magic/handle_click_empty(mob/user)
|
||||
if (user)
|
||||
user.visible_message("*wzhzhzh*", "<span class='danger'>The [name] whizzles quietly.</span>")
|
||||
else
|
||||
src.visible_message("*wzhzh*")
|
||||
playsound(src, 'sound/weapons/empty.ogg', 100, 1)
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
/obj/item/weapon/gun/magic/firestaff
|
||||
name = "flaming staff"
|
||||
desc = "A long, everburning torch."
|
||||
icon = 'icons/obj/wizard.dmi'
|
||||
icon_state = "staffoffire"
|
||||
item_state = "staff"
|
||||
fire_sound = 'sound/weapons/emitter.ogg'
|
||||
w_class = ITEMSIZE_HUGE
|
||||
checks_antimagic = TRUE
|
||||
max_charges = 6
|
||||
charges = 0
|
||||
recharge_rate = 4
|
||||
charge_tick = 0
|
||||
can_charge = TRUE
|
||||
|
||||
projectile_type = /obj/item/projectile/energy/fireball
|
||||
@@ -739,6 +739,8 @@
|
||||
|
||||
shot_from = launcher.name
|
||||
silenced = launcher.silenced
|
||||
if(user)
|
||||
firer = user
|
||||
|
||||
return launch_projectile(target, target_zone, user, params, angle_override, forced_spread)
|
||||
|
||||
|
||||
@@ -27,13 +27,15 @@
|
||||
var/list/help_messages = list("slaps", "pokes", "nudges", "bumps", "pinches")
|
||||
var/done_mob_unique = FALSE // Has the projectile already done something to a mob?
|
||||
|
||||
var/datum/beam/chain = null
|
||||
|
||||
/obj/item/projectile/energy/hook/launch_projectile(atom/target, target_zone, mob/user, params, angle_override, forced_spread = 0)
|
||||
var/expected_distance = get_dist(target, loc)
|
||||
range = expected_distance // So the hook hits the ground if no mob is hit.
|
||||
target_distance = expected_distance
|
||||
if(firer) // Needed to ensure later checks in impact and on hit function.
|
||||
launcher_intent = firer.a_intent
|
||||
firer.Beam(src,icon_state=beam_state,icon='icons/effects/beam.dmi',time=60, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=1)
|
||||
chain = firer.Beam(src,icon_state=beam_state,icon='icons/effects/beam.dmi',time=60, maxdistance=10,beam_type=/obj/effect/ebeam,beam_sleep_time=1)
|
||||
|
||||
if(launcher_intent)
|
||||
switch(launcher_intent)
|
||||
@@ -58,10 +60,22 @@
|
||||
|
||||
..() // Does the regular launching stuff.
|
||||
|
||||
/obj/item/projectile/energy/hook/after_move()
|
||||
if(chain)
|
||||
var/origin_turf = get_turf(firer)
|
||||
var/target_turf = get_turf(src)
|
||||
if(!chain.static_beam && (origin_turf != chain.origin_oldloc || target_turf != chain.target_oldloc))
|
||||
chain.origin_oldloc = origin_turf //so we don't keep checking against their initial positions, leading to endless Reset()+Draw() calls
|
||||
chain.target_oldloc = target_turf
|
||||
chain.Reset()
|
||||
chain.Draw()
|
||||
return
|
||||
|
||||
/obj/item/projectile/energy/hook/on_hit(var/atom/target, var/blocked = 0, var/def_zone = null)
|
||||
if(..())
|
||||
perform_intent_unique(target)
|
||||
|
||||
|
||||
/obj/item/projectile/energy/hook/on_impact(var/atom/A)
|
||||
perform_intent_unique(get_turf(A))
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
/obj/item/projectile/energy/fireball
|
||||
name = "fireball"
|
||||
icon_state = "fireball2"
|
||||
damage = 15
|
||||
damage_type = BURN
|
||||
check_armour = "bomb"
|
||||
armor_penetration = 25 // It's a great ball of fire.
|
||||
|
||||
combustion = TRUE
|
||||
|
||||
/obj/item/projectile/energy/fireball/on_hit(var/atom/target, var/blocked = 0)
|
||||
new /obj/effect/explosion(get_turf(target))
|
||||
explosion(target, -1, 0, 2)
|
||||
..()
|
||||
|
||||
/obj/item/projectile/energy/fireball/on_impact(var/atom/target)
|
||||
new /obj/effect/explosion(get_turf(target))
|
||||
explosion(target, -1, 0, 2)
|
||||
..()
|
||||
@@ -110,7 +110,7 @@
|
||||
remove_self(volume)
|
||||
|
||||
/datum/reagent/blood/synthblood
|
||||
name = "Synthetic blood"
|
||||
name = "synthetic blood"
|
||||
id = "synthblood"
|
||||
color = "#999966"
|
||||
volume_mod = 2
|
||||
@@ -119,8 +119,16 @@
|
||||
..()
|
||||
if(data && !data["blood_type"])
|
||||
data["blood_type"] = "O-"
|
||||
if(data && data["species"])
|
||||
data["species"] = null
|
||||
return
|
||||
|
||||
/datum/reagent/blood/synthblood/dilute
|
||||
name = "synthetic plasma"
|
||||
id = "synthblood_dilute"
|
||||
color = "#cacaaf"
|
||||
volume_mod = 1.2
|
||||
|
||||
// pure concentrated antibodies
|
||||
/datum/reagent/antibodies
|
||||
data = list("antibodies"=list())
|
||||
|
||||
@@ -128,6 +128,17 @@
|
||||
|
||||
temp_range = list(T0C + 100, T0C + 120)
|
||||
|
||||
/datum/chemical_reaction/distilling/synthplas
|
||||
name = "Distilling Synthplas"
|
||||
id = "distill_synthplas"
|
||||
result = "synthblood_dilute"
|
||||
required_reagents = list("protein" = 2, "antibodies" = 1, "bicaridine" = 1)
|
||||
result_amount = 3
|
||||
|
||||
reaction_rate = HALF_LIFE(15)
|
||||
|
||||
temp_range = list(T0C + 110, T0C + 130)
|
||||
|
||||
// Alcohol
|
||||
/datum/chemical_reaction/distilling/beer
|
||||
name = "Distilling Beer"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
var/target_temp = T20C
|
||||
|
||||
var/max_temp = T20C + 300
|
||||
var/max_temp = T0C + 300
|
||||
var/min_temp = T0C - 10
|
||||
|
||||
var/current_temp = T20C
|
||||
@@ -86,6 +86,16 @@
|
||||
|
||||
update_icon()
|
||||
|
||||
/obj/machinery/portable_atmospherics/powered/reagent_distillery/RefreshParts()
|
||||
var/total_laser_rating = 0
|
||||
for(var/obj/item/weapon/stock_parts/micro_laser/ML in component_parts)
|
||||
total_laser_rating += ML.rating
|
||||
|
||||
max_temp = initial(max_temp) + (50 * (total_laser_rating - 1))
|
||||
min_temp = max(1, initial(min_temp) - (30 * (total_laser_rating - 1)))
|
||||
|
||||
return
|
||||
|
||||
/obj/machinery/portable_atmospherics/powered/reagent_distillery/proc/setup_overlay_vars()
|
||||
overlay_output_beaker = image(icon = src.icon, icon_state = "[base_state]-output")
|
||||
overlay_input_beaker = image(icon = src.icon, icon_state = "[base_state]-input")
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
var/label_text = ""
|
||||
|
||||
var/blood_type = null
|
||||
var/reag_id = "blood"
|
||||
|
||||
/obj/item/weapon/reagent_containers/blood/Initialize()
|
||||
. = ..()
|
||||
@@ -35,7 +36,7 @@
|
||||
if(blood_type != null)
|
||||
label_text = "[blood_type]"
|
||||
update_iv_label()
|
||||
reagents.add_reagent("blood", 200, list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=blood_type,"resistances"=null,"trace_chem"=null))
|
||||
reagents.add_reagent(reag_id, 200, list("donor"=null,"viruses"=null,"blood_DNA"=null,"blood_type"=blood_type,"resistances"=null,"trace_chem"=null))
|
||||
update_icon()
|
||||
|
||||
/obj/item/weapon/reagent_containers/blood/on_reagent_change()
|
||||
@@ -95,6 +96,14 @@
|
||||
/obj/item/weapon/reagent_containers/blood/OMinus
|
||||
blood_type = "O-"
|
||||
|
||||
/obj/item/weapon/reagent_containers/blood/synthplas
|
||||
blood_type = "O-"
|
||||
reag_id = "synthblood_dilute"
|
||||
|
||||
/obj/item/weapon/reagent_containers/blood/synthblood
|
||||
blood_type = "O-"
|
||||
reag_id = "synthblood"
|
||||
|
||||
/obj/item/weapon/reagent_containers/blood/empty
|
||||
name = "Empty BloodPack"
|
||||
desc = "Seems pretty useless... Maybe if there were a way to fill it?"
|
||||
|
||||
@@ -1,157 +1,159 @@
|
||||
/obj/item/weapon/reagent_containers/borghypo
|
||||
name = "cyborg hypospray"
|
||||
desc = "An advanced chemical synthesizer and injection system, designed for heavy-duty medical equipment."
|
||||
icon = 'icons/obj/syringe.dmi'
|
||||
item_state = "hypo"
|
||||
icon_state = "borghypo"
|
||||
amount_per_transfer_from_this = 5
|
||||
volume = 30
|
||||
possible_transfer_amounts = null
|
||||
|
||||
var/mode = 1
|
||||
var/charge_cost = 50
|
||||
var/charge_tick = 0
|
||||
var/recharge_time = 5 //Time it takes for shots to recharge (in seconds)
|
||||
var/bypass_protection = FALSE // If true, can inject through things like spacesuits and armor.
|
||||
|
||||
var/list/reagent_ids = list("tricordrazine", "inaprovaline", "anti_toxin", "tramadol", "dexalin" ,"spaceacillin")
|
||||
var/list/reagent_volumes = list()
|
||||
var/list/reagent_names = list()
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/surgeon
|
||||
reagent_ids = list("tricordrazine", "inaprovaline", "oxycodone", "dexalin" ,"spaceacillin")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/crisis
|
||||
reagent_ids = list("tricordrazine", "inaprovaline", "anti_toxin", "tramadol", "dexalin" ,"spaceacillin")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/lost
|
||||
reagent_ids = list("tricordrazine", "bicaridine", "dexalin", "anti_toxin", "tramadol", "spaceacillin")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/merc
|
||||
name = "advanced cyborg hypospray"
|
||||
desc = "An advanced nanite and chemical synthesizer and injection system, designed for heavy-duty medical equipment. This type is capable of safely bypassing \
|
||||
thick materials that other hyposprays would struggle with."
|
||||
bypass_protection = TRUE // Because mercs tend to be in spacesuits.
|
||||
reagent_ids = list("healing_nanites", "hyperzine", "tramadol", "oxycodone", "spaceacillin", "peridaxon", "osteodaxon", "myelamine", "synthblood")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/Initialize()
|
||||
. = ..()
|
||||
|
||||
for(var/T in reagent_ids)
|
||||
reagent_volumes[T] = volume
|
||||
var/datum/reagent/R = SSchemistry.chemical_reagents[T]
|
||||
reagent_names += R.name
|
||||
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg+
|
||||
if(++charge_tick < recharge_time)
|
||||
return 0
|
||||
charge_tick = 0
|
||||
|
||||
if(isrobot(loc))
|
||||
var/mob/living/silicon/robot/R = loc
|
||||
if(R && R.cell)
|
||||
for(var/T in reagent_ids)
|
||||
if(reagent_volumes[T] < volume)
|
||||
R.cell.use(charge_cost)
|
||||
reagent_volumes[T] = min(reagent_volumes[T] + 5, volume)
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/attack(var/mob/living/M, var/mob/user)
|
||||
if(!istype(M))
|
||||
return
|
||||
|
||||
if(!reagent_volumes[reagent_ids[mode]])
|
||||
to_chat(user, "<span class='warning'>The injector is empty.</span>")
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(istype(H))
|
||||
var/obj/item/organ/external/affected = H.get_organ(user.zone_sel.selecting)
|
||||
if(!affected)
|
||||
to_chat(user, "<span class='danger'>\The [H] is missing that limb!</span>")
|
||||
return
|
||||
else if(affected.robotic >= ORGAN_ROBOT)
|
||||
to_chat(user, "<span class='danger'>You cannot inject a robotic limb.</span>")
|
||||
return
|
||||
|
||||
if(M.can_inject(user, 1, ignore_thickness = bypass_protection))
|
||||
to_chat(user, "<span class='notice'>You inject [M] with the injector.</span>")
|
||||
to_chat(M, "<span class='notice'>You feel a tiny prick!</span>")
|
||||
|
||||
if(M.reagents)
|
||||
var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]])
|
||||
M.reagents.add_reagent(reagent_ids[mode], t)
|
||||
reagent_volumes[reagent_ids[mode]] -= t
|
||||
add_attack_logs(user, M, "Borg injected with [reagent_ids[mode]]")
|
||||
to_chat(user, "<span class='notice'>[t] units injected. [reagent_volumes[reagent_ids[mode]]] units remaining.</span>")
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/attack_self(mob/user as mob) //Change the mode
|
||||
var/t = ""
|
||||
for(var/i = 1 to reagent_ids.len)
|
||||
if(t)
|
||||
t += ", "
|
||||
if(mode == i)
|
||||
t += "<b>[reagent_names[i]]</b>"
|
||||
else
|
||||
t += "<a href='?src=\ref[src];reagent=[reagent_ids[i]]'>[reagent_names[i]]</a>"
|
||||
t = "Available reagents: [t]."
|
||||
to_chat(user,t)
|
||||
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/Topic(var/href, var/list/href_list)
|
||||
if(href_list["reagent"])
|
||||
var/t = reagent_ids.Find(href_list["reagent"])
|
||||
if(t)
|
||||
playsound(src, 'sound/effects/pop.ogg', 50, 0)
|
||||
mode = t
|
||||
var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]]
|
||||
to_chat(usr, "<span class='notice'>Synthesizer is now producing '[R.name]'.</span>")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/examine(mob/user)
|
||||
. = ..()
|
||||
if(get_dist(user, src) <= 2)
|
||||
var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]]
|
||||
. += "<span class='notice'>It is currently producing [R.name] and has [reagent_volumes[reagent_ids[mode]]] out of [volume] units left.</span>"
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/service
|
||||
name = "cyborg drink synthesizer"
|
||||
desc = "A portable drink dispencer."
|
||||
icon = 'icons/obj/drinks.dmi'
|
||||
icon_state = "shaker"
|
||||
charge_cost = 20
|
||||
recharge_time = 3
|
||||
volume = 60
|
||||
possible_transfer_amounts = list(5, 10, 20, 30)
|
||||
reagent_ids = list("ale", "cider", "beer", "berryjuice", "bitters", "coffee", "cognac", "cola", "dr_gibb", "egg", "gin", "gingerale", "hot_coco", "ice", "icetea", "kahlua", "lemonjuice", "lemon_lime", "limejuice", "mead", "milk", "mint", "orangejuice", "rum", "sake", "sodawater", "soymilk", "space_up", "spacemountainwind", "specialwhiskey", "sugar", "tea", "tequilla", "tomatojuice", "tonic", "vermouth", "vodka", "water", "watermelonjuice", "whiskey", "wine")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/service/attack(var/mob/M, var/mob/user)
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/service/afterattack(var/obj/target, var/mob/user, var/proximity)
|
||||
if(!proximity)
|
||||
return
|
||||
|
||||
if(!target.is_open_container() || !target.reagents)
|
||||
return
|
||||
|
||||
if(!reagent_volumes[reagent_ids[mode]])
|
||||
to_chat(user, "<span class='notice'>[src] is out of this reagent, give it some time to refill.</span>")
|
||||
return
|
||||
|
||||
if(!target.reagents.get_free_space())
|
||||
to_chat(user, "<span class='notice'>[target] is full.</span>")
|
||||
return
|
||||
|
||||
var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]])
|
||||
target.reagents.add_reagent(reagent_ids[mode], t)
|
||||
reagent_volumes[reagent_ids[mode]] -= t
|
||||
to_chat(user, "<span class='notice'>You transfer [t] units of the solution to [target].</span>")
|
||||
return
|
||||
/obj/item/weapon/reagent_containers/borghypo
|
||||
name = "cyborg hypospray"
|
||||
desc = "An advanced chemical synthesizer and injection system, designed for heavy-duty medical equipment."
|
||||
icon = 'icons/obj/syringe.dmi'
|
||||
item_state = "hypo"
|
||||
icon_state = "borghypo"
|
||||
amount_per_transfer_from_this = 5
|
||||
volume = 30
|
||||
possible_transfer_amounts = null
|
||||
|
||||
var/mode = 1
|
||||
var/charge_cost = 50
|
||||
var/charge_tick = 0
|
||||
var/recharge_time = 5 //Time it takes for shots to recharge (in seconds)
|
||||
var/bypass_protection = FALSE // If true, can inject through things like spacesuits and armor.
|
||||
|
||||
var/list/reagent_ids = list("tricordrazine", "inaprovaline", "anti_toxin", "tramadol", "dexalin" ,"spaceacillin")
|
||||
var/list/reagent_volumes = list()
|
||||
var/list/reagent_names = list()
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/surgeon
|
||||
reagent_ids = list("tricordrazine", "inaprovaline", "oxycodone", "dexalin" ,"spaceacillin")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/crisis
|
||||
reagent_ids = list("tricordrazine", "inaprovaline", "anti_toxin", "tramadol", "dexalin" ,"spaceacillin")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/lost
|
||||
reagent_ids = list("tricordrazine", "bicaridine", "dexalin", "anti_toxin", "tramadol", "spaceacillin")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/merc
|
||||
name = "advanced cyborg hypospray"
|
||||
desc = "An advanced nanite and chemical synthesizer and injection system, designed for heavy-duty medical equipment. This type is capable of safely bypassing \
|
||||
thick materials that other hyposprays would struggle with."
|
||||
bypass_protection = TRUE // Because mercs tend to be in spacesuits.
|
||||
reagent_ids = list("healing_nanites", "hyperzine", "tramadol", "oxycodone", "spaceacillin", "peridaxon", "osteodaxon", "myelamine", "synthblood")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/Initialize()
|
||||
. = ..()
|
||||
|
||||
for(var/T in reagent_ids)
|
||||
reagent_volumes[T] = volume
|
||||
var/datum/reagent/R = SSchemistry.chemical_reagents[T]
|
||||
reagent_names += R.name
|
||||
|
||||
START_PROCESSING(SSobj, src)
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/Destroy()
|
||||
STOP_PROCESSING(SSobj, src)
|
||||
return ..()
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/process() //Every [recharge_time] seconds, recharge some reagents for the cyborg+
|
||||
if(++charge_tick < recharge_time)
|
||||
return 0
|
||||
charge_tick = 0
|
||||
|
||||
if(isrobot(loc))
|
||||
var/mob/living/silicon/robot/R = loc
|
||||
if(R && R.cell)
|
||||
for(var/T in reagent_ids)
|
||||
if(reagent_volumes[T] < volume)
|
||||
R.cell.use(charge_cost)
|
||||
reagent_volumes[T] = min(reagent_volumes[T] + 5, volume)
|
||||
return 1
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/attack(var/mob/living/M, var/mob/user)
|
||||
if(!istype(M))
|
||||
return
|
||||
|
||||
if(!reagent_volumes[reagent_ids[mode]])
|
||||
to_chat(user, "<span class='warning'>The injector is empty.</span>")
|
||||
return
|
||||
|
||||
var/mob/living/carbon/human/H = M
|
||||
if(istype(H))
|
||||
var/obj/item/organ/external/affected = H.get_organ(user.zone_sel.selecting)
|
||||
if(!affected)
|
||||
to_chat(user, "<span class='danger'>\The [H] is missing that limb!</span>")
|
||||
return
|
||||
/* since synths have oil/coolant streams now, it only makes sense that you should be able to inject stuff. preserved for posterity.
|
||||
else if(affected.robotic >= ORGAN_ROBOT)
|
||||
to_chat(user, "<span class='danger'>You cannot inject a robotic limb.</span>")
|
||||
return
|
||||
*/
|
||||
|
||||
if(M.can_inject(user, 1, ignore_thickness = bypass_protection))
|
||||
to_chat(user, "<span class='notice'>You inject [M] with the injector.</span>")
|
||||
to_chat(M, "<span class='notice'>You feel a tiny prick!</span>")
|
||||
|
||||
if(M.reagents)
|
||||
var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]])
|
||||
M.reagents.add_reagent(reagent_ids[mode], t)
|
||||
reagent_volumes[reagent_ids[mode]] -= t
|
||||
add_attack_logs(user, M, "Borg injected with [reagent_ids[mode]]")
|
||||
to_chat(user, "<span class='notice'>[t] units injected. [reagent_volumes[reagent_ids[mode]]] units remaining.</span>")
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/attack_self(mob/user as mob) //Change the mode
|
||||
var/t = ""
|
||||
for(var/i = 1 to reagent_ids.len)
|
||||
if(t)
|
||||
t += ", "
|
||||
if(mode == i)
|
||||
t += "<b>[reagent_names[i]]</b>"
|
||||
else
|
||||
t += "<a href='?src=\ref[src];reagent=[reagent_ids[i]]'>[reagent_names[i]]</a>"
|
||||
t = "Available reagents: [t]."
|
||||
to_chat(user,t)
|
||||
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/Topic(var/href, var/list/href_list)
|
||||
if(href_list["reagent"])
|
||||
var/t = reagent_ids.Find(href_list["reagent"])
|
||||
if(t)
|
||||
playsound(src, 'sound/effects/pop.ogg', 50, 0)
|
||||
mode = t
|
||||
var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]]
|
||||
to_chat(usr, "<span class='notice'>Synthesizer is now producing '[R.name]'.</span>")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/examine(mob/user)
|
||||
. = ..()
|
||||
if(get_dist(user, src) <= 2)
|
||||
var/datum/reagent/R = SSchemistry.chemical_reagents[reagent_ids[mode]]
|
||||
. += "<span class='notice'>It is currently producing [R.name] and has [reagent_volumes[reagent_ids[mode]]] out of [volume] units left.</span>"
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/service
|
||||
name = "cyborg drink synthesizer"
|
||||
desc = "A portable drink dispencer."
|
||||
icon = 'icons/obj/drinks.dmi'
|
||||
icon_state = "shaker"
|
||||
charge_cost = 20
|
||||
recharge_time = 3
|
||||
volume = 60
|
||||
possible_transfer_amounts = list(5, 10, 20, 30)
|
||||
reagent_ids = list("ale", "cider", "beer", "berryjuice", "bitters", "coffee", "cognac", "cola", "dr_gibb", "egg", "gin", "gingerale", "hot_coco", "ice", "icetea", "kahlua", "lemonjuice", "lemon_lime", "limejuice", "mead", "milk", "mint", "orangejuice", "rum", "sake", "sodawater", "soymilk", "space_up", "spacemountainwind", "specialwhiskey", "sugar", "tea", "tequilla", "tomatojuice", "tonic", "vermouth", "vodka", "water", "watermelonjuice", "whiskey", "wine")
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/service/attack(var/mob/M, var/mob/user)
|
||||
return
|
||||
|
||||
/obj/item/weapon/reagent_containers/borghypo/service/afterattack(var/obj/target, var/mob/user, var/proximity)
|
||||
if(!proximity)
|
||||
return
|
||||
|
||||
if(!target.is_open_container() || !target.reagents)
|
||||
return
|
||||
|
||||
if(!reagent_volumes[reagent_ids[mode]])
|
||||
to_chat(user, "<span class='notice'>[src] is out of this reagent, give it some time to refill.</span>")
|
||||
return
|
||||
|
||||
if(!target.reagents.get_free_space())
|
||||
to_chat(user, "<span class='notice'>[target] is full.</span>")
|
||||
return
|
||||
|
||||
var/t = min(amount_per_transfer_from_this, reagent_volumes[reagent_ids[mode]])
|
||||
target.reagents.add_reagent(reagent_ids[mode], t)
|
||||
reagent_volumes[reagent_ids[mode]] -= t
|
||||
to_chat(user, "<span class='notice'>You transfer [t] units of the solution to [target].</span>")
|
||||
return
|
||||
@@ -42,9 +42,11 @@
|
||||
if(!affected)
|
||||
to_chat(user, "<span class='danger'>\The [H] is missing that limb!</span>")
|
||||
return
|
||||
/* since synths have oil/coolant streams now, it only makes sense that you should be able to inject stuff. preserved for posterity.
|
||||
else if(affected.robotic >= ORGAN_ROBOT)
|
||||
to_chat(user, "<span class='danger'>You cannot inject a robotic limb.</span>")
|
||||
return
|
||||
*/
|
||||
|
||||
if(!H.stat)
|
||||
if(H != user)
|
||||
|
||||
@@ -169,9 +169,11 @@
|
||||
if(!affected)
|
||||
to_chat(user, "<span class='danger'>\The [H] is missing that limb!</span>")
|
||||
return
|
||||
/* since synths have oil/coolant streams now, it only makes sense that you should be able to inject stuff. preserved for posterity.
|
||||
else if(affected.robotic >= ORGAN_ROBOT)
|
||||
to_chat(user, "<span class='danger'>You cannot inject a robotic limb.</span>")
|
||||
return
|
||||
*/
|
||||
|
||||
var/cycle_time = injtime*0.33 //33% of the time slept between 5u doses
|
||||
var/warmup_time = 0 //0 for containers
|
||||
|
||||
@@ -536,3 +536,6 @@ ENABLE_NIGHT_SHIFTS
|
||||
# ALLOW_BYOND_LINKS
|
||||
# ALLOW_DISCORD_LINKS
|
||||
ALLOW_URL_LINKS
|
||||
|
||||
# Control which submaps are loaded for the Dynamic Engine system
|
||||
ENGINE_MAP Supermatter Engine,Edison's Bane
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
################################
|
||||
# Example Changelog File
|
||||
#
|
||||
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
|
||||
#
|
||||
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
|
||||
# When it is, any changes listed below will disappear.
|
||||
#
|
||||
# Valid Prefixes:
|
||||
# bugfix
|
||||
# wip (For works in progress)
|
||||
# tweak
|
||||
# soundadd
|
||||
# sounddel
|
||||
# rscadd (general adding of nice things)
|
||||
# rscdel (general deleting of nice things)
|
||||
# imageadd
|
||||
# imagedel
|
||||
# maptweak
|
||||
# spellcheck (typo fixes)
|
||||
# experiment
|
||||
#################################
|
||||
|
||||
# Your name.
|
||||
author: Killian
|
||||
|
||||
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
|
||||
delete-after: True
|
||||
|
||||
# Any changes you've made. See valid prefix list above.
|
||||
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
|
||||
# SCREW THIS UP AND IT WON'T WORK.
|
||||
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
|
||||
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
|
||||
changes:
|
||||
- tweak: "You can now inject reagents directly into a synth's 'blood' stream using syringes and hypos (inc. borg hypos). Taking oil/coolant samples is still impossible."
|
||||
@@ -0,0 +1,38 @@
|
||||
################################
|
||||
# Example Changelog File
|
||||
#
|
||||
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
|
||||
#
|
||||
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
|
||||
# When it is, any changes listed below will disappear.
|
||||
#
|
||||
# Valid Prefixes:
|
||||
# bugfix
|
||||
# wip (For works in progress)
|
||||
# tweak
|
||||
# soundadd
|
||||
# sounddel
|
||||
# rscadd (general adding of nice things)
|
||||
# rscdel (general deleting of nice things)
|
||||
# imageadd
|
||||
# imagedel
|
||||
# maptweak
|
||||
# spellcheck (typo fixes)
|
||||
# experiment
|
||||
#################################
|
||||
|
||||
# Your name.
|
||||
author: Mechoid
|
||||
|
||||
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
|
||||
delete-after: True
|
||||
|
||||
# Any changes you've made. See valid prefix list above.
|
||||
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
|
||||
# SCREW THIS UP AND IT WON'T WORK.
|
||||
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
|
||||
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
|
||||
changes:
|
||||
- rscadd: "Added Distillery to Chemistry, beside the Grinder."
|
||||
- rscadd: "Added distilling recipe for Synthetic Plasma, which acts as a blood-restoration of 1.2 units per synthplas. Also orderable in cargo."
|
||||
- tweak: "Moves wrapper, spacecleaner, and labeller from the grinder table to the Chem locker."
|
||||
@@ -0,0 +1,37 @@
|
||||
################################
|
||||
# Example Changelog File
|
||||
#
|
||||
# Note: This file, and files beginning with ".", and files that don't end in ".yml" will not be read. If you change this file, you will look really dumb.
|
||||
#
|
||||
# Your changelog will be merged with a master changelog. (New stuff added only, and only on the date entry for the day it was merged.)
|
||||
# When it is, any changes listed below will disappear.
|
||||
#
|
||||
# Valid Prefixes:
|
||||
# bugfix
|
||||
# wip (For works in progress)
|
||||
# tweak
|
||||
# soundadd
|
||||
# sounddel
|
||||
# rscadd (general adding of nice things)
|
||||
# rscdel (general deleting of nice things)
|
||||
# imageadd
|
||||
# imagedel
|
||||
# maptweak
|
||||
# spellcheck (typo fixes)
|
||||
# experiment
|
||||
#################################
|
||||
|
||||
# Your name.
|
||||
author: SplinterGP
|
||||
|
||||
# Optional: Remove this file after generating master changelog. Useful for PR changelogs that won't get used again.
|
||||
delete-after: True
|
||||
|
||||
# Any changes you've made. See valid prefix list above.
|
||||
# INDENT WITH TWO SPACES. NOT TABS. SPACES.
|
||||
# SCREW THIS UP AND IT WON'T WORK.
|
||||
# Also, all entries are changed into a single [] after a master changelog generation. Just remove the brackets when you add new entries.
|
||||
# Please surround your changes in double quotes ("), as certain characters otherwise screws up compiling. The quotes will not show up in the changelog.
|
||||
changes:
|
||||
- rscadd: "Adds new hailer masks with quotes with sound, allowing you to use a hailer on a face mask to attach a security hailer into one."
|
||||
- soundadd: "Adds new sound effects for hailer face masks."
|
||||
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 34 KiB |
@@ -6736,7 +6736,7 @@
|
||||
"cDl" = (/obj/effect/floor_decal/borderfloorwhite{dir = 8},/obj/effect/floor_decal/corner/paleblue/border{dir = 8},/obj/effect/floor_decal/borderfloorwhite/corner2{dir = 10},/obj/effect/floor_decal/corner/paleblue/bordercorner2{dir = 10},/turf/simulated/floor/tiled/white,/area/medical/foyer)
|
||||
"cDm" = (/obj/machinery/atmospherics/unary/vent_scrubber/on{dir = 1},/turf/simulated/floor/tiled/white,/area/medical/foyer)
|
||||
"cDn" = (/obj/structure/bed/chair{dir = 8},/obj/effect/floor_decal/borderfloorwhite{dir = 4},/obj/effect/floor_decal/corner/paleblue/border{dir = 4},/obj/effect/floor_decal/borderfloorwhite/corner2{dir = 5},/obj/effect/floor_decal/corner/paleblue/bordercorner2{dir = 5},/turf/simulated/floor/tiled/white,/area/medical/foyer)
|
||||
"cDo" = (/obj/structure/table/glass,/obj/item/weapon/packageWrap,/obj/item/weapon/hand_labeler,/obj/item/weapon/reagent_containers/spray/cleaner{desc = "Someone has crossed out the 'Space' from Space Cleaner and written in Chemistry. Scrawled on the back is, 'Okay, whoever filled this with polytrinic acid, it was only funny the first time. It was hard enough replacing the CMO's first cat!'"; name = "Chemistry Cleaner"},/obj/effect/floor_decal/borderfloorwhite{dir = 8},/obj/effect/floor_decal/corner/beige/border{dir = 8},/turf/simulated/floor/tiled/white,/area/medical/chemistry)
|
||||
"cDo" = (/obj/structure/table/glass,/obj/effect/floor_decal/borderfloorwhite{dir = 8},/obj/effect/floor_decal/corner/beige/border{dir = 8},/obj/machinery/portable_atmospherics/powered/reagent_distillery,/obj/item/weapon/reagent_containers/glass/beaker/large,/turf/simulated/floor/tiled/white,/area/medical/chemistry)
|
||||
"cDp" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/structure/disposalpipe/segment,/turf/simulated/floor/tiled/white,/area/medical/chemistry)
|
||||
"cDq" = (/obj/structure/closet/secure_closet/chemical,/obj/item/weapon/storage/box/pillbottles,/obj/machinery/alarm{dir = 8; pixel_x = 22},/obj/effect/floor_decal/borderfloorwhite{dir = 4},/obj/effect/floor_decal/corner/beige/border{dir = 4},/obj/item/weapon/storage/box/syringes,/obj/item/weapon/tool/screwdriver,/turf/simulated/floor/tiled/white,/area/medical/chemistry)
|
||||
"cDr" = (/obj/structure/bed/chair/wheelchair,/obj/item/device/radio/intercom/department/medbay{dir = 4; pixel_x = -21},/obj/effect/floor_decal/borderfloorwhite{dir = 8},/obj/effect/floor_decal/corner/paleblue/border{dir = 8},/turf/simulated/floor/tiled/white,/area/medical/medbay_primary_storage)
|
||||
@@ -6824,7 +6824,7 @@
|
||||
"cEV" = (/obj/item/stack/material/phoron,/obj/item/stack/material/phoron,/obj/item/stack/material/phoron,/obj/item/stack/material/phoron,/obj/item/stack/material/phoron,/obj/structure/table/glass,/obj/machinery/light,/obj/effect/floor_decal/borderfloorwhite/corner{dir = 8},/obj/effect/floor_decal/corner/beige/bordercorner{dir = 8},/turf/simulated/floor/tiled/white,/area/medical/chemistry)
|
||||
"cEW" = (/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/structure/cable/green{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/structure/disposalpipe/segment,/obj/effect/floor_decal/steeldecal/steel_decals4{dir = 8},/obj/effect/floor_decal/steeldecal/steel_decals4{dir = 5},/turf/simulated/floor/tiled/white,/area/medical/chemistry)
|
||||
"cEX" = (/obj/structure/cable/green{d1 = 4; d2 = 8; icon_state = "4-8"},/obj/machinery/camera/network/medbay{c_tag = "MED - Chemistry"; dir = 1},/obj/effect/floor_decal/borderfloorwhite/corner,/obj/effect/floor_decal/corner/beige/bordercorner,/turf/simulated/floor/tiled/white,/area/medical/chemistry)
|
||||
"cEY" = (/obj/structure/closet/wardrobe/chemistry_white,/obj/item/device/radio/headset/headset_med,/obj/machinery/power/apc{name = "south bump"; pixel_y = -24},/obj/machinery/light_switch{pixel_x = 11; pixel_y = -24},/obj/item/device/radio/intercom{dir = 4; name = "Station Intercom (General)"; pixel_x = 21},/obj/structure/cable/green{d2 = 8; icon_state = "0-8"},/obj/item/weapon/storage/box/pillbottles,/obj/effect/floor_decal/borderfloorwhite{dir = 6},/obj/effect/floor_decal/corner/beige/border{dir = 6},/turf/simulated/floor/tiled/white,/area/medical/chemistry)
|
||||
"cEY" = (/obj/structure/closet/wardrobe/chemistry_white,/obj/item/device/radio/headset/headset_med,/obj/machinery/power/apc{name = "south bump"; pixel_y = -24},/obj/machinery/light_switch{pixel_x = 11; pixel_y = -24},/obj/item/device/radio/intercom{dir = 4; name = "Station Intercom (General)"; pixel_x = 21},/obj/structure/cable/green{d2 = 8; icon_state = "0-8"},/obj/item/weapon/storage/box/pillbottles,/obj/effect/floor_decal/borderfloorwhite{dir = 6},/obj/effect/floor_decal/corner/beige/border{dir = 6},/obj/item/weapon/reagent_containers/spray/cleaner{desc = "Someone has crossed out the 'Space' from Space Cleaner and written in Chemistry. Scrawled on the back is, 'Okay, whoever filled this with polytrinic acid, it was only funny the first time. It was hard enough replacing the CMO's first cat!'"; name = "Chemistry Cleaner"},/obj/item/weapon/hand_labeler,/obj/item/weapon/packageWrap,/turf/simulated/floor/tiled/white,/area/medical/chemistry)
|
||||
"cEZ" = (/obj/machinery/alarm{dir = 4; pixel_x = -22},/turf/simulated/floor,/area/maintenance/cargo)
|
||||
"cFa" = (/obj/structure/cable/green{d1 = 2; d2 = 4; icon_state = "2-4"},/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers,/obj/machinery/atmospherics/pipe/simple/hidden/supply,/obj/effect/floor_decal/steeldecal/steel_decals4{dir = 8},/obj/effect/floor_decal/steeldecal/steel_decals4{dir = 5},/turf/simulated/floor/tiled/white,/area/medical/medbay_primary_storage)
|
||||
"cFb" = (/obj/structure/table/standard,/obj/item/roller,/obj/item/roller{pixel_y = 8},/obj/item/roller{pixel_y = 16},/obj/machinery/power/apc{dir = 4; name = "east bump"; pixel_x = 24},/obj/machinery/light_switch{name = "light switch "; pixel_x = 36},/obj/structure/cable/green{d2 = 8; icon_state = "0-8"},/obj/structure/disposalpipe/segment,/obj/effect/floor_decal/borderfloorwhite{dir = 4},/obj/effect/floor_decal/corner/paleblue/border{dir = 4},/turf/simulated/floor/tiled/white,/area/medical/medbay_primary_storage)
|
||||
|
||||
@@ -1593,6 +1593,7 @@
|
||||
#include "code\modules\clothing\masks\boxing.dm"
|
||||
#include "code\modules\clothing\masks\breath.dm"
|
||||
#include "code\modules\clothing\masks\gasmask.dm"
|
||||
#include "code\modules\clothing\masks\hailer.dm"
|
||||
#include "code\modules\clothing\masks\miscellaneous.dm"
|
||||
#include "code\modules\clothing\masks\voice.dm"
|
||||
#include "code\modules\clothing\rings\material.dm"
|
||||
@@ -2629,6 +2630,7 @@
|
||||
#include "code\modules\projectiles\brokenguns\projectile.dm"
|
||||
#include "code\modules\projectiles\guns\energy.dm"
|
||||
#include "code\modules\projectiles\guns\launcher.dm"
|
||||
#include "code\modules\projectiles\guns\magic.dm"
|
||||
#include "code\modules\projectiles\guns\modular_guns.dm"
|
||||
#include "code\modules\projectiles\guns\projectile.dm"
|
||||
#include "code\modules\projectiles\guns\vox.dm"
|
||||
@@ -2645,6 +2647,7 @@
|
||||
#include "code\modules\projectiles\guns\launcher\pneumatic.dm"
|
||||
#include "code\modules\projectiles\guns\launcher\rocket.dm"
|
||||
#include "code\modules\projectiles\guns\launcher\syringe_gun.dm"
|
||||
#include "code\modules\projectiles\guns\magic\fireball.dm"
|
||||
#include "code\modules\projectiles\guns\magnetic\bore.dm"
|
||||
#include "code\modules\projectiles\guns\magnetic\gasthrower.dm"
|
||||
#include "code\modules\projectiles\guns\magnetic\magnetic.dm"
|
||||
@@ -2670,6 +2673,7 @@
|
||||
#include "code\modules\projectiles\projectile\explosive.dm"
|
||||
#include "code\modules\projectiles\projectile\force.dm"
|
||||
#include "code\modules\projectiles\projectile\hook.dm"
|
||||
#include "code\modules\projectiles\projectile\magic.dm"
|
||||
#include "code\modules\projectiles\projectile\magnetic.dm"
|
||||
#include "code\modules\projectiles\projectile\pellets.dm"
|
||||
#include "code\modules\projectiles\projectile\scatter.dm"
|
||||
@@ -2729,7 +2733,7 @@
|
||||
#include "code\modules\reagents\hoses\hose.dm"
|
||||
#include "code\modules\reagents\hoses\hose_connector.dm"
|
||||
#include "code\modules\reagents\reagent_containers\blood_pack.dm"
|
||||
#include "code\modules\reagents\reagent_containers\borghydro.dm"
|
||||
#include "code\modules\reagents\reagent_containers\borghypo.dm"
|
||||
#include "code\modules\reagents\reagent_containers\dropper.dm"
|
||||
#include "code\modules\reagents\reagent_containers\glass.dm"
|
||||
#include "code\modules\reagents\reagent_containers\hypospray.dm"
|
||||
|
||||
@@ -369,7 +369,7 @@ rules:
|
||||
# max-depth: error
|
||||
## Enforce a maximum line length
|
||||
max-len: [error, {
|
||||
code: 80,
|
||||
code: 120, # Bump to 140 if this is still too in the way
|
||||
## Ignore imports
|
||||
ignorePattern: '^(import\s.+\sfrom\s|.*require\()',
|
||||
ignoreUrls: true,
|
||||
|
||||
@@ -77,7 +77,7 @@ input. The input's `action` and `params` are passed to the proc.
|
||||
```dm
|
||||
/obj/machinery/my_machine/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
return TRUE
|
||||
if(action == "change_color")
|
||||
var/new_color = params["color"]
|
||||
if(!(color in allowed_coors))
|
||||
@@ -305,7 +305,7 @@ upon code review):
|
||||
|
||||
/obj/copypasta/tgui_act(action, params)
|
||||
if(..())
|
||||
return
|
||||
return TRUE
|
||||
switch(action)
|
||||
if("copypasta")
|
||||
var/newvar = params["var"]
|
||||
|
||||
@@ -91,7 +91,7 @@ export const toTitleCase = str => {
|
||||
return str;
|
||||
}
|
||||
// Handle string
|
||||
const WORDS_UPPER = ['Id', 'Tv'];
|
||||
const WORDS_UPPER = ['Id', 'Tv', 'Rcd'];
|
||||
const WORDS_LOWER = [
|
||||
'A', 'An', 'And', 'As', 'At', 'But', 'By', 'For', 'For', 'From', 'In',
|
||||
'Into', 'Near', 'Nor', 'Of', 'On', 'Onto', 'Or', 'The', 'To', 'With',
|
||||
@@ -105,7 +105,7 @@ export const toTitleCase = str => {
|
||||
}
|
||||
for (let word of WORDS_UPPER) {
|
||||
const regex = new RegExp('\\b' + word + '\\b', 'g');
|
||||
currentStr = currentStr.replace(regex, str => str.toLowerCase());
|
||||
currentStr = currentStr.replace(regex, str => str.toUpperCase());
|
||||
}
|
||||
return currentStr;
|
||||
};
|
||||
@@ -120,7 +120,7 @@ export const decodeHtmlEntities = str => {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
const translate_re = /&(nbsp|amp|quot|lt|gt|apos);/g;
|
||||
const translate_re = /&(nbsp|amp|quot|lt|gt|apos|trade);/g;
|
||||
const translate = {
|
||||
nbsp: ' ',
|
||||
amp: '&',
|
||||
@@ -128,6 +128,7 @@ export const decodeHtmlEntities = str => {
|
||||
lt: '<',
|
||||
gt: '>',
|
||||
apos: '\'',
|
||||
trade: '™',
|
||||
};
|
||||
return str
|
||||
// Newline tags
|
||||
|
||||
@@ -73,7 +73,10 @@ const mapColorPropTo = attrName => (style, value) => {
|
||||
|
||||
const styleMapperByPropName = {
|
||||
// Direct mapping
|
||||
display: mapRawPropTo('display'),
|
||||
position: mapRawPropTo('position'),
|
||||
float: mapRawPropTo('float'),
|
||||
clear: mapRawPropTo('clear'),
|
||||
overflow: mapRawPropTo('overflow'),
|
||||
overflowX: mapRawPropTo('overflow-x'),
|
||||
overflowY: mapRawPropTo('overflow-y'),
|
||||
@@ -99,6 +102,8 @@ const styleMapperByPropName = {
|
||||
opacity: mapRawPropTo('opacity'),
|
||||
textAlign: mapRawPropTo('text-align'),
|
||||
verticalAlign: mapRawPropTo('vertical-align'),
|
||||
textTransform: mapRawPropTo('text-transform'),
|
||||
wordWrap: mapRawPropTo('word-wrap'),
|
||||
// Boolean props
|
||||
inline: mapBooleanPropTo('display', 'inline-block'),
|
||||
bold: mapBooleanPropTo('font-weight', 'bold'),
|
||||
@@ -136,6 +141,18 @@ const styleMapperByPropName = {
|
||||
color: mapColorPropTo('color'),
|
||||
textColor: mapColorPropTo('color'),
|
||||
backgroundColor: mapColorPropTo('background-color'),
|
||||
// Flex props
|
||||
order: mapRawPropTo('order'),
|
||||
flexDirection: mapRawPropTo('flex-direction'),
|
||||
flexGrow: mapRawPropTo('flex-grow'),
|
||||
flexShrink: mapRawPropTo('flex-shrink'),
|
||||
flexWrap: mapRawPropTo('flex-wrap'),
|
||||
flexFlow: mapRawPropTo('flex-flow'),
|
||||
flexBasis: mapRawPropTo('flex-basis'),
|
||||
flex: mapRawPropTo('flex'),
|
||||
alignItems: mapRawPropTo('align-items'),
|
||||
justifyContent: mapRawPropTo('justify-content'),
|
||||
alignSelf: mapRawPropTo('align-self'),
|
||||
// Utility props
|
||||
fillPositionedParent: (style, value) => {
|
||||
if (value) {
|
||||
@@ -151,6 +168,9 @@ const styleMapperByPropName = {
|
||||
export const computeBoxProps = props => {
|
||||
const computedProps = {};
|
||||
const computedStyles = {};
|
||||
if (props.double) {
|
||||
computedStyles["transform"] = "scale(2);";
|
||||
}
|
||||
// Compute props
|
||||
for (let propName of Object.keys(props)) {
|
||||
if (propName === 'style') {
|
||||
|
||||
@@ -25,10 +25,13 @@ export const Button = props => {
|
||||
selected,
|
||||
tooltip,
|
||||
tooltipPosition,
|
||||
tooltipScale,
|
||||
ellipsis,
|
||||
content,
|
||||
iconRotation,
|
||||
iconSpin,
|
||||
iconColor,
|
||||
iconSize,
|
||||
children,
|
||||
onclick,
|
||||
onClick,
|
||||
@@ -89,14 +92,17 @@ export const Button = props => {
|
||||
<Icon
|
||||
name={icon}
|
||||
rotation={iconRotation}
|
||||
spin={iconSpin} />
|
||||
spin={iconSpin}
|
||||
color={iconColor}
|
||||
fontSize={iconSize} />
|
||||
)}
|
||||
{content}
|
||||
{children}
|
||||
{tooltip && (
|
||||
<Tooltip
|
||||
content={tooltip}
|
||||
position={tooltipPosition} />
|
||||
position={tooltipPosition}
|
||||
scale={tooltipScale} />
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
|
||||
@@ -170,6 +170,7 @@ export class DraggableControl extends Component {
|
||||
onDrag,
|
||||
children,
|
||||
// Input props
|
||||
forcedInputWidth,
|
||||
height,
|
||||
lineHeight,
|
||||
fontSize,
|
||||
@@ -206,6 +207,7 @@ export class DraggableControl extends Component {
|
||||
style={{
|
||||
display: !editing ? 'none' : undefined,
|
||||
height: height,
|
||||
width: forcedInputWidth,
|
||||
'line-height': lineHeight,
|
||||
'font-size': fontSize,
|
||||
}}
|
||||
|
||||
@@ -52,16 +52,18 @@ export class Dropdown extends Component {
|
||||
{option}
|
||||
</div>
|
||||
));
|
||||
ops.unshift((
|
||||
<div
|
||||
key={placeholder}
|
||||
className="Dropdown__menuentry"
|
||||
onClick={() => {
|
||||
this.setSelected(null);
|
||||
}}>
|
||||
-- {placeholder} --
|
||||
</div>
|
||||
));
|
||||
if (placeholder) {
|
||||
ops.unshift((
|
||||
<div
|
||||
key={placeholder}
|
||||
className="Dropdown__menuentry"
|
||||
onClick={() => {
|
||||
this.setSelected(null);
|
||||
}}>
|
||||
-- {placeholder} --
|
||||
</div>
|
||||
));
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
@@ -73,6 +75,7 @@ export class Dropdown extends Component {
|
||||
noscroll,
|
||||
nochevron,
|
||||
width,
|
||||
maxHeight,
|
||||
onClick,
|
||||
selected,
|
||||
disabled,
|
||||
@@ -92,6 +95,7 @@ export class Dropdown extends Component {
|
||||
tabIndex="-1"
|
||||
style={{
|
||||
'width': width,
|
||||
'max-height': maxHeight,
|
||||
}}
|
||||
className={classes([
|
||||
noscroll && 'Dropdown__menu-noscroll' || 'Dropdown__menu',
|
||||
|
||||
@@ -13,9 +13,11 @@ export const computeFlexProps = props => {
|
||||
direction,
|
||||
wrap,
|
||||
align,
|
||||
alignContent,
|
||||
justify,
|
||||
inline,
|
||||
spacing = 0,
|
||||
spacingPrecise = 0,
|
||||
...rest
|
||||
} = props;
|
||||
return {
|
||||
@@ -28,6 +30,7 @@ export const computeFlexProps = props => {
|
||||
),
|
||||
inline && 'Flex--inline',
|
||||
spacing > 0 && 'Flex--spacing--' + spacing,
|
||||
spacingPrecise > 0 && 'Flex--spacingPrecise--' + spacingPrecise,
|
||||
className,
|
||||
]),
|
||||
style: {
|
||||
@@ -35,6 +38,7 @@ export const computeFlexProps = props => {
|
||||
'flex-direction': direction,
|
||||
'flex-wrap': wrap,
|
||||
'align-items': align,
|
||||
'align-content': alignContent,
|
||||
'justify-content': justify,
|
||||
},
|
||||
...rest,
|
||||
|
||||
@@ -76,6 +76,11 @@ export class Input extends Component {
|
||||
const input = this.inputRef.current;
|
||||
if (input) {
|
||||
input.value = toInputValue(nextValue);
|
||||
if (this.props.autofocus) {
|
||||
input.focus();
|
||||
input.selectionStart = 0;
|
||||
input.selectionEnd = input.value.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +109,7 @@ export class Input extends Component {
|
||||
value,
|
||||
maxLength,
|
||||
placeholder,
|
||||
autofocus,
|
||||
...boxProps
|
||||
} = props;
|
||||
// Box props
|
||||
|
||||
@@ -21,6 +21,7 @@ export const Knob = props => {
|
||||
const {
|
||||
// Draggable props (passthrough)
|
||||
animated,
|
||||
forcedInputWidth,
|
||||
format,
|
||||
maxValue,
|
||||
minValue,
|
||||
@@ -40,6 +41,7 @@ export const Knob = props => {
|
||||
size,
|
||||
bipolar,
|
||||
children,
|
||||
popUpPosition,
|
||||
...rest
|
||||
} = props;
|
||||
return (
|
||||
@@ -47,6 +49,7 @@ export const Knob = props => {
|
||||
dragMatrix={[0, -1]}
|
||||
{...{
|
||||
animated,
|
||||
forcedInputWidth,
|
||||
format,
|
||||
maxValue,
|
||||
minValue,
|
||||
@@ -107,7 +110,10 @@ export const Knob = props => {
|
||||
</div>
|
||||
</div>
|
||||
{dragging && (
|
||||
<div className="Knob__popupValue">
|
||||
<div className={classes([
|
||||
'Knob__popupValue',
|
||||
popUpPosition && 'Knob__popupValue--' + popUpPosition,
|
||||
])}>
|
||||
{displayElement}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -20,6 +20,7 @@ export const LabeledListItem = props => {
|
||||
labelColor = 'label',
|
||||
color,
|
||||
textAlign,
|
||||
verticalAlign,
|
||||
buttons,
|
||||
content,
|
||||
children,
|
||||
@@ -33,6 +34,7 @@ export const LabeledListItem = props => {
|
||||
<Box
|
||||
as="td"
|
||||
color={labelColor}
|
||||
verticalAlign={verticalAlign}
|
||||
className={classes([
|
||||
'LabeledList__cell',
|
||||
'LabeledList__label',
|
||||
@@ -43,6 +45,7 @@ export const LabeledListItem = props => {
|
||||
as="td"
|
||||
color={color}
|
||||
textAlign={textAlign}
|
||||
verticalAlign={verticalAlign}
|
||||
className={classes([
|
||||
'LabeledList__cell',
|
||||
'LabeledList__content',
|
||||
|
||||
@@ -6,10 +6,21 @@ export const Modal = props => {
|
||||
const {
|
||||
className,
|
||||
children,
|
||||
onEnter,
|
||||
...rest
|
||||
} = props;
|
||||
let handleKeyDown;
|
||||
if (onEnter) {
|
||||
handleKeyDown = e => {
|
||||
let key = e.which || e.keyCode;
|
||||
if (key === 13) {
|
||||
onEnter(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
return (
|
||||
<Dimmer>
|
||||
<Dimmer
|
||||
onKeyDown={handleKeyDown}>
|
||||
<div
|
||||
className={classes([
|
||||
'Modal',
|
||||
|
||||
@@ -2,16 +2,15 @@ import { Box, Icon, Tooltip } from '.';
|
||||
import { Component } from 'inferno';
|
||||
import { useBackend } from "../backend";
|
||||
import { resolveAsset } from '../assets';
|
||||
import { logger } from '../logging';
|
||||
|
||||
export class NanoMap extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Auto center based on window size
|
||||
const Xcenter = (window.innerWidth / 2) - 256;
|
||||
|
||||
this.state = {
|
||||
offsetX: Xcenter,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
transform: 'none',
|
||||
dragging: false,
|
||||
@@ -42,8 +41,8 @@ export class NanoMap extends Component {
|
||||
const newOffsetX = e.screenX - state.originX;
|
||||
const newOffsetY = e.screenY - state.originY;
|
||||
if (prevState.dragging) {
|
||||
state.offsetX += newOffsetX;
|
||||
state.offsetY += newOffsetY;
|
||||
state.offsetX += (newOffsetX / this.props.zoom);
|
||||
state.offsetY += (newOffsetY / this.props.zoom);
|
||||
state.originX = e.screenX;
|
||||
state.originY = e.screenY;
|
||||
} else {
|
||||
@@ -71,11 +70,12 @@ export class NanoMap extends Component {
|
||||
const { offsetX, offsetY } = this.state;
|
||||
const { children, zoom, reset } = this.props;
|
||||
|
||||
let matrix
|
||||
= `matrix(${zoom}, 0, 0, ${zoom}, ${offsetX * zoom}, ${offsetY * zoom})`;
|
||||
|
||||
const newStyle = {
|
||||
width: '512px',
|
||||
height: '512px',
|
||||
"margin-top": offsetY + 'px',
|
||||
"margin-left": offsetX + 'px',
|
||||
width: '560px',
|
||||
height: '560px',
|
||||
"overflow": "hidden",
|
||||
"position": "relative",
|
||||
"padding": "0px",
|
||||
@@ -83,8 +83,7 @@ export class NanoMap extends Component {
|
||||
"url("+config.map+"_nanomap_z"+config.mapZLevel+".png)",
|
||||
"background-size": "cover",
|
||||
"text-align": "center",
|
||||
"transform-origin": "center center",
|
||||
"transform": "scale(" + zoom + ")",
|
||||
"transform": matrix,
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -110,25 +109,26 @@ const NanoMapMarker = (props, context) => {
|
||||
icon,
|
||||
tooltip,
|
||||
color,
|
||||
onClick,
|
||||
} = props;
|
||||
const rx = -256 * (zoom - 1) + x * (2 * zoom) - 1.5 * zoom - 3;
|
||||
const ry = 512 * zoom - (y * 2 * zoom) + zoom - 1.5;
|
||||
const rx = (x * 4) - 5;
|
||||
const ry = (y * 4) - 4;
|
||||
|
||||
return (
|
||||
<div style={"transform: scale(" + 1 / zoom + ")"}>
|
||||
<Box
|
||||
position="absolute"
|
||||
className="NanoMap__marker"
|
||||
lineHeight="0"
|
||||
top={ry + 'px'}
|
||||
left={rx + 'px'} >
|
||||
<Icon
|
||||
name={icon}
|
||||
color={color}
|
||||
fontSize="6px"
|
||||
/>
|
||||
<Tooltip content={tooltip} />
|
||||
</Box>
|
||||
</div>
|
||||
<Box
|
||||
position="absolute"
|
||||
className="NanoMap__marker"
|
||||
lineHeight="0"
|
||||
bottom={ry + 'px'}
|
||||
left={rx + 'px'}
|
||||
onMouseDown={onClick} >
|
||||
<Icon
|
||||
name={icon}
|
||||
color={color}
|
||||
fontSize="4px"
|
||||
/>
|
||||
<Tooltip content={tooltip} scale={zoom} />
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -14,7 +14,11 @@ export const Section = props => {
|
||||
level = 1,
|
||||
buttons,
|
||||
fill,
|
||||
stretchContents,
|
||||
noTopPadding,
|
||||
children,
|
||||
scrollable,
|
||||
flexGrow,
|
||||
...rest
|
||||
} = props;
|
||||
const hasTitle = !isFalsy(title) || !isFalsy(buttons);
|
||||
@@ -25,6 +29,8 @@ export const Section = props => {
|
||||
'Section',
|
||||
'Section--level--' + level,
|
||||
fill && 'Section--fill',
|
||||
scrollable && 'Section--scrollable',
|
||||
flexGrow && 'Section--flex',
|
||||
className,
|
||||
...computeBoxClassName(rest),
|
||||
])}
|
||||
@@ -40,7 +46,11 @@ export const Section = props => {
|
||||
</div>
|
||||
)}
|
||||
{hasContent && (
|
||||
<div className="Section__content">
|
||||
<div className={classes([
|
||||
"Section__content",
|
||||
!!stretchContents && "Section__content--stretchContents",
|
||||
!!noTopPadding && "Section__content--noTopPadding",
|
||||
])}>
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -4,6 +4,7 @@ export const Tooltip = props => {
|
||||
const {
|
||||
content,
|
||||
position = 'bottom',
|
||||
scale,
|
||||
} = props;
|
||||
// Empirically calculated length of the string,
|
||||
// at which tooltip text starts to overflow.
|
||||
@@ -14,6 +15,7 @@ export const Tooltip = props => {
|
||||
'Tooltip',
|
||||
long && 'Tooltip--long',
|
||||
position && 'Tooltip--' + position,
|
||||
scale && 'Tooltip--scale--' + Math.floor(scale),
|
||||
])}
|
||||
data-tooltip={content} />
|
||||
);
|
||||
|
||||
@@ -26,4 +26,4 @@ export { Section } from './Section';
|
||||
export { Slider } from './Slider';
|
||||
export { Table } from './Table';
|
||||
export { Tabs } from './Tabs';
|
||||
export { Tooltip } from './Tooltip';
|
||||
export { Tooltip } from './Tooltip';
|
||||
@@ -4,6 +4,10 @@ export const UI_UPDATE = 1;
|
||||
export const UI_DISABLED = 0;
|
||||
export const UI_CLOSE = -1;
|
||||
|
||||
// Atmospheric helpers
|
||||
/** 0.0 Degrees Celsius in Kelvin */
|
||||
export const T0C = 273.15;
|
||||
|
||||
// All game related colors are stored here
|
||||
export const COLORS = {
|
||||
// Department colors
|
||||
@@ -48,77 +52,105 @@ export const CSS_COLORS = [
|
||||
'label',
|
||||
];
|
||||
|
||||
|
||||
// If you ever add a new radio channel, you can either manually update this, or
|
||||
// go use /client/verb/generate_tgui_radio_constants() in communications.dm.
|
||||
export const RADIO_CHANNELS = [
|
||||
{
|
||||
name: 'Syndicate',
|
||||
freq: 1213,
|
||||
color: '#a52a2a',
|
||||
"name": "Mercenary",
|
||||
"freq": 1213,
|
||||
"color": "#6D3F40",
|
||||
},
|
||||
{
|
||||
name: 'Red Team',
|
||||
freq: 1215,
|
||||
color: '#ff4444',
|
||||
"name": "Raider",
|
||||
"freq": 1277,
|
||||
"color": "#6D3F40",
|
||||
},
|
||||
{
|
||||
name: 'Blue Team',
|
||||
freq: 1217,
|
||||
color: '#3434fd',
|
||||
"name": "Special Ops",
|
||||
"freq": 1341,
|
||||
"color": "#5C5C8A",
|
||||
},
|
||||
{
|
||||
name: 'CentCom',
|
||||
freq: 1337,
|
||||
color: '#2681a5',
|
||||
"name": "AI Private",
|
||||
"freq": 1343,
|
||||
"color": "#FF00FF",
|
||||
},
|
||||
{
|
||||
name: 'Supply',
|
||||
freq: 1347,
|
||||
color: '#b88646',
|
||||
"name": "Response Team",
|
||||
"freq": 1345,
|
||||
"color": "#5C5C8A",
|
||||
},
|
||||
{
|
||||
name: 'Service',
|
||||
freq: 1349,
|
||||
color: '#6ca729',
|
||||
"name": "Supply",
|
||||
"freq": 1347,
|
||||
"color": "#5F4519",
|
||||
},
|
||||
{
|
||||
name: 'Science',
|
||||
freq: 1351,
|
||||
color: '#c68cfa',
|
||||
"name": "Service",
|
||||
"freq": 1349,
|
||||
"color": "#6eaa2c",
|
||||
},
|
||||
{
|
||||
name: 'Command',
|
||||
freq: 1353,
|
||||
color: '#5177ff',
|
||||
"name": "Science",
|
||||
"freq": 1351,
|
||||
"color": "#993399",
|
||||
},
|
||||
{
|
||||
name: 'Medical',
|
||||
freq: 1355,
|
||||
color: '#57b8f0',
|
||||
"name": "Command",
|
||||
"freq": 1353,
|
||||
"color": "#193A7A",
|
||||
},
|
||||
{
|
||||
name: 'Engineering',
|
||||
freq: 1357,
|
||||
color: '#f37746',
|
||||
"name": "Medical",
|
||||
"freq": 1355,
|
||||
"color": "#008160",
|
||||
},
|
||||
{
|
||||
name: 'Security',
|
||||
freq: 1359,
|
||||
color: '#dd3535',
|
||||
"name": "Engineering",
|
||||
"freq": 1357,
|
||||
"color": "#A66300",
|
||||
},
|
||||
{
|
||||
name: 'AI Private',
|
||||
freq: 1447,
|
||||
color: '#d65d95',
|
||||
"name": "Security",
|
||||
"freq": 1359,
|
||||
"color": "#A30000",
|
||||
},
|
||||
{
|
||||
name: 'Common',
|
||||
freq: 1459,
|
||||
color: '#1ecc43',
|
||||
"name": "Explorer",
|
||||
"freq": 1361,
|
||||
"color": "#555555",
|
||||
},
|
||||
{
|
||||
"name": "Talon",
|
||||
"freq": 1363,
|
||||
"color": "#555555",
|
||||
},
|
||||
{
|
||||
"name": "Common",
|
||||
"freq": 1459,
|
||||
"color": "#008000",
|
||||
},
|
||||
{
|
||||
"name": "Entertainment",
|
||||
"freq": 1461,
|
||||
"color": "#339966",
|
||||
},
|
||||
{
|
||||
"name": "Security(I)",
|
||||
"freq": 1475,
|
||||
"color": "#008000",
|
||||
},
|
||||
{
|
||||
"name": "Medical(I)",
|
||||
"freq": 1485,
|
||||
"color": "#008000",
|
||||
},
|
||||
];
|
||||
|
||||
const GASES = [
|
||||
{
|
||||
'id': 'o2',
|
||||
'id': 'oxygen',
|
||||
'name': 'Oxygen',
|
||||
'label': 'O₂',
|
||||
'color': 'blue',
|
||||
@@ -130,15 +162,15 @@ const GASES = [
|
||||
'color': 'red',
|
||||
},
|
||||
{
|
||||
'id': 'co2',
|
||||
'id': 'carbon dioxide',
|
||||
'name': 'Carbon Dioxide',
|
||||
'label': 'CO₂',
|
||||
'color': 'grey',
|
||||
},
|
||||
{
|
||||
'id': 'plasma',
|
||||
'name': 'Plasma',
|
||||
'label': 'Plasma',
|
||||
'id': 'phoron',
|
||||
'name': 'Phoron',
|
||||
'label': 'Phoron',
|
||||
'color': 'pink',
|
||||
},
|
||||
{
|
||||
@@ -201,6 +233,24 @@ const GASES = [
|
||||
'label': 'H₂',
|
||||
'color': 'white',
|
||||
},
|
||||
{
|
||||
'id': 'other',
|
||||
'name': 'Other',
|
||||
'label': 'Other',
|
||||
'color': 'white',
|
||||
},
|
||||
{
|
||||
'id': 'pressure',
|
||||
'name': 'Pressure',
|
||||
'label': 'Pressure',
|
||||
'color': 'average',
|
||||
},
|
||||
{
|
||||
'id': 'temperature',
|
||||
'name': 'Temperature',
|
||||
'label': 'Temperature',
|
||||
'color': 'yellow',
|
||||
},
|
||||
];
|
||||
|
||||
export const getGasLabel = (gasId, fallbackValue) => {
|
||||
|
||||
@@ -94,3 +94,23 @@ export const formatMoney = (value, precision = 0) => {
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
export const formatCommaNumber = value => {
|
||||
if (!Number.isFinite(value)) {
|
||||
return value;
|
||||
}
|
||||
// From http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
|
||||
let parts = value.toString().split(".");
|
||||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
||||
return parts.join(".");
|
||||
};
|
||||
|
||||
// Function from https://stackoverflow.com/a/34841026. CC BY-SA 4.0.
|
||||
export const formatTime = seconds => {
|
||||
let hours = Math.floor(seconds / 3600);
|
||||
let minutes = Math.floor(seconds / 60) % 60;
|
||||
return [hours, minutes, seconds % 60]
|
||||
.map(v => v < 10 ? "0" + v : v)
|
||||
.filter((v, i) => v !== "00" || i > 0)
|
||||
.join(":");
|
||||
};
|
||||
|
||||
@@ -17,6 +17,10 @@ export const KEY_CTRL = 17;
|
||||
export const KEY_ALT = 18;
|
||||
export const KEY_ESCAPE = 27;
|
||||
export const KEY_SPACE = 32;
|
||||
export const KEY_ARROWLEFT = 37;
|
||||
export const KEY_ARROWUP = 38;
|
||||
export const KEY_ARROWRIGHT = 39;
|
||||
export const KEY_ARROWDOWN = 40;
|
||||
export const KEY_0 = 48;
|
||||
export const KEY_1 = 49;
|
||||
export const KEY_2 = 50;
|
||||
|
||||
@@ -43,6 +43,10 @@ let reactRoot;
|
||||
let initialRender = true;
|
||||
|
||||
const renderLayout = () => {
|
||||
let loadingRoot = document.getElementById('tgui-loading');
|
||||
if (loadingRoot) {
|
||||
loadingRoot.style.display = 'none';
|
||||
}
|
||||
perf.mark('render/start');
|
||||
const state = store.getState();
|
||||
const { suspended, assets } = selectBackend(state);
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
import { useBackend } from "../backend";
|
||||
import { Button, ProgressBar, LabeledList, Box, Section } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
|
||||
export const AICard = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
has_ai,
|
||||
integrity,
|
||||
backup_capacitor,
|
||||
flushing,
|
||||
has_laws,
|
||||
laws,
|
||||
wireless,
|
||||
radio,
|
||||
} = data;
|
||||
|
||||
if (has_ai === 0) {
|
||||
return (
|
||||
<Window
|
||||
width={600}
|
||||
height={470}
|
||||
resizable>
|
||||
<Window.Content>
|
||||
<Section title="Stored AI">
|
||||
<Box>
|
||||
<h3>No AI detected.</h3>
|
||||
</Box>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
} else {
|
||||
|
||||
let integrityColor = null; // Handles changing color of the integrity bar
|
||||
if (integrity >= 75) { integrityColor = 'green'; }
|
||||
else if (integrity >= 25) { integrityColor = 'yellow'; }
|
||||
else { integrityColor = 'red'; }
|
||||
|
||||
let powerColor = null;
|
||||
if (backup_capacitor >= 75) { powerColor = 'green'; }
|
||||
if (backup_capacitor >= 25) { powerColor = 'yellow'; }
|
||||
else { powerColor = 'red'; }
|
||||
|
||||
return (
|
||||
<Window
|
||||
width={600}
|
||||
height={470}
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
<Section title="Stored AI">
|
||||
<Box bold display="inline-block">
|
||||
<h3>{name}</h3>
|
||||
</Box>
|
||||
<Box>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Integrity">
|
||||
<ProgressBar
|
||||
color={integrityColor}
|
||||
value={integrity/100} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Power">
|
||||
<ProgressBar
|
||||
color={powerColor}
|
||||
value={backup_capacitor/100} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Box>
|
||||
<Box color="red">
|
||||
<h2>{flushing === 1 ? "Wipe of AI in progress..." : ""}</h2>
|
||||
</Box>
|
||||
</Section>
|
||||
|
||||
<Section title="Laws">
|
||||
{!!has_laws && (
|
||||
<Box>
|
||||
{laws.map((value, key) => (
|
||||
<Box key={key} display="inline-block">
|
||||
{value}
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
) || ( // Else, no laws.
|
||||
<Box color="red">
|
||||
<h3>No laws detected.</h3>
|
||||
</Box>
|
||||
)}
|
||||
</Section>
|
||||
|
||||
<Section title="Actions">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Wireless Activity">
|
||||
<Button
|
||||
icon={wireless ? "check" : "times"}
|
||||
content={wireless ? "Enabled" : "Disabled"}
|
||||
color={wireless ? "green" : "red"}
|
||||
onClick={() => act("wireless")} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Subspace Transceiver">
|
||||
<Button
|
||||
icon={radio ? "check" : "times"}
|
||||
content={radio ? "Enabled" : "Disabled"}
|
||||
color={radio ? "green" : "red"}
|
||||
onClick={() => act("radio")} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="AI Power">
|
||||
<Button.Confirm
|
||||
icon="radiation"
|
||||
confirmIcon="radiation"
|
||||
disabled={flushing || integrity === 0}
|
||||
confirmColor="red"
|
||||
content="Shutdown"
|
||||
onClick={() => act("wipe")} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,288 @@
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, Dimmer, Icon, LabeledList, NoticeBox, ProgressBar, Section, Flex } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
|
||||
import { FullscreenNotice } from './common/FullscreenNotice';
|
||||
|
||||
export const APC = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
let body = <ApcContent />;
|
||||
|
||||
if (data.gridCheck) {
|
||||
body = <GridCheck />;
|
||||
} else if (data.failTime) {
|
||||
body = <ApcFailure />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Window
|
||||
width={450}
|
||||
height={475}
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
{body}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const powerStatusMap = {
|
||||
2: {
|
||||
color: 'good',
|
||||
externalPowerText: 'External Power',
|
||||
chargingText: 'Fully Charged',
|
||||
},
|
||||
1: {
|
||||
color: 'average',
|
||||
externalPowerText: 'Low External Power',
|
||||
chargingText: 'Charging',
|
||||
},
|
||||
0: {
|
||||
color: 'bad',
|
||||
externalPowerText: 'No External Power',
|
||||
chargingText: 'Not Charging',
|
||||
},
|
||||
};
|
||||
|
||||
const malfMap = {
|
||||
1: {
|
||||
icon: 'terminal',
|
||||
content: 'Override Programming',
|
||||
action: 'hack',
|
||||
},
|
||||
// 2: {
|
||||
// icon: 'caret-square-down',
|
||||
// content: 'Shunt Core Process',
|
||||
// action: 'occupy',
|
||||
// },
|
||||
// 3: {
|
||||
// icon: 'caret-square-left',
|
||||
// content: 'Return to Main Core',
|
||||
// action: 'deoccupy',
|
||||
// },
|
||||
// 4: {
|
||||
// icon: 'caret-square-down',
|
||||
// content: 'Shunt Core Process',
|
||||
// action: 'occupy',
|
||||
// },
|
||||
};
|
||||
|
||||
const ApcContent = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const locked = data.locked && !data.siliconUser;
|
||||
const normallyLocked = data.normallyLocked;
|
||||
const externalPowerStatus = powerStatusMap[data.externalPower]
|
||||
|| powerStatusMap[0];
|
||||
const chargingStatus = powerStatusMap[data.chargingStatus]
|
||||
|| powerStatusMap[0];
|
||||
const channelArray = data.powerChannels || [];
|
||||
// const malfStatus = malfMap[data.malfStatus] || null;
|
||||
const adjustedCellChange = data.powerCellStatus / 100;
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<InterfaceLockNoticeBox deny={data.emagged} denialMessage={(
|
||||
<Fragment>
|
||||
<Box color="bad" fontSize="1.5rem">Fault in ID authenticator.</Box>
|
||||
<Box color="bad">Please contact maintenance for service.</Box>
|
||||
</Fragment>
|
||||
)} />
|
||||
<Section title="Power Status">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Main Breaker"
|
||||
color={externalPowerStatus.color}
|
||||
buttons={(
|
||||
<Button
|
||||
icon={data.isOperating ? 'power-off' : 'times'}
|
||||
content={data.isOperating ? 'On' : 'Off'}
|
||||
selected={data.isOperating && !locked}
|
||||
color={data.isOperating ? "" : "bad"}
|
||||
disabled={locked}
|
||||
onClick={() => act('breaker')} />
|
||||
)}>
|
||||
[ {externalPowerStatus.externalPowerText} ]
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Power Cell">
|
||||
<ProgressBar
|
||||
color="good"
|
||||
value={adjustedCellChange} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Charge Mode"
|
||||
color={chargingStatus.color}
|
||||
buttons={(
|
||||
<Button
|
||||
icon={data.chargeMode ? 'sync' : 'times'}
|
||||
content={data.chargeMode ? 'Auto' : 'Off'}
|
||||
selected={data.chargeMode}
|
||||
disabled={locked}
|
||||
onClick={() => act('charge')} />
|
||||
)}>
|
||||
[ {chargingStatus.chargingText} ]
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section title="Power Channels">
|
||||
<LabeledList>
|
||||
{channelArray.map(channel => {
|
||||
const { topicParams } = channel;
|
||||
return (
|
||||
<LabeledList.Item
|
||||
key={channel.title}
|
||||
label={channel.title}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
<Box inline mx={2}
|
||||
color={channel.status >= 2 ? 'good' : 'bad'}>
|
||||
{channel.status >= 2 ? 'On' : 'Off'}
|
||||
</Box>
|
||||
<Button
|
||||
icon="sync"
|
||||
content="Auto"
|
||||
selected={!locked && (
|
||||
channel.status === 1 || channel.status === 3
|
||||
)}
|
||||
disabled={locked}
|
||||
onClick={() => act('channel', topicParams.auto)} />
|
||||
<Button
|
||||
icon="power-off"
|
||||
content="On"
|
||||
selected={!locked && channel.status === 2}
|
||||
disabled={locked}
|
||||
onClick={() => act('channel', topicParams.on)} />
|
||||
<Button
|
||||
icon="times"
|
||||
content="Off"
|
||||
selected={!locked && channel.status === 0}
|
||||
disabled={locked}
|
||||
onClick={() => act('channel', topicParams.off)} />
|
||||
</Fragment>
|
||||
)}>
|
||||
{channel.powerLoad} W
|
||||
</LabeledList.Item>
|
||||
);
|
||||
})}
|
||||
<LabeledList.Item label="Total Load">
|
||||
{data.totalCharging ? (
|
||||
<b>{data.totalLoad} W (+ {data.totalCharging} W charging)</b>
|
||||
) : (
|
||||
<b>{data.totalLoad} W</b>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section
|
||||
title="Misc"
|
||||
buttons={!!data.siliconUser && (
|
||||
<Button
|
||||
icon="lightbulb-o"
|
||||
content="Overload"
|
||||
onClick={() => act('overload')} />
|
||||
)}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Cover Lock"
|
||||
buttons={(
|
||||
<Button
|
||||
icon={data.coverLocked ? 'lock' : 'unlock'}
|
||||
content={data.coverLocked ? 'Engaged' : 'Disengaged'}
|
||||
selected={data.coverLocked}
|
||||
disabled={locked}
|
||||
onClick={() => act('cover')} />
|
||||
)} />
|
||||
<LabeledList.Item
|
||||
label="Night Shift Lighting"
|
||||
buttons={(
|
||||
<Fragment>
|
||||
<Button
|
||||
icon="lightbulb-o"
|
||||
content="Disabled"
|
||||
selected={data.nightshiftSetting === 2}
|
||||
onClick={() => act('nightshift', {
|
||||
nightshift: 2,
|
||||
})} />
|
||||
<Button
|
||||
icon="lightbulb-o"
|
||||
content="Automatic"
|
||||
selected={data.nightshiftSetting === 1}
|
||||
onClick={() => act('nightshift', {
|
||||
nightshift: 1,
|
||||
})} />
|
||||
<Button
|
||||
icon="lightbulb-o"
|
||||
content="Enabled"
|
||||
selected={data.nightshiftSetting === 3}
|
||||
onClick={() => act('nightshift', {
|
||||
nightshift: 3,
|
||||
})} />
|
||||
</Fragment>
|
||||
)} />
|
||||
<LabeledList.Item
|
||||
label="Emergency Lighting"
|
||||
buttons={(
|
||||
<Button
|
||||
icon="lightbulb-o"
|
||||
content={data.emergencyLights ? 'Enabled' : 'Disabled'}
|
||||
selected={data.emergencyLights}
|
||||
onClick={() => act('emergency_lighting')} />
|
||||
)} />
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
const GridCheck = (props, context) => {
|
||||
return (
|
||||
<FullscreenNotice title="System Failure">
|
||||
<Box fontSize="1.5rem" bold>
|
||||
<Icon
|
||||
name="exclamation-triangle"
|
||||
verticalAlign="middle"
|
||||
size={3}
|
||||
mr="1rem"
|
||||
/>
|
||||
</Box>
|
||||
<Box fontSize="1.5rem" bold>
|
||||
Power surge detected, grid check in effect...
|
||||
</Box>
|
||||
</FullscreenNotice>
|
||||
);
|
||||
};
|
||||
|
||||
const ApcFailure = (props, context) => {
|
||||
const { data, act } = useBackend(context);
|
||||
|
||||
let rebootOptions = (
|
||||
<Button
|
||||
icon="repeat"
|
||||
content="Restart Now"
|
||||
color="good"
|
||||
onClick={() => act('reboot')}
|
||||
/>
|
||||
);
|
||||
|
||||
if (data.locked && !data.siliconUser) {
|
||||
rebootOptions = (
|
||||
<Box color="bad">Swipe an ID card for manual reboot.</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Dimmer textAlign="center">
|
||||
<Box color="bad"><h1>SYSTEM FAILURE</h1></Box>
|
||||
<Box color="average">
|
||||
<h2>
|
||||
I/O regulators malfunction detected! Waiting for system reboot...
|
||||
</h2>
|
||||
</Box>
|
||||
<Box color="good">Automatic reboot in {data.failTime} seconds...</Box>
|
||||
<Box mt={4}>
|
||||
{rebootOptions}
|
||||
</Box>
|
||||
</Dimmer>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,193 @@
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, LabeledList, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
const dangerMap = {
|
||||
2: {
|
||||
color: 'good',
|
||||
localStatusText: 'Optimal',
|
||||
},
|
||||
1: {
|
||||
color: 'average',
|
||||
localStatusText: 'Caution',
|
||||
},
|
||||
0: {
|
||||
color: 'bad',
|
||||
localStatusText: 'Offline',
|
||||
},
|
||||
};
|
||||
|
||||
export const AiAirlock = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const statusMain = dangerMap[data.power.main] || dangerMap[0];
|
||||
const statusBackup = dangerMap[data.power.backup] || dangerMap[0];
|
||||
const statusElectrify = dangerMap[data.shock] || dangerMap[0];
|
||||
return (
|
||||
<Window
|
||||
width={500}
|
||||
height={390}>
|
||||
<Window.Content>
|
||||
<Section title="Power Status">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="Main"
|
||||
color={statusMain.color}
|
||||
buttons={(
|
||||
<Button
|
||||
icon="lightbulb-o"
|
||||
disabled={!data.power.main}
|
||||
content="Disrupt"
|
||||
onClick={() => act('disrupt-main')} />
|
||||
)}>
|
||||
{data.power.main ? 'Online' : 'Offline'}
|
||||
{' '}
|
||||
{(!data.wires.main_1 || !data.wires.main_2)
|
||||
&& '[Wires have been cut!]'
|
||||
|| (data.power.main_timeleft > 0
|
||||
&& `[${data.power.main_timeleft}s]`)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Backup"
|
||||
color={statusBackup.color}
|
||||
buttons={(
|
||||
<Button
|
||||
icon="lightbulb-o"
|
||||
disabled={!data.power.backup}
|
||||
content="Disrupt"
|
||||
onClick={() => act('disrupt-backup')} />
|
||||
)}>
|
||||
{data.power.backup ? 'Online' : 'Offline'}
|
||||
{' '}
|
||||
{(!data.wires.backup_1 || !data.wires.backup_2)
|
||||
&& '[Wires have been cut!]'
|
||||
|| (data.power.backup_timeleft > 0
|
||||
&& `[${data.power.backup_timeleft}s]`)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Electrify"
|
||||
color={statusElectrify.color}
|
||||
buttons={(
|
||||
<Fragment>
|
||||
<Button
|
||||
icon="wrench"
|
||||
disabled={!(data.wires.shock && data.shock === 0)}
|
||||
content="Restore"
|
||||
onClick={() => act('shock-restore')} />
|
||||
<Button
|
||||
icon="bolt"
|
||||
disabled={!data.wires.shock}
|
||||
content="Temporary"
|
||||
onClick={() => act('shock-temp')} />
|
||||
<Button
|
||||
icon="bolt"
|
||||
disabled={!data.wires.shock}
|
||||
content="Permanent"
|
||||
onClick={() => act('shock-perm')} />
|
||||
</Fragment>
|
||||
)}>
|
||||
{data.shock === 2 ? 'Safe' : 'Electrified'}
|
||||
{' '}
|
||||
{!data.wires.shock
|
||||
&& '[Wires have been cut!]'
|
||||
|| (data.shock_timeleft > 0
|
||||
&& `[${data.shock_timeleft}s]`)
|
||||
|| (data.shock_timeleft === -1
|
||||
&& '[Permanent]')}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section title="Access and Door Control">
|
||||
<LabeledList>
|
||||
<LabeledList.Item
|
||||
label="ID Scan"
|
||||
color="bad"
|
||||
buttons={(
|
||||
<Button
|
||||
icon={data.id_scanner ? 'power-off' : 'times'}
|
||||
content={data.id_scanner ? 'Enabled' : 'Disabled'}
|
||||
selected={data.id_scanner}
|
||||
disabled={!data.wires.id_scanner}
|
||||
onClick={() => act('idscan-toggle')} />
|
||||
)}>
|
||||
{!data.wires.id_scanner && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
<LabeledList.Item
|
||||
label="Door Bolts"
|
||||
color="bad"
|
||||
buttons={(
|
||||
<Button
|
||||
icon={data.locked ? 'lock' : 'unlock'}
|
||||
content={data.locked ? 'Lowered' : 'Raised'}
|
||||
selected={data.locked}
|
||||
disabled={!data.wires.bolts}
|
||||
onClick={() => act('bolt-toggle')} />
|
||||
)}>
|
||||
{!data.wires.bolts && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Door Bolt Lights"
|
||||
color="bad"
|
||||
buttons={(
|
||||
<Button
|
||||
icon={data.lights ? 'power-off' : 'times'}
|
||||
content={data.lights ? 'Enabled' : 'Disabled'}
|
||||
selected={data.lights}
|
||||
disabled={!data.wires.lights}
|
||||
onClick={() => act('light-toggle')} />
|
||||
)}>
|
||||
{!data.wires.lights && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Door Force Sensors"
|
||||
color="bad"
|
||||
buttons={(
|
||||
<Button
|
||||
icon={data.safe ? 'power-off' : 'times'}
|
||||
content={data.safe ? 'Enabled' : 'Disabled'}
|
||||
selected={data.safe}
|
||||
disabled={!data.wires.safe}
|
||||
onClick={() => act('safe-toggle')} />
|
||||
)}>
|
||||
{!data.wires.safe && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Door Timing Safety"
|
||||
color="bad"
|
||||
buttons={(
|
||||
<Button
|
||||
icon={data.speed ? 'power-off' : 'times'}
|
||||
content={data.speed ? 'Enabled' : 'Disabled'}
|
||||
selected={data.speed}
|
||||
disabled={!data.wires.timing}
|
||||
onClick={() => act('speed-toggle')} />
|
||||
)}>
|
||||
{!data.wires.timing && '[Wires have been cut!]'}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider />
|
||||
<LabeledList.Item
|
||||
label="Door Control"
|
||||
color="bad"
|
||||
buttons={(
|
||||
<Button
|
||||
icon={data.opened ? 'sign-out-alt' : 'sign-in-alt'}
|
||||
content={data.opened ? 'Open' : 'Closed'}
|
||||
selected={data.opened}
|
||||
disabled={(data.locked || data.welded)}
|
||||
onClick={() => act('open-close')} />
|
||||
)}>
|
||||
{!!(data.locked || data.welded) && (
|
||||
<span>
|
||||
[Door is {data.locked ? 'bolted' : ''}
|
||||
{(data.locked && data.welded) ? ' and ' : ''}
|
||||
{data.welded ? 'welded' : ''}!]
|
||||
</span>
|
||||
)}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
import { round } from 'common/math';
|
||||
import { useBackend } from "../backend";
|
||||
import { Box, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
import { formatSiUnit, formatPower } from "../format";
|
||||
import { FullscreenNotice } from './common/FullscreenNotice';
|
||||
|
||||
export const AiSupermatter = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
|
||||
const {
|
||||
integrity_percentage,
|
||||
ambient_temp,
|
||||
ambient_pressure,
|
||||
detonating,
|
||||
} = data;
|
||||
|
||||
let body = <AiSupermatterContent />;
|
||||
if (detonating) {
|
||||
body = <AiSupermatterDetonation />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Window
|
||||
width={500}
|
||||
height={300}>
|
||||
<Window.Content>
|
||||
{body}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const AiSupermatterDetonation = (props, context) => (
|
||||
<FullscreenNotice title="DETONATION IMMINENT">
|
||||
<Box fontSize="1.5rem" bold color="bad">
|
||||
<Icon
|
||||
color="bad"
|
||||
name="exclamation-triangle"
|
||||
verticalAlign="middle"
|
||||
size={3}
|
||||
mr="1rem"
|
||||
/>
|
||||
<Box color="bad">CRYSTAL DELAMINATING</Box>
|
||||
<Box color="bad">Evacuate area immediately</Box>
|
||||
</Box>
|
||||
</FullscreenNotice>
|
||||
);
|
||||
|
||||
const AiSupermatterContent = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
|
||||
const {
|
||||
integrity_percentage,
|
||||
ambient_temp,
|
||||
ambient_pressure,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Section title="Status">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Crystal Integrity">
|
||||
<ProgressBar
|
||||
value={integrity_percentage}
|
||||
maxValue={100}
|
||||
ranges={{
|
||||
good: [90, Infinity],
|
||||
average: [25, 90],
|
||||
bad: [-Infinity, 25],
|
||||
}} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Environment Temperature">
|
||||
<ProgressBar
|
||||
value={ambient_temp}
|
||||
maxValue={10000}
|
||||
ranges={{
|
||||
bad: [5000, Infinity],
|
||||
average: [4000, 5000],
|
||||
good: [-Infinity, 4000],
|
||||
}}>
|
||||
{ambient_temp} K
|
||||
</ProgressBar>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Environment Pressure">
|
||||
{ambient_pressure} kPa
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,325 @@
|
||||
import { toFixed } from 'common/math';
|
||||
import { decodeHtmlEntities } from 'common/string';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { Box, Button, LabeledList, NumberInput, Section } from '../components';
|
||||
import { getGasLabel, getGasColor } from '../constants';
|
||||
import { Window } from '../layouts';
|
||||
import { InterfaceLockNoticeBox } from './common/InterfaceLockNoticeBox';
|
||||
import { Vent, Scrubber } from './common/AtmosControls';
|
||||
|
||||
export const AirAlarm = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const locked = data.locked && !data.siliconUser && !data.remoteUser;
|
||||
return (
|
||||
<Window
|
||||
width={440}
|
||||
height={650}
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
<InterfaceLockNoticeBox />
|
||||
<AirAlarmStatus />
|
||||
<AirAlarmUnlockedControl />
|
||||
{!locked && (
|
||||
<AirAlarmControl />
|
||||
)}
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const AirAlarmStatus = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const entries = (data.environment_data || [])
|
||||
.filter(entry => entry.value >= 0.01);
|
||||
const dangerMap = {
|
||||
0: {
|
||||
color: 'good',
|
||||
localStatusText: 'Optimal',
|
||||
},
|
||||
1: {
|
||||
color: 'average',
|
||||
localStatusText: 'Caution',
|
||||
},
|
||||
2: {
|
||||
color: 'bad',
|
||||
localStatusText: 'Danger (Internals Required)',
|
||||
},
|
||||
};
|
||||
const localStatus = dangerMap[data.danger_level] || dangerMap[0];
|
||||
return (
|
||||
<Section title="Air Status">
|
||||
<LabeledList>
|
||||
{entries.length > 0 && (
|
||||
<Fragment>
|
||||
{entries.map(entry => {
|
||||
const status = dangerMap[entry.danger_level] || dangerMap[0];
|
||||
return (
|
||||
<LabeledList.Item
|
||||
key={entry.name}
|
||||
label={getGasLabel(entry.name)}
|
||||
color={status.color}>
|
||||
{toFixed(entry.value, 2)}{entry.unit}
|
||||
</LabeledList.Item>
|
||||
);
|
||||
})}
|
||||
<LabeledList.Item
|
||||
label="Local status"
|
||||
color={localStatus.color}>
|
||||
{localStatus.localStatusText}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item
|
||||
label="Area status"
|
||||
color={data.atmos_alarm || data.fire_alarm ? 'bad' : 'good'}>
|
||||
{data.atmos_alarm && 'Atmosphere Alarm'
|
||||
|| data.fire_alarm && 'Fire Alarm'
|
||||
|| 'Nominal'}
|
||||
</LabeledList.Item>
|
||||
</Fragment>
|
||||
) || (
|
||||
<LabeledList.Item
|
||||
label="Warning"
|
||||
color="bad">
|
||||
Cannot obtain air sample for analysis.
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
{!!data.emagged && (
|
||||
<LabeledList.Item
|
||||
label="Warning"
|
||||
color="bad">
|
||||
Safety measures offline. Device may exhibit abnormal behavior.
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const AirAlarmUnlockedControl = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
target_temperature,
|
||||
rcon,
|
||||
} = data;
|
||||
return (
|
||||
<Section
|
||||
title="Comfort Settings">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Remote Control">
|
||||
<Button
|
||||
selected={rcon === 1}
|
||||
content="Off"
|
||||
onClick={() => act('rcon', { 'rcon': 1 })} />
|
||||
<Button
|
||||
selected={rcon === 2}
|
||||
content="Auto"
|
||||
onClick={() => act('rcon', { 'rcon': 2 })} />
|
||||
<Button
|
||||
selected={rcon === 3}
|
||||
content="On"
|
||||
onClick={() => act('rcon', { 'rcon': 3 })} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Thermostat">
|
||||
<Button
|
||||
content={target_temperature}
|
||||
onClick={() => act('temperature')} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const AIR_ALARM_ROUTES = {
|
||||
home: {
|
||||
title: 'Air Controls',
|
||||
component: () => AirAlarmControlHome,
|
||||
},
|
||||
vents: {
|
||||
title: 'Vent Controls',
|
||||
component: () => AirAlarmControlVents,
|
||||
},
|
||||
scrubbers: {
|
||||
title: 'Scrubber Controls',
|
||||
component: () => AirAlarmControlScrubbers,
|
||||
},
|
||||
modes: {
|
||||
title: 'Operating Mode',
|
||||
component: () => AirAlarmControlModes,
|
||||
},
|
||||
thresholds: {
|
||||
title: 'Alarm Thresholds',
|
||||
component: () => AirAlarmControlThresholds,
|
||||
},
|
||||
};
|
||||
|
||||
const AirAlarmControl = (props, context) => {
|
||||
const [screen, setScreen] = useLocalState(context, 'screen');
|
||||
const route = AIR_ALARM_ROUTES[screen] || AIR_ALARM_ROUTES.home;
|
||||
const Component = route.component();
|
||||
return (
|
||||
<Section
|
||||
title={route.title}
|
||||
buttons={screen && (
|
||||
<Button
|
||||
icon="arrow-left"
|
||||
content="Back"
|
||||
onClick={() => setScreen()} />
|
||||
)}>
|
||||
<Component />
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// Home screen
|
||||
// --------------------------------------------------------
|
||||
|
||||
const AirAlarmControlHome = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const [screen, setScreen] = useLocalState(context, 'screen');
|
||||
const {
|
||||
mode,
|
||||
atmos_alarm,
|
||||
} = data;
|
||||
return (
|
||||
<Fragment>
|
||||
<Button
|
||||
icon={atmos_alarm
|
||||
? 'exclamation-triangle'
|
||||
: 'exclamation'}
|
||||
color={atmos_alarm && 'caution'}
|
||||
content="Area Atmosphere Alarm"
|
||||
onClick={() => act(atmos_alarm ? 'reset' : 'alarm')} />
|
||||
<Box mt={1} />
|
||||
<Button
|
||||
icon={mode === 3
|
||||
? 'exclamation-triangle'
|
||||
: 'exclamation'}
|
||||
color={mode === 3 && 'danger'}
|
||||
content="Panic Siphon"
|
||||
onClick={() => act('mode', {
|
||||
mode: mode === 3 ? 1 : 3,
|
||||
})} />
|
||||
<Box mt={2} />
|
||||
<Button
|
||||
icon="sign-out-alt"
|
||||
content="Vent Controls"
|
||||
onClick={() => setScreen('vents')} />
|
||||
<Box mt={1} />
|
||||
<Button
|
||||
icon="filter"
|
||||
content="Scrubber Controls"
|
||||
onClick={() => setScreen('scrubbers')} />
|
||||
<Box mt={1} />
|
||||
<Button
|
||||
icon="cog"
|
||||
content="Operating Mode"
|
||||
onClick={() => setScreen('modes')} />
|
||||
<Box mt={1} />
|
||||
<Button
|
||||
icon="chart-bar"
|
||||
content="Alarm Thresholds"
|
||||
onClick={() => setScreen('thresholds')} />
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
// Vents
|
||||
// --------------------------------------------------------
|
||||
|
||||
const AirAlarmControlVents = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const { vents } = data;
|
||||
if (!vents || vents.length === 0) {
|
||||
return 'Nothing to show';
|
||||
}
|
||||
return vents.map(vent => (
|
||||
<Vent
|
||||
key={vent.id_tag}
|
||||
vent={vent} />
|
||||
));
|
||||
};
|
||||
|
||||
// Scrubbers
|
||||
// --------------------------------------------------------
|
||||
|
||||
const AirAlarmControlScrubbers = (props, context) => {
|
||||
const { data } = useBackend(context);
|
||||
const { scrubbers } = data;
|
||||
if (!scrubbers || scrubbers.length === 0) {
|
||||
return 'Nothing to show';
|
||||
}
|
||||
return scrubbers.map(scrubber => (
|
||||
<Scrubber
|
||||
key={scrubber.id_tag}
|
||||
scrubber={scrubber} />
|
||||
));
|
||||
};
|
||||
|
||||
// Modes
|
||||
// --------------------------------------------------------
|
||||
|
||||
const AirAlarmControlModes = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { modes } = data;
|
||||
if (!modes || modes.length === 0) {
|
||||
return 'Nothing to show';
|
||||
}
|
||||
return modes.map(mode => (
|
||||
<Fragment key={mode.mode}>
|
||||
<Button
|
||||
icon={mode.selected ? 'check-square-o' : 'square-o'}
|
||||
selected={mode.selected}
|
||||
color={mode.selected && mode.danger && 'danger'}
|
||||
content={mode.name}
|
||||
onClick={() => act('mode', { mode: mode.mode })} />
|
||||
<Box mt={1} />
|
||||
</Fragment>
|
||||
));
|
||||
};
|
||||
|
||||
|
||||
// Thresholds
|
||||
// --------------------------------------------------------
|
||||
|
||||
const AirAlarmControlThresholds = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const { thresholds } = data;
|
||||
return (
|
||||
<table
|
||||
className="LabeledList"
|
||||
style={{ width: '100%' }}>
|
||||
<thead>
|
||||
<tr>
|
||||
<td />
|
||||
<td className="color-bad">min2</td>
|
||||
<td className="color-average">min1</td>
|
||||
<td className="color-average">max1</td>
|
||||
<td className="color-bad">max2</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{thresholds.map(threshold => (
|
||||
<tr key={threshold.name}>
|
||||
<td className="LabeledList__label">
|
||||
<span className={"color-" + getGasColor(threshold.name)}>
|
||||
{getGasLabel(threshold.name)}
|
||||
</span>
|
||||
</td>
|
||||
{threshold.settings.map(setting => (
|
||||
<td key={setting.val}>
|
||||
<Button
|
||||
content={toFixed(setting.selected, 2)}
|
||||
onClick={() => act('threshold', {
|
||||
env: setting.env,
|
||||
var: setting.val,
|
||||
})} />
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,111 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Box, Button, NoticeBox, LabeledList, ProgressBar, Section, Slider, Table } from '../components';
|
||||
import { formatPower } from '../format';
|
||||
import { Window } from '../layouts';
|
||||
import { round } from 'common/math';
|
||||
import { capitalize } from 'common/string';
|
||||
|
||||
export const AlgaeFarm = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
usePower,
|
||||
materials,
|
||||
last_flow_rate,
|
||||
last_power_draw,
|
||||
inputDir,
|
||||
outputDir,
|
||||
input,
|
||||
output,
|
||||
errorText,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Window
|
||||
width={500}
|
||||
height={300}
|
||||
resizable>
|
||||
<Window.Content>
|
||||
{errorText && (
|
||||
<NoticeBox warning>
|
||||
<Box display="inline-block" verticalAlign="middle">
|
||||
{errorText}
|
||||
</Box>
|
||||
</NoticeBox>
|
||||
)}
|
||||
<Section
|
||||
title="Status"
|
||||
buttons={
|
||||
<Button
|
||||
icon="power-off"
|
||||
content="Processing"
|
||||
selected={usePower === 2}
|
||||
onClick={() => act("toggle")} />
|
||||
}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Flow Rate">
|
||||
{last_flow_rate} L/s
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Power Draw">
|
||||
{last_power_draw} W
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Divider size={1} />
|
||||
{materials.map(material => (
|
||||
<LabeledList.Item
|
||||
key={material.name}
|
||||
label={capitalize(material.display)}>
|
||||
<ProgressBar
|
||||
width="80%"
|
||||
value={material.qty}
|
||||
maxValue={material.max}>
|
||||
{material.qty}/{material.max}
|
||||
</ProgressBar>
|
||||
<Button
|
||||
ml={1}
|
||||
content="Eject"
|
||||
onClick={() => act("ejectMaterial", {
|
||||
mat: material.name,
|
||||
})} />
|
||||
</LabeledList.Item>
|
||||
))}
|
||||
</LabeledList>
|
||||
<Table mt={1}>
|
||||
<Table.Row>
|
||||
<Table.Cell>
|
||||
<Section title={"Gas Input (" + inputDir + ")"}>
|
||||
{input ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Total Pressure">
|
||||
{input.pressure} kPa
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label={input.name}>
|
||||
{input.percent}% ({input.moles} moles)
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
) : (
|
||||
<Box color="bad">No connection detected.</Box>
|
||||
)}
|
||||
</Section>
|
||||
</Table.Cell>
|
||||
<Table.Cell>
|
||||
<Section title={"Gas Output (" + outputDir + ")"}>
|
||||
{output ? (
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Total Pressure">
|
||||
{output.pressure} kPa
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label={output.name}>
|
||||
{output.percent}% ({output.moles} moles)
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
) : (
|
||||
<Box color="bad">No connection detected.</Box>
|
||||
)}
|
||||
</Section>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
</Table>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,292 @@
|
||||
import { filter, sortBy } from 'common/collections';
|
||||
import { capitalize, decodeHtmlEntities } from 'common/string';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend, useLocalState } from "../backend";
|
||||
import { Box, Button, ByondUi, Flex, Icon, LabeledList, ProgressBar, Section, Tabs, ColorBox } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
|
||||
export const AppearanceChanger = (props, context) => {
|
||||
const { act, config, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
name,
|
||||
specimen,
|
||||
gender,
|
||||
gender_id,
|
||||
hair_style,
|
||||
facial_hair_style,
|
||||
change_race,
|
||||
change_gender,
|
||||
change_eye_color,
|
||||
change_skin_tone,
|
||||
change_skin_color,
|
||||
change_hair_color,
|
||||
change_facial_hair_color,
|
||||
change_hair,
|
||||
change_facial_hair,
|
||||
mapRef,
|
||||
} = data;
|
||||
|
||||
const {
|
||||
title,
|
||||
} = config;
|
||||
|
||||
const change_color = change_eye_color
|
||||
|| change_skin_tone
|
||||
|| change_skin_color
|
||||
|| change_hair_color
|
||||
|| change_facial_hair_color;
|
||||
|
||||
let firstAccesibleTab = -1;
|
||||
if (change_race) {
|
||||
firstAccesibleTab = 0;
|
||||
} else if (change_gender) {
|
||||
firstAccesibleTab = 1;
|
||||
} else if (change_color) {
|
||||
firstAccesibleTab = 2;
|
||||
} else if (change_hair) {
|
||||
firstAccesibleTab = 4;
|
||||
} else if (change_facial_hair) {
|
||||
firstAccesibleTab = 5;
|
||||
}
|
||||
|
||||
const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', firstAccesibleTab);
|
||||
|
||||
return (
|
||||
<Window width={700} height={650} title={decodeHtmlEntities(title)}>
|
||||
<Window.Content>
|
||||
<Section title="Reflection">
|
||||
<Flex>
|
||||
<Flex.Item grow={1}>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Name">
|
||||
{name}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Species" color={!change_race ? "grey" : null}>
|
||||
{specimen}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Biological Sex" color={!change_gender ? "grey" : null}>
|
||||
{capitalize(gender)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Gender Identity" color={!change_color ? "grey" : null}>
|
||||
{capitalize(gender_id)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Hair Style" color={!change_hair ? "grey" : null}>
|
||||
{capitalize(hair_style)}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Facial Hair Style" color={!change_facial_hair ? "grey" : null}>
|
||||
{capitalize(facial_hair_style)}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Flex.Item>
|
||||
<Flex.Item>
|
||||
<ByondUi
|
||||
style={{
|
||||
width: '256px',
|
||||
height: '256px',
|
||||
}}
|
||||
params={{
|
||||
id: mapRef,
|
||||
type: 'map',
|
||||
}} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
</Section>
|
||||
<Tabs>
|
||||
{change_race ? (
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 0}
|
||||
onClick={() => setTabIndex(0)}>
|
||||
Race
|
||||
</Tabs.Tab>
|
||||
) : null}
|
||||
{change_gender ? (
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 1}
|
||||
onClick={() => setTabIndex(1)}>
|
||||
Gender & Sex
|
||||
</Tabs.Tab>
|
||||
) : null}
|
||||
{change_color ? (
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 2}
|
||||
onClick={() => setTabIndex(2)}>
|
||||
Colors
|
||||
</Tabs.Tab>
|
||||
) : null}
|
||||
{change_hair ? (
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 3}
|
||||
onClick={() => setTabIndex(3)}>
|
||||
Hair
|
||||
</Tabs.Tab>
|
||||
) : null}
|
||||
{change_facial_hair ? (
|
||||
<Tabs.Tab
|
||||
selected={tabIndex === 4}
|
||||
onClick={() => setTabIndex(4)}>
|
||||
Facial Hair
|
||||
</Tabs.Tab>
|
||||
) : null}
|
||||
</Tabs>
|
||||
<Box height="43%">
|
||||
{(change_race && tabIndex === 0) ? <AppearanceChangerSpecies /> : null}
|
||||
{(change_gender && tabIndex === 1) ? <AppearanceChangerGender /> : null}
|
||||
{(change_color && tabIndex === 2) ? <AppearanceChangerColors /> : null}
|
||||
{(change_hair && tabIndex === 3) ? <AppearanceChangerHair /> : null}
|
||||
{(change_facial_hair && tabIndex === 4) ? <AppearanceChangerFacialHair /> : null}
|
||||
</Box>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const AppearanceChangerSpecies = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
species,
|
||||
specimen,
|
||||
} = data;
|
||||
|
||||
const sortedSpecies = sortBy(val => val.specimen)(species || []);
|
||||
|
||||
return (
|
||||
<Section title="Species" fill scrollable>
|
||||
{sortedSpecies.map(spec => (
|
||||
<Button
|
||||
key={spec.specimen}
|
||||
content={spec.specimen}
|
||||
selected={specimen === spec.specimen}
|
||||
onClick={() => act("race", { race: spec.specimen })} />
|
||||
))}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const AppearanceChangerGender = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
gender,
|
||||
gender_id,
|
||||
genders,
|
||||
id_genders,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Section title="Gender & Sex" fill scrollable>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Biological Sex">
|
||||
{genders.map(g => (
|
||||
<Button
|
||||
key={g.gender_key}
|
||||
selected={g.gender_key === gender}
|
||||
content={g.gender_name}
|
||||
onClick={() => act("gender", { "gender": g.gender_key })} />
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Gender Identity">
|
||||
{id_genders.map(g => (
|
||||
<Button
|
||||
key={g.gender_key}
|
||||
selected={g.gender_key === gender_id}
|
||||
content={g.gender_name}
|
||||
onClick={() => act("gender_id", { "gender_id": g.gender_key })} />
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const AppearanceChangerColors = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
change_eye_color,
|
||||
change_skin_tone,
|
||||
change_skin_color,
|
||||
change_hair_color,
|
||||
change_facial_hair_color,
|
||||
eye_color,
|
||||
skin_color,
|
||||
hair_color,
|
||||
facial_hair_color,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Section title="Colors" fill scrollable>
|
||||
{change_eye_color ? (
|
||||
<Box>
|
||||
<ColorBox color={eye_color} mr={1} />
|
||||
<Button content="Change Eye Color" onClick={() => act("eye_color")} />
|
||||
</Box>
|
||||
) : null}
|
||||
{change_skin_tone ? (
|
||||
<Box>
|
||||
<Button content="Change Skin Tone" onClick={() => act("skin_tone")} />
|
||||
</Box>
|
||||
) : null}
|
||||
{change_skin_color ? (
|
||||
<Box>
|
||||
<ColorBox color={skin_color} mr={1} />
|
||||
<Button content="Change Skin Color" onClick={() => act("skin_color")} />
|
||||
</Box>
|
||||
) : null}
|
||||
{change_hair_color ? (
|
||||
<Box>
|
||||
<ColorBox color={hair_color} mr={1} />
|
||||
<Button content="Change Hair Color" onClick={() => act("hair_color")} />
|
||||
</Box>
|
||||
) : null}
|
||||
{change_facial_hair_color ? (
|
||||
<Box>
|
||||
<ColorBox color={facial_hair_color} mr={1} />
|
||||
<Button content="Change Facial Hair Color" onClick={() => act("facial_hair_color")} />
|
||||
</Box>
|
||||
) : null}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const AppearanceChangerHair = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
hair_style,
|
||||
hair_styles,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Section title="Hair" fill scrollable>
|
||||
{hair_styles.map(hair => (
|
||||
<Button
|
||||
key={hair.hairstyle}
|
||||
onClick={() => act("hair", { hair: hair.hairstyle })}
|
||||
selected={hair.hairstyle === hair_style}
|
||||
content={hair.hairstyle} />
|
||||
))}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
|
||||
const AppearanceChangerFacialHair = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const {
|
||||
facial_hair_style,
|
||||
facial_hair_styles,
|
||||
} = data;
|
||||
|
||||
return (
|
||||
<Section title="Facial Hair" fill scrollable>
|
||||
{facial_hair_styles.map(hair => (
|
||||
<Button
|
||||
key={hair.facialhairstyle}
|
||||
onClick={() => act("facial_hair", { facial_hair: hair.facialhairstyle })}
|
||||
selected={hair.facialhairstyle === facial_hair_style}
|
||||
content={hair.facialhairstyle} />
|
||||
))}
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,121 @@
|
||||
import { Flex, Button, Box, LabeledList, Section } from "../components";
|
||||
import { useBackend, useLocalState } from "../backend";
|
||||
import { Window } from '../layouts';
|
||||
import { toTitleCase } from 'common/string';
|
||||
|
||||
|
||||
export const AreaScrubberControl = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
|
||||
const [
|
||||
showArea,
|
||||
setShowArea,
|
||||
] = useLocalState(context, "showArea", false);
|
||||
|
||||
const {
|
||||
scrubbers,
|
||||
} = data;
|
||||
|
||||
if (!scrubbers) {
|
||||
return (
|
||||
<Section title="Error">
|
||||
<Box color="bad">No Scrubbers Detected.</Box>
|
||||
<Button
|
||||
fluid
|
||||
icon="search"
|
||||
content="Scan"
|
||||
onClick={() => act("scan")} />
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Window
|
||||
width={600}
|
||||
height={400}
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
<Section>
|
||||
<Flex wrap="wrap">
|
||||
<Flex.Item m="2px" basis="49%">
|
||||
<Button
|
||||
textAlign="center"
|
||||
fluid
|
||||
icon="search"
|
||||
content="Scan"
|
||||
onClick={() => act("scan")} />
|
||||
</Flex.Item>
|
||||
<Flex.Item m="2px" basis="49%" grow={1}>
|
||||
<Button
|
||||
textAlign="center"
|
||||
fluid
|
||||
icon="layer-group"
|
||||
content="Show Areas"
|
||||
selected={showArea}
|
||||
onClick={() => setShowArea(!showArea)} />
|
||||
</Flex.Item>
|
||||
<Flex.Item m="2px" basis="49%">
|
||||
<Button
|
||||
textAlign="center"
|
||||
fluid
|
||||
icon="toggle-on"
|
||||
content="All On"
|
||||
onClick={() => act("allon")} />
|
||||
</Flex.Item>
|
||||
<Flex.Item m="2px" basis="49%" grow={1}>
|
||||
<Button
|
||||
textAlign="center"
|
||||
fluid
|
||||
icon="toggle-off"
|
||||
content="All Off"
|
||||
onClick={() => act("alloff")} />
|
||||
</Flex.Item>
|
||||
</Flex>
|
||||
<Flex wrap="wrap">
|
||||
{scrubbers.map(scrubber => (
|
||||
<Flex.Item m="2px" key={scrubber.id} basis="32%">
|
||||
<BigScrubber scrubber={scrubber} showArea={showArea} />
|
||||
</Flex.Item>
|
||||
))}
|
||||
</Flex>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
const BigScrubber = (props, context) => {
|
||||
const { act } = useBackend(context);
|
||||
|
||||
const {
|
||||
scrubber,
|
||||
showArea,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<Section title={scrubber.name}>
|
||||
<Button
|
||||
fluid
|
||||
icon="power-off"
|
||||
content={scrubber.on ? "Enabled" : "Disabled"}
|
||||
selected={scrubber.on}
|
||||
onClick={() => act("toggle", { id: scrubber.id })} />
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Pressure">
|
||||
{scrubber.pressure} kPa
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Flow Rate">
|
||||
{scrubber.flow_rate} L/s
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Load">
|
||||
{scrubber.load} W
|
||||
</LabeledList.Item>
|
||||
{showArea && (
|
||||
<LabeledList.Item label="Area">
|
||||
{toTitleCase(scrubber.area)}
|
||||
</LabeledList.Item>
|
||||
)}
|
||||
</LabeledList>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import { round } from 'common/math';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from "../backend";
|
||||
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, Section } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
|
||||
export const AssemblyInfrared = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
on,
|
||||
visible,
|
||||
} = data;
|
||||
return (
|
||||
<Window>
|
||||
<Window.Content>
|
||||
<Section title="Infrared Unit">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Laser">
|
||||
<Button
|
||||
icon="power-off"
|
||||
fluid
|
||||
selected={on}
|
||||
onClick={() => act("state")}>
|
||||
{on ? "On" : "Off"}
|
||||
</Button>
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Visibility">
|
||||
<Button
|
||||
icon="eye"
|
||||
fluid
|
||||
selected={visible}
|
||||
onClick={() => act("visible")}>
|
||||
{visible ? "Able to be seen" : "Invisible"}
|
||||
</Button>
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
import { round } from 'common/math';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from "../backend";
|
||||
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, NumberInput, Section } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
import { formatTime } from '../format';
|
||||
|
||||
export const AssemblyProx = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
timing,
|
||||
time,
|
||||
range,
|
||||
maxRange,
|
||||
scanning,
|
||||
} = data;
|
||||
return (
|
||||
<Window>
|
||||
<Window.Content>
|
||||
<Section title="Timing Unit">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Timer" buttons={
|
||||
<Button
|
||||
icon="stopwatch"
|
||||
selected={timing}
|
||||
onClick={() => act("timing")}>
|
||||
{timing ? "Counting Down" : "Disabled"}
|
||||
</Button>
|
||||
}>
|
||||
<NumberInput
|
||||
animated
|
||||
fluid
|
||||
value={time / 10}
|
||||
minValue={0}
|
||||
maxValue={600}
|
||||
format={val => formatTime(round(val))}
|
||||
onDrag={(e, val) => act("set_time", { time: val })} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
<Section title="Prox Unit">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Range">
|
||||
<NumberInput
|
||||
minValue={1}
|
||||
value={range}
|
||||
maxValue={maxRange}
|
||||
onDrag={(e, val) => act("range", { range: val })} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Armed">
|
||||
<Button
|
||||
mr={1}
|
||||
icon={scanning ? "lock" : "lock-open"}
|
||||
selected={scanning}
|
||||
onClick={() => act("scanning")}>
|
||||
{scanning ? "ARMED" : "Unarmed"}
|
||||
</Button>
|
||||
Movement sensor is active when armed!
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,41 @@
|
||||
import { round } from 'common/math';
|
||||
import { Fragment } from 'inferno';
|
||||
import { useBackend } from "../backend";
|
||||
import { Box, Button, Flex, Icon, LabeledList, ProgressBar, NumberInput, Section } from "../components";
|
||||
import { Window } from "../layouts";
|
||||
import { formatTime } from '../format';
|
||||
|
||||
export const AssemblyTimer = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const {
|
||||
timing,
|
||||
time,
|
||||
} = data;
|
||||
return (
|
||||
<Window>
|
||||
<Window.Content>
|
||||
<Section title="Timing Unit">
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Timer" buttons={
|
||||
<Button
|
||||
icon="stopwatch"
|
||||
selected={timing}
|
||||
onClick={() => act("timing")}>
|
||||
{timing ? "Counting Down" : "Disabled"}
|
||||
</Button>
|
||||
}>
|
||||
<NumberInput
|
||||
animated
|
||||
fluid
|
||||
value={time / 10}
|
||||
minValue={0}
|
||||
maxValue={600}
|
||||
format={val => formatTime(round(val))}
|
||||
onDrag={(e, val) => act("set_time", { time: val })} />
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,50 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, Section } from '../components';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const AtmosAlertConsole = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const priorityAlerts = data.priority_alarms || [];
|
||||
const minorAlerts = data.minor_alarms || [];
|
||||
return (
|
||||
<Window
|
||||
width={350}
|
||||
height={300}
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
<Section title="Alarms">
|
||||
<ul>
|
||||
{priorityAlerts.length === 0 && (
|
||||
<li className="color-good">
|
||||
No Priority Alerts
|
||||
</li>
|
||||
)}
|
||||
{priorityAlerts.map(alert => (
|
||||
<li key={alert.name}>
|
||||
<Button
|
||||
icon="times"
|
||||
content={alert.name}
|
||||
color="bad"
|
||||
onClick={() => act('clear', { ref: alert.ref })} />
|
||||
</li>
|
||||
))}
|
||||
{minorAlerts.length === 0 && (
|
||||
<li className="color-good">
|
||||
No Minor Alerts
|
||||
</li>
|
||||
)}
|
||||
{minorAlerts.map(alert => (
|
||||
<li key={alert.name}>
|
||||
<Button
|
||||
icon="times"
|
||||
content={alert.name}
|
||||
color="average"
|
||||
onClick={() => act('clear', { ref: alert.ref })} />
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,121 @@
|
||||
import { sortBy } from 'common/collections';
|
||||
import { Window } from '../layouts';
|
||||
import { Fragment } from 'inferno';
|
||||
import { Button, Box, NumberInput, Tabs, Icon, Section, NanoMap } from '../components';
|
||||
import { useBackend, useLocalState } from '../backend';
|
||||
import { createLogger } from '../logging';
|
||||
const logger = createLogger("fuck");
|
||||
|
||||
export const AtmosControl = (props, context) => {
|
||||
return (
|
||||
<Window
|
||||
width={600}
|
||||
height={440}
|
||||
resizable>
|
||||
<Window.Content scrollable>
|
||||
<AtmosControlContent />
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||
|
||||
export const AtmosControlContent = (props, context) => {
|
||||
const { act, data, config } = useBackend(context);
|
||||
|
||||
let sortedAlarms = sortBy(
|
||||
alarm => alarm.name
|
||||
)(data.alarms || []);
|
||||
|
||||
// sortedAlarms = sortedAlarms.slice(1, 3);
|
||||
|
||||
const [tabIndex, setTabIndex] = useLocalState(context, 'tabIndex', 0);
|
||||
const [mapZoom, setZoom] = useLocalState(context, 'mapZoom', 1);
|
||||
|
||||
let body;
|
||||
// Alarms View
|
||||
if (tabIndex === 0) {
|
||||
body = (
|
||||
<Section title="Alarms">
|
||||
{sortedAlarms.map(alarm => (
|
||||
<Button
|
||||
key={alarm.name}
|
||||
content={alarm.name}
|
||||
color={alarm.danger === 2
|
||||
? 'bad'
|
||||
: alarm.danger === 1
|
||||
? 'average'
|
||||
: ''}
|
||||
onClick={() => act('alarm', { 'alarm': alarm.ref })} />
|
||||
))}
|
||||
</Section>
|
||||
);
|
||||
} else if (tabIndex === 1) {
|
||||
// Please note, if you ever change the zoom values,
|
||||
// you MUST update styles/components/Tooltip.scss
|
||||
// and change the @for scss to match.
|
||||
body = (
|
||||
<Box textAlign="center">
|
||||
Zoom Level:
|
||||
<NumberInput
|
||||
animated
|
||||
width="40px"
|
||||
step={0.5}
|
||||
stepPixelSize="5"
|
||||
value={mapZoom}
|
||||
minValue={1}
|
||||
maxValue={8}
|
||||
onChange={(e, value) => setZoom(value)} />
|
||||
Z-Level:
|
||||
{data.map_levels
|
||||
.sort((a, b) => Number(a) - Number(b))
|
||||
.map(level => (
|
||||
<Button
|
||||
key={level}
|
||||
selected={~~level === ~~config.mapZLevel}
|
||||
content={level}
|
||||
onClick={() => {
|
||||
act("setZLevel", { "mapZLevel": level });
|
||||
}} />
|
||||
))}
|
||||
<NanoMap zoom={mapZoom}>
|
||||
{sortedAlarms
|
||||
.filter(x =>
|
||||
(~~x.z === ~~config.mapZLevel)
|
||||
).map(cm => (
|
||||
<NanoMap.Marker
|
||||
key={cm.ref}
|
||||
x={cm.x}
|
||||
y={cm.y}
|
||||
zoom={mapZoom}
|
||||
icon="bell"
|
||||
tooltip={cm.name}
|
||||
color={cm.danger ? 'red' : 'green'}
|
||||
onClick={() => act("alarm", { "alarm": cm.ref })} />
|
||||
))}
|
||||
</NanoMap>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<Tabs>
|
||||
<Tabs.Tab
|
||||
key="AlarmView"
|
||||
selected={0 === tabIndex}
|
||||
onClick={() => setTabIndex(0)}>
|
||||
<Icon name="table" /> Alarm View
|
||||
</Tabs.Tab>
|
||||
<Tabs.Tab
|
||||
key="MapView"
|
||||
selected={1 === tabIndex}
|
||||
onClick={() => setTabIndex(1)}>
|
||||
<Icon name="map-marked-alt" /> Map View
|
||||
</Tabs.Tab>
|
||||
</Tabs>
|
||||
<Box m={2}>
|
||||
{body}
|
||||
</Box>
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,65 @@
|
||||
import { useBackend } from '../backend';
|
||||
import { Button, LabeledList, NumberInput, Section, AnimatedNumber, Box } from '../components';
|
||||
import { getGasLabel } from '../constants';
|
||||
import { Window } from '../layouts';
|
||||
|
||||
export const AtmosFilter = (props, context) => {
|
||||
const { act, data } = useBackend(context);
|
||||
const filterTypes = data.filter_types || [];
|
||||
return (
|
||||
<Window
|
||||
width={390}
|
||||
height={187}
|
||||
resizable>
|
||||
<Window.Content>
|
||||
<Section>
|
||||
<LabeledList>
|
||||
<LabeledList.Item label="Power">
|
||||
<Button
|
||||
icon={data.on ? 'power-off' : 'times'}
|
||||
content={data.on ? 'On' : 'Off'}
|
||||
selected={data.on}
|
||||
onClick={() => act('power')} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Transfer Rate">
|
||||
<Box inline mr={1}>
|
||||
<AnimatedNumber
|
||||
value={data.last_flow_rate}
|
||||
format={val => val + " L/s"} />
|
||||
</Box>
|
||||
<NumberInput
|
||||
animated
|
||||
value={parseFloat(data.rate)}
|
||||
width="63px"
|
||||
unit="L/s"
|
||||
minValue={0}
|
||||
maxValue={200}
|
||||
onDrag={(e, value) => act('rate', {
|
||||
rate: value,
|
||||
})} />
|
||||
<Button
|
||||
ml={1}
|
||||
icon="plus"
|
||||
content="Max"
|
||||
disabled={data.rate === data.max_rate}
|
||||
onClick={() => act('rate', {
|
||||
rate: 'max',
|
||||
})} />
|
||||
</LabeledList.Item>
|
||||
<LabeledList.Item label="Filter">
|
||||
{filterTypes.map(filter => (
|
||||
<Button
|
||||
key={filter.name}
|
||||
selected={filter.selected}
|
||||
content={filter.name}
|
||||
onClick={() => act('filter', {
|
||||
filterset: filter.f_type,
|
||||
})} />
|
||||
))}
|
||||
</LabeledList.Item>
|
||||
</LabeledList>
|
||||
</Section>
|
||||
</Window.Content>
|
||||
</Window>
|
||||
);
|
||||
};
|
||||