isaacscript-common 10.0.0 → 10.1.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.
Files changed (88) hide show
  1. package/dist/index.d.ts +78 -22
  2. package/dist/isaacscript-common.lua +203 -102
  3. package/dist/package.lua +2 -2
  4. package/dist/src/features/deployJSONRoom.d.ts +16 -6
  5. package/dist/src/features/deployJSONRoom.d.ts.map +1 -1
  6. package/dist/src/features/deployJSONRoom.lua +16 -6
  7. package/dist/src/features/saveDataManager/load.d.ts.map +1 -1
  8. package/dist/src/features/saveDataManager/merge.d.ts.map +1 -1
  9. package/dist/src/features/saveDataManager/saveDataManagerConstants.d.ts +1 -1
  10. package/dist/src/features/saveDataManager/saveDataManagerConstants.d.ts.map +1 -1
  11. package/dist/src/functions/bitSet128.d.ts +2 -1
  12. package/dist/src/functions/bitSet128.d.ts.map +1 -1
  13. package/dist/src/functions/bitSet128.lua +2 -2
  14. package/dist/src/functions/color.d.ts +2 -3
  15. package/dist/src/functions/color.d.ts.map +1 -1
  16. package/dist/src/functions/color.lua +2 -2
  17. package/dist/src/functions/curses.d.ts +1 -1
  18. package/dist/src/functions/curses.lua +1 -1
  19. package/dist/src/functions/deepCopy.d.ts +2 -1
  20. package/dist/src/functions/deepCopy.d.ts.map +1 -1
  21. package/dist/src/functions/deepCopy.lua +19 -25
  22. package/dist/src/functions/deepCopyTests.lua +13 -26
  23. package/dist/src/functions/doors.d.ts +9 -1
  24. package/dist/src/functions/doors.d.ts.map +1 -1
  25. package/dist/src/functions/doors.lua +8 -1
  26. package/dist/src/functions/kColor.d.ts +2 -3
  27. package/dist/src/functions/kColor.d.ts.map +1 -1
  28. package/dist/src/functions/kColor.lua +2 -2
  29. package/dist/src/functions/rng.d.ts +2 -2
  30. package/dist/src/functions/rng.d.ts.map +1 -1
  31. package/dist/src/functions/serialization.d.ts +11 -4
  32. package/dist/src/functions/serialization.d.ts.map +1 -1
  33. package/dist/src/functions/serialization.lua +15 -0
  34. package/dist/src/functions/table.d.ts +2 -2
  35. package/dist/src/functions/table.d.ts.map +1 -1
  36. package/dist/src/functions/table.lua +6 -5
  37. package/dist/src/functions/vector.d.ts +2 -1
  38. package/dist/src/functions/vector.d.ts.map +1 -1
  39. package/dist/src/functions/vector.lua +2 -2
  40. package/dist/src/functions/weighted.d.ts +6 -0
  41. package/dist/src/functions/weighted.d.ts.map +1 -0
  42. package/dist/src/functions/weighted.lua +35 -0
  43. package/dist/src/index.d.ts +2 -1
  44. package/dist/src/index.d.ts.map +1 -1
  45. package/dist/src/index.lua +8 -0
  46. package/dist/src/interfaces/SaveData.d.ts +3 -8
  47. package/dist/src/interfaces/SaveData.d.ts.map +1 -1
  48. package/dist/src/objects/isaacAPIClassTypeToBrand.d.ts +1 -1
  49. package/dist/src/objects/isaacAPIClassTypeToBrand.d.ts.map +1 -1
  50. package/dist/src/objects/isaacAPIClassTypeToBrand.lua +2 -2
  51. package/dist/src/objects/isaacAPIClassTypeToFunctions.d.ts +36 -8
  52. package/dist/src/objects/isaacAPIClassTypeToFunctions.d.ts.map +1 -1
  53. package/dist/src/objects/isaacAPIClassTypeToFunctions.lua +42 -7
  54. package/dist/src/types/WeightedArray.d.ts +4 -0
  55. package/dist/src/types/WeightedArray.d.ts.map +1 -0
  56. package/dist/src/types/{SerializedIsaacAPIClass.lua → WeightedArray.lua} +0 -0
  57. package/package.json +2 -2
  58. package/src/callbacks/customRevive.ts +1 -2
  59. package/src/features/deployJSONRoom.ts +16 -6
  60. package/src/features/saveDataManager/exports.ts +1 -1
  61. package/src/features/saveDataManager/load.ts +0 -2
  62. package/src/features/saveDataManager/merge.ts +0 -3
  63. package/src/features/saveDataManager/saveDataManagerConstants.ts +1 -1
  64. package/src/functions/bitSet128.ts +8 -2
  65. package/src/functions/color.ts +8 -2
  66. package/src/functions/curses.ts +1 -1
  67. package/src/functions/deepCopy.ts +25 -23
  68. package/src/functions/deepCopyTests.ts +19 -41
  69. package/src/functions/doors.ts +13 -1
  70. package/src/functions/initArray.ts +1 -1
  71. package/src/functions/kColor.ts +8 -2
  72. package/src/functions/rng.ts +2 -0
  73. package/src/functions/serialization.ts +72 -18
  74. package/src/functions/table.ts +8 -5
  75. package/src/functions/vector.ts +11 -3
  76. package/src/functions/weighted.ts +36 -0
  77. package/src/index.ts +2 -1
  78. package/src/interfaces/SaveData.ts +4 -3
  79. package/src/objects/isaacAPIClassTypeToBrand.ts +1 -1
  80. package/src/objects/isaacAPIClassTypeToFunctions.ts +58 -9
  81. package/src/types/WeightedArray.ts +2 -0
  82. package/dist/src/enums/private/CopyableIsaacAPIClassType.d.ts +0 -12
  83. package/dist/src/enums/private/CopyableIsaacAPIClassType.d.ts.map +0 -1
  84. package/dist/src/enums/private/CopyableIsaacAPIClassType.lua +0 -10
  85. package/dist/src/types/SerializedIsaacAPIClass.d.ts +0 -11
  86. package/dist/src/types/SerializedIsaacAPIClass.d.ts.map +0 -1
  87. package/src/enums/private/CopyableIsaacAPIClassType.ts +0 -11
  88. package/src/types/SerializedIsaacAPIClass.ts +0 -9
