isaacscript-common 20.12.2 → 20.12.3

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 (52) hide show
  1. package/dist/index.d.ts +44 -40
  2. package/dist/isaacscript-common.lua +262 -416
  3. package/dist/src/classes/ModFeature.lua +0 -4
  4. package/dist/src/classes/ModUpgradedBase.lua +3 -9
  5. package/dist/src/classes/callbacks/PostNewRoomEarly.lua +2 -2
  6. package/dist/src/classes/features/callbackLogic/CustomRevive.lua +2 -5
  7. package/dist/src/classes/features/other/CustomStages.lua +4 -16
  8. package/dist/src/classes/features/other/CustomTrapdoors.lua +1 -4
  9. package/dist/src/classes/features/other/DeployJSONRoom.lua +8 -14
  10. package/dist/src/classes/features/other/Pause.lua +2 -2
  11. package/dist/src/classes/features/other/TaintedLazarusPlayers.lua +1 -1
  12. package/dist/src/classes/features/other/customStages/utils.lua +4 -16
  13. package/dist/src/classes/features/other/extraConsoleCommands/commands.lua +4 -4
  14. package/dist/src/classes/features/other/extraConsoleCommands/subroutines.lua +2 -2
  15. package/dist/src/classes/features/other/saveDataManager/loadFromDisk.lua +4 -7
  16. package/dist/src/classes/features/other/saveDataManager/restoreDefaults.lua +2 -2
  17. package/dist/src/classes/features/other/saveDataManager/saveToDisk.lua +1 -1
  18. package/dist/src/functions/benchmark.lua +2 -8
  19. package/dist/src/functions/debugFunctions.d.ts +2 -2
  20. package/dist/src/functions/debugFunctions.d.ts.map +1 -1
  21. package/dist/src/functions/debugFunctions.lua +4 -4
  22. package/dist/src/functions/deepCopy.lua +7 -7
  23. package/dist/src/functions/deepCopyTests.lua +1 -1
  24. package/dist/src/functions/globals.lua +3 -6
  25. package/dist/src/functions/hex.lua +4 -7
  26. package/dist/src/functions/jsonHelpers.lua +1 -1
  27. package/dist/src/functions/jsonRoom.lua +4 -16
  28. package/dist/src/functions/log.d.ts +8 -4
  29. package/dist/src/functions/log.d.ts.map +1 -1
  30. package/dist/src/functions/log.lua +18 -5
  31. package/dist/src/functions/logEntities.d.ts +8 -8
  32. package/dist/src/functions/logEntities.d.ts.map +1 -1
  33. package/dist/src/functions/logEntities.lua +24 -27
  34. package/dist/src/functions/logMisc.d.ts +26 -26
  35. package/dist/src/functions/logMisc.d.ts.map +1 -1
  36. package/dist/src/functions/logMisc.lua +114 -226
  37. package/dist/src/functions/merge.lua +6 -6
  38. package/dist/src/functions/run.lua +2 -5
  39. package/dist/src/functions/utils.d.ts.map +1 -1
  40. package/dist/src/functions/utils.lua +20 -1
  41. package/dist/src/lib/jsonLua.lua +16 -7
  42. package/dist/src/patchErrorFunctions.lua +1 -1
  43. package/package.json +1 -1
  44. package/src/classes/ModFeature.ts +0 -6
  45. package/src/classes/features/other/saveDataManager/saveToDisk.ts +3 -4
  46. package/src/functions/debugFunctions.ts +2 -2
  47. package/src/functions/deepCopy.ts +2 -0
  48. package/src/functions/log.ts +15 -4
  49. package/src/functions/logEntities.ts +14 -8
  50. package/src/functions/logMisc.ts +56 -39
  51. package/src/functions/utils.ts +30 -0
  52. package/src/lib/jsonLua.lua +16 -7
@@ -94,7 +94,7 @@ end
94
94
  --- Helper function to log a message to the "log.txt" file and to print it to the screen at the same
95
95
  -- time.
96
96
  function ____exports.logAndPrint(self, msg)
97
- log(nil, msg)
97
+ log(msg)
98
98
  print(msg)
99
99
  end
100
100
  --- Helper function to print whether something was enabled or disabled to the in-game console.
@@ -154,6 +154,20 @@ end
154
154
  -- From:
155
155
  -- https://stackoverflow.com/questions/16096872/how-to-sort-2-dimensional-array-by-column-value
