isaacscript-common 20.9.1 → 20.11.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.
@@ -1,43 +1,56 @@
1
1
  import { ModCallback, PlayerType } from "isaac-typescript-definitions";
2
2
  import { game } from "../../../core/cachedClasses";
3
3
  import { Exported } from "../../../decorators";
4
+ import { ISCFeature } from "../../../enums/ISCFeature";
4
5
  import { arrayRemoveInPlace } from "../../../functions/array";
5
6
  import { restart } from "../../../functions/run";
6
7
  import { Feature } from "../../private/Feature";
8
+ import { RoomHistory } from "./RoomHistory";
7
9
 
8
10
  /** Used for `runInNFrames` functions. */
9
- type QueuedFunctionTuple = [frameCountToFire: int, func: () => void];
11
+ interface QueuedFunction {
12
+ func: () => void;
13
+ frameCountToFire: int;
14
+ numRoomsEntered: int;
15
+ cancelIfRoomChanges: boolean;
16
+ }
10
17
 
11
18
  /**
12
19
  * Used for `setInterval` functions.
13
20
  *
14
21
  * The return value is whether or not to continue the function from firing.
15
22
  */
16
- type IntervalFunctionTuple = [
17
- frameCountToFire: int,
18
- func: () => boolean,
19
- numIntervalFrames: int,
20
- ];
23
+ interface IntervalFunction {
24
+ func: () => boolean;
25
+ frameCountToFire: int;
26
+ numRoomsEntered: int;
27
+ cancelIfRoomChanges: boolean;
28
+ numIntervalFrames: int;
29
+ }
21
30
 
