isaacscript-common 30.4.0 → 30.4.2

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 (29) hide show
  1. package/dist/index.rollup.d.ts +31 -21
  2. package/dist/isaacscript-common.lua +57 -52
  3. package/dist/lualib_bundle.lua +15 -0
  4. package/dist/src/classes/features/callbackLogic/CustomGridEntities.d.ts +4 -4
  5. package/dist/src/classes/features/callbackLogic/CustomGridEntities.d.ts.map +1 -1
  6. package/dist/src/classes/features/callbackLogic/CustomGridEntities.lua +1 -1
  7. package/dist/src/classes/features/other/PressInput.d.ts.map +1 -1
  8. package/dist/src/classes/features/other/PressInput.lua +7 -8
  9. package/dist/src/classes/features/other/StageHistory.d.ts +2 -4
  10. package/dist/src/classes/features/other/StageHistory.d.ts.map +1 -1
  11. package/dist/src/classes/features/other/StageHistory.lua +3 -13
  12. package/dist/src/functions/levelGrid.d.ts +20 -13
  13. package/dist/src/functions/levelGrid.d.ts.map +1 -1
  14. package/dist/src/functions/levelGrid.lua +7 -5
  15. package/dist/src/functions/playerHealth.d.ts.map +1 -1
  16. package/dist/src/functions/playerHealth.lua +19 -24
  17. package/dist/src/indexLua.d.ts +185 -0
  18. package/dist/src/indexLua.d.ts.map +1 -0
  19. package/dist/src/indexLua.lua +1114 -0
  20. package/dist/src/interfaces/StageHistoryEntry.d.ts +7 -0
  21. package/dist/src/interfaces/StageHistoryEntry.d.ts.map +1 -0
  22. package/dist/src/interfaces/StageHistoryEntry.lua +2 -0
  23. package/package.json +1 -1
  24. package/src/classes/features/callbackLogic/CustomGridEntities.ts +9 -5
  25. package/src/classes/features/other/PressInput.ts +10 -8
  26. package/src/classes/features/other/StageHistory.ts +8 -8
  27. package/src/functions/levelGrid.ts +45 -20
  28. package/src/functions/playerHealth.ts +29 -22
  29. package/src/interfaces/StageHistoryEntry.ts +7 -0
@@ -0,0 +1,7 @@
1
+ import { LevelStage, StageType } from "isaac-typescript-definitions";
2
+ /** This is used by the `StageHistory` feature. */
3
+ export interface StageHistoryEntry {
4
+ readonly stage: LevelStage;
5
+ readonly stageType: StageType;
6
+ }
7
+ //# sourceMappingURL=StageHistoryEntry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StageHistoryEntry.d.ts","sourceRoot":"","sources":["../../../src/interfaces/StageHistoryEntry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAC;AAErE,kDAAkD;AAClD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAC;CAC/B"}
@@ -0,0 +1,2 @@
1
+ local ____exports = {}
2
+ return ____exports
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isaacscript-common",
3
- "version": "30.4.0",
3
+ "version": "30.4.2",
4
4
  "description": "Helper functions and features for IsaacScript mods.",
