isaacscript-common 6.18.0 → 6.19.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 +1 @@
1
- {"version":3,"file":"playerInventory.d.ts","sourceRoot":"","sources":["../../src/features/playerInventory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAe,MAAM,8BAA8B,CAAC;AA0G5E;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,yBAAyB,UAAO,GAC/B,eAAe,EAAE,CAanB"}
1
+ {"version":3,"file":"playerInventory.d.ts","sourceRoot":"","sources":["../../src/features/playerInventory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAe,MAAM,8BAA8B,CAAC;AAyG5E;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,YAAY,EACpB,yBAAyB,UAAO,GAC/B,eAAe,EAAE,CAanB"}
@@ -1,6 +1,5 @@
1
1
  local ____lualib = require("lualib_bundle")
2
2
  local __TS__New = ____lualib.__TS__New
3
- local __TS__Iterator = ____lualib.__TS__Iterator
4
3
  local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
5
4
  local ____exports = {}
6
5
  local newPlayerInventory, resetInventory, useItemD4, postGameStarted, postCollectibleAdded, postCollectibleRemoved, v
@@ -19,7 +18,7 @@ local copyArray = ____array.copyArray
19
18
  local ____collectibles = require("functions.collectibles")
20
19
  local isActiveCollectible = ____collectibles.isActiveCollectible
21
20
  local ____collectibleSet = require("functions.collectibleSet")
22
- local getCollectibleSet = ____collectibleSet.getCollectibleSet
21
+ local getCollectibleArray = ____collectibleSet.getCollectibleArray
23
22
  local ____playerDataStructures = require("functions.playerDataStructures")
24
23
  local defaultMapGetPlayer = ____playerDataStructures.defaultMapGetPlayer
25
24
  local mapSetPlayer = ____playerDataStructures.mapSetPlayer
@@ -32,8 +31,7 @@ local ____exports = require("features.saveDataManager.exports")
32
31
  local saveDataManager = ____exports.saveDataManager
33
32
  function newPlayerInventory(self, player)
34
33
  local inventory = {}
35
- local collectibleSet = getCollectibleSet(nil)
36
- for ____, collectibleType in __TS__Iterator(collectibleSet:values()) do
34
+ for ____, collectibleType in ipairs(getCollectibleArray(nil)) do
37
35
  local numCollectibles = player:GetCollectibleNum(collectibleType, true)
38
36
  ____repeat(
39
37
  nil,
@@ -1 +1 @@
1
- {"version":3,"file":"eden.d.ts","sourceRoot":"","sources":["../../src/functions/eden.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAiB,MAAM,8BAA8B,CAAC;AAuB9E,wBAAgB,eAAe,IAAI,GAAG,CAAC,eAAe,CAAC,CAOtD;AAED,wBAAgB,oBAAoB,CAClC,SAAS,GAAE,IAAI,GAAG,GAAqB,EACvC,UAAU,GAAE,eAAe,EAAE,GAAG,SAAS,eAAe,EAAO,GAC9D,eAAe,CAWjB"}
1
+ {"version":3,"file":"eden.d.ts","sourceRoot":"","sources":["../../src/functions/eden.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAiB,MAAM,8BAA8B,CAAC;AAqB9E,wBAAgB,eAAe,IAAI,GAAG,CAAC,eAAe,CAAC,CAOtD;AAED,wBAAgB,oBAAoB,CAClC,SAAS,GAAE,IAAI,GAAG,GAAqB,EACvC,UAAU,GAAE,eAAe,EAAE,GAAG,SAAS,eAAe,EAAO,GAC9D,eAAe,CAWjB"}
@@ -1,7 +1,6 @@
1
1
  local ____lualib = require("lualib_bundle")
2
2
  local Set = ____lualib.Set
3
3
  local __TS__New = ____lualib.__TS__New
4
- local __TS__Iterator = ____lualib.__TS__Iterator
5
4
  local ____exports = {}
6
5
  local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
7
6
  local ItemConfigTag = ____isaac_2Dtypescript_2Ddefinitions.ItemConfigTag
@@ -9,7 +8,7 @@ local ____collectibles = require("functions.collectibles")
9
8
  local isHiddenCollectible = ____collectibles.isHiddenCollectible
10
9
  local isPassiveCollectible = ____collectibles.isPassiveCollectible
11
10
  local ____collectibleSet = require("functions.collectibleSet")
12
- local getCollectibleSet = ____collectibleSet.getCollectibleSet
11
+ local getCollectibleArray = ____collectibleSet.getCollectibleArray
13
12
  local ____collectibleTag = require("functions.collectibleTag")
14
13
  local collectibleHasTag = ____collectibleTag.collectibleHasTag
15
14
  local ____rng = require("functions.rng")
@@ -19,8 +18,7 @@ local copySet = ____set.copySet
19
18
  local getRandomSetElement = ____set.getRandomSetElement
20
19
  local EDEN_PASSIVE_COLLECTIBLES_SET = __TS__New(Set)
21
20
  local function initCollectibleSet(self)
22
- local collectibleSet = getCollectibleSet(nil)
23
- for ____, collectibleType in __TS__Iterator(collectibleSet:values()) do
21
+ for ____, collectibleType in ipairs(getCollectibleArray(nil)) do
24
22
  if isPassiveCollectible(nil, collectibleType) and not isHiddenCollectible(nil, collectibleType) and not collectibleHasTag(nil, collectibleType, ItemConfigTag.NO_EDEN) then
25
23
  EDEN_PASSIVE_COLLECTIBLES_SET:add(collectibleType)
26
24
  end
@@ -0,0 +1,18 @@
1
+ import { CollectibleType, ItemPoolType } from "isaac-typescript-definitions";
2
+ /**
3
+ * Helper function to get the remaining collectibles in a given item pool. This function is
4
+ * expensive, so only use it in situations where the lag is acceptable.
5
+ */
6
+ export declare function getCollectiblesInItemPool(itemPoolType: ItemPoolType): CollectibleType[];
7
+ /**
8
+ * Helper function to see if the given collectible is still present in the given item pool.
9
+ *
10
+ * If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
11
+ * then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
12
+ * collectibles from item pools).
13
+ *
14
+ * Under the hood, this function works by using the `ItemPool.AddRoomBlacklist` method to blacklist
15
+ * every collectible except for the one provided.
16
+ */
17
+ export declare function isCollectibleInItemPool(collectibleType: CollectibleType, itemPoolType: ItemPoolType): boolean;
18
+ //# sourceMappingURL=itemPool.d.ts.map
@@ -0,0 +1 @@
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;AAsBtC;;;GAGG;AACH,wBAAgB,yBAAyB,CACvC,YAAY,EAAE,YAAY,GACzB,eAAe,EAAE,CAKnB;AAED;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,eAAe,EAChC,YAAY,EAAE,YAAY,GACzB,OAAO,CA6DT"}
@@ -0,0 +1,133 @@
1
+ local ____lualib = require("lualib_bundle")
2
+ local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
3
+ local Map = ____lualib.Map
4
+ local __TS__New = ____lualib.__TS__New
5
+ local ____exports = {}
6
+ local removeItemsAndTrinketsThatAffectItemPools, restoreItemsAndTrinketsThatAffectItemPools, COLLECTIBLES_THAT_AFFECT_ITEM_POOLS, TRINKETS_THAT_AFFECT_ITEM_POOLS, COLLECTIBLE_TYPE_THAT_IS_NOT_IN_ANY_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 getCollectibleArray = ____collectibleSet.getCollectibleArray
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
+ --- Helper function to see if the given collectible is still present in the given item pool.
28
+ --
29
+ -- If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
30
+ -- then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
31
+ -- collectibles from item pools).
32
+ --
33
+ -- Under the hood, this function works by using the `ItemPool.AddRoomBlacklist` method to blacklist
34
+ -- every collectible except for the one provided.
35
+ function ____exports.isCollectibleInItemPool(self, collectibleType, itemPoolType)
36
+ if collectibleType == COLLECTIBLE_TYPE_THAT_IS_NOT_IN_ANY_POOLS then
37
+ return false
38
+ end
39
+ local taintedLosts = getPlayersOfType(nil, PlayerType.THE_LOST_B)
40
+ local isOffensive = collectibleHasTag(nil, collectibleType, ItemConfigTag.OFFENSIVE)
41
+ local changedPlayerTypes = false
42
+ if not isOffensive then
43
+ changedPlayerTypes = true
44
+ for ____, player in ipairs(taintedLosts) do
45
+ player:ChangePlayerType(PlayerType.ISAAC)
46
+ end
47
+ end
48
+ local removedItemsMap, removedTrinketsMap = table.unpack(removeItemsAndTrinketsThatAffectItemPools(nil))
49
+ local itemPool = game:GetItemPool()
50
+ itemPool:ResetRoomBlacklist()
51
+ for ____, collectibleTypeInSet in ipairs(getCollectibleArray(nil)) do
52
+ if collectibleTypeInSet ~= collectibleType then
53
+ itemPool:AddRoomBlacklist(collectibleTypeInSet)
54
+ end
55
+ end
56
+ local seed = 1
57
+ local retrievedCollectibleType = itemPool:GetCollectible(itemPoolType, false, seed, COLLECTIBLE_TYPE_THAT_IS_NOT_IN_ANY_POOLS)
58
+ local collectibleUnlocked = retrievedCollectibleType == collectibleType
59
+ itemPool:ResetRoomBlacklist()
60
+ restoreItemsAndTrinketsThatAffectItemPools(nil, removedItemsMap, removedTrinketsMap)
61
+ if changedPlayerTypes then
62
+ for ____, player in ipairs(taintedLosts) do
63
+ player:ChangePlayerType(PlayerType.THE_LOST_B)
64
+ end
65
+ end
66
+ return collectibleUnlocked
67
+ end
68
+ function removeItemsAndTrinketsThatAffectItemPools(self)
69
+ local removedItemsMap = __TS__New(Map)
70
+ local removedTrinketsMap = __TS__New(Map)
71
+ for ____, player in ipairs(getPlayers(nil)) do
72
+ local removedItems = {}
73
+ for ____, itemToRemove in ipairs(COLLECTIBLES_THAT_AFFECT_ITEM_POOLS) do
74
+ if player:HasCollectible(itemToRemove) then
75
+ local numCollectibles = player:GetCollectibleNum(itemToRemove)
76
+ ____repeat(
77
+ nil,
78
+ numCollectibles,
79
+ function()
80
+ player:RemoveCollectible(itemToRemove)
81
+ removedItems[#removedItems + 1] = itemToRemove
82
+ end
83
+ )
84
+ end
85
+ end
86
+ mapSetPlayer(nil, removedItemsMap, player, removedItems)
87
+ local removedTrinkets = {}
88
+ for ____, trinketToRemove in ipairs(TRINKETS_THAT_AFFECT_ITEM_POOLS) do
89
+ if player:HasTrinket(trinketToRemove) then
90
+ local numTrinkets = player:GetTrinketMultiplier(trinketToRemove)
91
+ ____repeat(
92
+ nil,
93
+ numTrinkets,
94
+ function()
95
+ player:TryRemoveTrinket(trinketToRemove)
96
+ removedTrinkets[#removedTrinkets + 1] = trinketToRemove
97
+ end
98
+ )
99
+ end
100
+ end
101
+ mapSetPlayer(nil, removedTrinketsMap, player, removedTrinkets)
102
+ end
103
+ return {removedItemsMap, removedTrinketsMap}
104
+ end
105
+ function restoreItemsAndTrinketsThatAffectItemPools(self, removedItemsMap, removedTrinketsMap)
106
+ for ____, player in ipairs(getPlayers(nil)) do
107
+ local removedItems = mapGetPlayer(nil, removedItemsMap, player)
108
+ if removedItems ~= nil then
109
+ for ____, collectibleType in ipairs(removedItems) do
110
+ player:AddCollectible(collectibleType, 0, false)
111
+ end
112
+ end
113
+ local removedTrinkets = mapGetPlayer(nil, removedTrinketsMap, player)
114
+ if removedTrinkets ~= nil then
115
+ for ____, trinketType in ipairs(removedTrinkets) do
116
+ player:AddTrinket(trinketType, false)
117
+ end
118
+ end
119
+ end
120
+ end
121
+ COLLECTIBLES_THAT_AFFECT_ITEM_POOLS = {CollectibleType.CHAOS, CollectibleType.SACRED_ORB, CollectibleType.TMTRAINER}
122
+ TRINKETS_THAT_AFFECT_ITEM_POOLS = {TrinketType.NO}
123
+ COLLECTIBLE_TYPE_THAT_IS_NOT_IN_ANY_POOLS = CollectibleType.KEY_PIECE_1
124
+ --- Helper function to get the remaining collectibles in a given item pool. This function is
125
+ -- expensive, so only use it in situations where the lag is acceptable.
126
+ function ____exports.getCollectiblesInItemPool(self, itemPoolType)
127
+ local collectibleArray = getCollectibleArray(nil)
128
+ return __TS__ArrayFilter(
129
+ collectibleArray,
130
+ function(____, collectibleType) return ____exports.isCollectibleInItemPool(nil, collectibleType, itemPoolType) end
131
+ )
132
+ end
133
+ return ____exports
@@ -24,10 +24,10 @@ export declare function getRockAltType(): RockAltType;
24
24
  * Most of the time, this function will do nothing, similar to how most of the time, when an
25
25
  * individual urn is destroyed, nothing will spawn.
26
26
  *
27
- * Note that in vanilla, collectibles and trinkets will not spawn if they have already been removed
28
- * from the respective pool. This function cannot replicate that behavior because there is no way to
29
- * check to see if a collectible or trinket is still in the pool. Thus, it will always have a chance
30
- * to spawn the respective collectible/trinket (e.g. Swallowed Penny from urns).
27
+ * Note that in vanilla, trinkets will not spawn if they have already been removed from the trinket
28
+ * pool. This function cannot replicate that behavior because there is no way to check to see if a
29
+ * trinket is still in the pool. Thus, it will always have a chance to spawn the respective trinket
30
+ * (e.g. Swallowed Penny from urns).
31
31
  *
32
32
  * The logic in this function is based on the rewards listed on the wiki:
33
33
  * https://bindingofisaacrebirth.fandom.com/wiki/Rocks
@@ -1 +1 @@
1
- {"version":3,"file":"rockAlt.d.ts","sourceRoot":"","sources":["../../src/functions/rockAlt.ts"],"names":[],"mappings":";;;AAaA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA8BnD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAK5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,EACxB,OAAO,SAAI,EACX,SAAS,GAAE,IAAI,GAAG,GAAqB,GACtC,OAAO,CAwBT"}
1
+ {"version":3,"file":"rockAlt.d.ts","sourceRoot":"","sources":["../../src/functions/rockAlt.ts"],"names":[],"mappings":";;;AAcA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA+BnD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,IAAI,WAAW,CAK5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,WAAW,EACxB,OAAO,SAAI,EACX,SAAS,GAAE,IAAI,GAAG,GAAqB,GACtC,OAAO,CAwBT"}
@@ -7,6 +7,7 @@ local CollectibleType = ____isaac_2Dtypescript_2Ddefinitions.CollectibleType
7
7
  local EffectVariant = ____isaac_2Dtypescript_2Ddefinitions.EffectVariant
8
8
  local EntityType = ____isaac_2Dtypescript_2Ddefinitions.EntityType
9
9
  local HeartSubType = ____isaac_2Dtypescript_2Ddefinitions.HeartSubType
10
+ local ItemPoolType = ____isaac_2Dtypescript_2Ddefinitions.ItemPoolType
10
11
  local PillColor = ____isaac_2Dtypescript_2Ddefinitions.PillColor
11
12
  local RoomType = ____isaac_2Dtypescript_2Ddefinitions.RoomType
12
13
  local TrinketType = ____isaac_2Dtypescript_2Ddefinitions.TrinketType
@@ -21,6 +22,8 @@ local BACKDROP_TYPE_TO_ROCK_ALT_TYPE = ____backdropTypeToRockAltType.BACKDROP_TY
21
22
  local ____entitiesSpecific = require("functions.entitiesSpecific")
22
23
  local spawnEffectWithSeed = ____entitiesSpecific.spawnEffectWithSeed
23
24
  local spawnNPCWithSeed = ____entitiesSpecific.spawnNPCWithSeed
25
+ local ____itemPool = require("functions.itemPool")
26
+ local isCollectibleInItemPool = ____itemPool.isCollectibleInItemPool
24
27
  local ____pickupsSpecific = require("functions.pickupsSpecific")
25
28
  local spawnCardWithSeed = ____pickupsSpecific.spawnCardWithSeed
26
29
  local spawnCoinWithSeed = ____pickupsSpecific.spawnCoinWithSeed
@@ -77,8 +80,12 @@ function spawnRockAltRewardUrn(self, position, rng)
77
80
  end
78
81
  totalChance = totalChance + ROCK_ALT_CHANCES.COLLECTIBLE
79
82
  if chance < totalChance then
80
- spawnCollectible(nil, CollectibleType.QUARTER, position, rng)
81
- return true
83
+ local stillInPools = isCollectibleInItemPool(nil, CollectibleType.QUARTER, ItemPoolType.DEVIL)
84
+ if stillInPools then
85
+ spawnCollectible(nil, CollectibleType.QUARTER, position, rng)
86
+ return true
87
+ end
88
+ return false
82
89
  end
83
90
  local numSpidersChance = getRandom(nil, rng)
84
91
  local numSpiders = numSpidersChance < 0.5 and 1 or 2
@@ -125,14 +132,30 @@ function spawnRockAltRewardMushroom(self, position, rng)
125
132
  if roomType == RoomType.SECRET then
126
133
  local wavyCapChance = getRandom(nil, rng)
127
134
  if wavyCapChance < 0.0272 then
128
- spawnCollectible(nil, CollectibleType.WAVY_CAP, position, rng)
129
- return true
135
+ local stillInPools = isCollectibleInItemPool(nil, CollectibleType.WAVY_CAP, ItemPoolType.SECRET)
136
+ if stillInPools then
137
+ spawnCollectible(nil, CollectibleType.WAVY_CAP, position, rng)
138
+ return true
139
+ end
130
140
  end
131
141
  end
132
- local collectibleChance = getRandom(nil, rng)
133
- local collectibleType = collectibleChance < 0.5 and CollectibleType.MAGIC_MUSHROOM or CollectibleType.MINI_MUSH
134
- spawnCollectible(nil, collectibleType, position, rng)
135
- return true
142
+ local magicMushroomStillInPools = isCollectibleInItemPool(nil, CollectibleType.MAGIC_MUSHROOM, ItemPoolType.TREASURE)
143
+ local miniMushStillInPools = isCollectibleInItemPool(nil, CollectibleType.MINI_MUSH, ItemPoolType.TREASURE)
144
+ if magicMushroomStillInPools and miniMushStillInPools then
145
+ local collectibleChance = getRandom(nil, rng)
146
+ local collectibleType = collectibleChance < 0.5 and CollectibleType.MAGIC_MUSHROOM or CollectibleType.MINI_MUSH
147
+ spawnCollectible(nil, collectibleType, position, rng)
148
+ return true
149
+ end
150
+ if magicMushroomStillInPools then
151
+ spawnCollectible(nil, CollectibleType.MINI_MUSH, position, rng)
152
+ return true
153
+ end
154
+ if miniMushStillInPools then
155
+ spawnCollectible(nil, CollectibleType.MAGIC_MUSHROOM, position, rng)
156
+ return true
157
+ end
158
+ return false
136
159
  end
137
160
  spawnEffectWithSeed(
138
161
  nil,
@@ -162,10 +185,23 @@ function spawnRockAltRewardSkull(self, position, rng)
162
185
  end
163
186
  totalChance = totalChance + ROCK_ALT_CHANCES.COLLECTIBLE
164
187
  if chance < totalChance then
165
- local collectibleChance = getRandom(nil, rng)
166
- local collectibleType = collectibleChance < 0.5 and CollectibleType.GHOST_BABY or CollectibleType.DRY_BABY
167
- spawnCollectible(nil, collectibleType, position, rng)
168
- return true
188
+ local ghostBabyStillInPools = isCollectibleInItemPool(nil, CollectibleType.GHOST_BABY, ItemPoolType.TREASURE)
189
+ local dryBabyStillInPools = isCollectibleInItemPool(nil, CollectibleType.DRY_BABY, ItemPoolType.TREASURE)
190
+ if ghostBabyStillInPools and dryBabyStillInPools then
191
+ local collectibleChance = getRandom(nil, rng)
192
+ local collectibleType = collectibleChance < 0.5 and CollectibleType.GHOST_BABY or CollectibleType.DRY_BABY
193
+ spawnCollectible(nil, collectibleType, position, rng)
194
+ return true
195
+ end
196
+ if ghostBabyStillInPools then
197
+ spawnCollectible(nil, CollectibleType.DRY_BABY, position, rng)
198
+ return true
199
+ end
200
+ if dryBabyStillInPools then
201
+ spawnCollectible(nil, CollectibleType.GHOST_BABY, position, rng)
202
+ return true
203
+ end
204
+ return false
169
205
  end
170
206
  spawnNPCWithSeed(
171
207
  nil,
@@ -196,10 +232,23 @@ function spawnRockAltRewardPolyp(self, position, rng)
196
232
  end
197
233
  totalChance = totalChance + ROCK_ALT_CHANCES.COLLECTIBLE
198
234
  if chance < totalChance then
199
- local collectibleChance = getRandom(nil, rng)
200
- local collectibleType = collectibleChance < 0.5 and CollectibleType.PLACENTA or CollectibleType.BLOOD_CLOT
201
- spawnCollectible(nil, collectibleType, position, rng)
202
- return true
235
+ local placentaStillInPools = isCollectibleInItemPool(nil, CollectibleType.PLACENTA, ItemPoolType.BOSS)
236
+ local bloodClotStillInPools = isCollectibleInItemPool(nil, CollectibleType.BLOOD_CLOT, ItemPoolType.BOSS)
237
+ if bloodClotStillInPools and placentaStillInPools then
238
+ local collectibleChance = getRandom(nil, rng)
239
+ local collectibleType = collectibleChance < 0.5 and CollectibleType.PLACENTA or CollectibleType.BLOOD_CLOT
240
+ spawnCollectible(nil, collectibleType, position, rng)
241
+ return true
242
+ end
243
+ if bloodClotStillInPools then
244
+ spawnCollectible(nil, CollectibleType.MINI_MUSH, position, rng)
245
+ return true
246
+ end
247
+ if placentaStillInPools then
248
+ spawnCollectible(nil, CollectibleType.MAGIC_MUSHROOM, position, rng)
249
+ return true
250
+ end
251
+ return false
203
252
  end
204
253
  spawnEffectWithSeed(
205
254
  nil,
@@ -252,10 +301,10 @@ end
252
301
  -- Most of the time, this function will do nothing, similar to how most of the time, when an
253
302
  -- individual urn is destroyed, nothing will spawn.
254
303
  --
255
- -- Note that in vanilla, collectibles and trinkets will not spawn if they have already been removed
256
- -- from the respective pool. This function cannot replicate that behavior because there is no way to
257
- -- check to see if a collectible or trinket is still in the pool. Thus, it will always have a chance
258
- -- to spawn the respective collectible/trinket (e.g. Swallowed Penny from urns).
304
+ -- Note that in vanilla, trinkets will not spawn if they have already been removed from the trinket
305
+ -- pool. This function cannot replicate that behavior because there is no way to check to see if a
306
+ -- trinket is still in the pool. Thus, it will always have a chance to spawn the respective trinket
307
+ -- (e.g. Swallowed Penny from urns).
259
308
  --
260
309
  -- The logic in this function is based on the rewards listed on the wiki:
261
310
  -- https://bindingofisaacrebirth.fandom.com/wiki/Rocks
@@ -1,7 +1,7 @@
1
1
  import { CollectibleType, ItemPoolType } from "isaac-typescript-definitions";
2
2
  /**
3
3
  * Helper function to see if the given collectible is unlocked on the current save file. This
4
- * requires providing the corresponding item pool that the collectible is located in.
4
+ * requires providing the corresponding item pool that the collectible is normally located in.
5
5
  *
6
6
  * - If any player currently has the collectible, then it is assumed to be unlocked. (This is
7
7
  * because in almost all cases, when a collectible is added to a player's inventory, it is
@@ -12,11 +12,6 @@ import { CollectibleType, ItemPoolType } from "isaac-typescript-definitions";
12
12
  * - If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
13
13
  * then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
14
14
  * collectibles from item pools).
15
- *
16
- * Under the hood, this function works by using the `ItemPool.AddRoomBlacklist` method to blacklist
17
- * every collectible except for the one provided. Unfortunately, this is not a general-purpose
18
- * "isCollectibleInItemPool" algorithm, because when a pool is depleted, it will automatically pull
19
- * items from the Treasure Room pool, and there is no way to distinguish when this happens.
20
15
  */
21
16
  export declare function isCollectibleUnlocked(collectibleType: CollectibleType, itemPoolType: ItemPoolType): boolean;
22
17
  //# sourceMappingURL=saveFile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"saveFile.d.ts","sourceRoot":"","sources":["../../src/functions/saveFile.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EAEf,YAAY,EAGb,MAAM,8BAA8B,CAAC;AAoBtC;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,eAAe,EAChC,YAAY,EAAE,YAAY,GACzB,OAAO,CA2DT"}
1
+ {"version":3,"file":"saveFile.d.ts","sourceRoot":"","sources":["../../src/functions/saveFile.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAI7E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,eAAe,EAChC,YAAY,EAAE,YAAY,GACzB,OAAO,CAMT"}
@@ -1,87 +1,10 @@
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
1
  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
2
+ local ____itemPool = require("functions.itemPool")
3
+ local isCollectibleInItemPool = ____itemPool.isCollectibleInItemPool
23
4
  local ____players = require("functions.players")
24
5
  local anyPlayerHasCollectible = ____players.anyPlayerHasCollectible
25
- local getPlayersOfType = ____players.getPlayersOfType
26
- local ____utils = require("functions.utils")
27
- local ____repeat = ____utils["repeat"]
28
- function removeItemsAndTrinketsThatAffectItemPools(self)
29
- local removedItemsMap = __TS__New(Map)
30
- local removedTrinketsMap = __TS__New(Map)
31
- for ____, player in ipairs(getPlayers(nil)) do
32
- local removedItems = {}
33
- for ____, itemToRemove in ipairs(COLLECTIBLES_THAT_AFFECT_ITEM_POOLS) do
34
- if player:HasCollectible(itemToRemove) then
35
- local numCollectibles = player:GetCollectibleNum(itemToRemove)
36
- ____repeat(
37
- nil,
38
- numCollectibles,
39
- function()
40
- player:RemoveCollectible(itemToRemove)
41
- removedItems[#removedItems + 1] = itemToRemove
42
- end
43
- )
44
- end
45
- end
46
- mapSetPlayer(nil, removedItemsMap, player, removedItems)
47
- local removedTrinkets = {}
48
- for ____, trinketToRemove in ipairs(TRINKETS_THAT_AFFECT_ITEM_POOLS) do
49
- if player:HasTrinket(trinketToRemove) then
50
- local numTrinkets = player:GetTrinketMultiplier(trinketToRemove)
51
- ____repeat(
52
- nil,
53
- numTrinkets,
54
- function()
55
- player:TryRemoveTrinket(trinketToRemove)
56
- removedTrinkets[#removedTrinkets + 1] = trinketToRemove
57
- end
58
- )
59
- end
60
- end
61
- mapSetPlayer(nil, removedTrinketsMap, player, removedTrinkets)
62
- end
63
- return {removedItemsMap, removedTrinketsMap}
64
- end
65
- function restoreItemsAndTrinketsThatAffectItemPools(self, removedItemsMap, removedTrinketsMap)
66
- for ____, player in ipairs(getPlayers(nil)) do
67
- local removedItems = mapGetPlayer(nil, removedItemsMap, player)
68
- if removedItems ~= nil then
69
- for ____, collectibleType in ipairs(removedItems) do
70
- player:AddCollectible(collectibleType, 0, false)
71
- end
72
- end
73
- local removedTrinkets = mapGetPlayer(nil, removedTrinketsMap, player)
74
- if removedTrinkets ~= nil then
75
- for ____, trinketType in ipairs(removedTrinkets) do
76
- player:AddTrinket(trinketType, false)
77
- end
78
- end
79
- end
80
- end
81
- COLLECTIBLES_THAT_AFFECT_ITEM_POOLS = {CollectibleType.CHAOS, CollectibleType.SACRED_ORB, CollectibleType.TMTRAINER}
82
- TRINKETS_THAT_AFFECT_ITEM_POOLS = {TrinketType.NO}
83
6
  --- Helper function to see if the given collectible is unlocked on the current save file. This
84
- -- requires providing the corresponding item pool that the collectible is located in.
7
+ -- requires providing the corresponding item pool that the collectible is normally located in.
85
8
  --
86
9
  -- - If any player currently has the collectible, then it is assumed to be unlocked. (This is
87
10
  -- because in almost all cases, when a collectible is added to a player's inventory, it is
@@ -92,42 +15,10 @@ TRINKETS_THAT_AFFECT_ITEM_POOLS = {TrinketType.NO}
92
15
  -- - If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
93
16
  -- then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
94
17
  -- collectibles from item pools).
95
- --
96
- -- Under the hood, this function works by using the `ItemPool.AddRoomBlacklist` method to blacklist
97
- -- every collectible except for the one provided. Unfortunately, this is not a general-purpose
98
- -- "isCollectibleInItemPool" algorithm, because when a pool is depleted, it will automatically pull
99
- -- items from the Treasure Room pool, and there is no way to distinguish when this happens.
100
18
  function ____exports.isCollectibleUnlocked(self, collectibleType, itemPoolType)
101
19
  if anyPlayerHasCollectible(nil, collectibleType) then
102
20
  return true
103
21
  end
104
- local taintedLosts = getPlayersOfType(nil, PlayerType.THE_LOST_B)
105
- local isOffensive = collectibleHasTag(nil, collectibleType, ItemConfigTag.OFFENSIVE)
106
- local changedPlayerTypes = false
107
- if not isOffensive then
108
- changedPlayerTypes = true
109
- for ____, player in ipairs(taintedLosts) do
110
- player:ChangePlayerType(PlayerType.ISAAC)
111
- end
112
- end
113
- local removedItemsMap, removedTrinketsMap = table.unpack(removeItemsAndTrinketsThatAffectItemPools(nil))
114
- local itemPool = game:GetItemPool()
115
- local collectibleSet = getCollectibleSet(nil)
116
- for ____, collectibleTypeInSet in __TS__Iterator(collectibleSet:values()) do
117
- if collectibleTypeInSet ~= collectibleType then
118
- itemPool:AddRoomBlacklist(collectibleTypeInSet)
119
- end
120
- end
121
- local seed = 1
122
- local retrievedCollectibleType = itemPool:GetCollectible(itemPoolType, false, seed)
123
- local collectibleUnlocked = retrievedCollectibleType == collectibleType
124
- itemPool:ResetRoomBlacklist()
125
- restoreItemsAndTrinketsThatAffectItemPools(nil, removedItemsMap, removedTrinketsMap)
126
- if changedPlayerTypes then
127
- for ____, player in ipairs(taintedLosts) do
128
- player:ChangePlayerType(PlayerType.THE_LOST_B)
129
- end
130
- end
131
- return collectibleUnlocked
22
+ return isCollectibleInItemPool(nil, collectibleType, itemPoolType)
132
23
  end
133
24
  return ____exports
package/dist/index.d.ts CHANGED
@@ -74,6 +74,7 @@ export * from "./functions/gridEntities";
74
74
  export * from "./functions/gridEntitiesSpecific";
75
75
  export * from "./functions/input";
76
76
  export * from "./functions/isaacAPIClass";
77
+ export * from "./functions/itemPool";
77
78
  export * from "./functions/jsonHelpers";
78
79
  export * from "./functions/jsonRoom";
79
80
  export * from "./functions/kColor";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.lua CHANGED
@@ -590,6 +590,14 @@ do
590
590
  end
591
591
  end
592
592
  end
593
+ do
594
+ local ____export = require("functions.itemPool")
595
+ for ____exportKey, ____exportValue in pairs(____export) do
596
+ if ____exportKey ~= "default" then
597
+ ____exports[____exportKey] = ____exportValue
598
+ end
599
+ end
600
+ end
593
601
  do
594
602
  local ____export = require("functions.jsonHelpers")
595
603
  for ____exportKey, ____exportValue in pairs(____export) do
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isaacscript-common",
3
- "version": "6.18.0",
3
+ "version": "6.19.0",
4
4
  "description": "Helper functions and features for IsaacScript mods.",
5
5
  "keywords": [
6
6
  "isaac",
@@ -5,7 +5,7 @@ import { ModCallbackCustom } from "../enums/ModCallbackCustom";
5
5
  import { errorIfFeaturesNotInitialized } from "../featuresInitialized";
6
6
  import { arrayRemoveInPlace, copyArray } from "../functions/array";
7
7
  import { isActiveCollectible } from "../functions/collectibles";
8
- import { getCollectibleSet } from "../functions/collectibleSet";
8
+ import { getCollectibleArray } from "../functions/collectibleSet";
9
9
  import {
10
10
  defaultMapGetPlayer,
11
11
  mapSetPlayer,
@@ -30,8 +30,7 @@ const v = {
30
30
  function newPlayerInventory(player: EntityPlayer) {
31
31
  const inventory: CollectibleType[] = [];
32
32
 
33
- const collectibleSet = getCollectibleSet();
34
- for (const collectibleType of collectibleSet.values()) {
33
+ for (const collectibleType of getCollectibleArray()) {
35
34
  const numCollectibles = player.GetCollectibleNum(collectibleType, true);
36
35
  repeat(numCollectibles, () => {
37
36
  inventory.push(collectibleType);
@@ -1,6 +1,6 @@
1
1
  import { CollectibleType, ItemConfigTag } from "isaac-typescript-definitions";
2
2
  import { isHiddenCollectible, isPassiveCollectible } from "./collectibles";
3
- import { getCollectibleSet } from "./collectibleSet";
3
+ import { getCollectibleArray } from "./collectibleSet";
4
4
  import { collectibleHasTag } from "./collectibleTag";
5
5
  import { getRandomSeed } from "./rng";
6
6
  import { copySet, getRandomSetElement } from "./set";
@@ -8,9 +8,7 @@ import { copySet, getRandomSetElement } from "./set";
8
8
  const EDEN_PASSIVE_COLLECTIBLES_SET = new Set<CollectibleType>();
9
9
 
10
10
  function initCollectibleSet() {
11
- const collectibleSet = getCollectibleSet();
12
-
13
- for (const collectibleType of collectibleSet.values()) {
11
+ for (const collectibleType of getCollectibleArray()) {
14
12
  if (
15
13
  isPassiveCollectible(collectibleType) &&
16
14
  !isHiddenCollectible(collectibleType) &&
@@ -0,0 +1,178 @@
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 { getCollectibleArray } from "./collectibleSet";
11
+ import { collectibleHasTag } from "./collectibleTag";
12
+ import { mapGetPlayer, mapSetPlayer } from "./playerDataStructures";
13
+ import { getPlayers } from "./playerIndex";
14
+ import { 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
+ ];
26
+
27
+ const COLLECTIBLE_TYPE_THAT_IS_NOT_IN_ANY_POOLS = CollectibleType.KEY_PIECE_1;
28
+
29
+ /**
30
+ * Helper function to get the remaining collectibles in a given item pool. This function is
31
+ * expensive, so only use it in situations where the lag is acceptable.
32
+ */
33
+ export function getCollectiblesInItemPool(
34
+ itemPoolType: ItemPoolType,
35
+ ): CollectibleType[] {
36
+ const collectibleArray = getCollectibleArray();
37
+ return collectibleArray.filter((collectibleType) =>
38
+ isCollectibleInItemPool(collectibleType, itemPoolType),
39
+ );
40
+ }
41
+
42
+ /**
43
+ * Helper function to see if the given collectible is still present in the given item pool.
44
+ *
45
+ * If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
46
+ * then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
47
+ * collectibles from item pools).
48
+ *
49
+ * Under the hood, this function works by using the `ItemPool.AddRoomBlacklist` method to blacklist
50
+ * every collectible except for the one provided.
51
+ */
52
+ export function isCollectibleInItemPool(
53
+ collectibleType: CollectibleType,
54
+ itemPoolType: ItemPoolType,
55
+ ): boolean {
56
+ // We use a specific collectible which is known to not be in any pools as a default value. Thus,
57
+ // we must explicitly handle this case.
58
+ if (collectibleType === COLLECTIBLE_TYPE_THAT_IS_NOT_IN_ANY_POOLS) {
59
+ return false;
60
+ }
61
+
62
+ // On Tainted Lost, it is impossible to retrieve non-offensive collectibles from pools, so we
63
+ // temporarily change the character to Isaac.
64
+ const taintedLosts = getPlayersOfType(PlayerType.THE_LOST_B);
65
+ const isOffensive = collectibleHasTag(
66
+ collectibleType,
67
+ ItemConfigTag.OFFENSIVE,
68
+ );
69
+ let changedPlayerTypes = false;
70
+ if (!isOffensive) {
71
+ changedPlayerTypes = true;
72
+ for (const player of taintedLosts) {
73
+ player.ChangePlayerType(PlayerType.ISAAC);
74
+ }
75
+ }
76
+
77
+ const [removedItemsMap, removedTrinketsMap] =
78
+ removeItemsAndTrinketsThatAffectItemPools();
79
+
80
+ // Blacklist every collectible in the game except for the provided collectible.
81
+ const itemPool = game.GetItemPool();
82
+ itemPool.ResetRoomBlacklist();
83
+ for (const collectibleTypeInSet of getCollectibleArray()) {
84
+ if (collectibleTypeInSet !== collectibleType) {
85
+ itemPool.AddRoomBlacklist(collectibleTypeInSet);
86
+ }
87
+ }
88
+
89
+ // Get a collectible from the pool and see if it is the intended collectible. (We can use any
90
+ // arbitrary value as the seed since it should not influence the result.)
91
+ const seed = 1 as Seed;
92
+ const retrievedCollectibleType = itemPool.GetCollectible(
93
+ itemPoolType,
94
+ false,
95
+ seed,
96
+ COLLECTIBLE_TYPE_THAT_IS_NOT_IN_ANY_POOLS,
97
+ );
98
+ const collectibleUnlocked = retrievedCollectibleType === collectibleType;
99
+
100
+ // Reset the blacklist
101
+ itemPool.ResetRoomBlacklist();
102
+
103
+ restoreItemsAndTrinketsThatAffectItemPools(
104
+ removedItemsMap,
105
+ removedTrinketsMap,
106
+ );
107
+
108
+ // Change any players back to Tainted Lost, if necessary.
109
+ if (changedPlayerTypes) {
110
+ for (const player of taintedLosts) {
111
+ player.ChangePlayerType(PlayerType.THE_LOST_B);
112
+ }
113
+ }
114
+
115
+ return collectibleUnlocked;
116
+ }
117
+
118
+ /**
119
+ * Before checking the item pools, remove any collectibles or trinkets that would affect the
120
+ * retrieved collectible types.
121
+ */
122
+ function removeItemsAndTrinketsThatAffectItemPools(): [
123
+ removedItemsMap: Map<PlayerIndex, CollectibleType[]>,
124
+ removedTrinketsMap: Map<PlayerIndex, TrinketType[]>,
125
+ ] {
126
+ const removedItemsMap = new Map<PlayerIndex, CollectibleType[]>();
127
+ const removedTrinketsMap = new Map<PlayerIndex, TrinketType[]>();
128
+ for (const player of getPlayers()) {
129
+ const removedItems: CollectibleType[] = [];
130
+ for (const itemToRemove of COLLECTIBLES_THAT_AFFECT_ITEM_POOLS) {
131
+ if (player.HasCollectible(itemToRemove)) {
132
+ const numCollectibles = player.GetCollectibleNum(itemToRemove);
133
+ repeat(numCollectibles, () => {
134
+ player.RemoveCollectible(itemToRemove);
135
+ removedItems.push(itemToRemove);
136
+ });
137
+ }
138
+ }
139
+
140
+ mapSetPlayer(removedItemsMap, player, removedItems);
141
+
142
+ const removedTrinkets: TrinketType[] = [];
143
+ for (const trinketToRemove of TRINKETS_THAT_AFFECT_ITEM_POOLS) {
144
+ if (player.HasTrinket(trinketToRemove)) {
145
+ const numTrinkets = player.GetTrinketMultiplier(trinketToRemove);
146
+ repeat(numTrinkets, () => {
147
+ player.TryRemoveTrinket(trinketToRemove);
148
+ removedTrinkets.push(trinketToRemove);
149
+ });
150
+ }
151
+ }
152
+
153
+ mapSetPlayer(removedTrinketsMap, player, removedTrinkets);
154
+ }
155
+
156
+ return [removedItemsMap, removedTrinketsMap];
157
+ }
158
+
159
+ function restoreItemsAndTrinketsThatAffectItemPools(
160
+ removedItemsMap: Map<PlayerIndex, CollectibleType[]>,
161
+ removedTrinketsMap: Map<PlayerIndex, TrinketType[]>,
162
+ ) {
163
+ for (const player of getPlayers()) {
164
+ const removedItems = mapGetPlayer(removedItemsMap, player);
165
+ if (removedItems !== undefined) {
166
+ for (const collectibleType of removedItems) {
167
+ player.AddCollectible(collectibleType, 0, false); // Prevent Chaos from spawning pickups
168
+ }
169
+ }
170
+
171
+ const removedTrinkets = mapGetPlayer(removedTrinketsMap, player);
172
+ if (removedTrinkets !== undefined) {
173
+ for (const trinketType of removedTrinkets) {
174
+ player.AddTrinket(trinketType, false);
175
+ }
176
+ }
177
+ }
178
+ }
@@ -5,6 +5,7 @@ import {
5
5
  EffectVariant,
6
6
  EntityType,
7
7
  HeartSubType,
8
+ ItemPoolType,
8
9
  PillColor,
9
10
  RoomType,
10
11
  TrinketType,
@@ -14,6 +15,7 @@ import { DISTANCE_OF_GRID_TILE } from "../constants";
14
15
  import { RockAltType } from "../enums/RockAltType";
15
16
  import { BACKDROP_TYPE_TO_ROCK_ALT_TYPE } from "../objects/backdropTypeToRockAltType";
16
17
  import { spawnEffectWithSeed, spawnNPCWithSeed } from "./entitiesSpecific";
18
+ import { isCollectibleInItemPool } from "./itemPool";
17
19
  import {
18
20
  spawnCardWithSeed,
19
21
  spawnCoinWithSeed,
@@ -69,10 +71,10 @@ export function getRockAltType(): RockAltType {
69
71
  * Most of the time, this function will do nothing, similar to how most of the time, when an
70
72
  * individual urn is destroyed, nothing will spawn.
71
73
  *
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).
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).
76
78
  *
77
79
  * The logic in this function is based on the rewards listed on the wiki:
78
80
  * https://bindingofisaacrebirth.fandom.com/wiki/Rocks
@@ -150,8 +152,16 @@ function spawnRockAltRewardUrn(position: Vector, rng: RNG): boolean {
150
152
 
151
153
  totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
152
154
  if (chance < totalChance) {
153
- spawnCollectible(CollectibleType.QUARTER, position, rng);
154
- return true;
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;
155
165
  }
156
166
 
157
167
  // Since the detrimental effect is the final option, we don't need to check the chance.
@@ -196,18 +206,46 @@ function spawnRockAltRewardMushroom(position: Vector, rng: RNG): boolean {
196
206
  if (roomType === RoomType.SECRET) {
197
207
  const wavyCapChance = getRandom(rng);
198
208
  if (wavyCapChance < 0.0272) {
199
- spawnCollectible(CollectibleType.WAVY_CAP, position, rng);
200
- return true;
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
+ }
201
217
  }
202
218
  }
203
219
 
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;
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;
211
249
  }
212
250
 
213
251
  // Since the detrimental effect is the final option, we don't need to check the chance.
@@ -238,13 +276,35 @@ function spawnRockAltRewardSkull(position: Vector, rng: RNG): boolean {
238
276
 
239
277
  totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
240
278
  if (chance < totalChance) {
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;
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;
248
308
  }
249
309
 
250
310
  // Since the detrimental effect is the final option, we don't need to check the chance.
@@ -275,13 +335,35 @@ function spawnRockAltRewardPolyp(position: Vector, rng: RNG): boolean {
275
335
 
276
336
  totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
277
337
  if (chance < totalChance) {
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;
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;
285
367
  }
286
368
 
287
369
  // Since the detrimental effect is the final option, we don't need to check the chance.
@@ -1,32 +1,10 @@
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
- ];
1
+ import { CollectibleType, ItemPoolType } from "isaac-typescript-definitions";
2
+ import { isCollectibleInItemPool } from "./itemPool";
3
+ import { anyPlayerHasCollectible } from "./players";
26
4
 
27
5
  /**
28
6
  * Helper function to see if the given collectible is unlocked on the current save file. This
29
- * requires providing the corresponding item pool that the collectible is located in.
7
+ * requires providing the corresponding item pool that the collectible is normally located in.
30
8
  *
31
9
  * - If any player currently has the collectible, then it is assumed to be unlocked. (This is
32
10
  * because in almost all cases, when a collectible is added to a player's inventory, it is
@@ -37,11 +15,6 @@ const TRINKETS_THAT_AFFECT_ITEM_POOLS: readonly TrinketType[] = [
37
15
  * - If the collectible is non-offensive, any Tainted Losts will be temporarily changed to Isaac and
38
16
  * then changed back. (This is because Tainted Lost is not able to retrieve non-offensive
39
17
  * 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.
45
18
  */
46
19
  export function isCollectibleUnlocked(
47
20
  collectibleType: CollectibleType,
@@ -51,120 +24,5 @@ export function isCollectibleUnlocked(
51
24
  return true;
52
25
  }
53
26
 
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
- }
27
+ return isCollectibleInItemPool(collectibleType, itemPoolType);
170
28
  }
package/src/index.ts CHANGED
@@ -117,6 +117,7 @@ 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";
120
121
  export * from "./functions/jsonHelpers";
121
122
  export * from "./functions/jsonRoom";
122
123
  export * from "./functions/kColor";