22
31
  export class RunInNFrames extends Feature {
23
32
  /** @internal */
24
33
  public override v = {
25
34
  run: {
26
- queuedGameFunctionTuples: [] as QueuedFunctionTuple[],
27
- queuedRenderFunctionTuples: [] as QueuedFunctionTuple[],
35
+ queuedGameFunctions: [] as QueuedFunction[],
36
+ queuedRenderFunctions: [] as QueuedFunction[],
28
37
 
29
- intervalGameFunctionTuples: [] as IntervalFunctionTuple[],
30
- intervalRenderFunctionTuples: [] as IntervalFunctionTuple[],
38
+ intervalGameFunctions: [] as IntervalFunction[],
39
+ intervalRenderFunctions: [] as IntervalFunction[],
31
40
  },
32
41
  };
33
42
 
34
43
  // eslint-disable-next-line class-methods-use-this
35
44
  public override vConditionalFunc = (): boolean => false;
36
45
 
46
+ private roomHistory: RoomHistory;
47
+
37
48
  /** @internal */
38
- constructor() {
49
+ constructor(roomHistory: RoomHistory) {
39
50
  super();
40
51
 
52
+ this.featuresUsed = [ISCFeature.ROOM_HISTORY];
53
+
41
54
  this.callbacksUsed = [
42
55
  // 1
43
56
  [ModCallback.POST_UPDATE, this.postUpdate],
@@ -45,33 +58,41 @@ export class RunInNFrames extends Feature {
45
58
  // 2
46
59
  [ModCallback.POST_RENDER, this.postRender],
47
60
  ];
61
+
62
+ this.roomHistory = roomHistory;
48
63
  }
49
64
 
50
65
  // ModCallback.POST_UPDATE (1)
51
66
  private postUpdate = (): void => {
52
67
  const gameFrameCount = game.GetFrameCount();
68
+ const numRoomsEntered = this.roomHistory.getNumRoomsEntered();
53
69
 
54
70
  checkExecuteQueuedFunctions(
71
+ this.v.run.queuedGameFunctions,
55
72
  gameFrameCount,
56
- this.v.run.queuedGameFunctionTuples,
73
+ numRoomsEntered,
57
74
  );
58
75
  checkExecuteIntervalFunctions(
76
+ this.v.run.intervalGameFunctions,
59
77
  gameFrameCount,
60
- this.v.run.intervalGameFunctionTuples,
78
+ numRoomsEntered,
61
79
  );
62
80
  };
63
81
 
64
82
  // ModCallback.POST_RENDER (2)
65
83
  private postRender = (): void => {
66
84
  const renderFrameCount = Isaac.GetFrameCount();
85
+ const numRoomsEntered = this.roomHistory.getNumRoomsEntered();
67
86
 
68
87
  checkExecuteQueuedFunctions(
88
+ this.v.run.queuedRenderFunctions,
69
89
  renderFrameCount,
70
- this.v.run.queuedRenderFunctionTuples,
90
+ numRoomsEntered,
71
91
  );
72
92
  checkExecuteIntervalFunctions(
93
+ this.v.run.intervalRenderFunctions,
73
94
  renderFrameCount,
74
- this.v.run.intervalRenderFunctionTuples,
95
+ numRoomsEntered,
75
96
  );
76
97
  };
77
98
 
@@ -102,13 +123,29 @@ export class RunInNFrames extends Feature {
102
123
  * deferred functions manually using serializable data.
103
124
  *
104
125
  * In order to use this function, you must upgrade your mod with `ISCFeature.RUN_IN_N_FRAMES`.
126
+ *
127
+ * @param func The function to run.
128
+ * @param gameFrames The amount of game frames to wait before running the function.
129
+ * @param cancelIfRoomChanges Optional. Whether or not to cancel running the function if a new
130
+ * room is loaded in the interim. Default is false.
105
131
  */
106
132
  @Exported
107
- public runInNGameFrames(func: () => void, gameFrames: int): void {
133
+ public runInNGameFrames(
134
+ func: () => void,
135
+ gameFrames: int,
136
+ cancelIfRoomChanges = false,
137
+ ): void {
108
138
  const gameFrameCount = game.GetFrameCount();
109
- const functionFireFrame = gameFrameCount + gameFrames;
110
- const tuple: QueuedFunctionTuple = [functionFireFrame, func];
111
- this.v.run.queuedGameFunctionTuples.push(tuple);
139
+ const numRoomsEntered = this.roomHistory.getNumRoomsEntered();
140
+
141
+ const frameCountToFire = gameFrameCount + gameFrames;
142
+ const queuedFunction: QueuedFunction = {
143
+ func,
144
+ frameCountToFire,
145
+ numRoomsEntered,
146
+ cancelIfRoomChanges,
147
+ };
148
+ this.v.run.queuedGameFunctions.push(queuedFunction);
112
149
  }
113
150
 
114
151
  /**
@@ -122,13 +159,29 @@ export class RunInNFrames extends Feature {
122
159
  * deferred functions manually using serializable data.
123
160
  *
124
161
  * In order to use this function, you must upgrade your mod with `ISCFeature.RUN_IN_N_FRAMES`.
162
+ *
163
+ * @param func The function to run.
164
+ * @param renderFrames The amount of render frames to wait before running the function.
165
+ * @param cancelIfRoomChanges Optional. Whether or not to cancel running the function if a new
166
+ * room is loaded in the interim. Default is false.
125
167
  */
126
168
  @Exported
127
- public runInNRenderFrames(func: () => void, renderFrames: int): void {
169
+ public runInNRenderFrames(
170
+ func: () => void,
171
+ renderFrames: int,
172
+ cancelIfRoomChanges = false,
173
+ ): void {
128
174
  const renderFrameCount = Isaac.GetFrameCount();
129
- const functionFireFrame = renderFrameCount + renderFrames;
130
- const tuple: QueuedFunctionTuple = [functionFireFrame, func];
131
- this.v.run.queuedRenderFunctionTuples.push(tuple);
175
+ const numRoomsEntered = this.roomHistory.getNumRoomsEntered();
176
+
177
+ const frameCountToFire = renderFrameCount + renderFrames;
178
+ const queuedFunction: QueuedFunction = {
179
+ func,
180
+ frameCountToFire,
181
+ numRoomsEntered,
182
+ cancelIfRoomChanges,
183
+ };
184
+ this.v.run.queuedRenderFunctions.push(queuedFunction);
132
185
  }
133
186
 
134
187
  /**
@@ -160,10 +213,14 @@ export class RunInNFrames extends Feature {
160
213
  * deferred functions manually using serializable data.
161
214
  *
162
215
  * In order to use this function, you must upgrade your mod with `ISCFeature.RUN_IN_N_FRAMES`.
216
+ *
217
+ * @param func The function to run.
218
+ * @param cancelIfRoomChanges Optional. Whether or not to cancel running the function if a new
219
+ * room is loaded in the interim. Default is false.
163
220
  */
164
221
  @Exported
165
- public runNextGameFrame(func: () => void): void {
166
- this.runInNGameFrames(func, 1);
222
+ public runNextGameFrame(func: () => void, cancelIfRoomChanges = false): void {
223
+ this.runInNGameFrames(func, 1, cancelIfRoomChanges);
167
224
  }
168
225
 
169
226
  /**
@@ -175,10 +232,17 @@ export class RunInNFrames extends Feature {
175
232
  * Note that this function will not handle saving and quitting.
176
233
  *
177
234
  * In order to use this function, you must upgrade your mod with `ISCFeature.RUN_IN_N_FRAMES`.
235
+ *
236
+ * @param func The function to run.
237
+ * @param cancelIfRoomChanges Optional. Whether or not to cancel running the function if a new
238
+ * room is loaded in the interim. Default is false.
178
239
  */
179
240
  @Exported
180
- public runNextRenderFrame(func: () => void): void {
181
- this.runInNRenderFrames(func, 1);
241
+ public runNextRenderFrame(
242
+ func: () => void,
243
+ cancelIfRoomChanges = false,
244
+ ): void {
245
+ this.runInNRenderFrames(func, 1, cancelIfRoomChanges);
182
246
  }
183
247
 
184
248
  /**
@@ -198,17 +262,27 @@ export class RunInNFrames extends Feature {
198
262
  * @param gameFrames The amount of game frames to wait between each run.
199
263
  * @param runImmediately Whether or not to execute the function right now before waiting for the
200
264
  * interval.
265
+ * @param cancelIfRoomChanges Optional. Whether or not to cancel running the function if a new
266
+ * room is loaded in the interim. Default is false.
201
267
  */
202
268
  @Exported
203
269
  public setIntervalGameFrames(
204
270
  func: () => boolean,
205
271
  gameFrames: int,
206
272
  runImmediately: boolean,
273
+ cancelIfRoomChanges = false,
207
274
  ): void {
208
275
  const gameFrameCount = game.GetFrameCount();
209
- const functionFireFrame = gameFrameCount + gameFrames;
210
- const tuple: IntervalFunctionTuple = [functionFireFrame, func, gameFrames];
211
- this.v.run.intervalGameFunctionTuples.push(tuple);
276
+ const numRoomsEntered = this.roomHistory.getNumRoomsEntered();
277
+
278
+ const intervalFunction: IntervalFunction = {
279
+ func,
280
+ frameCountToFire: gameFrameCount + gameFrames,
281
+ numRoomsEntered,
282
+ cancelIfRoomChanges,
283
+ numIntervalFrames: gameFrames,
284
+ };
285
+ this.v.run.intervalGameFunctions.push(intervalFunction);
212
286
 
213
287
  if (runImmediately) {
214
288
  func();
@@ -232,21 +306,27 @@ export class RunInNFrames extends Feature {
232
306
  * @param renderFrames The amount of game frames to wait between each run.
233
307
  * @param runImmediately Whether or not to execute the function right now before waiting for the
234
308
  * interval.
309
+ * @param cancelIfRoomChanges Optional. Whether or not to cancel running the function if a new
310
+ * room is loaded in the interim. Default is false.
235
311
  */
236
312
  @Exported
237
313
  public setIntervalRenderFrames(
238
314
  func: () => boolean,
239
315
  renderFrames: int,
240
316
  runImmediately: boolean,
317
+ cancelIfRoomChanges = false,
241
318
  ): void {
242
319
  const renderFrameCount = Isaac.GetFrameCount();
243
- const functionFireFrame = renderFrameCount + renderFrames;
244
- const tuple: IntervalFunctionTuple = [
245
- functionFireFrame,
320
+ const numRoomsEntered = this.roomHistory.getNumRoomsEntered();
321
+
322
+ const intervalFunction: IntervalFunction = {
246
323
  func,
247
- renderFrames,
248
- ];
249
- this.v.run.intervalGameFunctionTuples.push(tuple);
324
+ frameCountToFire: renderFrameCount + renderFrames,
325
+ numRoomsEntered,
326
+ cancelIfRoomChanges,
327
+ numIntervalFrames: renderFrames,
328
+ };
329
+ this.v.run.intervalGameFunctions.push(intervalFunction);
250
330
 
251
331
  if (runImmediately) {
252
332
  func();
@@ -255,42 +335,55 @@ export class RunInNFrames extends Feature {
255
335
  }
256
336
 
257
337
  function checkExecuteQueuedFunctions(
338
+ functionTuples: QueuedFunction[],
258
339
  frameCount: int,
259
- functionTuples: QueuedFunctionTuple[],
340
+ newNumRoomsEntered: int,
260
341
  ) {
261
342
  const firingFunctions = functionTuples.filter(
262
- ([frameCountToFire]) => frameCount >= frameCountToFire,
343
+ ({ frameCountToFire }) => frameCount >= frameCountToFire,
263
344
  );
264
345
 
265
- for (const tuple of firingFunctions) {
266
- const [_frameCountToFire, func] = tuple;
267
- func();
268
- arrayRemoveInPlace(functionTuples, tuple);
346
+ for (const firingFunction of firingFunctions) {
347
+ const { func, cancelIfRoomChanges, numRoomsEntered } = firingFunction;
348
+
349
+ if (!cancelIfRoomChanges || numRoomsEntered === newNumRoomsEntered) {
350
+ func();
351
+ }
352
+
353
+ arrayRemoveInPlace(functionTuples, firingFunction);
269
354
  }
270
355
  }
271
356
 
272
357
  function checkExecuteIntervalFunctions(
358
+ functionTuples: IntervalFunction[],
273
359
  frameCount: int,
274
- functionTuples: IntervalFunctionTuple[],
360
+ newNumRoomsEntered: int,
275
361
  ) {
276
362
  const firingFunctions = functionTuples.filter(
277
- ([frameCountToFire]) => frameCount >= frameCountToFire,
363
+ ({ frameCountToFire }) => frameCount >= frameCountToFire,
278
364
  );
279
365
 
280
- for (const tuple of firingFunctions) {
281
- const [_frameCountToFire, func, numIntervalFrames] = tuple;
282
- const returnValue = func();
283
- arrayRemoveInPlace(functionTuples, tuple);
366
+ for (const firingFunction of firingFunctions) {
367
+ const { func, cancelIfRoomChanges, numRoomsEntered, numIntervalFrames } =
368
+ firingFunction;
369
+
370
+ let returnValue = false;
371
+ if (!cancelIfRoomChanges || numRoomsEntered === newNumRoomsEntered) {
372
+ returnValue = func();
373
+ }
374
+
375
+ arrayRemoveInPlace(functionTuples, firingFunction);
284
376
 
285
377
  // Queue the next interval (as long as the function did not return false).
286
378
  if (returnValue) {
287
- const nextFireFrame = frameCount + numIntervalFrames;
288
- const newTuple: IntervalFunctionTuple = [
289
- nextFireFrame,
379
+ const newIntervalFunction: IntervalFunction = {
290
380
  func,
381
+ frameCountToFire: frameCount + numIntervalFrames,
382
+ numRoomsEntered,
383
+ cancelIfRoomChanges,
291
384
  numIntervalFrames,
292
- ];
293
- functionTuples.push(newTuple);
385
+ };
386
+ functionTuples.push(newIntervalFunction);
294
387
  }
295
388
  }
296
389
  }
package/src/features.ts CHANGED
@@ -143,11 +143,12 @@ export function getFeatures(
143
143
  const preventCollectibleRotation = new PreventCollectibleRotation();
144
144
  const roomClearFrame = new RoomClearFrame();
145
145
  const roomHistory = new RoomHistory();
146
- const runInNFrames = new RunInNFrames();
147
146
  const runNextRoom = new RunNextRoom();
148
147
  const saveDataManager = new SaveDataManager(mod);
149
148
  const stageHistory = new StageHistory();
150
149
 
150
+ const runInNFrames = new RunInNFrames(roomHistory);
151
+
151
152
  const customGridEntities = new CustomGridEntities(runInNFrames);
152
153
  const moddedElementSets = new ModdedElementSets(moddedElementDetection);
153
154
  const itemPoolDetection = new ItemPoolDetection(moddedElementSets);