wuira 0.2.4 → 0.2.6
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/CHANGELOG.md +33 -0
- package/README.md +62 -831
- package/data/icons/forever.json +90051 -0
- package/docs/icons.md +28 -0
- package/docs/source-converters.md +27 -0
- package/lib/datasources/DataSource.d.ts +8 -0
- package/lib/datasources/DataSource.lua +7 -0
- package/lib/datasources/SourceConverter.d.ts +30 -0
- package/lib/datasources/SourceConverter.lua +475 -0
- package/lib/datasources/UnitInfo.d.ts +6 -0
- package/lib/datasources/UnitInfo.lua +347 -15
- package/lib/datasources/index.d.ts +3 -0
- package/lib/datasources/index.lua +4 -0
- package/lib/icons/index.d.ts +45634 -0
- package/lib/icons/index.lua +3 -0
- package/lib/index.d.ts +1 -0
- package/lib/store/index.lua +20 -18
- package/package.json +20 -4
- package/tools/build-library.mjs +1 -1
- package/tools/generate-icons.mjs +10 -0
- package/tools/icon-data.mjs +59 -0
- package/tools/update-icons.mjs +36 -0
package/docs/icons.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Forever icon catalog
|
|
2
|
+
|
|
3
|
+
`Icons` contains native texture **FileDataIDs** from the same verified Forever build as the spell enums. Icon identity is independent of spell or item actions.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { UI } from "wuira";
|
|
7
|
+
import { Icons } from "wuira/icons";
|
|
8
|
+
import { Icon } from "wuira/ui";
|
|
9
|
+
|
|
10
|
+
const image = <Icon width={24} height={24} texture={Icons.BlessingOfFreedom_135968} />;
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Every icon has an explicit ID-suffixed member. A readable short alias is generated only when that name maps to one icon. Multiple spells or items may share the same texture; they produce one catalog entry with searchable aliases.
|
|
14
|
+
|
|
15
|
+
The snapshot combines SpellMisc spell/active icon fields with Item icon fields and ItemSparse names from the pinned client build. It covers every positive icon ID referenced by these tables, including unused records and entries without a readable name; it does not claim to list every texture or atlas in the game. No other client branch is merged into it. The names describe associated spells or items, rather than guessing asset filenames. Unnamed records receive a `Spell <id>` or `Item <id>` label.
|
|
16
|
+
|
|
17
|
+
The generated enum is erased to numeric literals by TypeScriptToLua. Editor tools can read the packaged `data/icons/forever.json` to create a searchable catalog without constructing every icon in game. A selected texture is saved as a FileDataID, not a spell ID.
|
|
18
|
+
|
|
19
|
+
Game data belongs to Blizzard Entertainment. Source URLs, row counts, build and SHA-256 hashes are recorded in the snapshot.
|
|
20
|
+
|
|
21
|
+
## Update
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
npm run icons:update -- --build 1.60.1.70374
|
|
25
|
+
npm run icons:generate
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Use `--cached` to regenerate the snapshot from downloaded CSVs. Updating requires the build to match `data/spells/forever.json`. Normal consumers use the committed snapshot and do not download game data during a build.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Source converters
|
|
2
|
+
|
|
3
|
+
`SourceConverter` transforms a readable source with a typed callback. It consumes change notifications, shares one upstream subscription across observers, and does not create a timer or dispose the shared input. Sources without notifications remain available as checked one-time reads.
|
|
4
|
+
|
|
5
|
+
```ts
|
|
6
|
+
import { SourceConverter, HP, ClassSource, Unit } from "wuira/datasources";
|
|
7
|
+
|
|
8
|
+
const hp = new HP(Unit.Player);
|
|
9
|
+
const doubled = new SourceConverter(hp, value => value * 2);
|
|
10
|
+
const characterClass = new ClassSource(Unit.Player);
|
|
11
|
+
const token = new SourceConverter(characterClass, info => info?.token);
|
|
12
|
+
const stop = doubled.BindTo(value => console.log(value));
|
|
13
|
+
|
|
14
|
+
// On removal:
|
|
15
|
+
stop();
|
|
16
|
+
doubled.Dispose();
|
|
17
|
+
token.Dispose();
|
|
18
|
+
characterClass.Dispose();
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`Value` and `Compute()` are checked reads. `TryCompute()` performs one checked read and returns `{ readable: false }` for restricted data without invoking the callback. Converter chains use it to read their leaf once, including when the data is restricted.
|
|
22
|
+
|
|
23
|
+
`onChange()` observes future readable output changes; `BindTo()` also applies the current readable value immediately. Identical scalar outputs do not emit again. `When()` filters emissions without changing the checked getter. `Dispose()` releases observers while leaving the input available to other consumers.
|
|
24
|
+
|
|
25
|
+
Restricted values, including nested secret fields, never reach a conversion callback. Restrictions are checked when consumed; combat alone does not mark a value secret. ClassSource subscriptions follow unit identity changes and native events, releasing listeners when the last consumer unsubscribes.
|
|
26
|
+
|
|
27
|
+
The SDK accepts TypeScript callbacks. Editable case rules and math formulas are LumaUI builder factories, implemented using this generic source. Converters produce data; protected button actions remain subject to native combat restrictions.
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
export type ReadResult<T> = {
|
|
2
|
+
readonly readable: true;
|
|
3
|
+
readonly value: T;
|
|
4
|
+
} | {
|
|
5
|
+
readonly readable: false;
|
|
6
|
+
};
|
|
1
7
|
/** A value-producing source. Secrecy describes data, not native frame protection. */
|
|
2
8
|
export declare abstract class DataSource<T> {
|
|
3
9
|
private value;
|
|
@@ -8,6 +14,8 @@ export declare abstract class DataSource<T> {
|
|
|
8
14
|
IsSecret(): boolean;
|
|
9
15
|
get Value(): T;
|
|
10
16
|
Compute<TResult>(compute: (value: T) => TResult): TResult;
|
|
17
|
+
/** One checked read, without exposing a restricted value or invoking its callback. */
|
|
18
|
+
TryCompute<TResult>(compute: (value: T) => TResult): ReadResult<TResult>;
|
|
11
19
|
protected GetValue(): T;
|
|
12
20
|
/** Filters native binding emissions; Value remains a checked, fresh snapshot. */
|
|
13
21
|
When(condition: (value: T) => boolean): this;
|
|
@@ -165,6 +165,13 @@ function DataSource.prototype.Compute(self, compute)
|
|
|
165
165
|
self:AssertReadable(value)
|
|
166
166
|
return compute(nil, value)
|
|
167
167
|
end
|
|
168
|
+
function DataSource.prototype.TryCompute(self, compute)
|
|
169
|
+
local value = self:GetValue()
|
|
170
|
+
return self:IsValueSecret(value) and ({readable = false}) or ({
|
|
171
|
+
readable = true,
|
|
172
|
+
value = compute(nil, value)
|
|
173
|
+
})
|
|
174
|
+
end
|
|
168
175
|
function DataSource.prototype.GetValue(self)
|
|
169
176
|
return self.value
|
|
170
177
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { DataSource } from "./DataSource";
|
|
2
|
+
import type { ReadResult } from "./DataSource";
|
|
3
|
+
/** Structural contract: converters can consume a shared source without owning it. */
|
|
4
|
+
export interface ValueSource<T> {
|
|
5
|
+
readonly Value: T;
|
|
6
|
+
IsSecret(): boolean;
|
|
7
|
+
TryCompute?<TResult>(compute: (value: T) => TResult): ReadResult<TResult>;
|
|
8
|
+
onChange?(listener: (value: T) => void): () => void;
|
|
9
|
+
}
|
|
10
|
+
/** Checked value transformation. No timers, native widgets or ownership of the input. */
|
|
11
|
+
export declare class SourceConverter<TInput, TOutput> extends DataSource<TOutput> {
|
|
12
|
+
private readonly input;
|
|
13
|
+
private readonly convert;
|
|
14
|
+
private readonly listeners;
|
|
15
|
+
private stop?;
|
|
16
|
+
private disposed;
|
|
17
|
+
private blocked;
|
|
18
|
+
private previous?;
|
|
19
|
+
private readable;
|
|
20
|
+
constructor(input: ValueSource<TInput>, convert: (value: TInput) => TOutput);
|
|
21
|
+
protected GetValue(): TOutput;
|
|
22
|
+
private Convert;
|
|
23
|
+
protected HasSecretFields(value: TOutput): boolean;
|
|
24
|
+
/** Future readable changes. The first observer attaches one upstream subscription. */
|
|
25
|
+
onChange(listener: (value: TOutput) => void): () => void;
|
|
26
|
+
private Refresh;
|
|
27
|
+
/** One immediate readable value, followed by event-driven changes when supported. */
|
|
28
|
+
BindTo(apply: (value: TOutput) => void): () => void;
|
|
29
|
+
Dispose(): void;
|
|
30
|
+
}
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
--[[ Generated with https://github.com/TypeScriptToLua/TypeScriptToLua ]]
|
|
2
|
+
-- Lua Library inline imports
|
|
3
|
+
local function __TS__Class(self)
|
|
4
|
+
local c = {prototype = {}}
|
|
5
|
+
c.prototype.__index = c.prototype
|
|
6
|
+
c.prototype.constructor = c
|
|
7
|
+
return c
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
local __TS__Symbol, Symbol
|
|
11
|
+
do
|
|
12
|
+
local symbolMetatable = {__tostring = function(self)
|
|
13
|
+
return ("Symbol(" .. (self.description or "")) .. ")"
|
|
14
|
+
end}
|
|
15
|
+
function __TS__Symbol(description)
|
|
16
|
+
return setmetatable({description = description}, symbolMetatable)
|
|
17
|
+
end
|
|
18
|
+
Symbol = {
|
|
19
|
+
asyncDispose = __TS__Symbol("Symbol.asyncDispose"),
|
|
20
|
+
dispose = __TS__Symbol("Symbol.dispose"),
|
|
21
|
+
iterator = __TS__Symbol("Symbol.iterator"),
|
|
22
|
+
hasInstance = __TS__Symbol("Symbol.hasInstance"),
|
|
23
|
+
species = __TS__Symbol("Symbol.species"),
|
|
24
|
+
toStringTag = __TS__Symbol("Symbol.toStringTag")
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
local __TS__Iterator
|
|
29
|
+
do
|
|
30
|
+
local function iteratorGeneratorStep(self)
|
|
31
|
+
local co = self.____coroutine
|
|
32
|
+
local status, value = coroutine.resume(co)
|
|
33
|
+
if not status then
|
|
34
|
+
error(value, 0)
|
|
35
|
+
end
|
|
36
|
+
if coroutine.status(co) == "dead" then
|
|
37
|
+
return
|
|
38
|
+
end
|
|
39
|
+
return true, value
|
|
40
|
+
end
|
|
41
|
+
local function iteratorIteratorStep(self)
|
|
42
|
+
local result = self:next()
|
|
43
|
+
if result.done then
|
|
44
|
+
return
|
|
45
|
+
end
|
|
46
|
+
return true, result.value
|
|
47
|
+
end
|
|
48
|
+
local function iteratorStringStep(self, index)
|
|
49
|
+
index = index + 1
|
|
50
|
+
if index > #self then
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
return index, string.sub(self, index, index)
|
|
54
|
+
end
|
|
55
|
+
function __TS__Iterator(iterable)
|
|
56
|
+
if type(iterable) == "string" then
|
|
57
|
+
return iteratorStringStep, iterable, 0
|
|
58
|
+
elseif iterable.____coroutine ~= nil then
|
|
59
|
+
return iteratorGeneratorStep, iterable
|
|
60
|
+
elseif iterable[Symbol.iterator] then
|
|
61
|
+
local iterator = iterable[Symbol.iterator](iterable)
|
|
62
|
+
return iteratorIteratorStep, iterator
|
|
63
|
+
else
|
|
64
|
+
return ipairs(iterable)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
local function __TS__New(target, ...)
|
|
70
|
+
local instance = setmetatable({}, target.prototype)
|
|
71
|
+
instance:____constructor(...)
|
|
72
|
+
return instance
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
local Set
|
|
76
|
+
do
|
|
77
|
+
Set = __TS__Class()
|
|
78
|
+
Set.name = "Set"
|
|
79
|
+
function Set.prototype.____constructor(self, values)
|
|
80
|
+
self[Symbol.toStringTag] = "Set"
|
|
81
|
+
self.size = 0
|
|
82
|
+
self.nextKey = {}
|
|
83
|
+
self.previousKey = {}
|
|
84
|
+
if values == nil then
|
|
85
|
+
return
|
|
86
|
+
end
|
|
87
|
+
local iterable = values
|
|
88
|
+
if iterable[Symbol.iterator] then
|
|
89
|
+
local iterator = iterable[Symbol.iterator](iterable)
|
|
90
|
+
while true do
|
|
91
|
+
local result = iterator:next()
|
|
92
|
+
if result.done then
|
|
93
|
+
break
|
|
94
|
+
end
|
|
95
|
+
self:add(result.value)
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
local array = values
|
|
99
|
+
for ____, value in ipairs(array) do
|
|
100
|
+
self:add(value)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
function Set.prototype.add(self, value)
|
|
105
|
+
local isNewValue = not self:has(value)
|
|
106
|
+
if isNewValue then
|
|
107
|
+
self.size = self.size + 1
|
|
108
|
+
end
|
|
109
|
+
if self.firstKey == nil then
|
|
110
|
+
self.firstKey = value
|
|
111
|
+
self.lastKey = value
|
|
112
|
+
elseif isNewValue then
|
|
113
|
+
self.nextKey[self.lastKey] = value
|
|
114
|
+
self.previousKey[value] = self.lastKey
|
|
115
|
+
self.lastKey = value
|
|
116
|
+
end
|
|
117
|
+
return self
|
|
118
|
+
end
|
|
119
|
+
function Set.prototype.clear(self)
|
|
120
|
+
self.nextKey = {}
|
|
121
|
+
self.previousKey = {}
|
|
122
|
+
self.firstKey = nil
|
|
123
|
+
self.lastKey = nil
|
|
124
|
+
self.size = 0
|
|
125
|
+
end
|
|
126
|
+
function Set.prototype.delete(self, value)
|
|
127
|
+
local contains = self:has(value)
|
|
128
|
+
if contains then
|
|
129
|
+
self.size = self.size - 1
|
|
130
|
+
local next = self.nextKey[value]
|
|
131
|
+
local previous = self.previousKey[value]
|
|
132
|
+
if next ~= nil and previous ~= nil then
|
|
133
|
+
self.nextKey[previous] = next
|
|
134
|
+
self.previousKey[next] = previous
|
|
135
|
+
elseif next ~= nil then
|
|
136
|
+
self.firstKey = next
|
|
137
|
+
self.previousKey[next] = nil
|
|
138
|
+
elseif previous ~= nil then
|
|
139
|
+
self.lastKey = previous
|
|
140
|
+
self.nextKey[previous] = nil
|
|
141
|
+
else
|
|
142
|
+
self.firstKey = nil
|
|
143
|
+
self.lastKey = nil
|
|
144
|
+
end
|
|
145
|
+
self.nextKey[value] = nil
|
|
146
|
+
self.previousKey[value] = nil
|
|
147
|
+
end
|
|
148
|
+
return contains
|
|
149
|
+
end
|
|
150
|
+
function Set.prototype.forEach(self, callback)
|
|
151
|
+
for ____, key in __TS__Iterator(self:keys()) do
|
|
152
|
+
callback(nil, key, key, self)
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
function Set.prototype.has(self, value)
|
|
156
|
+
return self.nextKey[value] ~= nil or self.lastKey == value
|
|
157
|
+
end
|
|
158
|
+
Set.prototype[Symbol.iterator] = function(self)
|
|
159
|
+
return self:values()
|
|
160
|
+
end
|
|
161
|
+
function Set.prototype.entries(self)
|
|
162
|
+
local function getFirstKey()
|
|
163
|
+
return self.firstKey
|
|
164
|
+
end
|
|
165
|
+
local nextKey = self.nextKey
|
|
166
|
+
local key
|
|
167
|
+
local started = false
|
|
168
|
+
return {
|
|
169
|
+
[Symbol.iterator] = function(self)
|
|
170
|
+
return self
|
|
171
|
+
end,
|
|
172
|
+
next = function(self)
|
|
173
|
+
if not started then
|
|
174
|
+
started = true
|
|
175
|
+
key = getFirstKey(nil)
|
|
176
|
+
else
|
|
177
|
+
key = nextKey[key]
|
|
178
|
+
end
|
|
179
|
+
return {done = not key, value = {key, key}}
|
|
180
|
+
end
|
|
181
|
+
}
|
|
182
|
+
end
|
|
183
|
+
function Set.prototype.keys(self)
|
|
184
|
+
local function getFirstKey()
|
|
185
|
+
return self.firstKey
|
|
186
|
+
end
|
|
187
|
+
local nextKey = self.nextKey
|
|
188
|
+
local key
|
|
189
|
+
local started = false
|
|
190
|
+
return {
|
|
191
|
+
[Symbol.iterator] = function(self)
|
|
192
|
+
return self
|
|
193
|
+
end,
|
|
194
|
+
next = function(self)
|
|
195
|
+
if not started then
|
|
196
|
+
started = true
|
|
197
|
+
key = getFirstKey(nil)
|
|
198
|
+
else
|
|
199
|
+
key = nextKey[key]
|
|
200
|
+
end
|
|
201
|
+
return {done = not key, value = key}
|
|
202
|
+
end
|
|
203
|
+
}
|
|
204
|
+
end
|
|
205
|
+
function Set.prototype.values(self)
|
|
206
|
+
local function getFirstKey()
|
|
207
|
+
return self.firstKey
|
|
208
|
+
end
|
|
209
|
+
local nextKey = self.nextKey
|
|
210
|
+
local key
|
|
211
|
+
local started = false
|
|
212
|
+
return {
|
|
213
|
+
[Symbol.iterator] = function(self)
|
|
214
|
+
return self
|
|
215
|
+
end,
|
|
216
|
+
next = function(self)
|
|
217
|
+
if not started then
|
|
218
|
+
started = true
|
|
219
|
+
key = getFirstKey(nil)
|
|
220
|
+
else
|
|
221
|
+
key = nextKey[key]
|
|
222
|
+
end
|
|
223
|
+
return {done = not key, value = key}
|
|
224
|
+
end
|
|
225
|
+
}
|
|
226
|
+
end
|
|
227
|
+
function Set.prototype.union(self, other)
|
|
228
|
+
local result = __TS__New(Set, self)
|
|
229
|
+
for ____, item in __TS__Iterator(other) do
|
|
230
|
+
result:add(item)
|
|
231
|
+
end
|
|
232
|
+
return result
|
|
233
|
+
end
|
|
234
|
+
function Set.prototype.intersection(self, other)
|
|
235
|
+
local result = __TS__New(Set)
|
|
236
|
+
for ____, item in __TS__Iterator(self) do
|
|
237
|
+
if other:has(item) then
|
|
238
|
+
result:add(item)
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
return result
|
|
242
|
+
end
|
|
243
|
+
function Set.prototype.difference(self, other)
|
|
244
|
+
local result = __TS__New(Set, self)
|
|
245
|
+
for ____, item in __TS__Iterator(other) do
|
|
246
|
+
result:delete(item)
|
|
247
|
+
end
|
|
248
|
+
return result
|
|
249
|
+
end
|
|
250
|
+
function Set.prototype.symmetricDifference(self, other)
|
|
251
|
+
local result = __TS__New(Set, self)
|
|
252
|
+
for ____, item in __TS__Iterator(other) do
|
|
253
|
+
if self:has(item) then
|
|
254
|
+
result:delete(item)
|
|
255
|
+
else
|
|
256
|
+
result:add(item)
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
return result
|
|
260
|
+
end
|
|
261
|
+
function Set.prototype.isSubsetOf(self, other)
|
|
262
|
+
for ____, item in __TS__Iterator(self) do
|
|
263
|
+
if not other:has(item) then
|
|
264
|
+
return false
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
return true
|
|
268
|
+
end
|
|
269
|
+
function Set.prototype.isSupersetOf(self, other)
|
|
270
|
+
for ____, item in __TS__Iterator(other) do
|
|
271
|
+
if not self:has(item) then
|
|
272
|
+
return false
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
return true
|
|
276
|
+
end
|
|
277
|
+
function Set.prototype.isDisjointFrom(self, other)
|
|
278
|
+
for ____, item in __TS__Iterator(self) do
|
|
279
|
+
if other:has(item) then
|
|
280
|
+
return false
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
return true
|
|
284
|
+
end
|
|
285
|
+
Set[Symbol.species] = Set
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
local function __TS__ObjectValues(obj)
|
|
289
|
+
local result = {}
|
|
290
|
+
local len = 0
|
|
291
|
+
for key in pairs(obj) do
|
|
292
|
+
len = len + 1
|
|
293
|
+
result[len] = obj[key]
|
|
294
|
+
end
|
|
295
|
+
return result
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
local function __TS__ClassExtends(target, base)
|
|
299
|
+
target.____super = base
|
|
300
|
+
local staticMetatable = setmetatable({__index = base}, base)
|
|
301
|
+
setmetatable(target, staticMetatable)
|
|
302
|
+
local baseMetatable = getmetatable(base)
|
|
303
|
+
if baseMetatable then
|
|
304
|
+
if type(baseMetatable.__index) == "function" then
|
|
305
|
+
staticMetatable.__index = baseMetatable.__index
|
|
306
|
+
end
|
|
307
|
+
if type(baseMetatable.__newindex) == "function" then
|
|
308
|
+
staticMetatable.__newindex = baseMetatable.__newindex
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
setmetatable(target.prototype, base.prototype)
|
|
312
|
+
if type(base.prototype.__index) == "function" then
|
|
313
|
+
target.prototype.__index = base.prototype.__index
|
|
314
|
+
end
|
|
315
|
+
if type(base.prototype.__newindex) == "function" then
|
|
316
|
+
target.prototype.__newindex = base.prototype.__newindex
|
|
317
|
+
end
|
|
318
|
+
if type(base.prototype.__tostring) == "function" then
|
|
319
|
+
target.prototype.__tostring = base.prototype.__tostring
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
-- End of Lua Library inline imports
|
|
323
|
+
local ____exports = {}
|
|
324
|
+
local ____DataSource = require("datasources.DataSource")
|
|
325
|
+
local DataSource = ____DataSource.DataSource
|
|
326
|
+
local function restricted(self, value, seen, depth)
|
|
327
|
+
if seen == nil then
|
|
328
|
+
seen = __TS__New(Set)
|
|
329
|
+
end
|
|
330
|
+
if depth == nil then
|
|
331
|
+
depth = 0
|
|
332
|
+
end
|
|
333
|
+
if issecretvalue(value) or depth > 32 then
|
|
334
|
+
return true
|
|
335
|
+
end
|
|
336
|
+
if type(value) ~= "table" or value == nil or seen:has(value) then
|
|
337
|
+
return false
|
|
338
|
+
end
|
|
339
|
+
seen:add(value)
|
|
340
|
+
for ____, child in ipairs(__TS__ObjectValues(value)) do
|
|
341
|
+
if restricted(nil, child, seen, depth + 1) then
|
|
342
|
+
return true
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
return false
|
|
346
|
+
end
|
|
347
|
+
--- Checked value transformation. No timers, native widgets or ownership of the input.
|
|
348
|
+
____exports.SourceConverter = __TS__Class()
|
|
349
|
+
local SourceConverter = ____exports.SourceConverter
|
|
350
|
+
SourceConverter.name = "SourceConverter"
|
|
351
|
+
__TS__ClassExtends(SourceConverter, DataSource)
|
|
352
|
+
function SourceConverter.prototype.____constructor(self, input, convert)
|
|
353
|
+
DataSource.prototype.____constructor(self, nil)
|
|
354
|
+
self.input = input
|
|
355
|
+
self.convert = convert
|
|
356
|
+
self.listeners = __TS__New(Set)
|
|
357
|
+
self.disposed = false
|
|
358
|
+
self.blocked = false
|
|
359
|
+
self.readable = false
|
|
360
|
+
end
|
|
361
|
+
function SourceConverter.prototype.GetValue(self)
|
|
362
|
+
if self.disposed then
|
|
363
|
+
error("SourceConverter is disposed")
|
|
364
|
+
end
|
|
365
|
+
if self.input.TryCompute ~= nil then
|
|
366
|
+
local result = self.input:TryCompute(function(____, value) return self:Convert(value) end)
|
|
367
|
+
if not result.readable then
|
|
368
|
+
self.blocked = true
|
|
369
|
+
return nil
|
|
370
|
+
end
|
|
371
|
+
return result.value
|
|
372
|
+
end
|
|
373
|
+
self.blocked = self.input:IsSecret()
|
|
374
|
+
if self.blocked then
|
|
375
|
+
return nil
|
|
376
|
+
end
|
|
377
|
+
return self:Convert(self.input.Value)
|
|
378
|
+
end
|
|
379
|
+
function SourceConverter.prototype.Convert(self, value)
|
|
380
|
+
self.blocked = restricted(nil, value)
|
|
381
|
+
local ____table_blocked_0
|
|
382
|
+
if self.blocked then
|
|
383
|
+
____table_blocked_0 = nil
|
|
384
|
+
else
|
|
385
|
+
____table_blocked_0 = self:convert(value)
|
|
386
|
+
end
|
|
387
|
+
return ____table_blocked_0
|
|
388
|
+
end
|
|
389
|
+
function SourceConverter.prototype.HasSecretFields(self, value)
|
|
390
|
+
return self.blocked or restricted(nil, value)
|
|
391
|
+
end
|
|
392
|
+
function SourceConverter.prototype.onChange(self, listener)
|
|
393
|
+
if self.disposed then
|
|
394
|
+
error("SourceConverter is disposed")
|
|
395
|
+
end
|
|
396
|
+
if self.listeners.size == 0 then
|
|
397
|
+
local value = self:GetValue()
|
|
398
|
+
self.readable = not self:HasSecretFields(value)
|
|
399
|
+
local ____table_readable_1
|
|
400
|
+
if self.readable then
|
|
401
|
+
____table_readable_1 = value
|
|
402
|
+
else
|
|
403
|
+
____table_readable_1 = nil
|
|
404
|
+
end
|
|
405
|
+
self.previous = ____table_readable_1
|
|
406
|
+
end
|
|
407
|
+
self.listeners:add(listener)
|
|
408
|
+
if self.listeners.size == 1 then
|
|
409
|
+
do
|
|
410
|
+
local function ____catch(failure)
|
|
411
|
+
self.listeners:delete(listener)
|
|
412
|
+
error(failure, 0)
|
|
413
|
+
end
|
|
414
|
+
local ____try, ____hasReturned = pcall(function()
|
|
415
|
+
local ____this_3
|
|
416
|
+
____this_3 = self.input
|
|
417
|
+
local ____opt_2 = ____this_3.onChange
|
|
418
|
+
self.stop = ____opt_2 and ____opt_2(
|
|
419
|
+
____this_3,
|
|
420
|
+
function() return self:Refresh() end
|
|
421
|
+
)
|
|
422
|
+
end)
|
|
423
|
+
if not ____try then
|
|
424
|
+
____catch(____hasReturned)
|
|
425
|
+
end
|
|
426
|
+
end
|
|
427
|
+
end
|
|
428
|
+
return function()
|
|
429
|
+
self.listeners:delete(listener)
|
|
430
|
+
if self.listeners.size == 0 then
|
|
431
|
+
local ____opt_4 = self.stop
|
|
432
|
+
if ____opt_4 ~= nil then
|
|
433
|
+
____opt_4(self)
|
|
434
|
+
end
|
|
435
|
+
self.stop = nil
|
|
436
|
+
end
|
|
437
|
+
end
|
|
438
|
+
end
|
|
439
|
+
function SourceConverter.prototype.Refresh(self)
|
|
440
|
+
local value = self:GetValue()
|
|
441
|
+
if self:HasSecretFields(value) then
|
|
442
|
+
self.readable = false
|
|
443
|
+
return
|
|
444
|
+
end
|
|
445
|
+
local same = self.readable and value == self.previous
|
|
446
|
+
self.previous = value
|
|
447
|
+
self.readable = true
|
|
448
|
+
if not same and self:CanEmit(value) then
|
|
449
|
+
for ____, listener in __TS__Iterator(self.listeners) do
|
|
450
|
+
listener(nil, value)
|
|
451
|
+
end
|
|
452
|
+
end
|
|
453
|
+
end
|
|
454
|
+
function SourceConverter.prototype.BindTo(self, apply)
|
|
455
|
+
if not self:IsSecret() then
|
|
456
|
+
local value = self.Value
|
|
457
|
+
if self:CanEmit(value) then
|
|
458
|
+
apply(nil, value)
|
|
459
|
+
end
|
|
460
|
+
end
|
|
461
|
+
return self:onChange(apply)
|
|
462
|
+
end
|
|
463
|
+
function SourceConverter.prototype.Dispose(self)
|
|
464
|
+
if self.disposed then
|
|
465
|
+
return
|
|
466
|
+
end
|
|
467
|
+
self.disposed = true
|
|
468
|
+
local ____opt_6 = self.stop
|
|
469
|
+
if ____opt_6 ~= nil then
|
|
470
|
+
____opt_6(self)
|
|
471
|
+
end
|
|
472
|
+
self.stop = nil
|
|
473
|
+
self.listeners:clear()
|
|
474
|
+
end
|
|
475
|
+
return ____exports
|
|
@@ -8,11 +8,17 @@ export interface ClassInfo {
|
|
|
8
8
|
}
|
|
9
9
|
export declare class ClassSource extends DataSource<ClassInfo | undefined> {
|
|
10
10
|
private restricted;
|
|
11
|
+
private readonly listeners;
|
|
12
|
+
private events?;
|
|
13
|
+
private stopUnit?;
|
|
11
14
|
readonly target: UnitSource;
|
|
12
15
|
constructor(unit: UnitInput);
|
|
13
16
|
get unit(): string;
|
|
14
17
|
protected HasSecretFields(): boolean;
|
|
15
18
|
protected GetValue(): ClassInfo | undefined;
|
|
19
|
+
onChange(listener: (value: ClassInfo | undefined) => void): () => void;
|
|
20
|
+
private StopListening;
|
|
21
|
+
Dispose(): void;
|
|
16
22
|
}
|
|
17
23
|
export interface SpecializationInfo {
|
|
18
24
|
readonly id: number;
|