156
156
  function ____exports.twoDimensionalSort(self, array1, array2)
157
+ local type1 = type(array1)
158
+ local type2 = type(array2)
159
+ if type1 ~= type2 then
160
+ error(((((((("Failed to two-dimensional sort since the two elements were disparate types: " .. tostring(array1)) .. " & ") .. tostring(array2)) .. " (") .. type1) .. " & ") .. type2) .. ")")
161
+ end
162
+ if type1 == "string" or type1 == "number" then
163
+ if array1 == array2 then
164
+ return 0
165
+ end
166
+ return array1 < array2 and -1 or 1
167
+ end
168
+ if type1 ~= "table" then
169
+ error("Failed to two-dimensional sort since the elements were not a string, number, or table.")
170
+ end
157
171
  local firstElement1 = array1[1]
158
172
  local firstElement2 = array2[1]
159
173
  if firstElement1 == nil or firstElement1 == nil then
@@ -162,6 +176,11 @@ function ____exports.twoDimensionalSort(self, array1, array2)
162
176
  if firstElement2 == nil or firstElement2 == nil then
163
177
  error("Failed to two-dimensional sort since the first element of the second array was undefined.")
164
178
  end
179
+ local elementType1 = type(firstElement1)
180
+ local elementType2 = type(firstElement2)
181
+ if elementType1 ~= elementType2 then
182
+ error(((((((("Failed to two-dimensional sort since the first element of each array were disparate types: " .. tostring(firstElement1)) .. " & ") .. tostring(firstElement2)) .. " (") .. elementType1) .. " & ") .. elementType2) .. ")")
183
+ end
165
184
  if firstElement1 == firstElement2 then
166
185
  return 0
167
186
  end
@@ -24,6 +24,9 @@
24
24
  -- SOFTWARE.
25
25
  --
26
26
 
27
+ -- The IsaacScript version of this file contains modifications for better error messages, which
28
+ -- assist when debugging.
29
+
27
30
  local json = { _version = "0.1.2" }
28
31
 
29
32
  -------------------------------------------------------------------------------
@@ -58,9 +61,10 @@ local function encode_nil(val)
58
61
  end
59
62
 
60
63
 
61
- local function encode_table(val, stack)
64
+ local function encode_table(val, stack, traversalDescription)
62
65
  local res = {}
63
66
  stack = stack or {}
67
+ traversalDescription = traversalDescription or ""
64
68
 
65
69
  -- Circular reference?
66
70
  if stack[val] then error("circular reference") end
@@ -72,7 +76,7 @@ local function encode_table(val, stack)
72
76
  local n = 0
73
77
  for k in pairs(val) do
74
78
  if type(k) ~= "number" then
75
- error("invalid table: mixed or invalid key types")
79
+ error("invalid table: mixed or invalid key types for array, excepted number, got: " .. tostring(type(k)))
76
80
  end
77
81
  n = n + 1
78
82
  end
@@ -81,7 +85,8 @@ local function encode_table(val, stack)
81
85
  end
82
86
  -- Encode
83
87
  for i, v in ipairs(val) do
84
- table.insert(res, encode(v, stack))
88
+ local newTraversalDescription = traversalDescription .. tostring(i) .. " - "
89
+ table.insert(res, encode(v, stack, newTraversalDescription))
85
90
  end
86
91
  stack[val] = nil
87
92
  return "[" .. table.concat(res, ",") .. "]"
@@ -89,10 +94,14 @@ local function encode_table(val, stack)
89
94
  else
90
95
  -- Treat as an object
91
96
  for k, v in pairs(val) do
97
+ local newTraversalDescription = traversalDescription .. tostring(k) .. " - "
92
98
  if type(k) ~= "string" then
93
- error("invalid table: mixed or invalid key types")
99
+ error(
100
+ "invalid table: mixed or invalid key types for object \"" .. newTraversalDescription .. "\", "
101
+ .. "excepted string, got: " .. tostring(type(k))
102
+ )
94
103
  end
95
- table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
104
+ table.insert(res, encode(k, stack, newTraversalDescription) .. ":" .. encode(v, stack, newTraversalDescription))
96
105
  end
97
106
  stack[val] = nil
98
107
  return "{" .. table.concat(res, ",") .. "}"
@@ -123,11 +132,11 @@ local type_func_map = {
123
132
  }
