tgui-core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.editorconfig +10 -0
- package/.eslintrc.cjs +78 -0
- package/.gitattributes +4 -0
- package/.prettierrc.yml +1 -0
- package/.vscode/extensions.json +6 -0
- package/.vscode/settings.json +5 -0
- package/README.md +1 -0
- package/global.d.ts +173 -0
- package/package.json +25 -0
- package/src/assets.ts +43 -0
- package/src/backend.ts +369 -0
- package/src/common/collections.ts +349 -0
- package/src/common/color.ts +94 -0
- package/src/common/events.ts +45 -0
- package/src/common/exhaustive.ts +19 -0
- package/src/common/fp.ts +38 -0
- package/src/common/keycodes.ts +86 -0
- package/src/common/keys.ts +39 -0
- package/src/common/math.ts +98 -0
- package/src/common/perf.ts +72 -0
- package/src/common/random.ts +32 -0
- package/src/common/react.ts +65 -0
- package/src/common/redux.ts +196 -0
- package/src/common/storage.js +196 -0
- package/src/common/string.ts +173 -0
- package/src/common/timer.ts +68 -0
- package/src/common/type-utils.ts +41 -0
- package/src/common/types.ts +9 -0
- package/src/common/uuid.ts +24 -0
- package/src/common/vector.ts +51 -0
- package/src/components/AnimatedNumber.tsx +185 -0
- package/src/components/Autofocus.tsx +23 -0
- package/src/components/Blink.jsx +69 -0
- package/src/components/BlockQuote.tsx +15 -0
- package/src/components/BodyZoneSelector.tsx +149 -0
- package/src/components/Box.tsx +255 -0
- package/src/components/Button.tsx +415 -0
- package/src/components/ByondUi.jsx +121 -0
- package/src/components/Chart.tsx +160 -0
- package/src/components/Collapsible.tsx +45 -0
- package/src/components/ColorBox.tsx +30 -0
- package/src/components/Dialog.tsx +85 -0
- package/src/components/Dimmer.tsx +19 -0
- package/src/components/Divider.tsx +26 -0
- package/src/components/DmIcon.tsx +72 -0
- package/src/components/DraggableControl.jsx +282 -0
- package/src/components/Dropdown.tsx +246 -0
- package/src/components/FakeTerminal.jsx +52 -0
- package/src/components/FitText.tsx +99 -0
- package/src/components/Flex.tsx +105 -0
- package/src/components/Grid.tsx +44 -0
- package/src/components/Icon.tsx +91 -0
- package/src/components/Image.tsx +63 -0
- package/src/components/InfinitePlane.jsx +192 -0
- package/src/components/Input.tsx +181 -0
- package/src/components/KeyListener.tsx +40 -0
- package/src/components/Knob.tsx +185 -0
- package/src/components/LabeledControls.tsx +50 -0
- package/src/components/LabeledList.tsx +130 -0
- package/src/components/MenuBar.tsx +238 -0
- package/src/components/Modal.tsx +25 -0
- package/src/components/NoticeBox.tsx +48 -0
- package/src/components/NumberInput.tsx +328 -0
- package/src/components/Popper.tsx +100 -0
- package/src/components/ProgressBar.tsx +79 -0
- package/src/components/RestrictedInput.jsx +301 -0
- package/src/components/RoundGauge.tsx +189 -0
- package/src/components/Section.tsx +125 -0
- package/src/components/Slider.tsx +173 -0
- package/src/components/Stack.tsx +101 -0
- package/src/components/StyleableSection.tsx +30 -0
- package/src/components/Table.tsx +90 -0
- package/src/components/Tabs.tsx +90 -0
- package/src/components/TextArea.tsx +198 -0
- package/src/components/TimeDisplay.jsx +64 -0
- package/src/components/Tooltip.tsx +147 -0
- package/src/components/TrackOutsideClicks.tsx +35 -0
- package/src/components/VirtualList.tsx +69 -0
- package/src/constants.ts +355 -0
- package/src/debug/KitchenSink.jsx +56 -0
- package/src/debug/actions.js +11 -0
- package/src/debug/hooks.js +10 -0
- package/src/debug/index.ts +10 -0
- package/src/debug/middleware.js +86 -0
- package/src/debug/reducer.js +22 -0
- package/src/debug/selectors.js +7 -0
- package/src/drag.ts +280 -0
- package/src/events.ts +237 -0
- package/src/focus.ts +25 -0
- package/src/format.ts +173 -0
- package/src/hotkeys.ts +212 -0
- package/src/http.ts +16 -0
- package/src/layouts/Layout.tsx +75 -0
- package/src/layouts/NtosWindow.tsx +162 -0
- package/src/layouts/Pane.tsx +56 -0
- package/src/layouts/Window.tsx +227 -0
- package/src/renderer.ts +50 -0
- package/styles/base.scss +32 -0
- package/styles/colors.scss +92 -0
- package/styles/components/BlockQuote.scss +20 -0
- package/styles/components/Button.scss +175 -0
- package/styles/components/ColorBox.scss +12 -0
- package/styles/components/Dialog.scss +105 -0
- package/styles/components/Dimmer.scss +22 -0
- package/styles/components/Divider.scss +27 -0
- package/styles/components/Dropdown.scss +72 -0
- package/styles/components/Flex.scss +31 -0
- package/styles/components/Icon.scss +25 -0
- package/styles/components/Input.scss +68 -0
- package/styles/components/Knob.scss +131 -0
- package/styles/components/LabeledList.scss +49 -0
- package/styles/components/MenuBar.scss +75 -0
- package/styles/components/Modal.scss +14 -0
- package/styles/components/NoticeBox.scss +65 -0
- package/styles/components/NumberInput.scss +76 -0
- package/styles/components/ProgressBar.scss +63 -0
- package/styles/components/RoundGauge.scss +88 -0
- package/styles/components/Section.scss +143 -0
- package/styles/components/Slider.scss +54 -0
- package/styles/components/Stack.scss +59 -0
- package/styles/components/Table.scss +44 -0
- package/styles/components/Tabs.scss +144 -0
- package/styles/components/TextArea.scss +84 -0
- package/styles/components/Tooltip.scss +24 -0
- package/styles/functions.scss +79 -0
- package/styles/layouts/Layout.scss +57 -0
- package/styles/layouts/NtosHeader.scss +20 -0
- package/styles/layouts/NtosWindow.scss +26 -0
- package/styles/layouts/TitleBar.scss +111 -0
- package/styles/layouts/Window.scss +103 -0
- package/styles/main.scss +97 -0
- package/styles/reset.scss +68 -0
- package/styles/themes/abductor.scss +68 -0
- package/styles/themes/admin.scss +38 -0
- package/styles/themes/cardtable.scss +57 -0
- package/styles/themes/hackerman.scss +70 -0
- package/styles/themes/malfunction.scss +67 -0
- package/styles/themes/neutral.scss +50 -0
- package/styles/themes/ntOS95.scss +166 -0
- package/styles/themes/ntos.scss +44 -0
- package/styles/themes/ntos_cat.scss +148 -0
- package/styles/themes/ntos_darkmode.scss +44 -0
- package/styles/themes/ntos_lightmode.scss +67 -0
- package/styles/themes/ntos_spooky.scss +69 -0
- package/styles/themes/ntos_synth.scss +99 -0
- package/styles/themes/ntos_terminal.scss +112 -0
- package/styles/themes/paper.scss +184 -0
- package/styles/themes/retro.scss +72 -0
- package/styles/themes/spookyconsole.scss +73 -0
- package/styles/themes/syndicate.scss +67 -0
- package/styles/themes/wizard.scss +68 -0
- package/tsconfig.json +34 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Component } from 'react';
|
|
2
|
+
|
|
3
|
+
import { formatTime } from '../format';
|
|
4
|
+
|
|
5
|
+
// AnimatedNumber Copypaste
|
|
6
|
+
const isSafeNumber = (value) => {
|
|
7
|
+
return (
|
|
8
|
+
typeof value === 'number' && Number.isFinite(value) && !Number.isNaN(value)
|
|
9
|
+
);
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export class TimeDisplay extends Component {
|
|
13
|
+
constructor(props) {
|
|
14
|
+
super(props);
|
|
15
|
+
this.timer = null;
|
|
16
|
+
this.last_seen_value = undefined;
|
|
17
|
+
this.state = {
|
|
18
|
+
value: 0,
|
|
19
|
+
};
|
|
20
|
+
// Set initial state with value provided in props
|
|
21
|
+
if (isSafeNumber(props.value)) {
|
|
22
|
+
this.state.value = Number(props.value);
|
|
23
|
+
this.last_seen_value = Number(props.value);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
componentDidUpdate() {
|
|
28
|
+
if (this.props.auto !== undefined) {
|
|
29
|
+
clearInterval(this.timer);
|
|
30
|
+
this.timer = setInterval(() => this.tick(), 1000); // every 1 s
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
tick() {
|
|
35
|
+
let current = Number(this.state.value);
|
|
36
|
+
if (this.props.value !== this.last_seen_value) {
|
|
37
|
+
this.last_seen_value = this.props.value;
|
|
38
|
+
current = this.props.value;
|
|
39
|
+
}
|
|
40
|
+
const mod = this.props.auto === 'up' ? 10 : -10; // Time down by default.
|
|
41
|
+
const value = Math.max(0, current + mod); // one sec tick
|
|
42
|
+
this.setState({ value });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
componentDidMount() {
|
|
46
|
+
if (this.props.auto !== undefined) {
|
|
47
|
+
this.timer = setInterval(() => this.tick(), 1000); // every 1 s
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
componentWillUnmount() {
|
|
52
|
+
clearInterval(this.timer);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
render() {
|
|
56
|
+
const val = this.state.value;
|
|
57
|
+
// Directly display weird stuff
|
|
58
|
+
if (!isSafeNumber(val)) {
|
|
59
|
+
return this.state.value || null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return formatTime(val);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/* eslint-disable react/no-deprecated */
|
|
2
|
+
// TODO: Rewrite as an FC, remove this lint disable
|
|
3
|
+
import { createPopper, Placement, VirtualElement } from '@popperjs/core';
|
|
4
|
+
import { Component, ReactNode } from 'react';
|
|
5
|
+
import { findDOMNode, render } from 'react-dom';
|
|
6
|
+
|
|
7
|
+
type TooltipProps = {
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
content: ReactNode;
|
|
10
|
+
position?: Placement;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
type TooltipState = {
|
|
14
|
+
hovered: boolean;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const DEFAULT_OPTIONS = {
|
|
18
|
+
modifiers: [
|
|
19
|
+
{
|
|
20
|
+
name: 'eventListeners',
|
|
21
|
+
enabled: false,
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const NULL_RECT: DOMRect = {
|
|
27
|
+
width: 0,
|
|
28
|
+
height: 0,
|
|
29
|
+
top: 0,
|
|
30
|
+
right: 0,
|
|
31
|
+
bottom: 0,
|
|
32
|
+
left: 0,
|
|
33
|
+
x: 0,
|
|
34
|
+
y: 0,
|
|
35
|
+
toJSON: () => null,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export class Tooltip extends Component<TooltipProps, TooltipState> {
|
|
39
|
+
// Mounting poppers is really laggy because popper.js is very slow.
|
|
40
|
+
// Thus, instead of using the Popper component, Tooltip creates ONE popper
|
|
41
|
+
// and stores every tooltip inside that.
|
|
42
|
+
// This means you can never have two tooltips at once, for instance.
|
|
43
|
+
static renderedTooltip: HTMLDivElement | undefined;
|
|
44
|
+
static singletonPopper: ReturnType<typeof createPopper> | undefined;
|
|
45
|
+
static currentHoveredElement: Element | undefined;
|
|
46
|
+
static virtualElement: VirtualElement = {
|
|
47
|
+
getBoundingClientRect: () =>
|
|
48
|
+
Tooltip.currentHoveredElement?.getBoundingClientRect() ?? NULL_RECT,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
getDOMNode() {
|
|
52
|
+
// HACK: We don't want to create a wrapper, as it could break the layout
|
|
53
|
+
// of consumers, so we use findDOMNode.
|
|
54
|
+
// This is usually bad as refs are usually better, but refs did
|
|
55
|
+
// not work in this case, as they weren't propagating correctly.
|
|
56
|
+
// A previous attempt was made as a render prop that passed an ID,
|
|
57
|
+
// but this made consuming use too unwieldly.
|
|
58
|
+
// Because this component is written in TypeScript, we will know
|
|
59
|
+
// immediately if this internal variable is removed.
|
|
60
|
+
//
|
|
61
|
+
// eslint-disable-next-line react/no-find-dom-node
|
|
62
|
+
return findDOMNode(this) as Element;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
componentDidMount() {
|
|
66
|
+
const domNode = this.getDOMNode();
|
|
67
|
+
|
|
68
|
+
if (!domNode) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
domNode.addEventListener('mouseenter', () => {
|
|
73
|
+
let renderedTooltip = Tooltip.renderedTooltip;
|
|
74
|
+
if (renderedTooltip === undefined) {
|
|
75
|
+
renderedTooltip = document.createElement('div');
|
|
76
|
+
renderedTooltip.className = 'Tooltip';
|
|
77
|
+
document.body.appendChild(renderedTooltip);
|
|
78
|
+
Tooltip.renderedTooltip = renderedTooltip;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Tooltip.currentHoveredElement = domNode;
|
|
82
|
+
|
|
83
|
+
renderedTooltip.style.opacity = '1';
|
|
84
|
+
|
|
85
|
+
this.renderPopperContent();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
domNode.addEventListener('mouseleave', () => {
|
|
89
|
+
this.fadeOut();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
fadeOut() {
|
|
94
|
+
if (Tooltip.currentHoveredElement !== this.getDOMNode()) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
Tooltip.currentHoveredElement = undefined;
|
|
99
|
+
Tooltip.renderedTooltip!.style.opacity = '0';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
renderPopperContent() {
|
|
103
|
+
const renderedTooltip = Tooltip.renderedTooltip;
|
|
104
|
+
if (!renderedTooltip) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
render(<span>{this.props.content}</span>, renderedTooltip, () => {
|
|
109
|
+
let singletonPopper = Tooltip.singletonPopper;
|
|
110
|
+
if (singletonPopper === undefined) {
|
|
111
|
+
singletonPopper = createPopper(
|
|
112
|
+
Tooltip.virtualElement,
|
|
113
|
+
renderedTooltip!,
|
|
114
|
+
{
|
|
115
|
+
...DEFAULT_OPTIONS,
|
|
116
|
+
placement: this.props.position || 'auto',
|
|
117
|
+
}
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
Tooltip.singletonPopper = singletonPopper;
|
|
121
|
+
} else {
|
|
122
|
+
singletonPopper.setOptions({
|
|
123
|
+
...DEFAULT_OPTIONS,
|
|
124
|
+
placement: this.props.position || 'auto',
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
singletonPopper.update();
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
componentDidUpdate() {
|
|
133
|
+
if (Tooltip.currentHoveredElement !== this.getDOMNode()) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
this.renderPopperContent();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
componentWillUnmount() {
|
|
141
|
+
this.fadeOut();
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
render() {
|
|
145
|
+
return this.props.children;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Component, createRef, PropsWithChildren } from 'react';
|
|
2
|
+
|
|
3
|
+
type Props = {
|
|
4
|
+
onOutsideClick: () => void;
|
|
5
|
+
} & PropsWithChildren;
|
|
6
|
+
|
|
7
|
+
export class TrackOutsideClicks extends Component<Props> {
|
|
8
|
+
ref = createRef<HTMLDivElement>();
|
|
9
|
+
|
|
10
|
+
constructor(props) {
|
|
11
|
+
super(props);
|
|
12
|
+
|
|
13
|
+
this.handleOutsideClick = this.handleOutsideClick.bind(this);
|
|
14
|
+
|
|
15
|
+
document.addEventListener('click', this.handleOutsideClick);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
componentWillUnmount() {
|
|
19
|
+
document.removeEventListener('click', this.handleOutsideClick);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
handleOutsideClick(event: MouseEvent) {
|
|
23
|
+
if (!(event.target instanceof Node)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (this.ref.current && !this.ref.current.contains(event.target)) {
|
|
28
|
+
this.props.onOutsideClick();
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
render() {
|
|
33
|
+
return <div ref={this.ref}>{this.props.children}</div>;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PropsWithChildren,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A vertical list that renders items to fill space up to the extents of the
|
|
11
|
+
* current window, and then defers rendering of other items until they come
|
|
12
|
+
* into view.
|
|
13
|
+
*/
|
|
14
|
+
export const VirtualList = (props: PropsWithChildren) => {
|
|
15
|
+
const { children } = props;
|
|
16
|
+
const containerRef = useRef(null as HTMLDivElement | null);
|
|
17
|
+
const [visibleElements, setVisibleElements] = useState(1);
|
|
18
|
+
const [padding, setPadding] = useState(0);
|
|
19
|
+
|
|
20
|
+
const adjustExtents = useCallback(() => {
|
|
21
|
+
const { current } = containerRef;
|
|
22
|
+
|
|
23
|
+
if (
|
|
24
|
+
!children ||
|
|
25
|
+
!Array.isArray(children) ||
|
|
26
|
+
!current ||
|
|
27
|
+
visibleElements >= children.length
|
|
28
|
+
) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const unusedArea =
|
|
33
|
+
document.body.offsetHeight - current.getBoundingClientRect().bottom;
|
|
34
|
+
|
|
35
|
+
const averageItemHeight = Math.ceil(current.offsetHeight / visibleElements);
|
|
36
|
+
|
|
37
|
+
if (unusedArea > 0) {
|
|
38
|
+
const newVisibleElements = Math.min(
|
|
39
|
+
children.length,
|
|
40
|
+
visibleElements +
|
|
41
|
+
Math.max(1, Math.ceil(unusedArea / averageItemHeight)),
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
setVisibleElements(newVisibleElements);
|
|
45
|
+
|
|
46
|
+
setPadding((children.length - newVisibleElements) * averageItemHeight);
|
|
47
|
+
}
|
|
48
|
+
}, [containerRef, visibleElements, children]);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
adjustExtents();
|
|
52
|
+
|
|
53
|
+
const interval = setInterval(adjustExtents, 100);
|
|
54
|
+
|
|
55
|
+
return () => clearInterval(interval);
|
|
56
|
+
}, [adjustExtents]);
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<div className={'VirtualList'}>
|
|
60
|
+
<div className={'VirtualList__Container'} ref={containerRef}>
|
|
61
|
+
{Array.isArray(children) ? children.slice(0, visibleElements) : null}
|
|
62
|
+
</div>
|
|
63
|
+
<div
|
|
64
|
+
className={'VirtualList__Padding'}
|
|
65
|
+
style={{ paddingBottom: `${padding}px` }}
|
|
66
|
+
/>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
};
|
package/src/constants.ts
ADDED
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file
|
|
3
|
+
* @copyright 2020 Aleksej Komarov
|
|
4
|
+
* @license MIT
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
type Gas = {
|
|
8
|
+
id: string;
|
|
9
|
+
path: string;
|
|
10
|
+
name: string;
|
|
11
|
+
label: string;
|
|
12
|
+
color: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// UI states, which are mirrored from the BYOND code.
|
|
16
|
+
export const UI_INTERACTIVE = 2;
|
|
17
|
+
export const UI_UPDATE = 1;
|
|
18
|
+
export const UI_DISABLED = 0;
|
|
19
|
+
export const UI_CLOSE = -1;
|
|
20
|
+
|
|
21
|
+
// All game related colors are stored here
|
|
22
|
+
export const COLORS = {
|
|
23
|
+
// Department colors
|
|
24
|
+
department: {
|
|
25
|
+
captain: '#c06616',
|
|
26
|
+
security: '#e74c3c',
|
|
27
|
+
medbay: '#3498db',
|
|
28
|
+
science: '#9b59b6',
|
|
29
|
+
engineering: '#f1c40f',
|
|
30
|
+
cargo: '#f39c12',
|
|
31
|
+
service: '#7cc46a',
|
|
32
|
+
centcom: '#00c100',
|
|
33
|
+
other: '#c38312',
|
|
34
|
+
},
|
|
35
|
+
// Damage type colors
|
|
36
|
+
damageType: {
|
|
37
|
+
oxy: '#3498db',
|
|
38
|
+
toxin: '#2ecc71',
|
|
39
|
+
burn: '#e67e22',
|
|
40
|
+
brute: '#e74c3c',
|
|
41
|
+
},
|
|
42
|
+
// reagent / chemistry related colours
|
|
43
|
+
reagent: {
|
|
44
|
+
acidicbuffer: '#fbc314',
|
|
45
|
+
basicbuffer: '#3853a4',
|
|
46
|
+
},
|
|
47
|
+
} as const;
|
|
48
|
+
|
|
49
|
+
// Colors defined in CSS
|
|
50
|
+
export const CSS_COLORS = [
|
|
51
|
+
'average',
|
|
52
|
+
'bad',
|
|
53
|
+
'black',
|
|
54
|
+
'blue',
|
|
55
|
+
'brown',
|
|
56
|
+
'good',
|
|
57
|
+
'green',
|
|
58
|
+
'grey',
|
|
59
|
+
'label',
|
|
60
|
+
'olive',
|
|
61
|
+
'orange',
|
|
62
|
+
'pink',
|
|
63
|
+
'purple',
|
|
64
|
+
'red',
|
|
65
|
+
'teal',
|
|
66
|
+
'transparent',
|
|
67
|
+
'violet',
|
|
68
|
+
'white',
|
|
69
|
+
'yellow',
|
|
70
|
+
] as const;
|
|
71
|
+
|
|
72
|
+
export type CssColor = (typeof CSS_COLORS)[number];
|
|
73
|
+
|
|
74
|
+
/* IF YOU CHANGE THIS KEEP IT IN SYNC WITH CHAT CSS */
|
|
75
|
+
export const RADIO_CHANNELS = [
|
|
76
|
+
{
|
|
77
|
+
name: 'Syndicate',
|
|
78
|
+
freq: 1213,
|
|
79
|
+
color: '#8f4a4b',
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
name: 'Red Team',
|
|
83
|
+
freq: 1215,
|
|
84
|
+
color: '#ff4444',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'Blue Team',
|
|
88
|
+
freq: 1217,
|
|
89
|
+
color: '#3434fd',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: 'Green Team',
|
|
93
|
+
freq: 1219,
|
|
94
|
+
color: '#34fd34',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
name: 'Yellow Team',
|
|
98
|
+
freq: 1221,
|
|
99
|
+
color: '#fdfd34',
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
name: 'CentCom',
|
|
103
|
+
freq: 1337,
|
|
104
|
+
color: '#2681a5',
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'Supply',
|
|
108
|
+
freq: 1347,
|
|
109
|
+
color: '#b88646',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: 'Service',
|
|
113
|
+
freq: 1349,
|
|
114
|
+
color: '#6ca729',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'Science',
|
|
118
|
+
freq: 1351,
|
|
119
|
+
color: '#c68cfa',
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
name: 'Command',
|
|
123
|
+
freq: 1353,
|
|
124
|
+
color: '#fcdf03',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
name: 'Medical',
|
|
128
|
+
freq: 1355,
|
|
129
|
+
color: '#57b8f0',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'Engineering',
|
|
133
|
+
freq: 1357,
|
|
134
|
+
color: '#f37746',
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
name: 'Security',
|
|
138
|
+
freq: 1359,
|
|
139
|
+
color: '#dd3535',
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'AI Private',
|
|
143
|
+
freq: 1447,
|
|
144
|
+
color: '#d65d95',
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'Common',
|
|
148
|
+
freq: 1459,
|
|
149
|
+
color: '#1ecc43',
|
|
150
|
+
},
|
|
151
|
+
] as const;
|
|
152
|
+
|
|
153
|
+
const GASES = [
|
|
154
|
+
{
|
|
155
|
+
id: 'o2',
|
|
156
|
+
path: '/datum/gas/oxygen',
|
|
157
|
+
name: 'Oxygen',
|
|
158
|
+
label: 'O₂',
|
|
159
|
+
color: 'blue',
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
id: 'n2',
|
|
163
|
+
path: '/datum/gas/nitrogen',
|
|
164
|
+
name: 'Nitrogen',
|
|
165
|
+
label: 'N₂',
|
|
166
|
+
color: 'yellow',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: 'co2',
|
|
170
|
+
path: '/datum/gas/carbon_dioxide',
|
|
171
|
+
name: 'Carbon Dioxide',
|
|
172
|
+
label: 'CO₂',
|
|
173
|
+
color: 'grey',
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
id: 'plasma',
|
|
177
|
+
path: '/datum/gas/plasma',
|
|
178
|
+
name: 'Plasma',
|
|
179
|
+
label: 'Plasma',
|
|
180
|
+
color: 'pink',
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
id: 'water_vapor',
|
|
184
|
+
path: '/datum/gas/water_vapor',
|
|
185
|
+
name: 'Water Vapor',
|
|
186
|
+
label: 'H₂O',
|
|
187
|
+
color: 'lightsteelblue',
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
id: 'hypernoblium',
|
|
191
|
+
path: '/datum/gas/hypernoblium',
|
|
192
|
+
name: 'Hyper-noblium',
|
|
193
|
+
label: 'Hyper-nob',
|
|
194
|
+
color: 'teal',
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
id: 'n2o',
|
|
198
|
+
path: '/datum/gas/nitrous_oxide',
|
|
199
|
+
name: 'Nitrous Oxide',
|
|
200
|
+
label: 'N₂O',
|
|
201
|
+
color: 'bisque',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'no2',
|
|
205
|
+
path: '/datum/gas/nitrium',
|
|
206
|
+
name: 'Nitrium',
|
|
207
|
+
label: 'Nitrium',
|
|
208
|
+
color: 'brown',
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
id: 'tritium',
|
|
212
|
+
path: '/datum/gas/tritium',
|
|
213
|
+
name: 'Tritium',
|
|
214
|
+
label: 'Tritium',
|
|
215
|
+
color: 'limegreen',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: 'bz',
|
|
219
|
+
path: '/datum/gas/bz',
|
|
220
|
+
name: 'BZ',
|
|
221
|
+
label: 'BZ',
|
|
222
|
+
color: 'mediumpurple',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: 'pluoxium',
|
|
226
|
+
path: '/datum/gas/pluoxium',
|
|
227
|
+
name: 'Pluoxium',
|
|
228
|
+
label: 'Pluoxium',
|
|
229
|
+
color: 'mediumslateblue',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
id: 'miasma',
|
|
233
|
+
path: '/datum/gas/miasma',
|
|
234
|
+
name: 'Miasma',
|
|
235
|
+
label: 'Miasma',
|
|
236
|
+
color: 'olive',
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
id: 'freon',
|
|
240
|
+
path: '/datum/gas/freon',
|
|
241
|
+
name: 'Freon',
|
|
242
|
+
label: 'Freon',
|
|
243
|
+
color: 'paleturquoise',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
id: 'hydrogen',
|
|
247
|
+
path: '/datum/gas/hydrogen',
|
|
248
|
+
name: 'Hydrogen',
|
|
249
|
+
label: 'H₂',
|
|
250
|
+
color: 'white',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: 'healium',
|
|
254
|
+
path: '/datum/gas/healium',
|
|
255
|
+
name: 'Healium',
|
|
256
|
+
label: 'Healium',
|
|
257
|
+
color: 'salmon',
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
id: 'proto_nitrate',
|
|
261
|
+
path: '/datum/gas/proto_nitrate',
|
|
262
|
+
name: 'Proto Nitrate',
|
|
263
|
+
label: 'Proto-Nitrate',
|
|
264
|
+
color: 'greenyellow',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
id: 'zauker',
|
|
268
|
+
path: '/datum/gas/zauker',
|
|
269
|
+
name: 'Zauker',
|
|
270
|
+
label: 'Zauker',
|
|
271
|
+
color: 'darkgreen',
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
id: 'halon',
|
|
275
|
+
path: '/datum/gas/halon',
|
|
276
|
+
name: 'Halon',
|
|
277
|
+
label: 'Halon',
|
|
278
|
+
color: 'purple',
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: 'helium',
|
|
282
|
+
path: '/datum/gas/helium',
|
|
283
|
+
name: 'Helium',
|
|
284
|
+
label: 'He',
|
|
285
|
+
color: 'aliceblue',
|
|
286
|
+
},
|
|
287
|
+
{
|
|
288
|
+
id: 'antinoblium',
|
|
289
|
+
path: '/datum/gas/antinoblium',
|
|
290
|
+
name: 'Antinoblium',
|
|
291
|
+
label: 'Anti-Noblium',
|
|
292
|
+
color: 'maroon',
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
id: 'nitrium',
|
|
296
|
+
path: '/datum/gas/nitrium',
|
|
297
|
+
name: 'Nitrium',
|
|
298
|
+
label: 'Nitrium',
|
|
299
|
+
color: 'brown',
|
|
300
|
+
},
|
|
301
|
+
] as const;
|
|
302
|
+
|
|
303
|
+
// Returns gas label based on gasId
|
|
304
|
+
export const getGasLabel = (gasId: string, fallbackValue?: string) => {
|
|
305
|
+
if (!gasId) return fallbackValue || 'None';
|
|
306
|
+
|
|
307
|
+
const gasSearchString = gasId.toLowerCase();
|
|
308
|
+
|
|
309
|
+
for (let idx = 0; idx < GASES.length; idx++) {
|
|
310
|
+
if (GASES[idx].id === gasSearchString) {
|
|
311
|
+
return GASES[idx].label;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return fallbackValue || 'None';
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
// Returns gas color based on gasId
|
|
319
|
+
export const getGasColor = (gasId: string) => {
|
|
320
|
+
if (!gasId) return 'black';
|
|
321
|
+
|
|
322
|
+
const gasSearchString = gasId.toLowerCase();
|
|
323
|
+
|
|
324
|
+
for (let idx = 0; idx < GASES.length; idx++) {
|
|
325
|
+
if (GASES[idx].id === gasSearchString) {
|
|
326
|
+
return GASES[idx].color;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
return 'black';
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
// Returns gas object based on gasId
|
|
334
|
+
export const getGasFromId = (gasId: string): Gas | undefined => {
|
|
335
|
+
if (!gasId) return;
|
|
336
|
+
|
|
337
|
+
const gasSearchString = gasId.toLowerCase();
|
|
338
|
+
|
|
339
|
+
for (let idx = 0; idx < GASES.length; idx++) {
|
|
340
|
+
if (GASES[idx].id === gasSearchString) {
|
|
341
|
+
return GASES[idx];
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
// Returns gas object based on gasPath
|
|
347
|
+
export const getGasFromPath = (gasPath: string): Gas | undefined => {
|
|
348
|
+
if (!gasPath) return;
|
|
349
|
+
|
|
350
|
+
for (let idx = 0; idx < GASES.length; idx++) {
|
|
351
|
+
if (GASES[idx].path === gasPath) {
|
|
352
|
+
return GASES[idx];
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
};
|