isaacscript-common 6.16.2 → 6.18.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 (56) hide show
  1. package/dist/enums/ModCallbackCustom.d.ts +1 -1
  2. package/dist/features/customGridEntity.d.ts +3 -3
  3. package/dist/features/customGridEntity.d.ts.map +1 -1
  4. package/dist/features/customGridEntity.lua +18 -11
  5. package/dist/features/extraConsoleCommands/listCommands.lua +2 -2
  6. package/dist/features/persistentEntities.d.ts.map +1 -1
  7. package/dist/features/persistentEntities.lua +15 -5
  8. package/dist/features/pickupIndex.d.ts.map +1 -1
  9. package/dist/features/pickupIndex.lua +5 -6
  10. package/dist/features/roomHistory.d.ts +11 -4
  11. package/dist/features/roomHistory.d.ts.map +1 -1
  12. package/dist/features/roomHistory.lua +18 -5
  13. package/dist/functions/log.d.ts +1 -1
  14. package/dist/functions/log.d.ts.map +1 -1
  15. package/dist/functions/log.lua +33 -28
  16. package/dist/functions/npcs.d.ts +9 -0
  17. package/dist/functions/npcs.d.ts.map +1 -1
  18. package/dist/functions/npcs.lua +16 -3
  19. package/dist/functions/playerHealth.d.ts.map +1 -1
  20. package/dist/functions/playerHealth.lua +29 -12
  21. package/dist/functions/rockAlt.d.ts +4 -4
  22. package/dist/functions/rockAlt.d.ts.map +1 -1
  23. package/dist/functions/rockAlt.lua +38 -87
  24. package/dist/functions/saveFile.d.ts +5 -0
  25. package/dist/functions/saveFile.d.ts.map +1 -1
  26. package/dist/functions/saveFile.lua +112 -3
  27. package/dist/index.d.ts +1 -2
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.lua +1 -9
  30. package/dist/interfaces/{CustomGridEntityData.d.ts → GridEntityCustomData.d.ts} +2 -2
  31. package/dist/interfaces/{CustomGridEntityData.d.ts.map → GridEntityCustomData.d.ts.map} +1 -1
  32. package/dist/interfaces/{CustomGridEntityData.lua → GridEntityCustomData.lua} +0 -0
  33. package/dist/interfaces/PlayerHealth.d.ts +4 -1
  34. package/dist/interfaces/PlayerHealth.d.ts.map +1 -1
  35. package/dist/interfaces/RoomDescription.d.ts +1 -0
  36. package/dist/interfaces/RoomDescription.d.ts.map +1 -1
  37. package/package.json +1 -1
  38. package/src/enums/ModCallbackCustom.ts +1 -1
  39. package/src/features/customGridEntity.ts +30 -17
  40. package/src/features/extraConsoleCommands/listCommands.ts +2 -2
  41. package/src/features/persistentEntities.ts +19 -5
  42. package/src/features/pickupIndex.ts +9 -15
  43. package/src/features/roomHistory.ts +24 -4
  44. package/src/functions/log.ts +35 -30
  45. package/src/functions/npcs.ts +27 -2
  46. package/src/functions/playerHealth.ts +25 -12
  47. package/src/functions/rockAlt.ts +50 -132
  48. package/src/functions/saveFile.ts +146 -4
  49. package/src/index.ts +1 -2
  50. package/src/interfaces/{CustomGridEntityData.ts → GridEntityCustomData.ts} +1 -1
  51. package/src/interfaces/PlayerHealth.ts +12 -1
  52. package/src/interfaces/RoomDescription.ts +1 -0
  53. package/dist/functions/itemPool.d.ts +0 -10
  54. package/dist/functions/itemPool.d.ts.map +0 -1
  55. package/dist/functions/itemPool.lua +0 -116
  56. package/src/functions/itemPool.ts +0 -153