5
5
  "keywords": [
6
6
  "isaac",
@@ -351,9 +351,10 @@ export class CustomGridEntities extends Feature {
351
351
  * `ISCFeature.CUSTOM_GRID_ENTITIES`.
352
352
  */
353
353
  @Exported
354
- public getCustomGridEntities(): Array<
355
- [gridEntity: GridEntity, data: GridEntityCustomData]
356
- > {
354
+ public getCustomGridEntities(): Array<{
355
+ gridEntity: GridEntity;
356
+ data: GridEntityCustomData;
357
+ }> {
357
358
  const roomListIndex = getRoomListIndex();
358
359
  const roomCustomGridEntities =
359
360
  v.level.customGridEntities.get(roomListIndex);
@@ -362,11 +363,14 @@ export class CustomGridEntities extends Feature {
362
363
  }
363
364
 
364
365
  const room = game.GetRoom();
365
- const customGridEntities: Array<[GridEntity, GridEntityCustomData]> = [];
366
+ const customGridEntities: Array<{
367
+ gridEntity: GridEntity;
368
+ data: GridEntityCustomData;
369
+ }> = [];
366
370
  for (const [gridIndex, data] of roomCustomGridEntities) {
367
371
  const gridEntity = room.GetGridEntity(gridIndex);
368
372
  if (gridEntity !== undefined) {
369
- customGridEntities.push([gridEntity, data]);
373
+ customGridEntities.push({ gridEntity, data });
370
374
  }
371
375
  }
372
376
 
@@ -10,7 +10,10 @@ import { Feature } from "../../private/Feature";
10
10
 
11
11
  const v = {
12
12
  run: {
13
- buttonActionTuples: [] as Array<[PlayerIndex, ButtonAction]>,
13
+ buttonActionPairs: [] as Array<{
14
+ playerIndex: PlayerIndex;
15
+ buttonAction: ButtonAction;
16
+ }>,
14
17
  },
15
18
  };
16
19
 
@@ -50,16 +53,15 @@ export class PressInput extends Feature {
50
53
 
51
54
  const playerIndex = getPlayerIndex(player);
52
55
 
53
- for (let i = v.run.buttonActionTuples.length - 1; i >= 0; i--) {
56
+ for (let i = v.run.buttonActionPairs.length - 1; i >= 0; i--) {
54
57
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
55
- const tuple = v.run.buttonActionTuples[i]!;
56
- const [tuplePlayerIndex, tupleButtonAction] = tuple;
58
+ const pair = v.run.buttonActionPairs[i]!;
57
59
 
58
60
  if (
59
- tuplePlayerIndex === playerIndex &&
60
- tupleButtonAction === buttonAction
61
+ pair.playerIndex === playerIndex &&
62
+ pair.buttonAction === buttonAction
61
63
  ) {
62
- v.run.buttonActionTuples.splice(i);
64
+ v.run.buttonActionPairs.splice(i);
63
65
  return true;
64
66
  }
65
67
  }
@@ -77,6 +79,6 @@ export class PressInput extends Feature {
77
79
  @Exported
78
80
  public pressInput(player: EntityPlayer, buttonAction: ButtonAction): void {
79
81
  const playerIndex = getPlayerIndex(player);
80
- v.run.buttonActionTuples.push([playerIndex, buttonAction]);
82
+ v.run.buttonActionPairs.push({ playerIndex, buttonAction });
81
83
  }
82
84
  }
@@ -12,11 +12,12 @@ import {
12
12
  onRepentanceStage,
13
13
  } from "../../../functions/stage";
14
14
  import { asNumber } from "../../../functions/types";
15
+ import { StageHistoryEntry } from "../../../interfaces/StageHistoryEntry";
15
16
  import { Feature } from "../../private/Feature";
16
17
 
17
18
  const v = {
18
19
  run: {
19
- stageHistory: [] as Array<[stage: LevelStage, stageType: StageType]>,
20
+ stageHistory: [] as StageHistoryEntry[],
20
21
  },
21
22
  };
22
23
 
@@ -39,7 +40,7 @@ export class StageHistory extends Feature {
39
40
  const stage = level.GetStage();
40
41
  const stageType = level.GetStageType();
41
42
 
42
- v.run.stageHistory.push([stage, stageType]);
43
+ v.run.stageHistory.push({ stage, stageType });
43
44
  };
44
45
 
45
46
  /**
@@ -296,9 +297,7 @@ export class StageHistory extends Feature {
296
297
  * In order to use this function, you must upgrade your mod with `ISCFeature.STAGE_HISTORY`.
297
298
  */
298
299
  @Exported
299
- public getStageHistory(): ReadonlyArray<
300
- [stage: LevelStage, stageType: StageType]
301
- > {
300
+ public getStageHistory(): readonly StageHistoryEntry[] {
302
301
  return v.run.stageHistory;
303
302
  }
304
303
 
@@ -316,13 +315,14 @@ export class StageHistory extends Feature {
316
315
  public hasVisitedStage(stage: LevelStage, stageType?: StageType): boolean {
317
316
  if (stageType === undefined) {
318
317
  return v.run.stageHistory.some(
319
- ([previousStage]) => previousStage === stage,
318
+ (stageHistoryEntry) => stageHistoryEntry.stage === stage,
320
319
  );
321
320
  }
322
321
 
323
322
  return v.run.stageHistory.some(
324
- ([previousStage, previousStageType]) =>
325
- previousStage === stage && previousStageType === stageType,
323
+ (stageHistoryEntry) =>
324
+ stageHistoryEntry.stage === stage &&
325
+ stageHistoryEntry.stageType === stageType,
326
326
  );
327
327
  }
328
328
  }
@@ -53,7 +53,9 @@ const ADJACENT_ROOM_GRID_INDEX_DELTAS = [LEFT, UP, RIGHT, DOWN] as const;
53
53
  * This is just a filtering of the results of the `getAdjacentExistingRoomGridIndexes` function. See
54
54
  * that function for more information.
55
55
  */
56
- export function getAdjacentExistingRoomGridIndexes(roomGridIndex?: int): int[] {
56
+ export function getAdjacentExistingRoomGridIndexes(
57
+ roomGridIndex?: int,
58
+ ): readonly int[] {
57
59
  const adjacentRoomGridIndexes = getAdjacentRoomGridIndexes(roomGridIndex);
58
60
  return adjacentRoomGridIndexes.filter(
59
61
  (adjacentRoomGridIndex) => getRoomData(adjacentRoomGridIndex) !== undefined,
@@ -69,7 +71,7 @@ export function getAdjacentExistingRoomGridIndexes(roomGridIndex?: int): int[] {
69
71
  */
70
72
  export function getAdjacentNonExistingRoomGridIndexes(
71
73
  roomGridIndex?: int,
72
- ): int[] {
74
+ ): readonly int[] {
73
75
  const adjacentRoomGridIndexes = getAdjacentRoomGridIndexes(roomGridIndex);
74
76
  return adjacentRoomGridIndexes.filter(
75
77
  (adjacentRoomGridIndex) => getRoomData(adjacentRoomGridIndex) === undefined,
@@ -91,7 +93,9 @@ export function getAdjacentNonExistingRoomGridIndexes(
91
93
  *
92
94
  * @param roomGridIndex Optional. Default is the current room index.
93
95
  */
94
- export function getAdjacentRoomGridIndexes(roomGridIndex?: int): int[] {
96
+ export function getAdjacentRoomGridIndexes(
97
+ roomGridIndex?: int,
98
+ ): readonly int[] {
95
99
  const roomGridIndexToUse =
96
100
  roomGridIndex === undefined ? getRoomGridIndex() : roomGridIndex;
97
101
 
@@ -109,7 +113,7 @@ export function getAdjacentRoomGridIndexes(roomGridIndex?: int): int[] {
109
113
  }
110
114
 
111
115
  /** Helper function to get the room safe grid index for every room on the entire floor. */
112
- export function getAllRoomGridIndexes(): int[] {
116
+ export function getAllRoomGridIndexes(): readonly int[] {
113
117
  const rooms = getRooms();
114
118
  return rooms.map((roomDescriptor) => roomDescriptor.SafeGridIndex);
115
119
  }
@@ -124,10 +128,12 @@ export function getAllRoomGridIndexes(): int[] {
124
128
  * @returns Either a tuple of adjacent room grid index, `DoorSlot`, and new room grid index, or
125
129
  * undefined.
126
130
  */
127
- export function getNewRoomCandidate(
128
- seedOrRNG: Seed | RNG = getRandomSeed(),
129
- ):
130
- | [adjacentRoomGridIndex: int, doorSlot: DoorSlot, newRoomGridIndex: int]
131
+ export function getNewRoomCandidate(seedOrRNG: Seed | RNG = getRandomSeed()):
132
+ | {
133
+ readonly adjacentRoomGridIndex: int;
134
+ readonly doorSlot: DoorSlot;
135
+ readonly newRoomGridIndex: int;
136
+ }
131
137
  | undefined {
132
138
  const newRoomCandidatesForLevel = getNewRoomCandidatesForLevel();
133
139
  if (newRoomCandidatesForLevel.length === 0) {
@@ -147,7 +153,7 @@ export function getNewRoomCandidate(
147
153
  */
148
154
  export function getNewRoomCandidatesBesideRoom(
149
155
  roomGridIndex?: int,
150
- ): Array<[doorSlot: DoorSlot, roomGridIndex: int]> {
156
+ ): ReadonlyArray<{ readonly doorSlot: DoorSlot; readonly roomGridIndex: int }> {
151
157
  const roomDescriptor = getRoomDescriptor(roomGridIndex);
152
158
 
153
159
  // First, handle the case of rooms outside of the grid, which obviously cannot have any possible
@@ -168,7 +174,10 @@ export function getNewRoomCandidatesBesideRoom(
168
174
  roomData.Shape,
169
175
  );
170
176
 
171
- const roomCandidates: Array<[DoorSlot, int]> = [];
177
+ const roomCandidates: Array<{
178
+ readonly doorSlot: DoorSlot;
179
+ readonly roomGridIndex: int;
180
+ }> = [];
172
181
 
173
182
  for (const [doorSlot, adjacentRoomGridIndex] of doorSlotToRoomGridIndexes) {
174
183
  // The "getRoomShapeAdjacentNonExistingGridIndexes" returns grid indexes for every possible
@@ -186,7 +195,10 @@ export function getNewRoomCandidatesBesideRoom(
186
195
  continue;
187
196
  }
188
197
 
189
- roomCandidates.push([doorSlot, adjacentRoomGridIndex]);
198
+ roomCandidates.push({
199
+ doorSlot,
200
+ roomGridIndex: adjacentRoomGridIndex,
201
+ });
190
202
  }
191
203
 
192
204
  return roomCandidates;
@@ -199,9 +211,11 @@ export function getNewRoomCandidatesBesideRoom(
199
211
  * @returns A array of tuples containing the adjacent room grid index, the `DoorSlot`, and the new
200
212
  * room grid index.
201
213
  */
202
- export function getNewRoomCandidatesForLevel(): Array<
203
- [adjacentRoomGridIndex: int, doorSlot: DoorSlot, newRoomGridIndex: int]
204
- > {
214
+ export function getNewRoomCandidatesForLevel(): ReadonlyArray<{
215
+ readonly adjacentRoomGridIndex: int;
216
+ readonly doorSlot: DoorSlot;
217
+ readonly newRoomGridIndex: int;
218
+ }> {
205
219
  // We want to iterate over every room on the floor and search for potential new room spots.
206
220
  const rooms = getRoomsInsideGrid();
207
221
 
@@ -216,14 +230,22 @@ export function getNewRoomCandidatesForLevel(): Array<
216
230
  room.Data.Subtype !== asNumber(MinesRoomSubType.MINESHAFT_ENTRANCE),
217
231
  );
218
232
 
219
- const newRoomCandidates: Array<[int, DoorSlot, int]> = [];
233
+ const newRoomCandidates: Array<{
234
+ readonly adjacentRoomGridIndex: int;
235
+ readonly doorSlot: DoorSlot;
236
+ readonly newRoomGridIndex: int;
237
+ }> = [];
220
238
 
221
239
  for (const room of normalRooms) {
222
240
  const newRoomCandidatesBesideRoom = getNewRoomCandidatesBesideRoom(
223
241
  room.SafeGridIndex,
224
242
  );
225
- for (const [doorSlot, newRoomGridIndex] of newRoomCandidatesBesideRoom) {
226
- newRoomCandidates.push([room.SafeGridIndex, doorSlot, newRoomGridIndex]);
243
+ for (const { doorSlot, roomGridIndex } of newRoomCandidatesBesideRoom) {
244
+ newRoomCandidates.push({
245
+ adjacentRoomGridIndex: room.SafeGridIndex,
246
+ doorSlot,
247
+ newRoomGridIndex: roomGridIndex,
248
+ });
227
249
  }
228
250
  }
229
251
 
@@ -291,7 +313,9 @@ export function getRoomDescriptorsForType(
291
313
  * This function is variadic, meaning that you can specify N arguments to get the combined grid
292
314
  * indexes for N room types.
293
315
  */
294
- export function getRoomGridIndexesForType(...roomTypes: RoomType[]): int[] {
316
+ export function getRoomGridIndexesForType(
317
+ ...roomTypes: RoomType[]
318
+ ): readonly int[] {
295
319
  const roomDescriptors = getRoomDescriptorsForType(...roomTypes);
296
320
  return roomDescriptors.map((roomDescriptor) => roomDescriptor.SafeGridIndex);
297
321
  }
@@ -492,7 +516,8 @@ export function newRoom(seedOrRNG?: Seed | RNG): int | undefined {
492
516
  if (newRoomCandidate === undefined) {
493
517
  return undefined;
494
518
  }
495
- const [adjacentRoomGridIndex, doorSlot, newRoomGridIndex] = newRoomCandidate;
519
+ const { adjacentRoomGridIndex, doorSlot, newRoomGridIndex } =
520
+ newRoomCandidate;
496
521
 
497
522
  level.MakeRedRoomDoor(adjacentRoomGridIndex, doorSlot);
498
523
 
@@ -552,7 +577,7 @@ export function roomExists(roomGridIndex: int): boolean {
552
577
  * - The bottom-left grid index of 156 is equal to coordinates of: (0, 12)
553
578
  * - The bottom-right grid index of 168 is equal to coordinates of: (12, 12)
554
579
  */
555
- export function roomGridIndexToVector(roomGridIndex: int): Vector {
580
+ export function roomGridIndexToVector(roomGridIndex: int): Readonly<Vector> {
556
581
  const x = roomGridIndex % LEVEL_GRID_ROW_WIDTH;
557
582
  const y = Math.floor(roomGridIndex / LEVEL_GRID_ROW_WIDTH);
558
583
 
@@ -5,14 +5,18 @@ import {
5
5
  PlayerType,
6
6
  TrinketType,
7
7
  } from "isaac-typescript-definitions";
8
- import { ACTIVE_SLOT_VALUES } from "../arrays/cachedEnumValues";
9
8
  import { MAX_PLAYER_HEART_CONTAINERS } from "../core/constants";
10
9
  import { HealthType } from "../enums/HealthType";
11
10
  import { PlayerHealth, SoulHeartType } from "../interfaces/PlayerHealth";
12
11
  import { countSetBits, getKBitOfN, getNumBitsOfN } from "./bitwise";
13
12
  import { getCharacterMaxHeartContainers } from "./characters";
14
13
  import { getTotalCharge } from "./charge";
15
- import { isCharacter, isKeeper, setActiveItem } from "./players";
14
+ import {
15
+ getActiveItemSlots,
16
+ isCharacter,
17
+ isKeeper,
18
+ setActiveItem,
19
+ } from "./players";
16
20
  import { repeat } from "./utils";
17
21
 
18
22
  export function addPlayerHealthType(
@@ -543,21 +547,24 @@ export function setPlayerHealth(
543
547
  const character = player.GetPlayerType();
544
548
  const subPlayer = player.GetSubPlayer();
545
549
 
546
- removeAllPlayerHealth(player);
547
-
548
- // Before we add any health, we have to take away Alabaster Box, if present.
549
- const alabasterBoxes: Array<[slot: ActiveSlot, totalCharge: int]> = [];
550
- if (player.HasCollectible(CollectibleType.ALABASTER_BOX)) {
551
- for (const activeSlot of ACTIVE_SLOT_VALUES) {
552
- const activeItem = player.GetActiveItem();
553
- if (activeItem === CollectibleType.ALABASTER_BOX) {
554
- const totalCharge = getTotalCharge(player, activeSlot);
555
- setActiveItem(player, CollectibleType.NULL, activeSlot);
556
- alabasterBoxes.push([activeSlot, totalCharge]);
557
- }
558
- }
550
+ // Before we add or remove any health, we have to take away Alabaster Box, if present. (Removing
551
+ // soul hearts from the player will remove Alabaster Box charges.)
552
+ const alabasterBoxDescriptions: Array<{
553
+ activeSlot: ActiveSlot;
554
+ totalCharge: int;
555
+ }> = [];
556
+ const alabasterBoxActiveSlots = getActiveItemSlots(
557
+ player,
558
+ CollectibleType.ALABASTER_BOX,
559
+ );
560
+ for (const activeSlot of alabasterBoxActiveSlots) {
561
+ const totalCharge = getTotalCharge(player, activeSlot);
562
+ setActiveItem(player, CollectibleType.NULL, activeSlot);
563
+ alabasterBoxDescriptions.push({ activeSlot, totalCharge });
559
564
  }
560
565
 
566
+ removeAllPlayerHealth(player);
567
+
561
568
  // Add the red heart containers.
562
569
  if (character === PlayerType.SOUL && subPlayer !== undefined) {
563
570
  // Adding health to The Soul is a special case.
@@ -608,14 +615,14 @@ export function setPlayerHealth(
608
615
  /**
609
616
  * Fill in the red heart containers.
610
617
  *
611
- * (Rotten Hearts must be filled in first in order for this to work properly, since they conflict
612
- * with half red hearts.)
618
+ * Rotten Hearts must be filled in first in order for this to work properly, since they conflict
619
+ * with half red hearts.
613
620
  *
614
- * The `EntityPlayer.AddRottenHearts` method is not like actually picking up a rotten heart, since
615
- * it will only grant one rotten heart to Tainted Magdalene (whereas picking up a rotten heart
616
- * would grant two).
621
+ * We multiply by two because the `EntityPlayer.GetRottenHearts` function returns the actual
622
+ * number of rotten hearts, but the `EntityPlayer.AddRottenHearts` works like the other heart
623
+ * functions in that a value of 1 is equivalent to a half-heart.
617
624
  */
618
- player.AddRottenHearts(playerHealth.rottenHearts);
625
+ player.AddRottenHearts(playerHealth.rottenHearts * 2);
619
626
 
620
627
  if (character === PlayerType.MAGDALENE_B) {
621
628
  // Adding 1 heart to Tainted Magdalene will actually add two hearts.
@@ -649,7 +656,7 @@ export function setPlayerHealth(
649
656
  }
650
657
 
651
658
  // Re-add the Alabaster Box, if present.
652
- for (const [activeSlot, totalCharge] of alabasterBoxes) {
659
+ for (const { activeSlot, totalCharge } of alabasterBoxDescriptions) {
653
660
  setActiveItem(
654
661
  player,
655
662
  CollectibleType.ALABASTER_BOX,
@@ -0,0 +1,7 @@
1
+ import { LevelStage, StageType } from "isaac-typescript-definitions";
2
+
3
+ /** This is used by the `StageHistory` feature. */
4
+ export interface StageHistoryEntry {
5
+ readonly stage: LevelStage;
6
+ readonly stageType: StageType;
7
+ }