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.
- package/dist/enums/ModCallbackCustom.d.ts +1 -1
- package/dist/features/customGridEntity.d.ts +3 -3
- package/dist/features/customGridEntity.d.ts.map +1 -1
- package/dist/features/customGridEntity.lua +18 -11
- package/dist/features/extraConsoleCommands/listCommands.lua +2 -2
- package/dist/features/persistentEntities.d.ts.map +1 -1
- package/dist/features/persistentEntities.lua +15 -5
- package/dist/features/pickupIndex.d.ts.map +1 -1
- package/dist/features/pickupIndex.lua +5 -6
- package/dist/features/roomHistory.d.ts +11 -4
- package/dist/features/roomHistory.d.ts.map +1 -1
- package/dist/features/roomHistory.lua +18 -5
- package/dist/functions/log.d.ts +1 -1
- package/dist/functions/log.d.ts.map +1 -1
- package/dist/functions/log.lua +33 -28
- package/dist/functions/npcs.d.ts +9 -0
- package/dist/functions/npcs.d.ts.map +1 -1
- package/dist/functions/npcs.lua +16 -3
- package/dist/functions/playerHealth.d.ts.map +1 -1
- package/dist/functions/playerHealth.lua +29 -12
- package/dist/functions/rockAlt.d.ts +4 -4
- package/dist/functions/rockAlt.d.ts.map +1 -1
- package/dist/functions/rockAlt.lua +38 -87
- package/dist/functions/saveFile.d.ts +5 -0
- package/dist/functions/saveFile.d.ts.map +1 -1
- package/dist/functions/saveFile.lua +112 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.lua +1 -9
- package/dist/interfaces/{CustomGridEntityData.d.ts → GridEntityCustomData.d.ts} +2 -2
- package/dist/interfaces/{CustomGridEntityData.d.ts.map → GridEntityCustomData.d.ts.map} +1 -1
- package/dist/interfaces/{CustomGridEntityData.lua → GridEntityCustomData.lua} +0 -0
- package/dist/interfaces/PlayerHealth.d.ts +4 -1
- package/dist/interfaces/PlayerHealth.d.ts.map +1 -1
- package/dist/interfaces/RoomDescription.d.ts +1 -0
- package/dist/interfaces/RoomDescription.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/enums/ModCallbackCustom.ts +1 -1
- package/src/features/customGridEntity.ts +30 -17
- package/src/features/extraConsoleCommands/listCommands.ts +2 -2
- package/src/features/persistentEntities.ts +19 -5
- package/src/features/pickupIndex.ts +9 -15
- package/src/features/roomHistory.ts +24 -4
- package/src/functions/log.ts +35 -30
- package/src/functions/npcs.ts +27 -2
- package/src/functions/playerHealth.ts +25 -12
- package/src/functions/rockAlt.ts +50 -132
- package/src/functions/saveFile.ts +146 -4
- package/src/index.ts +1 -2
- package/src/interfaces/{CustomGridEntityData.ts → GridEntityCustomData.ts} +1 -1
- package/src/interfaces/PlayerHealth.ts +12 -1
- package/src/interfaces/RoomDescription.ts +1 -0
- package/dist/functions/itemPool.d.ts +0 -10
- package/dist/functions/itemPool.d.ts.map +0 -1
- package/dist/functions/itemPool.lua +0 -116
- package/src/functions/itemPool.ts +0 -153
package/src/functions/rockAlt.ts
CHANGED
|
@@ -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
|
-
|
|
35
|
-
|
|
32
|
+
NOTHING: 0.68,
|
|
33
|
+
BASIC_DROP: 0.0967,
|
|
36
34
|
|
|
37
35
|
/** Also used for e.g. black hearts from skulls. */
|
|
38
|
-
|
|
36
|
+
TRINKET: 0.025,
|
|
39
37
|
|
|
40
|
-
|
|
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
|
|
75
|
-
* pool. This function cannot replicate that behavior because there is no way to
|
|
76
|
-
* trinket is still in the pool. Thus, it will always have a chance
|
|
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.
|
|
127
|
+
totalChance += ROCK_ALT_CHANCES.NOTHING;
|
|
130
128
|
if (chance < totalChance) {
|
|
131
129
|
return false;
|
|
132
130
|
}
|
|
133
131
|
|
|
134
|
-
totalChance += ROCK_ALT_CHANCES.
|
|
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.
|
|
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.
|
|
151
|
+
totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
|
|
154
152
|
if (chance < totalChance) {
|
|
155
|
-
|
|
156
|
-
|
|
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.
|
|
177
|
+
totalChance += ROCK_ALT_CHANCES.NOTHING;
|
|
188
178
|
if (chance < totalChance) {
|
|
189
179
|
return false;
|
|
190
180
|
}
|
|
191
181
|
|
|
192
|
-
totalChance += ROCK_ALT_CHANCES.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
210
|
-
|
|
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
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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.
|
|
222
|
+
totalChance += ROCK_ALT_CHANCES.NOTHING;
|
|
261
223
|
if (chance < totalChance) {
|
|
262
224
|
return false;
|
|
263
225
|
}
|
|
264
226
|
|
|
265
|
-
totalChance += ROCK_ALT_CHANCES.
|
|
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.
|
|
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.
|
|
239
|
+
totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
|
|
278
240
|
if (chance < totalChance) {
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
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.
|
|
259
|
+
totalChance += ROCK_ALT_CHANCES.NOTHING;
|
|
320
260
|
if (chance < totalChance) {
|
|
321
261
|
return false;
|
|
322
262
|
}
|
|
323
263
|
|
|
324
|
-
totalChance += ROCK_ALT_CHANCES.
|
|
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.
|
|
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.
|
|
276
|
+
totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
|
|
337
277
|
if (chance < totalChance) {
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
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.
|
|
307
|
+
totalChance += ROCK_ALT_CHANCES.NOTHING;
|
|
390
308
|
if (chance < totalChance) {
|
|
391
309
|
return false;
|
|
392
310
|
}
|
|
@@ -1,6 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
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";
|
|
@@ -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
|
-
|
|
20
|
+
|
|
21
|
+
soulHeartTypes: SoulHeartType[];
|
|
16
22
|
}
|
|
23
|
+
|
|
24
|
+
export type SoulHeartType =
|
|
25
|
+
| HeartSubType.SOUL // 3
|
|
26
|
+
| HeartSubType.BLACK // 6
|
|
27
|
+
| HeartSubType.BONE; // 11
|
|
@@ -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
|