124
133
 
125
134
 
126
- encode = function(val, stack)
135
+ encode = function(val, stack, traversalDescription)
127
136
  local t = type(val)
128
137
  local f = type_func_map[t]
129
138
  if f then
130
- return f(val, stack)
139
+ return f(val, stack, traversalDescription)
131
140
  end
132
141
  error("unexpected type '" .. t .. "'")
133
142
  end
@@ -14,7 +14,7 @@ function errorWithTraceback(message, level)
14
14
  if level == nil then
15
15
  level = 1
16
16
  end
17
- local tracebackOutput = getTraceback(nil)
17
+ local tracebackOutput = getTraceback()
18
18
  local slimmedTracebackOutput = slimTracebackOutput(nil, tracebackOutput)
19
19
  message = message .. "\n"
20
20
  message = message .. slimmedTracebackOutput
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isaacscript-common",
3
- "version": "20.12.2",
3
+ "version": "20.12.3",
4
4
  "description": "Helper functions and features for IsaacScript mods.",
5
5
  "keywords": [
6
6
  "isaac",
@@ -330,18 +330,12 @@ function initSaveDataManager(
330
330
  tstlClassName: string,
331
331
  init: boolean,
332
332
  ) {
333
- if (tstlClassName === "AtePoopBaby") {
334
- Isaac.DebugString("GETTING HERE 1");
335
- }
336
-
337
333
  // Do nothing if this class does not have any variables.
338
334
  const { v } = modFeature as unknown as Record<string, unknown>;
339
335
  if (v === undefined) {
340
336
  return;
341
337
  }
342
338
 
343
- Isaac.DebugString("GETTING HERE 2");
344
-
345
339
  if (!isTable(v)) {
346
340
  error(
347
341
  'Failed to initialize a mod feature class due to having a "v" property that is not an object. (The "v" property is supposed to be an object that holds the variables for the class, managed by the save data manager.)',
@@ -16,7 +16,7 @@ export function saveToDisk(
16
16
  saveDataConditionalFuncMap,
17
17
  );
18
18
  const jsonString = jsonEncode(allSaveData);
19
- mod.SaveData(jsonString); // Write it to the "save#.dat" file
19
+ mod.SaveData(jsonString);
20
20
  log('The save data manager wrote data to the "save#.dat" file.');
21
21
  }
22
22
 
@@ -52,9 +52,8 @@ function getAllSaveDataToWriteToDisk(
52
52
  return;
53
53
  }
54
54
 
55
- // If we encode TypeScriptToLua Maps into JSON, it will result in a lot of extraneous data
56
- // that is unnecessary. Make a copy of the data and recursively convert all TypeScriptToLua
57
- // Maps into Lua tables.
55
+ // We need to serialize TypeScriptToLua maps and Isaac API objects such as `Color`.
56
+ // Recursively convert all such objects to Lua tables.
58
57
  const saveDataCopy = deepCopy(
59
58
  saveDataWithoutRoom,
60
59
  SerializationType.SERIALIZE,
@@ -45,7 +45,7 @@ export function getTime(useSocketIfAvailable = true): float {
45
45
  * This will only work if the `--luadebug` launch option is enabled or the Racing+ sandbox is
46
46
  * enabled.
47
47
  */
48
- export function getTraceback(): string {
48
+ export function getTraceback(this: void): string {
49
49
  if (SandboxGetTraceback !== undefined) {
50
50
  return SandboxGetTraceback();
51
51
  }
@@ -88,7 +88,7 @@ export function isLuaDebugEnabled(): boolean {
88
88
  * This will only work if the `--luadebug` launch option is enabled or the Racing+ sandbox is
89
89
  * enabled.
90
90
  */
91
- export function traceback(): void {
91
+ export function traceback(this: void): void {
92
92
  const tracebackOutput = getTraceback();
93
93
  log(tracebackOutput);
94
94
  }
@@ -615,6 +615,8 @@ function deepCopyNormalLuaTable(
615
615
  /**
616
616
  * Recursively clones the object's entries, automatically converting number keys to strings, if
617
617
  * necessary.
618
+ *
619
+ * This should work on objects/tables, maps, sets, default maps, and classes.
618
620
  */
619
621
  function getCopiedEntries(
620
622
  object: unknown,
@@ -9,6 +9,7 @@
9
9
  * function, and the third level is the parent of the calling function).
10
10
  */
11
11
  export function getParentFunctionDescription(
12
+ this: void,
12
13
  // We use 3 as a default because:
13
14
  // - The first level is this function.
14
15
  // - The second level is the calling function.
@@ -36,8 +37,8 @@ export function getParentFunctionDescription(
36
37
  /**
37
38
  * Helper function to avoid typing out `Isaac.DebugString()`.
38
39
  *
39
- * If you have the "--luadebug" launch flag turned on or the Racing+ sandbox enabled, then this
40
- * function will also prepend the function name and the line number before the string, like this:
40
+ * If you have the "--luadebug" launch flag turned on, then this function will also prepend the
41
+ * function name and the line number before the string, like this:
41
42
  *
42
43
  * ```text
43
44
  * [INFO] - Lua Debug: saveToDisk:42494 - The save data manager wrote data to the "save#.dat" file.
@@ -45,9 +46,19 @@ export function getParentFunctionDescription(
45
46
  *
46
47
  * Subsequently, it is recommended that you turn on the "--luadebug" launch flag when developing
47
48
  * your mod so that debugging becomes a little bit easier.
49
+ *
50
+ * @param msg The message to log.
51
+ * @param includeParentFunction Optional. Whether to prefix the message with the function name and
52
+ * line number, as shown in the above example. Default is true.
48
53
  */
49
- export function log(msg: string): void {
50
- const parentFunctionDescription = getParentFunctionDescription();
54
+ export function log(
55
+ this: void,
56
+ msg: string,
57
+ includeParentFunction = true,
58
+ ): void {
59
+ const parentFunctionDescription = includeParentFunction
60
+ ? getParentFunctionDescription()
61
+ : undefined;
51
62
  const debugMsg =
52
63
  parentFunctionDescription === undefined
53
64
  ? msg
@@ -29,6 +29,7 @@ const IGNORE_EFFECT_VARIANTS: ReadonlySet<EffectVariant> = new Set([
29
29
 
30
30
  /** Helper function for printing out every entity (or filtered entity) in the current room. */
31
31
  export function logAllEntities(
32
+ this: void,
32
33
  includeBackgroundEffects: boolean,
33
34
  entityTypeFilter?: EntityType,
34
35
  ): void {
@@ -83,6 +84,7 @@ export function logAllEntities(
83
84
  * Default is undefined.
84
85
  */
85
86
  export function logAllGridEntities(
87
+ this: void,
86
88
  includeWalls = false,
87
89
  gridEntityTypeFilter?: GridEntityType,
88
90
  ): void {
@@ -136,19 +138,19 @@ export function logAllGridEntities(
136
138
  }
137
139
 
138
140
  /** Helper function for logging an array of specific entities. */
139
- export function logEntities(entities: Entity[]): void {
141
+ export function logEntities(this: void, entities: Entity[]): void {
140
142
  for (const entity of entities) {
141
143
  logEntity(entity);
142
144
  }
143
145
  }
144
146
 
145
147
  /** Helper function to log information about a specific entity. */
146
- export function logEntity(entity: Entity): void {
148
+ export function logEntity(this: void, entity: Entity): void {
147
149
  const msg = getEntityLogLine(entity);
148
150
  log(msg);
149
151
  }
150
152
 
151
- function getEntityLogLine(entity: Entity, num?: int): string {
153
+ function getEntityLogLine(this: void, entity: Entity, num?: int): string {
152
154
  let msg = num === undefined ? "" : `${num}) `;
153
155
 
154
156
  msg += getEntityID(entity);
@@ -230,19 +232,23 @@ function getEntityLogLine(entity: Entity, num?: int): string {
230
232
  }
231
233
 
232
234
  /** Helper function for logging an array of specific grid entities. */
233
- export function logGridEntities(gridEntities: GridEntity[]): void {
235
+ export function logGridEntities(this: void, gridEntities: GridEntity[]): void {
234
236
  for (const gridEntity of gridEntities) {
235
237
  logGridEntity(gridEntity);
236
238
  }
237
239
  }
238
240
 
239
241
  /** Helper function for log information about a specific grid entity. */
240
- export function logGridEntity(gridEntity: GridEntity): void {
242
+ export function logGridEntity(this: void, gridEntity: GridEntity): void {
241
243
  const msg = getGridEntityLogLine(gridEntity);
242
244
  log(msg);
243
245
  }
244
246
 
245
- function getGridEntityLogLine(gridEntity: GridEntity, num?: int): string {
247
+ function getGridEntityLogLine(
248
+ this: void,
249
+ gridEntity: GridEntity,
250
+ num?: int,
251
+ ): string {
246
252
  const gridEntityDesc = gridEntity.GetSaveState();
247
253
 
248
254
  let msg = num === undefined ? "" : `${num}) `;
@@ -303,7 +309,7 @@ function getGridEntityLogLine(gridEntity: GridEntity, num?: int): string {
303
309
  * Helper function to log information about the entity that corresponding to a pointer hash. (Only
304
310
  * use this when debugging, since retrieving the corresponding entity is expensive.)
305
311
  */
306
- export function logPtrHash(ptrHash: PtrHash): void {
312
+ export function logPtrHash(this: void, ptrHash: PtrHash): void {
307
313
  log(`PtrHash: ${ptrHash}`);
308
314
  const entity = getEntityFromPtrHash(ptrHash);
309
315
  if (entity === undefined) {
@@ -317,7 +323,7 @@ export function logPtrHash(ptrHash: PtrHash): void {
317
323
  * Helper function to log information about the entity that corresponding to one or more pointer
318
324
  * hashes. (Only use this when debugging, since retrieving the corresponding entity is expensive.)
319
325
  */
320
- export function logPtrHashes(ptrHashes: PtrHash[]): void {
326
+ export function logPtrHashes(this: void, ptrHashes: PtrHash[]): void {
321
327
  for (const ptrHash of ptrHashes) {
322
328
  logPtrHash(ptrHash);
323
329
  }
@@ -31,7 +31,7 @@ import { isTable, isUserdata } from "./types";
31
31
  import { vectorToString } from "./vector";
32
32
 
33
33
  /** Helper function to enumerate all of the values in an array. */
34
- export function logArray<T>(array: T[] | readonly T[]): void {
34
+ export function logArray<T>(this: void, array: T[] | readonly T[]): void {
35
35
  // We do not assume the given array has contiguous values in order to be more permissive about the
36
36
  // kinds of arrays that will successfully log without a run-time error.
37
37
  if (!isArray(array, false)) {
@@ -43,7 +43,10 @@ export function logArray<T>(array: T[] | readonly T[]): void {
43
43
  log(`Array: ${arrayString}`);
44
44
  }
45
45
 
46
- export function logCollectibleTypes(collectibleTypes: CollectibleType[]): void {
46
+ export function logCollectibleTypes(
47
+ this: void,
48
+ collectibleTypes: CollectibleType[],
49
+ ): void {
47
50
  log("Collectibles:");
48
51
 
49
52
  let i = 1;
@@ -54,23 +57,29 @@ export function logCollectibleTypes(collectibleTypes: CollectibleType[]): void {
54
57
  }
55
58
  }
56
59
 
57
- export function logColor(color: Color): void {
60
+ export function logColor(this: void, color: Color): void {
58
61
  log(
59
62
  `Color: R${color.R}, G${color.G}, B${color.B}, A${color.A}, RO${color.RO}, BO${color.BO}, GO${color.GO}`,
60
63
  );
61
64
  }
62
65
 
63
66
  /** Helper function for printing out every damage flag that is turned on. Useful when debugging. */
64
- export function logDamageFlags(flags: DamageFlag | BitFlags<DamageFlag>): void {
67
+ export function logDamageFlags(
68
+ this: void,
69
+ flags: DamageFlag | BitFlags<DamageFlag>,
70
+ ): void {
65
71
  logFlags(flags, DamageFlag, "damage");
66
72
  }
67
73
 
68
74
  /** Helper function for printing out every entity flag that is turned on. Useful when debugging. */
69
- export function logEntityFlags(flags: EntityFlag | BitFlags<EntityFlag>): void {
75
+ export function logEntityFlags(
76
+ this: void,
77
+ flags: EntityFlag | BitFlags<EntityFlag>,
78
+ ): void {
70
79
  logFlags(flags, EntityFlag, "entity");
71
80
  }
72
81
 
73
- export function logEntityID(entity: Entity): void {
82
+ export function logEntityID(this: void, entity: Entity): void {
74
83
  const entityID = getEntityID(entity);
75
84
  log(`Entity: ${entityID}`);
76
85
  }
@@ -81,7 +90,7 @@ export function logEntityID(entity: Entity): void {
81
90
  * This is useful in situations where using the `error` function would be dangerous (since it
82
91
  * prevents all of the subsequent code in the callback from running).
83
92
  */
84
- export function logError(msg: string): void {
93
+ export function logError(this: void, msg: string): void {
85
94
  const errorMsg = `Error: ${msg}`;
86
95
  log(errorMsg);
87
96
  print(errorMsg);
@@ -89,6 +98,7 @@ export function logError(msg: string): void {
89
98
 
90
99
  /** Helper function for printing out every flag that is turned on. Useful when debugging. */
91
100
  export function logFlags<T extends BitFlag | BitFlag128>(
101
+ this: void,
92
102
  flags: T | BitFlags<T>,
93
103
  flagEnum: Record<string, T>,
94
104
  description = "",
@@ -116,7 +126,7 @@ export function logFlags<T extends BitFlag | BitFlag128>(
116
126
  /**
117
127
  * Helper function for printing out every game state flag that is turned on. Useful when debugging.
118
128
  */
119
- export function logGameStateFlags(): void {
129
+ export function logGameStateFlags(this: void): void {
120
130
  log("Logging game state flags:");
121
131
 
122
132
  const gameStateFlagEntries = getEnumEntries(GameStateFlag);
@@ -135,7 +145,7 @@ export function logGameStateFlags(): void {
135
145
  }
136
146
  }
137
147
 
138
- export function logKColor(kColor: KColor): void {
148
+ export function logKColor(this: void, kColor: KColor): void {
139
149
  log(
140
150
  `Color: R${kColor.Red}, G${kColor.Green}, B${kColor.Blue}, A${kColor.Alpha}`,
141
151
  );
@@ -144,7 +154,7 @@ export function logKColor(kColor: KColor): void {
144
154
  /**
145
155
  * Helper function for printing out every level state flag that is turned on. Useful when debugging.
146
156
  */
147
- export function logLevelStateFlags(): void {
157
+ export function logLevelStateFlags(this: void): void {
148
158
  const level = game.GetLevel();
149
159
 
150
160
  const levelStateFlagEntries = getEnumEntries(LevelStateFlag);
@@ -164,7 +174,7 @@ export function logLevelStateFlags(): void {
164
174
  }
165
175
  }
166
176
 
167
- export function logMap(map: Map<AnyNotNil, unknown>): void {
177
+ export function logMap(this: void, map: Map<AnyNotNil, unknown>): void {
168
178
  if (!isTSTLMap(map) && !isDefaultMap(map)) {
169
179
  log("Tried to log a TSTL map, but the given object was not a TSTL map.");
170
180
  return;
@@ -184,7 +194,7 @@ export function logMap(map: Map<AnyNotNil, unknown>): void {
184
194
  log(` The size of the map was: ${map.size}`);
185
195
  }
186
196
 
187
- export function logPlayerEffects(player: EntityPlayer): void {
197
+ export function logPlayerEffects(this: void, player: EntityPlayer): void {
188
198
  const effects = getEffectsList(player);
189
199
 
190
200
  log("Logging player effects:");
@@ -212,7 +222,7 @@ export function logPlayerEffects(player: EntityPlayer): void {
212
222
  });
213
223
  }
214
224
 
215
- export function logPlayerHealth(player: EntityPlayer): void {
225
+ export function logPlayerHealth(this: void, player: EntityPlayer): void {
216
226
  const playerName = getPlayerName(player);
217
227
  const playerHealth = getPlayerHealth(player);
218
228
 
@@ -238,13 +248,14 @@ export function logPlayerHealth(player: EntityPlayer): void {
238
248
  * Helper function for printing out every projectile flag that is turned on. Useful when debugging.
239
249
  */
240
250
  export function logProjectileFlags(
251
+ this: void,
241
252
  flags: ProjectileFlag | BitFlags<ProjectileFlag>,
242
253
  ): void {
243
254
  logFlags(flags, ProjectileFlag, "projectile");
244
255
  }
245
256
 
246
257
  /** Helper function for logging information about the current room. */
247
- export function logRoom(): void {
258
+ export function logRoom(this: void): void {
248
259
  const room = game.GetRoom();
249
260
  const bossID = room.GetBossID();
250
261
  const roomGridIndex = getRoomGridIndex();
@@ -277,7 +288,7 @@ export function logRoom(): void {
277
288
  * Helper function for printing out every seed effect (i.e. Easter Egg) that is turned on for the
278
289
  * particular run.
279
290
  */
280
- export function logSeedEffects(): void {
291
+ export function logSeedEffects(this: void): void {
281
292
  const seeds = game.GetSeeds();
282
293
 
283
294
  const seedEffectEntries = getEnumEntries(SeedEffect);
@@ -296,7 +307,10 @@ export function logSeedEffects(): void {
296
307
  }
297
308
  }
298
309
 
299
- export function logSet(set: Set<AnyNotNil> | ReadonlySet<AnyNotNil>): void {
310
+ export function logSet(
311
+ this: void,
312
+ set: Set<AnyNotNil> | ReadonlySet<AnyNotNil>,
313
+ ): void {
300
314
  if (!isTSTLSet(set)) {
301
315
  log("Tried to log a TSTL set, but the given object was not a TSTL set.");
302
316
  return;
@@ -314,7 +328,7 @@ export function logSet(set: Set<AnyNotNil> | ReadonlySet<AnyNotNil>): void {
314
328
  }
315
329
 
316
330
  /** Helper function for logging every sound effect that is currently playing. */
317
- export function logSounds(): void {
331
+ export function logSounds(this: void): void {
318
332
  const soundEffects = getEnumEntries(SoundEffect);
319
333
 
320
334
  for (const [key, soundEffect] of soundEffects) {
@@ -334,9 +348,13 @@ export function logSounds(): void {
334
348
  * In order to prevent infinite recursion, this function will not log a sub-table when there are 10
335
349
  * or more parent tables.
336
350
  */
337
- export function logTable(luaTable: unknown, parentTables = 0): void {
351
+ export function logTable(
352
+ this: void,
353
+ luaTable: unknown,
354
+ parentTables = 0,
355
+ ): void {
338
356
  if (parentTables === 0) {
339
- log("Printing out a Lua table:");
357
+ log("Printing out a Lua table:", false);
340
358
  } else if (parentTables > 10) {
341
359
  return;
342
360
  }
@@ -345,14 +363,10 @@ export function logTable(luaTable: unknown, parentTables = 0): void {
345
363
  const indentation = " ".repeat(numSpaces);
346
364
 
347
365
  if (!isTable(luaTable)) {
348
- // Put it in an IIFE so that it will show as "func" instead of "logTable" and align with the
349
- // other text. We have to use a non-arrow function to prevent Lua language server errors with
350
- // the self argument.
351
- (function func() {
352
- log(
353
- `${indentation}n/a (encountered a variable of type "${typeof luaTable}" instead of a table)`,
354
- );
355
- })();
366
+ log(
367
+ `${indentation}n/a (encountered a variable of type "${typeof luaTable}" instead of a table)`,
368
+ false,
369
+ );
356
370
 
357
371
  return;
358
372
  }
@@ -360,12 +374,13 @@ export function logTable(luaTable: unknown, parentTables = 0): void {
360
374
  let numElements = 0;
361
375
  iterateTableInOrder(luaTable, (key, value) => {
362
376
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
363
- log(`${indentation}${key} --> ${value}`);
377
+ log(`${indentation}${key} --> ${value}`, false);
364
378
 
365
379
  if (isTable(value)) {
366
380
  if (key === "__class") {
367
381
  log(
368
382
  `${indentation} (skipping enumerating this key to avoid infinite recursion)`,
383
+ false,
369
384
  );
370
385
  } else {
371
386
  logTable(value, parentTables + 1);
@@ -375,12 +390,7 @@ export function logTable(luaTable: unknown, parentTables = 0): void {
375
390
  numElements++;
376
391
  });
377
392
 
378
- // Put it in an IIFE so that it will show as "func" instead of "logTable" and align with the other
379
- // text. We have to use a non-arrow function to prevent Lua language server errors with the self
380
- // argument.
381
- (function func() {
382
- log(`${indentation}The size of the table was: ${numElements}`);
383
- })();
393
+ log(`${indentation}The size of the table was: ${numElements}`, false);
384
394
  }
385
395
 
386
396
  /**
@@ -388,6 +398,7 @@ export function logTable(luaTable: unknown, parentTables = 0): void {
388
398
  * will only do a shallow comparison.
389
399
  */
390
400
  export function logTableDifferences<K extends AnyNotNil, V>(
401
+ this: void,
391
402
  table1: LuaMap<K, V>,
392
403
  table2: LuaMap<K, V>,
393
404
  ): void {
@@ -428,7 +439,7 @@ export function logTableDifferences<K extends AnyNotNil, V>(
428
439
  *
429
440
  * This function is useful for tables that have recursive references.
430
441
  */
431
- export function logTableKeys(luaTable: unknown): void {
442
+ export function logTableKeys(this: void, luaTable: unknown): void {
432
443
  log("Printing out the keys of a Lua table:");
433
444
 
434
445
  if (!isTable(luaTable)) {
@@ -449,12 +460,18 @@ export function logTableKeys(luaTable: unknown): void {
449
460
  }
450
461
 
451
462
  /** Helper function for printing out every tear flag that is turned on. Useful when debugging. */
452
- export function logTearFlags(flags: TearFlag | BitFlags<TearFlag>): void {
463
+ export function logTearFlags(
464
+ this: void,
465
+ flags: TearFlag | BitFlags<TearFlag>,
466
+ ): void {
453
467
  logFlags(flags, TearFlag, "tear");
454
468
  }
455
469
 
456
470
  /** Helper function for printing out every use flag that is turned on. Useful when debugging. */
457
- export function logUseFlags(flags: UseFlag | BitFlags<UseFlag>): void {
471
+ export function logUseFlags(
472
+ this: void,
473
+ flags: UseFlag | BitFlags<UseFlag>,
474
+ ): void {
458
475
  logFlags(flags, UseFlag, "use");
459
476
  }
460
477
 
@@ -462,7 +479,7 @@ export function logUseFlags(flags: UseFlag | BitFlags<UseFlag>): void {
462
479
  * Helper function to enumerate all of the properties of a "userdata" object (i.e. an object from
463
480
  * the Isaac API).
464
481
  */
465
- export function logUserdata(userdata: unknown): void {
482
+ export function logUserdata(this: void, userdata: unknown): void {
466
483
  if (!isUserdata(userdata)) {
467
484
  log("Userdata: [not userdata]");
468
485
  return;
@@ -484,7 +501,7 @@ export function logUserdata(userdata: unknown): void {
484
501
  logTable(metatable);
485
502
  }
486
503
 
487
- export function logVector(vector: Vector, round = false): void {
504
+ export function logVector(this: void, vector: Vector, round = false): void {
488
505
  const vectorString = vectorToString(vector, round);
489
506
  log(`Vector: ${vectorString}`);
490
507
  }
@@ -174,6 +174,28 @@ export function todo(...args: unknown[]): void {}
174
174
  * https://stackoverflow.com/questions/16096872/how-to-sort-2-dimensional-array-by-column-value
175
175
  */
176
176
  export function twoDimensionalSort<T>(array1: T[], array2: T[]): -1 | 0 | 1 {
177
+ const type1 = type(array1);
178
+ const type2 = type(array2);
179
+ if (type1 !== type2) {
180
+ error(
181
+ `Failed to two-dimensional sort since the two elements were disparate types: ${array1} & ${array2} (${type1} & ${type2})`,
182
+ );
183
+ }
184
+
185
+ if (type1 === "string" || type1 === "number") {
186
+ if (array1 === array2) {
187
+ return 0;
188
+ }
189
+
190
+ return array1 < array2 ? -1 : 1;
191
+ }
192
+
193
+ if (type1 !== "table") {
194
+ error(
195
+ "Failed to two-dimensional sort since the elements were not a string, number, or table.",
196
+ );
197
+ }
198
+
177
199
  const firstElement1 = array1[0];
178
200
  const firstElement2 = array2[0];
179
201
 
@@ -189,6 +211,14 @@ export function twoDimensionalSort<T>(array1: T[], array2: T[]): -1 | 0 | 1 {
189
211
  );
190
212
  }
191
213
 
214
+ const elementType1 = type(firstElement1);
215
+ const elementType2 = type(firstElement2);
216
+ if (elementType1 !== elementType2) {
217
+ error(
218
+ `Failed to two-dimensional sort since the first element of each array were disparate types: ${firstElement1} & ${firstElement2} (${elementType1} & ${elementType2})`,
219
+ );
220
+ }
221
+
192
222
  if (firstElement1 === firstElement2) {
193
223
  return 0;
194
224
  }