@@ -45,7 +45,6 @@ export function merge(
45
45
  newTable: LuaMap<AnyNotNil, unknown>,
46
46
  traversalDescription: string,
47
47
  ): void {
48
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
49
48
  if (SAVE_DATA_MANAGER_DEBUG) {
50
49
  log(`merge is traversing: ${traversalDescription}`);
51
50
  }
@@ -148,7 +147,6 @@ function mergeTable(
148
147
  iterateTableInOrder(
149
148
  newTable,
150
149
  (key, value) => {
151
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
152
150
  if (SAVE_DATA_MANAGER_DEBUG) {
153
151
  const valueToPrint = value === "" ? "(empty string)" : `${value}`;
154
152
  log(`merge is merging: ${traversalDescription} --> ${valueToPrint}`);
@@ -160,7 +158,6 @@ function mergeTable(
160
158
 
161
159
  // Handle the special case of serialized Isaac API classes.
162
160
  if (isSerializedIsaacAPIClass(value)) {
163
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
164
161
  if (SAVE_DATA_MANAGER_DEBUG) {
165
162
  log("merge found a serialized Isaac API class.");
166
163
  }
@@ -1,4 +1,4 @@
1
1
  /** Set this to true to enable more verbosity in the save data manger. */
2
- export const SAVE_DATA_MANAGER_DEBUG = false;
2
+ export const SAVE_DATA_MANAGER_DEBUG = false as boolean;
3
3
 
4
4
  export const SAVE_DATA_MANAGER_FEATURE_NAME = "save data manager";
@@ -1,10 +1,16 @@
1
+ import { CopyableIsaacAPIClassType } from "isaac-typescript-definitions";
1
2
  import { SerializationBrand } from "../enums/private/SerializationBrand";
2
3
  import { isIsaacAPIClassOfType } from "./isaacAPIClass";
3
- import { copyValuesToTable, getNumbersFromTable, tableHasKeys } from "./table";
4
+ import {
5
+ copyUserdataValuesToTable,
6
+ getNumbersFromTable,
7
+ tableHasKeys,
8
+ } from "./table";
4
9
  import { isTable } from "./types";
5
10
 
6
11
  export type SerializedBitSet128 = LuaMap<string, unknown> & {
7
12
  readonly __serializedBitSet128Brand: symbol;
13
+ readonly __kind: CopyableIsaacAPIClassType.BIT_SET_128;
8
14
  };
9
15
 
10
16
  const OBJECT_NAME = "BitSet128";
@@ -86,7 +92,7 @@ export function serializeBitSet128(bitSet128: BitSet128): SerializedBitSet128 {
86
92
  }
87
93
 
88
94
  const bitSet128Table = new LuaMap<string, unknown>();
89
- copyValuesToTable(bitSet128, KEYS, bitSet128Table);
95
+ copyUserdataValuesToTable(bitSet128, KEYS, bitSet128Table);
90
96
  bitSet128Table.set(SerializationBrand.BIT_SET_128, "");
91
97
  return bitSet128Table as SerializedBitSet128;
92
98
  }
@@ -1,12 +1,18 @@
1
+ import { CopyableIsaacAPIClassType } from "isaac-typescript-definitions";
1
2
  import { SerializationBrand } from "../enums/private/SerializationBrand";
2
3
  import { isaacAPIClassEquals, isIsaacAPIClassOfType } from "./isaacAPIClass";
3
4
  import { getRandom } from "./random";
4
5
  import { getRandomSeed, isRNG, newRNG } from "./rng";
5
- import { copyValuesToTable, getNumbersFromTable, tableHasKeys } from "./table";
6
+ import {
7
+ copyUserdataValuesToTable,
8
+ getNumbersFromTable,
9
+ tableHasKeys,
10
+ } from "./table";
6
11
  import { isTable } from "./types";
7
12
 
8
13
  export type SerializedColor = LuaMap<string, unknown> & {
9
14
  readonly __serializedColorBrand: symbol;
15
+ readonly __kind: CopyableIsaacAPIClassType.COLOR;
10
16
  };
11
17
 
12
18
  const OBJECT_NAME = "Color";
@@ -120,7 +126,7 @@ export function serializeColor(color: Color): SerializedColor {
120
126
  }
121
127
 
122
128
  const colorTable = new LuaMap<string, unknown>();
123
- copyValuesToTable(color, KEYS, colorTable);
129
+ copyUserdataValuesToTable(color, KEYS, colorTable);
124
130
  colorTable.set(SerializationBrand.COLOR, "");
125
131
  return colorTable as SerializedColor;
126
132
  }
@@ -5,7 +5,7 @@ import { hasFlag } from "./flag";
5
5
  /**
6
6
  * Helper function to get the actual bit flag for modded curses.
7
7
  *
8
- * Will throw a runtime error if the provided curse does not exist.
8
+ * Will throw a run-time error if the provided curse does not exist.
9
9
  *
10
10
  * Use this over the `Isaac.GetCurseIdByName` method because that will return an integer instead of
11
11
  * a bit flag.
@@ -1,17 +1,16 @@
1
1
  import { DefaultMap } from "../classes/DefaultMap";
2
- import { CopyableIsaacAPIClassType } from "../enums/private/CopyableIsaacAPIClassType";
3
2
  import { SerializationBrand } from "../enums/private/SerializationBrand";
4
3
  import { SerializationType } from "../enums/SerializationType";
5
4
  import { SAVE_DATA_MANAGER_DEBUG } from "../features/saveDataManager/saveDataManagerConstants";
6
5
  import { isSerializationBrand } from "../features/saveDataManager/serializationBrands";
7
6
  import { TSTLClass } from "../types/TSTLClass";
8
7
  import { isArray } from "./array";
9
- import { getEnumValues } from "./enums";
10
8
  import { getIsaacAPIClassName } from "./isaacAPIClass";
11
9
  import { log } from "./log";
12
10
  import {
13
11
  copyIsaacAPIClass,
14
12
  deserializeIsaacAPIClass,
13
+ isCopyableIsaacAPIClass,
15
14
  isSerializedIsaacAPIClass,
16
15
  serializeIsaacAPIClass,
17
16
  } from "./serialization";
@@ -26,10 +25,6 @@ import {
26
25
  import { asString, isNumber, isPrimitive } from "./types";
27
26
  import { getTraversalDescription, twoDimensionalSort } from "./utils";
28
27
 
29
- const COPYABLE_ISAAC_API_CLASS_TYPES_SET = new Set<string>(
30
- getEnumValues(CopyableIsaacAPIClassType),
31
- );
32
-
33
28
  /**
34
29
  * `deepCopy` is a semi-generic deep cloner. It will recursively copy all of the values so that none
35
30
  * of the nested references remain.
@@ -64,13 +59,24 @@ const COPYABLE_ISAAC_API_CLASS_TYPES_SET = new Set<string>(
64
59
  * @param insideMap Optional. Tracks whether or not the deep copy function is in the process of
65
60
  * recursively copying a TSTL Map. Default is false.
66
61
  */
62
+ export function deepCopy<T>(
63
+ value: T,
64
+ serializationType?: SerializationType.NONE,
65
+ traversalDescription?: string,
66
+ insideMap?: boolean,
67
+ ): T;
68
+ export function deepCopy(
69
+ value: unknown,
70
+ serializationType: SerializationType,
71
+ traversalDescription?: string,
72
+ insideMap?: boolean,
73
+ ): unknown; // The return types for serialization/deserialization are non-trivial, so we do not type them.
67
74
  export function deepCopy(
68
75
  value: unknown,
69
76
  serializationType = SerializationType.NONE,
70
77
  traversalDescription = "",
71
78
  insideMap = false,
72
79
  ): unknown {
73
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
74
80
  if (SAVE_DATA_MANAGER_DEBUG) {
75
81
  let logString = `deepCopy is operating on: ${traversalDescription}`;
76
82
  if (serializationType === SerializationType.SERIALIZE) {
@@ -101,6 +107,12 @@ export function deepCopy(
101
107
  );
102
108
  }
103
109
 
110
+ if (serializationType === SerializationType.DESERIALIZE) {
111
+ error(
112
+ `The deep copy function does not support deserialization of "${traversalDescription}", since it is type: ${valueType}`,
113
+ );
114
+ }
115
+
104
116
  // We cannot copy this, so simply return the reference.
105
117
  return value;
106
118
  }
@@ -126,7 +138,7 @@ function deepCopyTable(
126
138
  serializationType: SerializationType,
127
139
  traversalDescription: string,
128
140
  insideMap: boolean,
129
- ) {
141
+ ): unknown {
130
142
  // First, handle the cases of TSTL classes or serialized TSTL classes.
131
143
  if (isDefaultMap(luaMap) || luaMap.has(SerializationBrand.DEFAULT_MAP)) {
132
144
  return deepCopyDefaultMap(
@@ -214,7 +226,6 @@ function deepCopyDefaultMap(
214
226
  traversalDescription: string,
215
227
  insideMap: boolean,
216
228
  ) {
217
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
218
229
  if (SAVE_DATA_MANAGER_DEBUG) {
219
230
  log("deepCopy is copying a DefaultMap.");
220
231
  }
@@ -346,7 +357,6 @@ function deepCopyMap(
346
357
  traversalDescription: string,
347
358
  insideMap: boolean,
348
359
  ) {
349
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
350
360
  if (SAVE_DATA_MANAGER_DEBUG) {
351
361
  log("deepCopy is copying a Map.");
352
362
  }
@@ -399,7 +409,6 @@ function deepCopySet(
399
409
  traversalDescription: string,
400
410
  insideMap: boolean,
401
411
  ) {
402
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
403
412
  if (SAVE_DATA_MANAGER_DEBUG) {
404
413
  log("deepCopy is copying a Set.");
405
414
  }
@@ -453,7 +462,6 @@ function deepCopyTSTLClass(
453
462
  traversalDescription: string,
454
463
  insideMap: boolean,
455
464
  ) {
456
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
457
465
  if (SAVE_DATA_MANAGER_DEBUG) {
458
466
  log("deepCopy is copying a TSTL class.");
459
467
  }
@@ -492,7 +500,6 @@ function deepCopyArray(
492
500
  traversalDescription: string,
493
501
  insideMap: boolean,
494
502
  ) {
495
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
496
503
  if (SAVE_DATA_MANAGER_DEBUG) {
497
504
  log("deepCopy is copying an array.");
498
505
  }
@@ -518,7 +525,6 @@ function deepCopyNormalLuaTable(
518
525
  traversalDescription: string,
519
526
  insideMap: boolean,
520
527
  ) {
521
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
522
528
  if (SAVE_DATA_MANAGER_DEBUG) {
523
529
  log("deepCopy is copying a normal Lua table.");
524
530
  }
@@ -568,7 +574,6 @@ function getCopiedEntries(
568
574
  }
569
575
  }
570
576
 
571
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
572
577
  if (SAVE_DATA_MANAGER_DEBUG) {
573
578
  entries.sort(twoDimensionalSort);
574
579
  }
@@ -651,11 +656,6 @@ function deepCopyUserdata(
651
656
  serializationType: SerializationType,
652
657
  traversalDescription: string,
653
658
  ) {
654
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
655
- if (SAVE_DATA_MANAGER_DEBUG) {
656
- log("deepCopy is copying userdata.");
657
- }
658
-
659
659
  const classType = getIsaacAPIClassName(value);
660
660
  if (classType === undefined) {
661
661
  error(
@@ -663,9 +663,9 @@ function deepCopyUserdata(
663
663
  );
664
664
  }
665
665
 
666
- if (!COPYABLE_ISAAC_API_CLASS_TYPES_SET.has(classType)) {
666
+ if (!isCopyableIsaacAPIClass(value)) {
667
667
  error(
668
- `The deep copy function does not support copying "${traversalDescription}", since it is an Isaac API class of type: ${classType}`,
668
+ `The deep copy function does not support serializing "${traversalDescription}", since it is an Isaac API class of type: ${classType}`,
669
669
  );
670
670
  }
671
671
 
@@ -679,7 +679,9 @@ function deepCopyUserdata(
679
679
  }
680
680
 
681
681
  case SerializationType.DESERIALIZE: {
682
- return deserializeIsaacAPIClass(value);
682
+ error(
683
+ `The deep copy function can not deserialize "${traversalDescription}", since it is userdata.`,
684
+ );
683
685
  }
684
686
  }
685
687
  }
@@ -49,7 +49,7 @@ function copiedObjectIsTable() {
49
49
  abc: "def",
50
50
  };
51
51
  const newObject = deepCopy(
52
- oldObject as unknown as LuaMap,
52
+ oldObject,
53
53
  SerializationType.NONE,
54
54
  "copiedObjectIsTable",
55
55
  );
@@ -64,12 +64,11 @@ function copiedObjectHasKeyAndValueString() {
64
64
  const oldObject = {
65
65
  abc: valueToLookFor,
66
66
  };
67
- const newTable = deepCopy(
68
- oldObject as unknown as LuaMap,
67
+ const newObject = deepCopy(
68
+ oldObject,
69
69
  SerializationType.NONE,
70
70
  "copiedObjectHasKeyAndValueString",
71
71
  );
72
- const newObject = newTable as typeof oldObject;
73
72
 
74
73
  const value = newObject[keyToLookFor];
75
74
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -91,12 +90,11 @@ function copiedTableHasKeyAndValueNumber() {
91
90
  const oldTable = new LuaMap<AnyNotNil, unknown>();
92
91
  oldTable.set(keyToLookFor, valueToLookFor);
93
92
 
94
- const newObject = deepCopy(
93
+ const newTable = deepCopy(
95
94
  oldTable,
96
95
  SerializationType.NONE,
97
96
  "copiedTableHasKeyAndValueNumber",
98
97
  );
99
- const newTable = newObject as LuaMap<AnyNotNil, unknown>;
100
98
 
101
99
  const value = newTable.get(keyToLookFor) as number | undefined;
102
100
  if (value === undefined) {
@@ -117,12 +115,11 @@ function copiedTableDoesNotCoerceTypes() {
117
115
  const oldTable = new LuaMap<AnyNotNil, unknown>();
118
116
  oldTable.set(keyToLookFor, valueToLookFor);
119
117
 
120
- const newObject = deepCopy(
118
+ const newTable = deepCopy(
121
119
  oldTable,
122
120
  SerializationType.NONE,
123
121
  "copiedTableDoesNotCoerceTypes",
124
122
  );
125
- const newTable = newObject as LuaMap<AnyNotNil, unknown>;
126
123
 
127
124
  const keyString = tostring(keyToLookFor);
128
125
  const valueString = tostring(valueToLookFor);
@@ -148,12 +145,11 @@ function copiedObjectHasNoReferencesForPrimitivesForward() {
148
145
  abc: originalStringValue,
149
146
  def: originalNumberValue,
150
147
  };
151
- const newTable = deepCopy(
152
- oldObject as unknown as LuaMap,
148
+ const newObject = deepCopy(
149
+ oldObject,
153
150
  SerializationType.NONE,
154
151
  "copiedObjectHasNoReferencesForPrimitivesForward",
155
152
  );
156
- const newObject = newTable as typeof oldObject;
157
153
 
158
154
  oldObject.abc = "newValue";
159
155
  if (oldObject.abc === newObject.abc) {
@@ -173,12 +169,11 @@ function copiedObjectHasNoReferencesForPrimitivesBackward() {
173
169
  abc: originalStringValue,
174
170
  def: originalNumberValue,
175
171
  };
176
- const newTable = deepCopy(
177
- oldObject as unknown as LuaMap,
172
+ const newObject = deepCopy(
173
+ oldObject,
178
174
  SerializationType.NONE,
179
175
  "copiedObjectHasNoReferencesForPrimitivesBackward",
180
176
  );
181
- const newObject = newTable as typeof oldObject;
182
177
 
183
178
  newObject.abc = "newValue";
184
179
  if (newObject.abc === oldObject.abc) {
@@ -196,12 +191,11 @@ function copiedObjectHasNoReferencesForArray() {
196
191
  const oldObject = {
197
192
  abc: [1, 2, 3],
198
193
  };
199
- const newTable = deepCopy(
200
- oldObject as unknown as LuaMap,
194
+ const newObject = deepCopy(
195
+ oldObject,
201
196
  SerializationType.NONE,
202
197
  "copiedObjectHasNoReferencesForArray",
203
198
  );
204
- const newObject = newTable as typeof oldObject;
205
199
 
206
200
  if (oldObject.abc === newObject.abc) {
207
201
  error("The copied object has the same point to the child array.");
@@ -237,12 +231,11 @@ function copiedObjectHasChildObject() {
237
231
  def: valueToLookFor,
238
232
  },
239
233
  };
240
- const newTable = deepCopy(
241
- oldObject as unknown as LuaMap,
234
+ const newObject = deepCopy(
235
+ oldObject,
242
236
  SerializationType.NONE,
243
237
  "copiedObjectHasChildObject",
244
238
  );
245
- const newObject = newTable as typeof oldObject;
246
239
 
247
240
  const childObject = newObject[childObjectIndex];
248
241
  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
@@ -274,8 +267,7 @@ function copiedMapIsMap() {
274
267
  const oldMap = new Map<string, string>();
275
268
  oldMap.set(keyToLookFor, valueToLookFor);
276
269
 
277
- const newObject = deepCopy(oldMap, SerializationType.NONE, "copiedMapIsMap");
278
- const newMap = newObject as Map<string, string>;
270
+ const newMap = deepCopy(oldMap, SerializationType.NONE, "copiedMapIsMap");
279
271
 
280
272
  if (!isTSTLMap(newMap)) {
281
273
  error(`The copied Map was not a Map and has a type of: ${typeof newMap}`);
@@ -288,13 +280,7 @@ function copiedMapHasValue() {
288
280
  const oldMap = new Map<string, string>();
289
281
  oldMap.set(keyToLookFor, valueToLookFor);
290
282
 
291
- const newTable = deepCopy(
292
- oldMap,
293
- SerializationType.NONE,
294
- "copiedMapHasValue",
295
- );
296
-
297
- const newMap = newTable as typeof oldMap;
283
+ const newMap = deepCopy(oldMap, SerializationType.NONE, "copiedMapHasValue");
298
284
 
299
285
  if (!isTSTLMap(newMap)) {
300
286
  error(`The copied Map was not a Map and has a type of: ${typeof newMap}`);
@@ -314,8 +300,7 @@ function copiedSetIsSet() {
314
300
  const oldSet = new Set<string>();
315
301
  oldSet.add(valueToLookFor);
316
302
 
317
- const newTable = deepCopy(oldSet, SerializationType.NONE, "copiedSetIsSet");
318
- const newSet = newTable as Set<string>;
303
+ const newSet = deepCopy(oldSet, SerializationType.NONE, "copiedSetIsSet");
319
304
 
320
305
  if (!isTSTLSet(newSet)) {
321
306
  error(`The copied Set was not a Set and has a type of: ${typeof newSet}`);
@@ -327,12 +312,7 @@ function copiedSetHasValue() {
327
312
  const oldSet = new Set<string>();
328
313
  oldSet.add(valueToLookFor);
329
314
 
330
- const newTable = deepCopy(
331
- oldSet,
332
- SerializationType.NONE,
333
- "copiedSetHasValue",
334
- );
335
- const newSet = newTable as Set<string>;
315
+ const newSet = deepCopy(oldSet, SerializationType.NONE, "copiedSetHasValue");
336
316
 
337
317
  if (!isTSTLSet(newSet)) {
338
318
  error(`The copied Set was not a Set and has a type of: ${typeof newSet}`);
@@ -354,12 +334,11 @@ function copiedMapHasChildMap() {
354
334
  const oldMap = new Map<string, Map<number, number>>();
355
335
  oldMap.set(keyToLookFor, oldChildMap);
356
336
 
357
- const newTable = deepCopy(
337
+ const newMap = deepCopy(
358
338
  oldMap,
359
339
  SerializationType.NONE,
360
340
  "copiedMapHasChildMap",
361
341
  );
362
- const newMap = newTable as typeof oldMap;
363
342
 
364
343
  if (!isTSTLMap(newMap)) {
365
344
  error(`The copied Map was not a Map and had a type of: ${typeof newMap}`);
@@ -398,12 +377,11 @@ function copiedDefaultMapHasChildDefaultMap() {
398
377
  oldChildMap.getAndSetDefault(childMapKey1);
399
378
  oldChildMap.set(childMapKey2, childMapCustomValue);
400
379
 
401
- const newTable = deepCopy(
380
+ const newParentMap = deepCopy(
402
381
  oldParentMap,
403
382
  SerializationType.NONE,
404
383
  "copiedDefaultMapHasChildDefaultMap",
405
384
  );
406
- const newParentMap = newTable as typeof oldParentMap;
407
385
 
408
386
  if (!isDefaultMap(newParentMap)) {
409
387
  error(
@@ -259,7 +259,10 @@ export function getRoomShapeDoorSlotCoordinates(
259
259
  return coordinatesMap.get(doorSlot);
260
260
  }
261
261
 
262
- /** Helper function to find unused door slots in the room that can be used to make custom doors. */
262
+ /**
263
+ * Helper function to find unused door slots in the current room that can be used to make custom
264
+ * doors.
265
+ */
263
266
  export function getUnusedDoorSlots(): DoorSlot[] {
264
267
  const room = game.GetRoom();
265
268
  const doorSlots = getEnumValues(DoorSlot);
@@ -269,6 +272,15 @@ export function getUnusedDoorSlots(): DoorSlot[] {
269
272
  );
270
273
  }
271
274
 
275
+ /**
276
+ * Helper function to check if the current room has one or more open door slots that can be used to
277
+ * make custom doors.
278
+ */
279
+ export function hasUnusedDoorSlot(): boolean {
280
+ const unusedDoorSlots = getUnusedDoorSlots();
281
+ return unusedDoorSlots.length > 0;
282
+ }
283
+
272
284
  export function isAngelRoomDoor(door: GridEntityDoor): boolean {
273
285
  return door.TargetRoomType === RoomType.ANGEL;
274
286
  }
@@ -23,7 +23,7 @@ export function initArray<T>(defaultValue: T, size: int): T[] {
23
23
  if (isPrimitive(defaultValue)) {
24
24
  array.push(defaultValue);
25
25
  } else {
26
- const copy = deepCopy(defaultValue) as T;
26
+ const copy = deepCopy(defaultValue);
27
27
  array.push(copy);
28
28
  }
29
29
  });
@@ -1,12 +1,18 @@
1
+ import { CopyableIsaacAPIClassType } from "isaac-typescript-definitions";
1
2
  import { SerializationBrand } from "../enums/private/SerializationBrand";
2
3
  import { isaacAPIClassEquals, isIsaacAPIClassOfType } from "./isaacAPIClass";
3
4
  import { getRandom } from "./random";
4
5
  import { getRandomSeed, isRNG, newRNG } from "./rng";
5
- import { copyValuesToTable, getNumbersFromTable, tableHasKeys } from "./table";
6
+ import {
7
+ copyUserdataValuesToTable,
8
+ getNumbersFromTable,
9
+ tableHasKeys,
10
+ } from "./table";
6
11
  import { isTable } from "./types";
7
12
 
8
13
  export type SerializedKColor = LuaMap<string, unknown> & {
9
14
  readonly __serializedKColorBrand: symbol;
15
+ readonly __kind: CopyableIsaacAPIClassType.K_COLOR;
10
16
  };
11
17
 
12
18
  const OBJECT_NAME = "KColor";
@@ -117,7 +123,7 @@ export function serializeKColor(kColor: KColor): SerializedKColor {
117
123
  }
118
124
 
119
125
  const kColorTable = new LuaMap<string, unknown>();
120
- copyValuesToTable(kColor, KEYS, kColorTable);
126
+ copyUserdataValuesToTable(kColor, KEYS, kColorTable);
121
127
  kColorTable.set(SerializationBrand.K_COLOR, "");
122
128
  return kColorTable as SerializedKColor;
123
129
  }
@@ -1,3 +1,4 @@
1
+ import { CopyableIsaacAPIClassType } from "isaac-typescript-definitions";
1
2
  import { game } from "../core/cachedClasses";
2
3
  import { SerializationBrand } from "../enums/private/SerializationBrand";
3
4
  import { isaacAPIClassEquals, isIsaacAPIClassOfType } from "./isaacAPIClass";
@@ -6,6 +7,7 @@ import { isTable } from "./types";
6
7
 
7
8
  export type SerializedRNG = LuaMap<string, unknown> & {
8
9
  readonly __serializedRNGBrand: symbol;
10
+ readonly __kind: CopyableIsaacAPIClassType.RNG;
9
11
  };
10
12
 
11
13
  /**
@@ -1,7 +1,13 @@
1
- import { CopyableIsaacAPIClassType } from "../enums/private/CopyableIsaacAPIClassType";
1
+ import { CopyableIsaacAPIClassType } from "isaac-typescript-definitions";
2
2
  import { ISAAC_API_CLASS_TYPE_TO_BRAND } from "../objects/isaacAPIClassTypeToBrand";
3
- import { ISAAC_API_CLASS_TYPE_TO_FUNCTIONS } from "../objects/isaacAPIClassTypeToFunctions";
4
- import { SerializedIsaacAPIClass } from "../types/SerializedIsaacAPIClass";
3
+ import {
4
+ CopyableIsaacAPIClass,
5
+ IsaacAPIClassTypeFunctions,
6
+ IsaacAPIClassTypeToSerializedType,
7
+ IsaacAPIClassTypeToType,
8
+ ISAAC_API_CLASS_TYPE_TO_FUNCTIONS,
9
+ SerializedIsaacAPIClass,
10
+ } from "../objects/isaacAPIClassTypeToFunctions";
5
11
  import { getIsaacAPIClassName } from "./isaacAPIClass";
6
12
  import { isTable, isUserdata } from "./types";
7
13
 
@@ -11,7 +17,9 @@ import { isTable, isUserdata } from "./types";
11
17
  *
12
18
  * For the list of supported classes, see the `CopyableIsaacAPIClassType` enum.
13
19
  */
14
- export function copyIsaacAPIClass(isaacAPIClass: unknown): unknown {
20
+ export function copyIsaacAPIClass<T extends CopyableIsaacAPIClass>(
21
+ isaacAPIClass: T,
22
+ ): T {
15
23
  if (!isUserdata(isaacAPIClass)) {
16
24
  error(
17
25
  `Failed to copy an Isaac API class since the provided object was of type: ${typeof isaacAPIClass}`,
@@ -27,9 +35,19 @@ export function copyIsaacAPIClass(isaacAPIClass: unknown): unknown {
27
35
 
28
36
  const copyableIsaacAPIClassType =
29
37
  isaacAPIClassType as CopyableIsaacAPIClassType;
30
- const functions =
31
- ISAAC_API_CLASS_TYPE_TO_FUNCTIONS[copyableIsaacAPIClassType];
32
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
38
+
39
+ type ThisIsaacAPIClassType = T;
40
+ type ThisSerializedIsaacAPIClassType =
41
+ IsaacAPIClassTypeToSerializedType[T["__kind"]];
42
+
43
+ const functions = ISAAC_API_CLASS_TYPE_TO_FUNCTIONS[
44
+ copyableIsaacAPIClassType
45
+ ] as unknown as
46
+ | IsaacAPIClassTypeFunctions<
47
+ ThisIsaacAPIClassType,
48
+ ThisSerializedIsaacAPIClassType
49
+ >
50
+ | undefined;
33
51
  if (functions === undefined) {
34
52
  error(
35
53
  `Failed to copy an Isaac API class since the associated functions were not found for Isaac API class type: ${copyableIsaacAPIClassType}`,
@@ -46,9 +64,11 @@ export function copyIsaacAPIClass(isaacAPIClass: unknown): unknown {
46
64
  *
47
65
  * For the list of supported classes, see the `CopyableIsaacAPIClassType` enum.
48
66
  */
49
- export function deserializeIsaacAPIClass(
50
- serializedIsaacAPIClass: unknown,
51
- ): unknown {
67
+ export function deserializeIsaacAPIClass<
68
+ SerializedT extends SerializedIsaacAPIClass,
69
+ >(
70
+ serializedIsaacAPIClass: SerializedT,
71
+ ): IsaacAPIClassTypeToType[SerializedT["__kind"]] {
52
72
  if (!isTable(serializedIsaacAPIClass)) {
53
73
  error(
54
74
  `Failed to deserialize an Isaac API class since the provided object was of type: ${typeof serializedIsaacAPIClass}`,
@@ -64,9 +84,17 @@ export function deserializeIsaacAPIClass(
64
84
  );
65
85
  }
66
86
 
67
- const functions =
68
- ISAAC_API_CLASS_TYPE_TO_FUNCTIONS[copyableIsaacAPIClassType];
69
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
87
+ type ThisIsaacAPIClassType = IsaacAPIClassTypeToType[SerializedT["__kind"]];
88
+ type ThisSerializedIsaacAPIClassType = SerializedT;
89
+
90
+ const functions = ISAAC_API_CLASS_TYPE_TO_FUNCTIONS[
91
+ copyableIsaacAPIClassType
92
+ ] as unknown as
93
+ | IsaacAPIClassTypeFunctions<
94
+ ThisIsaacAPIClassType,
95
+ ThisSerializedIsaacAPIClassType
96
+ >
97
+ | undefined;
70
98
  if (functions === undefined) {
71
99
  error(
72
100
  `Failed to deserialize an Isaac API class since the associated functions were not found for class type: ${copyableIsaacAPIClassType}`,
@@ -81,7 +109,7 @@ export function deserializeIsaacAPIClass(
81
109
  * serialized table for brands.
82
110
  */
83
111
  function getSerializedTableType(
84
- serializedIsaacAPIClass: LuaMap<AnyNotNil, unknown>,
112
+ serializedIsaacAPIClass: SerializedIsaacAPIClass,
85
113
  ): CopyableIsaacAPIClassType | undefined {
86
114
  for (const [copyableIsaacAPIClassType, serializationBrand] of Object.entries(
87
115
  ISAAC_API_CLASS_TYPE_TO_BRAND,
@@ -94,6 +122,20 @@ function getSerializedTableType(
94
122
  return undefined;
95
123
  }
96
124
 
125
+ /**
126
+ * Helper function to generically check if a given object is a copyable Isaac API class. (This is
127
+ * used by the save data manager when determining what is safe to copy.)
128
+ *
129
+ * For the list of supported classes, see the `CopyableIsaacAPIClassType` enum.
130
+ */
131
+ export function isCopyableIsaacAPIClass(
132
+ object: unknown,
133
+ ): object is CopyableIsaacAPIClass {
134
+ const allFunctions = Object.values(ISAAC_API_CLASS_TYPE_TO_FUNCTIONS);
135
+ const isFunctions = allFunctions.map((functions) => functions.is);
136
+ return isFunctions.some((identityFunction) => identityFunction(object));
137
+ }
138
+
97
139
  /**
98
140
  * Helper function to generically check if a given Lua table is a serialized Isaac API class. (This
99
141
  * is used by the save data manager when reading data from the "save#.dat" file.)
@@ -118,7 +160,9 @@ export function isSerializedIsaacAPIClass(
118
160
  *
119
161
  * For the list of supported classes, see the `CopyableIsaacAPIClassType` enum.
120
162
  */
121
- export function serializeIsaacAPIClass(isaacAPIClass: unknown): unknown {
163
+ export function serializeIsaacAPIClass<T extends CopyableIsaacAPIClass>(
164
+ isaacAPIClass: T,
165
+ ): IsaacAPIClassTypeToSerializedType[T["__kind"]] {
122
166
  if (!isUserdata(isaacAPIClass)) {
123
167
  error(
124
168
  `Failed to serialize an Isaac API class since the provided object was of type: ${typeof isaacAPIClass}`,
@@ -134,9 +178,19 @@ export function serializeIsaacAPIClass(isaacAPIClass: unknown): unknown {
134
178
 
135
179
  const copyableIsaacAPIClassType =
136
180
  isaacAPIClassType as CopyableIsaacAPIClassType;
137
- const functions =
138
- ISAAC_API_CLASS_TYPE_TO_FUNCTIONS[copyableIsaacAPIClassType];
139
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
181
+
182
+ type ThisIsaacAPIClassType = T;
183
+ type ThisSerializedIsaacAPIClassType =
184
+ IsaacAPIClassTypeToSerializedType[T["__kind"]];
185
+
186
+ const functions = ISAAC_API_CLASS_TYPE_TO_FUNCTIONS[
187
+ copyableIsaacAPIClassType
188
+ ] as unknown as
189
+ | IsaacAPIClassTypeFunctions<
190
+ ThisIsaacAPIClassType,
191
+ ThisSerializedIsaacAPIClassType
192
+ >
193
+ | undefined;
140
194
  if (functions === undefined) {
141
195
  error(
142
196
  `Failed to serialize an Isaac API class since the associated functions were not found for class type: ${copyableIsaacAPIClassType}`,