@@ -5,7 +5,6 @@ import {
5
5
  EffectVariant,
6
6
  EntityType,
7
7
  HeartSubType,
8
- ItemPoolType,
9
8
  PillColor,
10
9
  RoomType,
11
10
  TrinketType,
@@ -15,7 +14,6 @@ import { DISTANCE_OF_GRID_TILE } from "../constants";
15
14
  import { RockAltType } from "../enums/RockAltType";
16
15
  import { BACKDROP_TYPE_TO_ROCK_ALT_TYPE } from "../objects/backdropTypeToRockAltType";
17
16
  import { spawnEffectWithSeed, spawnNPCWithSeed } from "./entitiesSpecific";
18
- import { isCollectibleInItemPool } from "./itemPool";
19
17
  import {
20
18
  spawnCardWithSeed,
21
19
  spawnCoinWithSeed,
@@ -31,13 +29,13 @@ import { repeat } from "./utils";
31
29
  import { getRandomVector } from "./vector";
32
30
 
33
31
  const ROCK_ALT_CHANCES = {
34
- Nothing: 0.68,
35
- BasicDrop: 0.0967,
32
+ NOTHING: 0.68,
33
+ BASIC_DROP: 0.0967,
36
34
 
37
35
  /** Also used for e.g. black hearts from skulls. */
38
- Trinket: 0.025,
36
+ TRINKET: 0.025,
39
37
 
40
- Collectible: 0.005,
38
+ COLLECTIBLE: 0.005,
41
39
  } as const;
42
40
 
43
41
  const POLYP_PROJECTILE_SPEED = 10;
@@ -71,10 +69,10 @@ export function getRockAltType(): RockAltType {
71
69
  * Most of the time, this function will do nothing, similar to how most of the time, when an
72
70
  * individual urn is destroyed, nothing will spawn.
73
71
  *
74
- * Note that in vanilla, trinkets will not spawn if they have already been removed from the trinket
75
- * pool. This function cannot replicate that behavior because there is no way to check to see if a
76
- * trinket is still in the pool. Thus, it will always have a chance to spawn the respective trinket
77
- * (e.g. Swallowed Penny from urns).
72
+ * Note that in vanilla, collectibles and trinkets will not spawn if they have already been removed
73
+ * from the respective pool. This function cannot replicate that behavior because there is no way to
74
+ * check to see if a collectible or trinket is still in the pool. Thus, it will always have a chance
75
+ * to spawn the respective collectible/trinket (e.g. Swallowed Penny from urns).
78
76
  *
79
77
  * The logic in this function is based on the rewards listed on the wiki:
80
78
  * https://bindingofisaacrebirth.fandom.com/wiki/Rocks
@@ -126,12 +124,12 @@ function spawnRockAltRewardUrn(position: Vector, rng: RNG): boolean {
126
124
  const chance = getRandom(rng);
127
125
  let totalChance = 0;
128
126
 
129
- totalChance += ROCK_ALT_CHANCES.Nothing;
127
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
130
128
  if (chance < totalChance) {
131
129
  return false;
132
130
  }
133
131
 
134
- totalChance += ROCK_ALT_CHANCES.BasicDrop;
132
+ totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
135
133
  if (chance < totalChance) {
136
134
  const numCoinsChance = getRandom(rng);
137
135
  const numCoins = numCoinsChance < 0.5 ? 1 : 2;
@@ -144,24 +142,16 @@ function spawnRockAltRewardUrn(position: Vector, rng: RNG): boolean {
144
142
  return true;
145
143
  }
146
144
 
147
- totalChance += ROCK_ALT_CHANCES.Trinket;
145
+ totalChance += ROCK_ALT_CHANCES.TRINKET;
148
146
  if (chance < totalChance) {
149
147
  spawnTrinketWithSeed(TrinketType.SWALLOWED_PENNY, position, rng);
150
148
  return true;
151
149
  }
152
150
 
153
- totalChance += ROCK_ALT_CHANCES.Collectible;
151
+ totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
154
152
  if (chance < totalChance) {
155
- const stillInPools = isCollectibleInItemPool(
156
- CollectibleType.QUARTER,
157
- ItemPoolType.DEVIL,
158
- );
159
- if (stillInPools) {
160
- spawnCollectible(CollectibleType.QUARTER, position, rng);
161
- return true;
162
- }
163
-
164
- return false;
153
+ spawnCollectible(CollectibleType.QUARTER, position, rng);
154
+ return true;
165
155
  }
166
156
 
167
157
  // Since the detrimental effect is the final option, we don't need to check the chance.
@@ -184,68 +174,40 @@ function spawnRockAltRewardMushroom(position: Vector, rng: RNG): boolean {
184
174
  const chance = getRandom(rng);
185
175
  let totalChance = 0;
186
176
 
187
- totalChance += ROCK_ALT_CHANCES.Nothing;
177
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
188
178
  if (chance < totalChance) {
189
179
  return false;
190
180
  }
191
181
 
192
- totalChance += ROCK_ALT_CHANCES.BasicDrop;
182
+ totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
193
183
  if (chance < totalChance) {
194
184
  spawnPillWithSeed(PillColor.NULL, position, rng);
195
185
  return true;
196
186
  }
197
187
 
198
- totalChance += ROCK_ALT_CHANCES.Trinket;
188
+ totalChance += ROCK_ALT_CHANCES.TRINKET;
199
189
  if (chance < totalChance) {
200
190
  spawnTrinketWithSeed(TrinketType.LIBERTY_CAP, position, rng);
201
191
  return true;
202
192
  }
203
193
 
204
- totalChance += ROCK_ALT_CHANCES.Collectible;
194
+ totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
205
195
  if (chance < totalChance) {
206
196
  if (roomType === RoomType.SECRET) {
207
197
  const wavyCapChance = getRandom(rng);
208
198
  if (wavyCapChance < 0.0272) {
209
- const stillInPools = isCollectibleInItemPool(
210
- CollectibleType.WAVY_CAP,
211
- ItemPoolType.SECRET,
212
- );
213
- if (stillInPools) {
214
- spawnCollectible(CollectibleType.WAVY_CAP, position, rng);
215
- return true;
216
- }
199
+ spawnCollectible(CollectibleType.WAVY_CAP, position, rng);
200
+ return true;
217
201
  }
218
202
  }
219
203
 
220
- const magicMushroomStillInPools = isCollectibleInItemPool(
221
- CollectibleType.MAGIC_MUSHROOM,
222
- ItemPoolType.TREASURE,
223
- );
224
- const miniMushStillInPools = isCollectibleInItemPool(
225
- CollectibleType.MINI_MUSH,
226
- ItemPoolType.TREASURE,
227
- );
228
- if (magicMushroomStillInPools && miniMushStillInPools) {
229
- const collectibleChance = getRandom(rng);
230
- const collectibleType =
231
- collectibleChance < 0.5
232
- ? CollectibleType.MAGIC_MUSHROOM // 12
233
- : CollectibleType.MINI_MUSH; // 71
234
- spawnCollectible(collectibleType, position, rng);
235
- return true;
236
- }
237
-
238
- if (magicMushroomStillInPools) {
239
- spawnCollectible(CollectibleType.MINI_MUSH, position, rng);
240
- return true;
241
- }
242
-
243
- if (miniMushStillInPools) {
244
- spawnCollectible(CollectibleType.MAGIC_MUSHROOM, position, rng);
245
- return true;
246
- }
247
-
248
- return false;
204
+ const collectibleChance = getRandom(rng);
205
+ const collectibleType =
206
+ collectibleChance < 0.5
207
+ ? CollectibleType.MAGIC_MUSHROOM // 12
208
+ : CollectibleType.MINI_MUSH; // 71
209
+ spawnCollectible(collectibleType, position, rng);
210
+ return true;
249
211
  }
250
212
 
251
213
  // Since the detrimental effect is the final option, we don't need to check the chance.
@@ -257,54 +219,32 @@ function spawnRockAltRewardSkull(position: Vector, rng: RNG): boolean {
257
219
  const chance = getRandom(rng);
258
220
  let totalChance = 0;
259
221
 
260
- totalChance += ROCK_ALT_CHANCES.Nothing;
222
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
261
223
  if (chance < totalChance) {
262
224
  return false;
263
225
  }
264
226
 
265
- totalChance += ROCK_ALT_CHANCES.BasicDrop;
227
+ totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
266
228
  if (chance < totalChance) {
267
229
  spawnCardWithSeed(Card.NULL, position, rng);
268
230
  return true;
269
231
  }
270
232
 
271
- totalChance += ROCK_ALT_CHANCES.Trinket;
233
+ totalChance += ROCK_ALT_CHANCES.TRINKET;
272
234
  if (chance < totalChance) {
273
235
  spawnHeartWithSeed(HeartSubType.BLACK, position, rng);
274
236
  return true;
275
237
  }
276
238
 
277
- totalChance += ROCK_ALT_CHANCES.Collectible;
239
+ totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
278
240
  if (chance < totalChance) {
279
- const ghostBabyStillInPools = isCollectibleInItemPool(
280
- CollectibleType.GHOST_BABY,
281
- ItemPoolType.TREASURE,
282
- );
283
- const dryBabyStillInPools = isCollectibleInItemPool(
284
- CollectibleType.DRY_BABY,
285
- ItemPoolType.TREASURE,
286
- );
287
- if (ghostBabyStillInPools && dryBabyStillInPools) {
288
- const collectibleChance = getRandom(rng);
289
- const collectibleType =
290
- collectibleChance < 0.5
291
- ? CollectibleType.GHOST_BABY // 163
292
- : CollectibleType.DRY_BABY; // 265
293
- spawnCollectible(collectibleType, position, rng);
294
- return true;
295
- }
296
-
297
- if (ghostBabyStillInPools) {
298
- spawnCollectible(CollectibleType.DRY_BABY, position, rng);
299
- return true;
300
- }
301
-
302
- if (dryBabyStillInPools) {
303
- spawnCollectible(CollectibleType.GHOST_BABY, position, rng);
304
- return true;
305
- }
306
-
307
- return false;
241
+ const collectibleChance = getRandom(rng);
242
+ const collectibleType =
243
+ collectibleChance < 0.5
244
+ ? CollectibleType.GHOST_BABY // 163
245
+ : CollectibleType.DRY_BABY; // 265
246
+ spawnCollectible(collectibleType, position, rng);
247
+ return true;
308
248
  }
309
249
 
310
250
  // Since the detrimental effect is the final option, we don't need to check the chance.
@@ -316,54 +256,32 @@ function spawnRockAltRewardPolyp(position: Vector, rng: RNG): boolean {
316
256
  const chance = getRandom(rng);
317
257
  let totalChance = 0;
318
258
 
319
- totalChance += ROCK_ALT_CHANCES.Nothing;
259
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
320
260
  if (chance < totalChance) {
321
261
  return false;
322
262
  }
323
263
 
324
- totalChance += ROCK_ALT_CHANCES.BasicDrop;
264
+ totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
325
265
  if (chance < totalChance) {
326
266
  spawnHeartWithSeed(HeartSubType.NULL, position, rng);
327
267
  return true;
328
268
  }
329
269
 
330
- totalChance += ROCK_ALT_CHANCES.Trinket;
270
+ totalChance += ROCK_ALT_CHANCES.TRINKET;
331
271
  if (chance < totalChance) {
332
272
  spawnTrinketWithSeed(TrinketType.UMBILICAL_CORD, position, rng);
333
273
  return true;
334
274
  }
335
275
 
336
- totalChance += ROCK_ALT_CHANCES.Collectible;
276
+ totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
337
277
  if (chance < totalChance) {
338
- const placentaStillInPools = isCollectibleInItemPool(
339
- CollectibleType.PLACENTA,
340
- ItemPoolType.BOSS,
341
- );
342
- const bloodClotStillInPools = isCollectibleInItemPool(
343
- CollectibleType.BLOOD_CLOT,
344
- ItemPoolType.BOSS,
345
- );
346
- if (bloodClotStillInPools && placentaStillInPools) {
347
- const collectibleChance = getRandom(rng);
348
- const collectibleType =
349
- collectibleChance < 0.5
350
- ? CollectibleType.PLACENTA // 218
351
- : CollectibleType.BLOOD_CLOT; // 254
352
- spawnCollectible(collectibleType, position, rng);
353
- return true;
354
- }
355
-
356
- if (bloodClotStillInPools) {
357
- spawnCollectible(CollectibleType.MINI_MUSH, position, rng);
358
- return true;
359
- }
360
-
361
- if (placentaStillInPools) {
362
- spawnCollectible(CollectibleType.MAGIC_MUSHROOM, position, rng);
363
- return true;
364
- }
365
-
366
- return false;
278
+ const collectibleChance = getRandom(rng);
279
+ const collectibleType =
280
+ collectibleChance < 0.5
281
+ ? CollectibleType.PLACENTA // 218
282
+ : CollectibleType.BLOOD_CLOT; // 254
283
+ spawnCollectible(collectibleType, position, rng);
284
+ return true;
367
285
  }
368
286
 
369
287
  // Since the detrimental effect is the final option, we don't need to check the chance.
@@ -386,7 +304,7 @@ function spawnRockAltRewardBucket(
386
304
  const chance = getRandom(rng);
387
305
  let totalChance = 0;
388
306
 
389
- totalChance += ROCK_ALT_CHANCES.Nothing;
307
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
390
308
  if (chance < totalChance) {
391
309
  return false;
392
310
  }
@@ -1,6 +1,28 @@
1
- import { CollectibleType, ItemPoolType } from "isaac-typescript-definitions";
2
- import { isCollectibleInItemPool } from "./itemPool";
3
- import { anyPlayerHasCollectible } from "./players";
1
+ import {
2
+ CollectibleType,
3
+ ItemConfigTag,
4
+ ItemPoolType,
5
+ PlayerType,
6
+ TrinketType,
7
+ } from "isaac-typescript-definitions";
8
+ import { game } from "../cachedClasses";
9
+ import { PlayerIndex } from "../types/PlayerIndex";
10
+ import { getCollectibleSet } from "./collectibleSet";
11
+ import { collectibleHasTag } from "./collectibleTag";
12
+ import { mapGetPlayer, mapSetPlayer } from "./playerDataStructures";
13
+ import { getPlayers } from "./playerIndex";
14
+ import { anyPlayerHasCollectible, getPlayersOfType } from "./players";
15
+ import { repeat } from "./utils";
16
+
17
+ const COLLECTIBLES_THAT_AFFECT_ITEM_POOLS: readonly CollectibleType[] = [
18
+ CollectibleType.CHAOS, // 402
19
+ CollectibleType.SACRED_ORB, // 691
20
+ CollectibleType.TMTRAINER, // 721
21
+ ];
22
+
23
+ const TRINKETS_THAT_AFFECT_ITEM_POOLS: readonly TrinketType[] = [
24
+ TrinketType.NO,
25
+ ];
4
26
 
5
27
  /**
6
28
  * Helper function to see if the given collectible is unlocked on the current save file. This
@@ -15,6 +37,11 @@ import { anyPlayerHasCollectible } from "./players";
15
37
  * - If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
16
38
  * then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
17
39
  * collectibles from item pools).
40
+ *
41
+ * Under the hood, this function works by using the `ItemPool.AddRoomBlacklist` method to blacklist
42
+ * every collectible except for the one provided. Unfortunately, this is not a general-purpose
43
+ * "isCollectibleInItemPool" algorithm, because when a pool is depleted, it will automatically pull
44
+ * items from the Treasure Room pool, and there is no way to distinguish when this happens.
18
45
  */
19
46
  export function isCollectibleUnlocked(
20
47
  collectibleType: CollectibleType,
@@ -24,5 +51,120 @@ export function isCollectibleUnlocked(
24
51
  return true;
25
52
  }
26
53
 
27
- return isCollectibleInItemPool(collectibleType, itemPoolType);
54
+ // On Tainted Lost, it is impossible to retrieve non-offensive collectibles from pools, so we
55
+ // temporarily change the character to Isaac.
56
+ const taintedLosts = getPlayersOfType(PlayerType.THE_LOST_B);
57
+ const isOffensive = collectibleHasTag(
58
+ collectibleType,
59
+ ItemConfigTag.OFFENSIVE,
60
+ );
61
+ let changedPlayerTypes = false;
62
+ if (!isOffensive) {
63
+ changedPlayerTypes = true;
64
+ for (const player of taintedLosts) {
65
+ player.ChangePlayerType(PlayerType.ISAAC);
66
+ }
67
+ }
68
+
69
+ const [removedItemsMap, removedTrinketsMap] =
70
+ removeItemsAndTrinketsThatAffectItemPools();
71
+
72
+ // Blacklist every collectible in the game except for the provided collectible.
73
+ const itemPool = game.GetItemPool();
74
+ const collectibleSet = getCollectibleSet();
75
+ for (const collectibleTypeInSet of collectibleSet.values()) {
76
+ if (collectibleTypeInSet !== collectibleType) {
77
+ itemPool.AddRoomBlacklist(collectibleTypeInSet);
78
+ }
79
+ }
80
+
81
+ // Get a collectible from the pool and see if it is the intended collectible. (We can use any
82
+ // arbitrary value as the seed since it should not influence the result.)
83
+ const seed = 1 as Seed;
84
+ const retrievedCollectibleType = itemPool.GetCollectible(
85
+ itemPoolType,
86
+ false,
87
+ seed,
88
+ );
89
+
90
+ const collectibleUnlocked = retrievedCollectibleType === collectibleType;
91
+
92
+ // Reset the blacklist
93
+ itemPool.ResetRoomBlacklist();
94
+
95
+ restoreItemsAndTrinketsThatAffectItemPools(
96
+ removedItemsMap,
97
+ removedTrinketsMap,
98
+ );
99
+
100
+ // Change any players back to Tainted Lost, if necessary.
101
+ if (changedPlayerTypes) {
102
+ for (const player of taintedLosts) {
103
+ player.ChangePlayerType(PlayerType.THE_LOST_B);
104
+ }
105
+ }
106
+
107
+ return collectibleUnlocked;
108
+ }
109
+
110
+ /**
111
+ * Before checking the item pools, remove any collectibles or trinkets that would affect the
112
+ * retrieved collectible types.
113
+ */
114
+ function removeItemsAndTrinketsThatAffectItemPools(): [
115
+ removedItemsMap: Map<PlayerIndex, CollectibleType[]>,
116
+ removedTrinketsMap: Map<PlayerIndex, TrinketType[]>,
117
+ ] {
118
+ const removedItemsMap = new Map<PlayerIndex, CollectibleType[]>();
119
+ const removedTrinketsMap = new Map<PlayerIndex, TrinketType[]>();
120
+ for (const player of getPlayers()) {
121
+ const removedItems: CollectibleType[] = [];
122
+ for (const itemToRemove of COLLECTIBLES_THAT_AFFECT_ITEM_POOLS) {
123
+ if (player.HasCollectible(itemToRemove)) {
124
+ const numCollectibles = player.GetCollectibleNum(itemToRemove);
125
+ repeat(numCollectibles, () => {
126
+ player.RemoveCollectible(itemToRemove);
127
+ removedItems.push(itemToRemove);
128
+ });
129
+ }
130
+ }
131
+
132
+ mapSetPlayer(removedItemsMap, player, removedItems);
133
+
134
+ const removedTrinkets: TrinketType[] = [];
135
+ for (const trinketToRemove of TRINKETS_THAT_AFFECT_ITEM_POOLS) {
136
+ if (player.HasTrinket(trinketToRemove)) {
137
+ const numTrinkets = player.GetTrinketMultiplier(trinketToRemove);
138
+ repeat(numTrinkets, () => {
139
+ player.TryRemoveTrinket(trinketToRemove);
140
+ removedTrinkets.push(trinketToRemove);
141
+ });
142
+ }
143
+ }
144
+
145
+ mapSetPlayer(removedTrinketsMap, player, removedTrinkets);
146
+ }
147
+
148
+ return [removedItemsMap, removedTrinketsMap];
149
+ }
150
+
151
+ function restoreItemsAndTrinketsThatAffectItemPools(
152
+ removedItemsMap: Map<PlayerIndex, CollectibleType[]>,
153
+ removedTrinketsMap: Map<PlayerIndex, TrinketType[]>,
154
+ ) {
155
+ for (const player of getPlayers()) {
156
+ const removedItems = mapGetPlayer(removedItemsMap, player);
157
+ if (removedItems !== undefined) {
158
+ for (const collectibleType of removedItems) {
159
+ player.AddCollectible(collectibleType, 0, false); // Prevent Chaos from spawning pickups
160
+ }
161
+ }
162
+
163
+ const removedTrinkets = mapGetPlayer(removedTrinketsMap, player);
164
+ if (removedTrinkets !== undefined) {
165
+ for (const trinketType of removedTrinkets) {
166
+ player.AddTrinket(trinketType, false);
167
+ }
168
+ }
169
+ }
28
170
  }
package/src/index.ts CHANGED
@@ -23,7 +23,7 @@ export {
23
23
  export { registerCharacterStats } from "./features/characterStats";
24
24
  export { getCollectibleItemPoolType } from "./features/collectibleItemPoolType";
25
25
  export {
26
- removeCustomGrid,
26
+ removeCustomGridEntity as removeCustomGrid,
27
27
  spawnCustomGridEntity as spawnCustomGrid,
28
28
  } from "./features/customGridEntity";
29
29
  export * from "./features/customStage/exports";
@@ -117,7 +117,6 @@ export * from "./functions/gridEntities";
117
117
  export * from "./functions/gridEntitiesSpecific";
118
118
  export * from "./functions/input";
119
119
  export * from "./functions/isaacAPIClass";
120
- export * from "./functions/itemPool";
121
120
  export * from "./functions/jsonHelpers";
122
121
  export * from "./functions/jsonRoom";
123
122
  export * from "./functions/kColor";
@@ -3,7 +3,7 @@ import {
3
3
  GridEntityType,
4
4
  } from "isaac-typescript-definitions";
5
5
 
6
- export interface CustomGridEntityData {
6
+ export interface GridEntityCustomData {
7
7
  /**
8
8
  * This is not a real `GridEntityType`; rather it is an arbitrary integer selected by end-user
9
9
  * mods.
@@ -5,12 +5,23 @@ export interface PlayerHealth {
5
5
  maxHearts: int;
6
6
  hearts: int;
7
7
  eternalHearts: int;
8
+
9
+ /** For soul hearts to apply, they also have to be specified in the `soulHeartTypes` array. */
8
10
  soulHearts: int;
11
+
12
+ /** For bone hearts to apply, they also have to be specified in the `soulHeartTypes` array. */
9
13
  boneHearts: int;
14
+
10
15
  goldenHearts: int;
11
16
  rottenHearts: int;
12
17
  brokenHearts: int;
13
18
  soulCharges: int;
14
19
  bloodCharges: int;
15
- soulHeartTypes: HeartSubType[];
20
+
21
+ soulHeartTypes: SoulHeartType[];
16
22
  }
23
+
24
+ export type SoulHeartType =
25
+ | HeartSubType.SOUL // 3
26
+ | HeartSubType.BLACK // 6
27
+ | HeartSubType.BONE; // 11
@@ -16,4 +16,5 @@ export interface RoomDescription {
16
16
  roomName: string;
17
17
  roomGridIndex: int;
18
18
  roomListIndex: int;
19
+ roomVisitedCount: int;
19
20
  }
@@ -1,10 +0,0 @@
1
- import { CollectibleType, ItemPoolType } from "isaac-typescript-definitions";
2
- /**
3
- * Helper function to see if the given collectible is still present in the given item pool.
4
- *
5
- * If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
6
- * then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
7
- * collectibles from item pools).
8
- */
9
- export declare function isCollectibleInItemPool(collectibleType: CollectibleType, itemPoolType: ItemPoolType): boolean;
10
- //# sourceMappingURL=itemPool.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"itemPool.d.ts","sourceRoot":"","sources":["../../src/functions/itemPool.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAEf,YAAY,EAGb,MAAM,8BAA8B,CAAC;AAoBtC;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,eAAe,EAChC,YAAY,EAAE,YAAY,GACzB,OAAO,CAsDT"}
@@ -1,116 +0,0 @@
1
- local ____lualib = require("lualib_bundle")
2
- local Map = ____lualib.Map
3
- local __TS__Iterator = ____lualib.__TS__Iterator
4
- local __TS__New = ____lualib.__TS__New
5
- local ____exports = {}
6
- local removeItemsAndTrinketsThatAffectItemPools, restoreItemsAndTrinketsThatAffectItemPools, COLLECTIBLES_THAT_AFFECT_ITEM_POOLS, TRINKETS_THAT_AFFECT_ITEM_POOLS
7
- local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
8
- local CollectibleType = ____isaac_2Dtypescript_2Ddefinitions.CollectibleType
9
- local ItemConfigTag = ____isaac_2Dtypescript_2Ddefinitions.ItemConfigTag
10
- local PlayerType = ____isaac_2Dtypescript_2Ddefinitions.PlayerType
11
- local TrinketType = ____isaac_2Dtypescript_2Ddefinitions.TrinketType
12
- local ____cachedClasses = require("cachedClasses")
13
- local game = ____cachedClasses.game
14
- local ____collectibleSet = require("functions.collectibleSet")
15
- local getCollectibleSet = ____collectibleSet.getCollectibleSet
16
- local ____collectibleTag = require("functions.collectibleTag")
17
- local collectibleHasTag = ____collectibleTag.collectibleHasTag
18
- local ____playerDataStructures = require("functions.playerDataStructures")
19
- local mapGetPlayer = ____playerDataStructures.mapGetPlayer
20
- local mapSetPlayer = ____playerDataStructures.mapSetPlayer
21
- local ____playerIndex = require("functions.playerIndex")
22
- local getPlayers = ____playerIndex.getPlayers
23
- local ____players = require("functions.players")
24
- local getPlayersOfType = ____players.getPlayersOfType
25
- local ____utils = require("functions.utils")
26
- local ____repeat = ____utils["repeat"]
27
- function removeItemsAndTrinketsThatAffectItemPools(self)
28
- local removedItemsMap = __TS__New(Map)
29
- local removedTrinketsMap = __TS__New(Map)
30
- for ____, player in ipairs(getPlayers(nil)) do
31
- local removedItems = {}
32
- for ____, itemToRemove in ipairs(COLLECTIBLES_THAT_AFFECT_ITEM_POOLS) do
33
- if player:HasCollectible(itemToRemove) then
34
- local numCollectibles = player:GetCollectibleNum(itemToRemove)
35
- ____repeat(
36
- nil,
37
- numCollectibles,
38
- function()
39
- player:RemoveCollectible(itemToRemove)
40
- removedItems[#removedItems + 1] = itemToRemove
41
- end
42
- )
43
- end
44
- end
45
- mapSetPlayer(nil, removedItemsMap, player, removedItems)
46
- local removedTrinkets = {}
47
- for ____, trinketToRemove in ipairs(TRINKETS_THAT_AFFECT_ITEM_POOLS) do
48
- if player:HasTrinket(trinketToRemove) then
49
- local numTrinkets = player:GetTrinketMultiplier(trinketToRemove)
50
- ____repeat(
51
- nil,
52
- numTrinkets,
53
- function()
54
- player:TryRemoveTrinket(trinketToRemove)
55
- removedTrinkets[#removedTrinkets + 1] = trinketToRemove
56
- end
57
- )
58
- end
59
- end
60
- mapSetPlayer(nil, removedTrinketsMap, player, removedTrinkets)
61
- end
62
- return {removedItemsMap, removedTrinketsMap}
63
- end
64
- function restoreItemsAndTrinketsThatAffectItemPools(self, removedItemsMap, removedTrinketsMap)
65
- for ____, player in ipairs(getPlayers(nil)) do
66
- local removedItems = mapGetPlayer(nil, removedItemsMap, player)
67
- if removedItems ~= nil then
68
- for ____, collectibleType in ipairs(removedItems) do
69
- player:AddCollectible(collectibleType, 0, false)
70
- end
71
- end
72
- local removedTrinkets = mapGetPlayer(nil, removedTrinketsMap, player)
73
- if removedTrinkets ~= nil then
74
- for ____, trinketType in ipairs(removedTrinkets) do
75
- player:AddTrinket(trinketType, false)
76
- end
77
- end
78
- end
79
- end
80
- COLLECTIBLES_THAT_AFFECT_ITEM_POOLS = {CollectibleType.CHAOS, CollectibleType.SACRED_ORB, CollectibleType.TMTRAINER}
81
- TRINKETS_THAT_AFFECT_ITEM_POOLS = {TrinketType.NO}
82
- --- Helper function to see if the given collectible is still present in the given item pool.
83
- --
84
- -- If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
85
- -- then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
86
- -- collectibles from item pools).
87
- function ____exports.isCollectibleInItemPool(self, collectibleType, itemPoolType)
88
- local taintedLosts = getPlayersOfType(nil, PlayerType.THE_LOST_B)
89
- local isOffensive = collectibleHasTag(nil, collectibleType, ItemConfigTag.OFFENSIVE)
90
- local changedPlayerTypes = false
91
- if not isOffensive then
92
- changedPlayerTypes = true
93
- for ____, player in ipairs(taintedLosts) do
94
- player:ChangePlayerType(PlayerType.ISAAC)
95
- end
96
- end
97
- local removedItemsMap, removedTrinketsMap = table.unpack(removeItemsAndTrinketsThatAffectItemPools(nil))
98
- local itemPool = game:GetItemPool()
99
- local collectibleSet = getCollectibleSet(nil)
100
- for ____, collectibleTypeInSet in __TS__Iterator(collectibleSet:values()) do
101
- if collectibleTypeInSet ~= collectibleType then
102
- itemPool:AddRoomBlacklist(collectibleTypeInSet)
103
- end
104
- end
105
- local retrievedCollectibleType = itemPool:GetCollectible(itemPoolType, false, 1)
106
- local collectibleUnlocked = retrievedCollectibleType == collectibleType
107
- itemPool:ResetRoomBlacklist()
108
- restoreItemsAndTrinketsThatAffectItemPools(nil, removedItemsMap, removedTrinketsMap)
109
- if changedPlayerTypes then
110
- for ____, player in ipairs(taintedLosts) do
111
- player:ChangePlayerType(PlayerType.THE_LOST_B)
112
- end
113
- end
114
- return collectibleUnlocked
115
- end
116
- return ____exports