isaacscript-common 6.16.2 → 6.17.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":"persistentEntities.d.ts","sourceRoot":"","sources":["../../src/features/persistentEntities.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EAEX,MAAM,8BAA8B,CAAC;AAkGtC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,EACZ,QAAQ,EAAE,MAAM,GACf,CAAC,MAAM,EAAE,GAAG,CAAC,CAcf;AAwBD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,qBAAqB,EAAE,GAAG,EAC1B,YAAY,UAAO,GAClB,IAAI,CAiBN"}
1
+ {"version":3,"file":"persistentEntities.d.ts","sourceRoot":"","sources":["../../src/features/persistentEntities.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EAEX,MAAM,8BAA8B,CAAC;AAgHtC;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,UAAU,EACtB,OAAO,EAAE,GAAG,EACZ,OAAO,EAAE,GAAG,EACZ,QAAQ,EAAE,MAAM,GACf,CAAC,MAAM,EAAE,GAAG,CAAC,CAcf;AAwBD;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,qBAAqB,EAAE,GAAG,EAC1B,YAAY,UAAO,GAClB,IAAI,CAiBN"}
@@ -3,7 +3,7 @@ local Map = ____lualib.Map
3
3
  local __TS__New = ____lualib.__TS__New
4
4
  local __TS__Iterator = ____lualib.__TS__Iterator
5
5
  local ____exports = {}
6
- local postEntityRemove, postNewRoomReordered, spawnAndTrack, v
6
+ local postEntityRemove, checkDespawningFromPlayerLeavingRoom, trackDespawningPickupPosition, postNewRoomReordered, spawnAndTrack, v
7
7
  local ____isaac_2Dtypescript_2Ddefinitions = require("isaac-typescript-definitions")
8
8
  local EntityFlag = ____isaac_2Dtypescript_2Ddefinitions.EntityFlag
9
9
  local ModCallback = ____isaac_2Dtypescript_2Ddefinitions.ModCallback
@@ -17,15 +17,25 @@ local ____roomData = require("functions.roomData")
17
17
  local getRoomListIndex = ____roomData.getRoomListIndex
18
18
  local ____roomHistory = require("features.roomHistory")
19
19
  local getLatestRoomDescription = ____roomHistory.getLatestRoomDescription
20
+ local isLeavingRoom = ____roomHistory.isLeavingRoom
20
21
  local ____exports = require("features.saveDataManager.exports")
21
22
  local saveDataManager = ____exports.saveDataManager
22
23
  function postEntityRemove(self, entity)
24
+ checkDespawningFromPlayerLeavingRoom(nil, entity)
25
+ end
26
+ function checkDespawningFromPlayerLeavingRoom(self, entity)
23
27
  local ptrHash = GetPtrHash(entity)
24
28
  local tuple = v.room.spawnedPersistentEntities:get(ptrHash)
25
29
  if tuple == nil then
26
30
  return
27
31
  end
28
32
  local index = tuple[1]
33
+ if not isLeavingRoom(nil) then
34
+ return
35
+ end
36
+ trackDespawningPickupPosition(nil, entity, index)
37
+ end
38
+ function trackDespawningPickupPosition(self, entity, index)
29
39
  local previousRoomDescription = getLatestRoomDescription(nil)
30
40
  local previousRoomListIndex = previousRoomDescription.roomListIndex
31
41
  local persistentEntityDescription = {
@@ -44,7 +54,7 @@ function postNewRoomReordered(self)
44
54
  local description = ____value[2]
45
55
  do
46
56
  if roomListIndex ~= description.roomListIndex then
47
- goto __continue6
57
+ goto __continue9
48
58
  end
49
59
  v.level.persistentEntities:delete(index)
50
60
  spawnAndTrack(
@@ -57,7 +67,7 @@ function postNewRoomReordered(self)
57
67
  true
58
68
  )
59
69
  end
60
- ::__continue6::
70
+ ::__continue9::
61
71
  end
62
72
  end
63
73
  function spawnAndTrack(self, entityType, variant, subType, position, index, respawning)
@@ -142,14 +152,14 @@ function ____exports.removePersistentEntity(self, persistentEntityIndex, removeE
142
152
  do
143
153
  local index, entityPtr = table.unpack(tuple)
144
154
  if index ~= persistentEntityIndex then
145
- goto __continue13
155
+ goto __continue16
146
156
  end
147
157
  v.room.spawnedPersistentEntities:delete(ptrHash)
148
158
  if removeEntity and entityPtr.Ref ~= nil then
149
159
  entityPtr.Ref:Remove()
150
160
  end
151
161
  end
152
- ::__continue13::
162
+ ::__continue16::
153
163
  end
154
164
  end
155
165
  return ____exports
@@ -1 +1 @@
1
- {"version":3,"file":"pickupIndex.d.ts","sourceRoot":"","sources":["../../src/features/pickupIndex.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAQrD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAgCnD,wBAAgB,eAAe,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAatD;AA0KD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAahE"}
1
+ {"version":3,"file":"pickupIndex.d.ts","sourceRoot":"","sources":["../../src/features/pickupIndex.ts"],"names":[],"mappings":";AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAQrD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AA+BnD,wBAAgB,eAAe,CAAC,GAAG,EAAE,WAAW,GAAG,IAAI,CAatD;AAqKD;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,GAAG,WAAW,CAahE"}
@@ -28,6 +28,7 @@ local ____vector = require("functions.vector")
28
28
  local vectorEquals = ____vector.vectorEquals
29
29
  local ____roomHistory = require("features.roomHistory")
30
30
  local getLatestRoomDescription = ____roomHistory.getLatestRoomDescription
31
+ local isLeavingRoom = ____roomHistory.isLeavingRoom
31
32
  local ____exports = require("features.saveDataManager.exports")
32
33
  local saveDataManager = ____exports.saveDataManager
33
34
  function postPickupInit(self, pickup)
@@ -44,7 +45,6 @@ function postPickupInit(self, pickup)
44
45
  local ____v_run_0, ____pickupCounter_1 = v.run, "pickupCounter"
45
46
  ____v_run_0[____pickupCounter_1] = ____v_run_0[____pickupCounter_1] + 1
46
47
  v.room.pickupIndexes:set(ptrHash, v.run.pickupCounter)
47
- v.run.currentRoomListIndex = getRoomListIndex(nil)
48
48
  end
49
49
  function postEntityRemovePickup(self, entity)
50
50
  checkDespawningFromPlayerLeavingRoom(nil, entity)
@@ -55,8 +55,7 @@ function checkDespawningFromPlayerLeavingRoom(self, entity)
55
55
  if pickupIndex == nil then
56
56
  return
57
57
  end
58
- local roomListIndex = getRoomListIndex(nil)
59
- if roomListIndex == v.run.currentRoomListIndex then
58
+ if not isLeavingRoom(nil) then
60
59
  return
61
60
  end
62
61
  trackDespawningPickupMetadata(nil, entity, pickupIndex)
@@ -157,7 +156,6 @@ local FEATURE_NAME = "pickupIndex"
157
156
  v = {
158
157
  run = {
159
158
  pickupCounter = 0,
160
- currentRoomListIndex = 0,
161
159
  pickupDataTreasureRooms = __TS__New(Map),
162
160
  pickupDataBossRooms = __TS__New(Map)
163
161
  },
@@ -15,10 +15,17 @@ export declare function getPreviousRoomDescription(): RoomDescription;
15
15
  * Helper function to get information about the most recent room that is stored in the room history
16
16
  * array.
17
17
  *
18
- * This is useful in the `POST_ENTITY_REMOVE` callback, since if an entity is despawning due to a
19
- * player having left the room, the current room will have changed already, but the `POST_NEW_ROOM`
20
- * callback will not have fired yet, and there will not be an entry in the room history array for
21
- * the current room.
18
+ * This is useful in the `POST_ENTITY_REMOVE` callback; see the `isLeavingRoom` function.
22
19
  */
23
20
  export declare function getLatestRoomDescription(): RoomDescription;
21
+ /**
22
+ * Helper function to detect if the game is in the state where the room index has changed to a new
23
+ * room, but the entities from the previous room are currently in the process of despawning. (At
24
+ * this point, the `POST_NEW_ROOM` callback will not have fired yet, and there will not be an entry
25
+ * in the room history array for the current room.)
26
+ *
27
+ * This function is intended to be used in the `POST_ENTITY_REMOVE` callback to detect when an
28
+ * entity is pseudo-persistent entity such as a pickup is despawning.
29
+ */
30
+ export declare function isLeavingRoom(): boolean;
24
31
  //# sourceMappingURL=roomHistory.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"roomHistory.d.ts","sourceRoot":"","sources":["../../src/features/roomHistory.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAiDhE;;;GAGG;AACH,wBAAgB,cAAc,IAAI,SAAS,eAAe,EAAE,CAG3D;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,eAAe,CAe5D;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,IAAI,eAAe,CAS1D"}
1
+ {"version":3,"file":"roomHistory.d.ts","sourceRoot":"","sources":["../../src/features/roomHistory.ts"],"names":[],"mappings":"AAcA,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAmDhE;;;GAGG;AACH,wBAAgB,cAAc,IAAI,SAAS,eAAe,EAAE,CAG3D;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,IAAI,eAAe,CAe5D;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,IAAI,eAAe,CAS1D;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,IAAI,OAAO,CASvC"}
@@ -15,6 +15,7 @@ local getRoomName = ____roomData.getRoomName
15
15
  local getRoomStageID = ____roomData.getRoomStageID
16
16
  local getRoomSubType = ____roomData.getRoomSubType
17
17
  local getRoomVariant = ____roomData.getRoomVariant
18
+ local getRoomVisitedCount = ____roomData.getRoomVisitedCount
18
19
  local ____exports = require("features.saveDataManager.exports")
19
20
  local saveDataManager = ____exports.saveDataManager
20
21
  function postNewRoomEarly(self)
@@ -29,6 +30,7 @@ function postNewRoomEarly(self)
29
30
  local roomName = getRoomName(nil)
30
31
  local roomGridIndex = getRoomGridIndex(nil)
31
32
  local roomListIndex = getRoomListIndex(nil)
33
+ local roomVisitedCount = getRoomVisitedCount(nil)
32
34
  local roomDescription = {
33
35
  stage = stage,
34
36
  stageType = stageType,
@@ -38,7 +40,8 @@ function postNewRoomEarly(self)
38
40
  roomSubType = roomSubType,
39
41
  roomName = roomName,
40
42
  roomGridIndex = roomGridIndex,
41
- roomListIndex = roomListIndex
43
+ roomListIndex = roomListIndex,
44
+ roomVisitedCount = roomVisitedCount
42
45
  }
43
46
  local ____v_run_roomHistory_0 = v.run.roomHistory
44
47
  ____v_run_roomHistory_0[#____v_run_roomHistory_0 + 1] = roomDescription
@@ -75,10 +78,7 @@ end
75
78
  --- Helper function to get information about the most recent room that is stored in the room history
76
79
  -- array.
77
80
  --
78
- -- This is useful in the `POST_ENTITY_REMOVE` callback, since if an entity is despawning due to a
79
- -- player having left the room, the current room will have changed already, but the `POST_NEW_ROOM`
80
- -- callback will not have fired yet, and there will not be an entry in the room history array for
81
- -- the current room.
81
+ -- This is useful in the `POST_ENTITY_REMOVE` callback; see the `isLeavingRoom` function.
82
82
  function ____exports.getLatestRoomDescription(self)
83
83
  local latestRoomDescription = getLastElement(nil, v.run.roomHistory)
84
84
  if latestRoomDescription == nil then
@@ -86,4 +86,17 @@ function ____exports.getLatestRoomDescription(self)
86
86
  end
87
87
  return latestRoomDescription
88
88
  end
89
+ --- Helper function to detect if the game is in the state where the room index has changed to a new
90
+ -- room, but the entities from the previous room are currently in the process of despawning. (At
91
+ -- this point, the `POST_NEW_ROOM` callback will not have fired yet, and there will not be an entry
92
+ -- in the room history array for the current room.)
93
+ --
94
+ -- This function is intended to be used in the `POST_ENTITY_REMOVE` callback to detect when an
95
+ -- entity is pseudo-persistent entity such as a pickup is despawning.
96
+ function ____exports.isLeavingRoom(self)
97
+ local roomListIndex = getRoomListIndex(nil)
98
+ local roomVisitedCount = getRoomVisitedCount(nil)
99
+ local latestRoomDescription = ____exports.getLatestRoomDescription(nil)
100
+ return roomListIndex ~= latestRoomDescription.roomListIndex or roomVisitedCount ~= latestRoomDescription.roomVisitedCount
101
+ end
89
102
  return ____exports
@@ -1 +1 @@
1
- {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/functions/log.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../../src/functions/log.ts"],"names":[],"mappings":"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"}
@@ -16,6 +16,7 @@ local EntityFlag = ____isaac_2Dtypescript_2Ddefinitions.EntityFlag
16
16
  local GameStateFlag = ____isaac_2Dtypescript_2Ddefinitions.GameStateFlag
17
17
  local GridEntityType = ____isaac_2Dtypescript_2Ddefinitions.GridEntityType
18
18
  local GridRoom = ____isaac_2Dtypescript_2Ddefinitions.GridRoom
19
+ local HeartSubType = ____isaac_2Dtypescript_2Ddefinitions.HeartSubType
19
20
  local LevelStateFlag = ____isaac_2Dtypescript_2Ddefinitions.LevelStateFlag
20
21
  local ProjectileFlag = ____isaac_2Dtypescript_2Ddefinitions.ProjectileFlag
21
22
  local SeedEffect = ____isaac_2Dtypescript_2Ddefinitions.SeedEffect
@@ -430,7 +431,11 @@ function ____exports.logPlayerHealth(player)
430
431
  ____exports.log(" Broken hearts: " .. tostring(playerHealth.brokenHearts))
431
432
  ____exports.log(" Soul charges: " .. tostring(playerHealth.soulCharges))
432
433
  ____exports.log(" Blood charges: " .. tostring(playerHealth.bloodCharges))
433
- ____exports.log((" Soul heart types: [" .. table.concat(playerHealth.soulHeartTypes, ",")) .. "]")
434
+ ____exports.log(" Soul heart types: [")
435
+ for ____, soulHeartType in ipairs(playerHealth.soulHeartTypes) do
436
+ ____exports.log(" HeartSubType." .. tostring(HeartSubType[soulHeartType]))
437
+ end
438
+ ____exports.log(" ]")
434
439
  end
435
440
  --- Helper function for printing out every projectile flag that is turned on. Useful when debugging.
436
441
  function ____exports.logProjectileFlags(flags)
@@ -19,6 +19,15 @@ export declare function getAliveNPCs(entityType?: EntityType, variant?: number,
19
19
  * not actually keep the doors closed (like Death's scythes).
20
20
  */
21
21
  export declare function isAliveExceptionNPC(npc: EntityNPC): boolean;
22
+ /**
23
+ * Helper function to distinguish between a normal Daddy Long Legs / Triachnid and the child entity
24
+ * that is spawned when the boss does the multi-stomp attack.
25
+ *
26
+ * When this attack occurs, four extra copies of Daddy Long Legs will be spawned with the same
27
+ * entity type, variant, and sub-type. The `Entity.Parent` property will be undefined in this case,
28
+ * so the way to tell them apart is to check for a non-undefined `Entity.SpawnerEntity` property.
29
+ */
30
+ export declare function isDaddyLongLegsChildStompEntity(npc: EntityNPC): boolean;
22
31
  /**
23
32
  * Helper function to detect the custom death state of an Eggy. Eggies are never actually marked
24
33
  * dead by the game. Instead, when Eggies take fatal damage, they go into NpcState.STATE_SUICIDE and
@@ -1 +1 @@
1
- {"version":3,"file":"npcs.d.ts","sourceRoot":"","sources":["../../src/functions/npcs.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,UAAU,EAQX,MAAM,8BAA8B,CAAC;AA+BtC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,UAAU,GAAE,UAAe,EAC3B,OAAO,SAAK,EACZ,OAAO,SAAK,EACZ,cAAc,UAAQ,GACrB,SAAS,EAAE,CAGb;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAoB3D;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAKpE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAO3D"}
1
+ {"version":3,"file":"npcs.d.ts","sourceRoot":"","sources":["../../src/functions/npcs.ts"],"names":[],"mappings":"AAAA,OAAO,EAOL,UAAU,EASX,MAAM,8BAA8B,CAAC;AA+BtC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,YAAY,CAC1B,UAAU,GAAE,UAAe,EAC3B,OAAO,SAAK,EACZ,OAAO,SAAK,EACZ,cAAc,UAAQ,GACrB,SAAS,EAAE,CAGb;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CA4B3D;AAED;;;;;;;GAOG;AACH,wBAAgB,+BAA+B,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAIvE;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAOpE;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAO3D"}
@@ -12,6 +12,7 @@ local ChargerVariant = ____isaac_2Dtypescript_2Ddefinitions.ChargerVariant
12
12
  local DarkEsauVariant = ____isaac_2Dtypescript_2Ddefinitions.DarkEsauVariant
13
13
  local DeathVariant = ____isaac_2Dtypescript_2Ddefinitions.DeathVariant
14
14
  local EntityType = ____isaac_2Dtypescript_2Ddefinitions.EntityType
15
+ local HopperVariant = ____isaac_2Dtypescript_2Ddefinitions.HopperVariant
15
16
  local MamaGurdyVariant = ____isaac_2Dtypescript_2Ddefinitions.MamaGurdyVariant
16
17
  local MotherSubType = ____isaac_2Dtypescript_2Ddefinitions.MotherSubType
17
18
  local MotherVariant = ____isaac_2Dtypescript_2Ddefinitions.MotherVariant
@@ -34,19 +35,31 @@ function ____exports.isAliveExceptionNPC(self, npc)
34
35
  if NON_ALIVE_NPCS_TYPE_VARIANT_SUBTYPE:has(entityTypeVariantSubType) then
35
36
  return true
36
37
  end
37
- if ____exports.isRaglingDeathPatch(nil, npc) then
38
+ if ____exports.isDyingEggyWithNoSpidersLeft(nil, npc) then
38
39
  return true
39
40
  end
40
- if ____exports.isDyingEggyWithNoSpidersLeft(nil, npc) then
41
+ if ____exports.isDaddyLongLegsChildStompEntity(nil, npc) then
42
+ return true
43
+ end
44
+ if ____exports.isRaglingDeathPatch(nil, npc) then
41
45
  return true
42
46
  end
43
47
  return false
44
48
  end
49
+ --- Helper function to distinguish between a normal Daddy Long Legs / Triachnid and the child entity
50
+ -- that is spawned when the boss does the multi-stomp attack.
51
+ --
52
+ -- When this attack occurs, four extra copies of Daddy Long Legs will be spawned with the same
53
+ -- entity type, variant, and sub-type. The `Entity.Parent` property will be undefined in this case,
54
+ -- so the way to tell them apart is to check for a non-undefined `Entity.SpawnerEntity` property.
55
+ function ____exports.isDaddyLongLegsChildStompEntity(self, npc)
56
+ return npc.Type == EntityType.DADDY_LONG_LEGS and npc.SpawnerEntity ~= nil
57
+ end
45
58
  --- Helper function to detect the custom death state of an Eggy. Eggies are never actually marked
46
59
  -- dead by the game. Instead, when Eggies take fatal damage, they go into NpcState.STATE_SUICIDE and
47
60
  -- spawn 14 Swarm Spiders while their StateFrame ticks upwards.
48
61
  function ____exports.isDyingEggyWithNoSpidersLeft(self, npc)
49
- return npc.State == NpcState.SUICIDE and npc.StateFrame >= EGGY_STATE_FRAME_OF_FINAL_SPIDER
62
+ return npc.Type == EntityType.HOPPER and npc.Variant == HopperVariant.EGGY and npc.State == NpcState.SUICIDE and npc.StateFrame >= EGGY_STATE_FRAME_OF_FINAL_SPIDER
50
63
  end
51
64
  --- Helper function to detect the custom death state of a Rag Man Ragling. When Rag Man Raglings die,
52
65
  -- they turn into a patch on the ground and can be revived by Rag Man at a later time. This causes
@@ -1 +1 @@
1
- {"version":3,"file":"playerHealth.d.ts","sourceRoot":"","sources":["../../src/functions/playerHealth.ts"],"names":[],"mappings":";;AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAW1D,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,GAAG,GACb,IAAI,CA+CN;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CA2ElE;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,GACrB,GAAG,CAmDL;AAED,mDAAmD;AACnD,wBAAgB,eAAe,IAAI,YAAY,CAc9C;AAED,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,YAAY,GACnB,IAAI,CAQN;AAED,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,YAAY,GACnB,IAAI,CAQN;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAMjE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAwBhE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,YAAY,GACzB,IAAI,CA4FN"}
1
+ {"version":3,"file":"playerHealth.d.ts","sourceRoot":"","sources":["../../src/functions/playerHealth.ts"],"names":[],"mappings":";;AAOA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,YAAY,EAAiB,MAAM,4BAA4B,CAAC;AAWzE,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,EACtB,SAAS,EAAE,GAAG,GACb,IAAI,CA+CN;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CA2ElE;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,YAAY,EACpB,UAAU,EAAE,UAAU,GACrB,GAAG,CAmDL;AAED,mDAAmD;AACnD,wBAAgB,eAAe,IAAI,YAAY,CAc9C;AAED,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,YAAY,GACnB,IAAI,CAQN;AAED,wBAAgB,oCAAoC,CAClD,MAAM,EAAE,YAAY,GACnB,IAAI,CAQN;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAMjE;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI,CAwBhE;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,YAAY,EACpB,YAAY,EAAE,YAAY,GACzB,IAAI,CAyGN"}
@@ -69,21 +69,38 @@ function ____exports.setPlayerHealth(self, player, playerHealth)
69
69
  local soulHeartsRemaining = playerHealth.soulHearts
70
70
  __TS__ArrayForEach(
71
71
  playerHealth.soulHeartTypes,
72
- function(____, heartType, i)
72
+ function(____, soulHeartType, i)
73
73
  local isHalf = playerHealth.soulHearts + playerHealth.boneHearts * 2 < (i + 1) * 2
74
74
  local addAmount = 2
75
- if isHalf or heartType == HeartSubType.BONE or soulHeartsRemaining < 2 then
75
+ if isHalf or soulHeartType == HeartSubType.BONE or soulHeartsRemaining < 2 then
76
76
  addAmount = 1
77
77
  end
78
- if heartType == HeartSubType.SOUL then
79
- player:AddSoulHearts(addAmount)
80
- soulHeartsRemaining = soulHeartsRemaining - addAmount
81
- elseif heartType == HeartSubType.BLACK then
82
- player:AddBlackHearts(addAmount)
83
- soulHeartsRemaining = soulHeartsRemaining - addAmount
84
- elseif heartType == HeartSubType.BONE then
85
- player:AddBoneHearts(addAmount)
86
- end
78
+ repeat
79
+ local ____switch52 = soulHeartType
80
+ local ____cond52 = ____switch52 == HeartSubType.SOUL
81
+ if ____cond52 then
82
+ do
83
+ player:AddSoulHearts(addAmount)
84
+ soulHeartsRemaining = soulHeartsRemaining - addAmount
85
+ break
86
+ end
87
+ end
88
+ ____cond52 = ____cond52 or ____switch52 == HeartSubType.BLACK
89
+ if ____cond52 then
90
+ do
91
+ player:AddBlackHearts(addAmount)
92
+ soulHeartsRemaining = soulHeartsRemaining - addAmount
93
+ break
94
+ end
95
+ end
96
+ ____cond52 = ____cond52 or ____switch52 == HeartSubType.BONE
97
+ if ____cond52 then
98
+ do
99
+ player:AddBoneHearts(addAmount)
100
+ break
101
+ end
102
+ end
103
+ until true
87
104
  end
88
105
  )
89
106
  player:AddRottenHearts(playerHealth.rottenHearts)
@@ -187,7 +204,6 @@ end
187
204
  -- This is based on the `REVEL.StoreHealth` function in the Revelations mod.
188
205
  function ____exports.getPlayerHealth(self, player)
189
206
  local character = player:GetPlayerType()
190
- local soulHeartTypes = {}
191
207
  local maxHearts = player:GetMaxHearts()
192
208
  local hearts = getPlayerHearts(nil, player)
193
209
  local soulHearts = player:GetSoulHearts()
@@ -209,6 +225,7 @@ function ____exports.getPlayerHealth(self, player)
209
225
  end
210
226
  local extraHearts = math.ceil(soulHearts / 2) + boneHearts
211
227
  local currentSoulHeart = 0
228
+ local soulHeartTypes = {}
212
229
  do
213
230
  local i = 0
214
231
  while i < extraHearts do
@@ -47,11 +47,11 @@ local getRandomVector = ____vector.getRandomVector
47
47
  function spawnRockAltRewardUrn(self, position, rng)
48
48
  local chance = getRandom(nil, rng)
49
49
  local totalChance = 0
50
- totalChance = totalChance + ROCK_ALT_CHANCES.Nothing
50
+ totalChance = totalChance + ROCK_ALT_CHANCES.NOTHING
51
51
  if chance < totalChance then
52
52
  return false
53
53
  end
54
- totalChance = totalChance + ROCK_ALT_CHANCES.BasicDrop
54
+ totalChance = totalChance + ROCK_ALT_CHANCES.BASIC_DROP
55
55
  if chance < totalChance then
56
56
  local numCoinsChance = getRandom(nil, rng)
57
57
  local numCoins = numCoinsChance < 0.5 and 1 or 2
@@ -73,12 +73,12 @@ function spawnRockAltRewardUrn(self, position, rng)
73
73
  )
74
74
  return true
75
75
  end
76
- totalChance = totalChance + ROCK_ALT_CHANCES.Trinket
76
+ totalChance = totalChance + ROCK_ALT_CHANCES.TRINKET
77
77
  if chance < totalChance then
78
78
  spawnTrinketWithSeed(nil, TrinketType.SWALLOWED_PENNY, position, rng)
79
79
  return true
80
80
  end
81
- totalChance = totalChance + ROCK_ALT_CHANCES.Collectible
81
+ totalChance = totalChance + ROCK_ALT_CHANCES.COLLECTIBLE
82
82
  if chance < totalChance then
83
83
  local stillInPools = isCollectibleInItemPool(nil, CollectibleType.QUARTER, ItemPoolType.DEVIL)
84
84
  if stillInPools then
@@ -113,21 +113,21 @@ function spawnRockAltRewardMushroom(self, position, rng)
113
113
  local roomType = room:GetType()
114
114
  local chance = getRandom(nil, rng)
115
115
  local totalChance = 0
116
- totalChance = totalChance + ROCK_ALT_CHANCES.Nothing
116
+ totalChance = totalChance + ROCK_ALT_CHANCES.NOTHING
117
117
  if chance < totalChance then
118
118
  return false
119
119
  end
120
- totalChance = totalChance + ROCK_ALT_CHANCES.BasicDrop
120
+ totalChance = totalChance + ROCK_ALT_CHANCES.BASIC_DROP
121
121
  if chance < totalChance then
122
122
  spawnPillWithSeed(nil, PillColor.NULL, position, rng)
123
123
  return true
124
124
  end
125
- totalChance = totalChance + ROCK_ALT_CHANCES.Trinket
125
+ totalChance = totalChance + ROCK_ALT_CHANCES.TRINKET
126
126
  if chance < totalChance then
127
127
  spawnTrinketWithSeed(nil, TrinketType.LIBERTY_CAP, position, rng)
128
128
  return true
129
129
  end
130
- totalChance = totalChance + ROCK_ALT_CHANCES.Collectible
130
+ totalChance = totalChance + ROCK_ALT_CHANCES.COLLECTIBLE
131
131
  if chance < totalChance then
132
132
  if roomType == RoomType.SECRET then
133
133
  local wavyCapChance = getRandom(nil, rng)
@@ -169,21 +169,21 @@ end
169
169
  function spawnRockAltRewardSkull(self, position, rng)
170
170
  local chance = getRandom(nil, rng)
171
171
  local totalChance = 0
172
- totalChance = totalChance + ROCK_ALT_CHANCES.Nothing
172
+ totalChance = totalChance + ROCK_ALT_CHANCES.NOTHING
173
173
  if chance < totalChance then
174
174
  return false
175
175
  end
176
- totalChance = totalChance + ROCK_ALT_CHANCES.BasicDrop
176
+ totalChance = totalChance + ROCK_ALT_CHANCES.BASIC_DROP
177
177
  if chance < totalChance then
178
178
  spawnCardWithSeed(nil, Card.NULL, position, rng)
179
179
  return true
180
180
  end
181
- totalChance = totalChance + ROCK_ALT_CHANCES.Trinket
181
+ totalChance = totalChance + ROCK_ALT_CHANCES.TRINKET
182
182
  if chance < totalChance then
183
183
  spawnHeartWithSeed(nil, HeartSubType.BLACK, position, rng)
184
184
  return true
185
185
  end
186
- totalChance = totalChance + ROCK_ALT_CHANCES.Collectible
186
+ totalChance = totalChance + ROCK_ALT_CHANCES.COLLECTIBLE
187
187
  if chance < totalChance then
188
188
  local ghostBabyStillInPools = isCollectibleInItemPool(nil, CollectibleType.GHOST_BABY, ItemPoolType.TREASURE)
189
189
  local dryBabyStillInPools = isCollectibleInItemPool(nil, CollectibleType.DRY_BABY, ItemPoolType.TREASURE)
@@ -216,21 +216,21 @@ end
216
216
  function spawnRockAltRewardPolyp(self, position, rng)
217
217
  local chance = getRandom(nil, rng)
218
218
  local totalChance = 0
219
- totalChance = totalChance + ROCK_ALT_CHANCES.Nothing
219
+ totalChance = totalChance + ROCK_ALT_CHANCES.NOTHING
220
220
  if chance < totalChance then
221
221
  return false
222
222
  end
223
- totalChance = totalChance + ROCK_ALT_CHANCES.BasicDrop
223
+ totalChance = totalChance + ROCK_ALT_CHANCES.BASIC_DROP
224
224
  if chance < totalChance then
225
225
  spawnHeartWithSeed(nil, HeartSubType.NULL, position, rng)
226
226
  return true
227
227
  end
228
- totalChance = totalChance + ROCK_ALT_CHANCES.Trinket
228
+ totalChance = totalChance + ROCK_ALT_CHANCES.TRINKET
229
229
  if chance < totalChance then
230
230
  spawnTrinketWithSeed(nil, TrinketType.UMBILICAL_CORD, position, rng)
231
231
  return true
232
232
  end
233
- totalChance = totalChance + ROCK_ALT_CHANCES.Collectible
233
+ totalChance = totalChance + ROCK_ALT_CHANCES.COLLECTIBLE
234
234
  if chance < totalChance then
235
235
  local placentaStillInPools = isCollectibleInItemPool(nil, CollectibleType.PLACENTA, ItemPoolType.BOSS)
236
236
  local bloodClotStillInPools = isCollectibleInItemPool(nil, CollectibleType.BLOOD_CLOT, ItemPoolType.BOSS)
@@ -269,13 +269,13 @@ end
269
269
  function spawnRockAltRewardBucket(self, _position, rng, _variant)
270
270
  local chance = getRandom(nil, rng)
271
271
  local totalChance = 0
272
- totalChance = totalChance + ROCK_ALT_CHANCES.Nothing
272
+ totalChance = totalChance + ROCK_ALT_CHANCES.NOTHING
273
273
  if chance < totalChance then
274
274
  return false
275
275
  end
276
276
  return false
277
277
  end
278
- ROCK_ALT_CHANCES = {Nothing = 0.68, BasicDrop = 0.0967, Trinket = 0.025, Collectible = 0.005}
278
+ ROCK_ALT_CHANCES = {NOTHING = 0.68, BASIC_DROP = 0.0967, TRINKET = 0.025, COLLECTIBLE = 0.005}
279
279
  POLYP_PROJECTILE_SPEED = 10
280
280
  POLYP_NUM_PROJECTILES = 6
281
281
  --- Helper function to get the alternate rock type (i.e. urn, mushroom, etc.) that the current room
@@ -4,13 +4,16 @@ export interface PlayerHealth {
4
4
  maxHearts: int;
5
5
  hearts: int;
6
6
  eternalHearts: int;
7
+ /** For soul hearts to apply, they also have to be specified in the `soulHeartTypes` array. */
7
8
  soulHearts: int;
9
+ /** For bone hearts to apply, they also have to be specified in the `soulHeartTypes` array. */
8
10
  boneHearts: int;
9
11
  goldenHearts: int;
10
12
  rottenHearts: int;
11
13
  brokenHearts: int;
12
14
  soulCharges: int;
13
15
  bloodCharges: int;
14
- soulHeartTypes: HeartSubType[];
16
+ soulHeartTypes: SoulHeartType[];
15
17
  }
18
+ export declare type SoulHeartType = HeartSubType.SOUL | HeartSubType.BLACK | HeartSubType.BONE;
16
19
  //# sourceMappingURL=PlayerHealth.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PlayerHealth.d.ts","sourceRoot":"","sources":["../../src/interfaces/PlayerHealth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE5D,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,GAAG,CAAC;IACZ,aAAa,EAAE,GAAG,CAAC;IACnB,UAAU,EAAE,GAAG,CAAC;IAChB,UAAU,EAAE,GAAG,CAAC;IAChB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,GAAG,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC;IACjB,YAAY,EAAE,GAAG,CAAC;IAClB,cAAc,EAAE,YAAY,EAAE,CAAC;CAChC"}
1
+ {"version":3,"file":"PlayerHealth.d.ts","sourceRoot":"","sources":["../../src/interfaces/PlayerHealth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAE5D,4EAA4E;AAC5E,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,GAAG,CAAC;IACf,MAAM,EAAE,GAAG,CAAC;IACZ,aAAa,EAAE,GAAG,CAAC;IAEnB,8FAA8F;IAC9F,UAAU,EAAE,GAAG,CAAC;IAEhB,8FAA8F;IAC9F,UAAU,EAAE,GAAG,CAAC;IAEhB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,GAAG,CAAC;IAClB,YAAY,EAAE,GAAG,CAAC;IAClB,WAAW,EAAE,GAAG,CAAC;IACjB,YAAY,EAAE,GAAG,CAAC;IAElB,cAAc,EAAE,aAAa,EAAE,CAAC;CACjC;AAED,oBAAY,aAAa,GACrB,YAAY,CAAC,IAAI,GACjB,YAAY,CAAC,KAAK,GAClB,YAAY,CAAC,IAAI,CAAC"}
@@ -10,5 +10,6 @@ export interface RoomDescription {
10
10
  roomName: string;
11
11
  roomGridIndex: int;
12
12
  roomListIndex: int;
13
+ roomVisitedCount: int;
13
14
  }
14
15
  //# sourceMappingURL=RoomDescription.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RoomDescription.d.ts","sourceRoot":"","sources":["../../src/interfaces/RoomDescription.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,QAAQ,EACR,OAAO,EACP,SAAS,EACV,MAAM,8BAA8B,CAAC;AAEtC,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,GAAG,CAAC;IACjB,WAAW,EAAE,GAAG,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,GAAG,CAAC;IACnB,aAAa,EAAE,GAAG,CAAC;CACpB"}
1
+ {"version":3,"file":"RoomDescription.d.ts","sourceRoot":"","sources":["../../src/interfaces/RoomDescription.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,QAAQ,EACR,OAAO,EACP,SAAS,EACV,MAAM,8BAA8B,CAAC;AAEtC,wCAAwC;AACxC,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,UAAU,CAAC;IAClB,SAAS,EAAE,SAAS,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,QAAQ,CAAC;IACnB,WAAW,EAAE,GAAG,CAAC;IACjB,WAAW,EAAE,GAAG,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,GAAG,CAAC;IACnB,aAAa,EAAE,GAAG,CAAC;IACnB,gBAAgB,EAAE,GAAG,CAAC;CACvB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "isaacscript-common",
3
- "version": "6.16.2",
3
+ "version": "6.17.0",
4
4
  "description": "Helper functions and features for IsaacScript mods.",
5
5
  "keywords": [
6
6
  "isaac",
@@ -8,7 +8,7 @@ import { ModCallbackCustom } from "../enums/ModCallbackCustom";
8
8
  import { errorIfFeaturesNotInitialized } from "../featuresInitialized";
9
9
  import { spawn } from "../functions/entities";
10
10
  import { getRoomListIndex } from "../functions/roomData";
11
- import { getLatestRoomDescription } from "./roomHistory";
11
+ import { getLatestRoomDescription, isLeavingRoom } from "./roomHistory";
12
12
  import { saveDataManager } from "./saveDataManager/exports";
13
13
 
14
14
  interface PersistentEntityDescription {
@@ -56,6 +56,10 @@ export function persistentEntitiesInit(mod: ModUpgraded): void {
56
56
 
57
57
  // ModCallback.POST_ENTITY_REMOVE (67)
58
58
  function postEntityRemove(entity: Entity) {
59
+ checkDespawningFromPlayerLeavingRoom(entity);
60
+ }
61
+
62
+ function checkDespawningFromPlayerLeavingRoom(entity: Entity) {
59
63
  const ptrHash = GetPtrHash(entity);
60
64
  const tuple = v.room.spawnedPersistentEntities.get(ptrHash);
61
65
  if (tuple === undefined) {
@@ -63,10 +67,20 @@ function postEntityRemove(entity: Entity) {
63
67
  }
64
68
  const index = tuple[0];
65
69
 
66
- // The persistent entity is despawning, presumably because the player is in the process of leaving
67
- // the room. Keep track of the position for later. We use the previous room list index because
68
- // even though the `POST_NEW_ROOM` callback was not fired yet, we have already traveled to the
69
- // next room.
70
+ if (!isLeavingRoom()) {
71
+ return;
72
+ }
73
+
74
+ trackDespawningPickupPosition(entity, index);
75
+ }
76
+
77
+ /**
78
+ * The persistent entity is despawning because the player is in the process of leaving the room.
79
+ * Keep track of the position for later.
80
+ */
81
+ function trackDespawningPickupPosition(entity: Entity, index: int) {
82
+ // (The "latest" room description is really the previous room, because the `POST_NEW_ROOM`
83
+ // callback was not fired yet.)
70
84
  const previousRoomDescription = getLatestRoomDescription();
71
85
  const previousRoomListIndex = previousRoomDescription.roomListIndex;
72
86
  const persistentEntityDescription: PersistentEntityDescription = {
@@ -14,7 +14,7 @@ import { getRoomListIndex } from "../functions/roomData";
14
14
  import { onAscent } from "../functions/stage";
15
15
  import { vectorEquals } from "../functions/vector";
16
16
  import { PickupIndex } from "../types/PickupIndex";
17
- import { getLatestRoomDescription } from "./roomHistory";
17
+ import { getLatestRoomDescription, isLeavingRoom } from "./roomHistory";
18
18
  import { saveDataManager } from "./saveDataManager/exports";
19
19
 
20
20
  interface PickupDescription {
@@ -27,7 +27,6 @@ const FEATURE_NAME = "pickupIndex";
27
27
  const v = {
28
28
  run: {
29
29
  pickupCounter: 0 as PickupIndex,
30
- currentRoomListIndex: 0,
31
30
 
32
31
  pickupDataTreasureRooms: new Map<PickupIndex, PickupDescription>(),
33
32
  pickupDataBossRooms: new Map<PickupIndex, PickupDescription>(),
@@ -81,9 +80,6 @@ function postPickupInit(pickup: EntityPickup) {
81
80
 
82
81
  v.run.pickupCounter++;
83
82
  v.room.pickupIndexes.set(ptrHash, v.run.pickupCounter);
84
-
85
- // Additionally, keep track of which room we are storing the pointer hashes for.
86
- v.run.currentRoomListIndex = getRoomListIndex();
87
83
  }
88
84
 
89
85
  // ModCallback.POST_ENTITY_REMOVE (67)
@@ -99,10 +95,7 @@ function checkDespawningFromPlayerLeavingRoom(entity: Entity) {
99
95
  return;
100
96
  }
101
97
 
102
- const roomListIndex = getRoomListIndex();
103
- if (roomListIndex === v.run.currentRoomListIndex) {
104
- // This is a pickup that is despawning in the current room. For example, it could be a heart
105
- // pickup that the player picked up. Thus, we do not need to keep track of it's metadata.
98
+ if (!isLeavingRoom()) {
106
99
  return;
107
100
  }
108
101
 
@@ -111,13 +104,14 @@ function checkDespawningFromPlayerLeavingRoom(entity: Entity) {
111
104
 
112
105
  /**
113
106
  * This is a pickup that is despawning because the player is in the process of leaving the room.
114
- * Keep track of the metadata for later. We need to use the previous room list index because even
115
- * though the `POST_NEW_ROOM` callback was not fired yet, we have already traveled to the next room.
107
+ * Keep track of the metadata for later.
116
108
  */
117
109
  function trackDespawningPickupMetadata(
118
110
  entity: Entity,
119
111
  pickupIndex: PickupIndex,
120
112
  ) {
113
+ // The "latest" room description is really the previous room, because the `POST_NEW_ROOM` callback
114
+ // was not fired yet.
121
115
  const previousRoomDescription = getLatestRoomDescription();
122
116
  const previousRoomListIndex = previousRoomDescription.roomListIndex;
123
117
  const pickupDescriptions = v.level.pickupData.getAndSetDefault(
@@ -10,6 +10,7 @@ import {
10
10
  getRoomStageID,
11
11
  getRoomSubType,
12
12
  getRoomVariant,
13
+ getRoomVisitedCount,
13
14
  } from "../functions/roomData";
14
15
  import { RoomDescription } from "../interfaces/RoomDescription";
15
16
  import { saveDataManager } from "./saveDataManager/exports";
@@ -45,6 +46,7 @@ function postNewRoomEarly() {
45
46
  const roomName = getRoomName();
46
47
  const roomGridIndex = getRoomGridIndex();
47
48
  const roomListIndex = getRoomListIndex();
49
+ const roomVisitedCount = getRoomVisitedCount();
48
50
 
49
51
  const roomDescription: RoomDescription = {
50
52
  stage,
@@ -56,6 +58,7 @@ function postNewRoomEarly() {
56
58
  roomName,
57
59
  roomGridIndex,
58
60
  roomListIndex,
61
+ roomVisitedCount,
59
62
  };
60
63
  v.run.roomHistory.push(roomDescription);
61
64
  }
@@ -96,10 +99,7 @@ export function getPreviousRoomDescription(): RoomDescription {
96
99
  * Helper function to get information about the most recent room that is stored in the room history
97
100
  * array.
98
101
  *
99
- * This is useful in the `POST_ENTITY_REMOVE` callback, since if an entity is despawning due to a
100
- * player having left the room, the current room will have changed already, but the `POST_NEW_ROOM`
101
- * callback will not have fired yet, and there will not be an entry in the room history array for
102
- * the current room.
102
+ * This is useful in the `POST_ENTITY_REMOVE` callback; see the `isLeavingRoom` function.
103
103
  */
104
104
  export function getLatestRoomDescription(): RoomDescription {
105
105
  const latestRoomDescription = getLastElement(v.run.roomHistory);
@@ -111,3 +111,23 @@ export function getLatestRoomDescription(): RoomDescription {
111
111
 
112
112
  return latestRoomDescription;
113
113
  }
114
+
115
+ /**
116
+ * Helper function to detect if the game is in the state where the room index has changed to a new
117
+ * room, but the entities from the previous room are currently in the process of despawning. (At
118
+ * this point, the `POST_NEW_ROOM` callback will not have fired yet, and there will not be an entry
119
+ * in the room history array for the current room.)
120
+ *
121
+ * This function is intended to be used in the `POST_ENTITY_REMOVE` callback to detect when an
122
+ * entity is pseudo-persistent entity such as a pickup is despawning.
123
+ */
124
+ export function isLeavingRoom(): boolean {
125
+ const roomListIndex = getRoomListIndex();
126
+ const roomVisitedCount = getRoomVisitedCount();
127
+ const latestRoomDescription = getLatestRoomDescription();
128
+
129
+ return (
130
+ roomListIndex !== latestRoomDescription.roomListIndex ||
131
+ roomVisitedCount !== latestRoomDescription.roomVisitedCount
132
+ );
133
+ }
@@ -6,6 +6,7 @@ import {
6
6
  GameStateFlag,
7
7
  GridEntityType,
8
8
  GridRoom,
9
+ HeartSubType,
9
10
  LevelStateFlag,
10
11
  ProjectileFlag,
11
12
  SeedEffect,
@@ -519,7 +520,11 @@ export function logPlayerHealth(this: void, player: EntityPlayer): void {
519
520
  log(` Broken hearts: ${playerHealth.brokenHearts}`);
520
521
  log(` Soul charges: ${playerHealth.soulCharges}`);
521
522
  log(` Blood charges: ${playerHealth.bloodCharges}`);
522
- log(` Soul heart types: [${playerHealth.soulHeartTypes.join(",")}]`);
523
+ log(" Soul heart types: [");
524
+ for (const soulHeartType of playerHealth.soulHeartTypes) {
525
+ log(` HeartSubType.${HeartSubType[soulHeartType]}`);
526
+ }
527
+ log(" ]");
523
528
  }
524
529
 
525
530
  /**
@@ -6,6 +6,7 @@ import {
6
6
  DarkEsauVariant,
7
7
  DeathVariant,
8
8
  EntityType,
9
+ HopperVariant,
9
10
  MamaGurdyVariant,
10
11
  MotherSubType,
11
12
  MotherVariant,
@@ -83,17 +84,39 @@ export function isAliveExceptionNPC(npc: EntityNPC): boolean {
83
84
  return true;
84
85
  }
85
86
 
86
- if (isRaglingDeathPatch(npc)) {
87
+ // EntityType.HOPPER (29)
88
+ // HopperVariant.EGGY (2)
89
+ if (isDyingEggyWithNoSpidersLeft(npc)) {
87
90
  return true;
88
91
  }
89
92
 
90
- if (isDyingEggyWithNoSpidersLeft(npc)) {
93
+ // EntityType.DADDY_LONG_LEGS (101)
94
+ if (isDaddyLongLegsChildStompEntity(npc)) {
95
+ return true;
96
+ }
97
+
98
+ // EntityType.RAGLING (256)
99
+ if (isRaglingDeathPatch(npc)) {
91
100
  return true;
92
101
  }
93
102
 
94
103
  return false;
95
104
  }
96
105
 
106
+ /**
107
+ * Helper function to distinguish between a normal Daddy Long Legs / Triachnid and the child entity
108
+ * that is spawned when the boss does the multi-stomp attack.
109
+ *
110
+ * When this attack occurs, four extra copies of Daddy Long Legs will be spawned with the same
111
+ * entity type, variant, and sub-type. The `Entity.Parent` property will be undefined in this case,
112
+ * so the way to tell them apart is to check for a non-undefined `Entity.SpawnerEntity` property.
113
+ */
114
+ export function isDaddyLongLegsChildStompEntity(npc: EntityNPC): boolean {
115
+ return (
116
+ npc.Type === EntityType.DADDY_LONG_LEGS && npc.SpawnerEntity !== undefined
117
+ );
118
+ }
119
+
97
120
  /**
98
121
  * Helper function to detect the custom death state of an Eggy. Eggies are never actually marked
99
122
  * dead by the game. Instead, when Eggies take fatal damage, they go into NpcState.STATE_SUICIDE and
@@ -101,6 +124,8 @@ export function isAliveExceptionNPC(npc: EntityNPC): boolean {
101
124
  */
102
125
  export function isDyingEggyWithNoSpidersLeft(npc: EntityNPC): boolean {
103
126
  return (
127
+ npc.Type === EntityType.HOPPER &&
128
+ npc.Variant === (HopperVariant.EGGY as int) &&
104
129
  npc.State === NpcState.SUICIDE &&
105
130
  npc.StateFrame >= EGGY_STATE_FRAME_OF_FINAL_SPIDER
106
131
  );
@@ -6,7 +6,7 @@ import {
6
6
  } from "isaac-typescript-definitions";
7
7
  import { MAX_PLAYER_HEART_CONTAINERS } from "../constants";
8
8
  import { HealthType } from "../enums/HealthType";
9
- import { PlayerHealth } from "../interfaces/PlayerHealth";
9
+ import { PlayerHealth, SoulHeartType } from "../interfaces/PlayerHealth";
10
10
  import { getTotalCharge } from "./charge";
11
11
  import { getEnumValues } from "./enums";
12
12
  import {
@@ -79,7 +79,6 @@ export function addPlayerHealthType(
79
79
  */
80
80
  export function getPlayerHealth(player: EntityPlayer): PlayerHealth {
81
81
  const character = player.GetPlayerType();
82
- const soulHeartTypes: HeartSubType[] = [];
83
82
  let maxHearts = player.GetMaxHearts();
84
83
  let hearts = getPlayerHearts(player); // We use the helper function to remove rotten hearts
85
84
  let soulHearts = player.GetSoulHearts();
@@ -115,6 +114,7 @@ export function getPlayerHealth(player: EntityPlayer): PlayerHealth {
115
114
  // track which soul heart we're currently at.
116
115
  let currentSoulHeart = 0;
117
116
 
117
+ const soulHeartTypes: SoulHeartType[] = [];
118
118
  for (let i = 0; i < extraHearts; i++) {
119
119
  let isBoneHeart = player.IsBoneHeart(i);
120
120
  if (character === PlayerType.THE_FORGOTTEN && subPlayer !== undefined) {
@@ -333,24 +333,37 @@ export function setPlayerHealth(
333
333
 
334
334
  // Add the soul / black / bone hearts.
335
335
  let soulHeartsRemaining = playerHealth.soulHearts;
336
- playerHealth.soulHeartTypes.forEach((heartType, i) => {
336
+ playerHealth.soulHeartTypes.forEach((soulHeartType, i) => {
337
337
  const isHalf =
338
338
  playerHealth.soulHearts + playerHealth.boneHearts * 2 < (i + 1) * 2;
339
339
  let addAmount = 2;
340
- if (isHalf || heartType === HeartSubType.BONE || soulHeartsRemaining < 2) {
340
+ if (
341
+ isHalf ||
342
+ soulHeartType === HeartSubType.BONE ||
343
+ soulHeartsRemaining < 2
344
+ ) {
341
345
  // Fix the bug where a half soul heart to the left of a bone heart will be treated as a full
342
346
  // soul heart.
343
347
  addAmount = 1;
344
348
  }
345
349
 
346
- if (heartType === HeartSubType.SOUL) {
347
- player.AddSoulHearts(addAmount);
348
- soulHeartsRemaining -= addAmount;
349
- } else if (heartType === HeartSubType.BLACK) {
350
- player.AddBlackHearts(addAmount);
351
- soulHeartsRemaining -= addAmount;
352
- } else if (heartType === HeartSubType.BONE) {
353
- player.AddBoneHearts(addAmount);
350
+ switch (soulHeartType) {
351
+ case HeartSubType.SOUL: {
352
+ player.AddSoulHearts(addAmount);
353
+ soulHeartsRemaining -= addAmount;
354
+ break;
355
+ }
356
+
357
+ case HeartSubType.BLACK: {
358
+ player.AddBlackHearts(addAmount);
359
+ soulHeartsRemaining -= addAmount;
360
+ break;
361
+ }
362
+
363
+ case HeartSubType.BONE: {
364
+ player.AddBoneHearts(addAmount);
365
+ break;
366
+ }
354
367
  }
355
368
  });
356
369
 
@@ -31,13 +31,13 @@ import { repeat } from "./utils";
31
31
  import { getRandomVector } from "./vector";
32
32
 
33
33
  const ROCK_ALT_CHANCES = {
34
- Nothing: 0.68,
35
- BasicDrop: 0.0967,
34
+ NOTHING: 0.68,
35
+ BASIC_DROP: 0.0967,
36
36
 
37
37
  /** Also used for e.g. black hearts from skulls. */
38
- Trinket: 0.025,
38
+ TRINKET: 0.025,
39
39
 
40
- Collectible: 0.005,
40
+ COLLECTIBLE: 0.005,
41
41
  } as const;
42
42
 
43
43
  const POLYP_PROJECTILE_SPEED = 10;
@@ -126,12 +126,12 @@ function spawnRockAltRewardUrn(position: Vector, rng: RNG): boolean {
126
126
  const chance = getRandom(rng);
127
127
  let totalChance = 0;
128
128
 
129
- totalChance += ROCK_ALT_CHANCES.Nothing;
129
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
130
130
  if (chance < totalChance) {
131
131
  return false;
132
132
  }
133
133
 
134
- totalChance += ROCK_ALT_CHANCES.BasicDrop;
134
+ totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
135
135
  if (chance < totalChance) {
136
136
  const numCoinsChance = getRandom(rng);
137
137
  const numCoins = numCoinsChance < 0.5 ? 1 : 2;
@@ -144,13 +144,13 @@ function spawnRockAltRewardUrn(position: Vector, rng: RNG): boolean {
144
144
  return true;
145
145
  }
146
146
 
147
- totalChance += ROCK_ALT_CHANCES.Trinket;
147
+ totalChance += ROCK_ALT_CHANCES.TRINKET;
148
148
  if (chance < totalChance) {
149
149
  spawnTrinketWithSeed(TrinketType.SWALLOWED_PENNY, position, rng);
150
150
  return true;
151
151
  }
152
152
 
153
- totalChance += ROCK_ALT_CHANCES.Collectible;
153
+ totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
154
154
  if (chance < totalChance) {
155
155
  const stillInPools = isCollectibleInItemPool(
156
156
  CollectibleType.QUARTER,
@@ -184,24 +184,24 @@ function spawnRockAltRewardMushroom(position: Vector, rng: RNG): boolean {
184
184
  const chance = getRandom(rng);
185
185
  let totalChance = 0;
186
186
 
187
- totalChance += ROCK_ALT_CHANCES.Nothing;
187
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
188
188
  if (chance < totalChance) {
189
189
  return false;
190
190
  }
191
191
 
192
- totalChance += ROCK_ALT_CHANCES.BasicDrop;
192
+ totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
193
193
  if (chance < totalChance) {
194
194
  spawnPillWithSeed(PillColor.NULL, position, rng);
195
195
  return true;
196
196
  }
197
197
 
198
- totalChance += ROCK_ALT_CHANCES.Trinket;
198
+ totalChance += ROCK_ALT_CHANCES.TRINKET;
199
199
  if (chance < totalChance) {
200
200
  spawnTrinketWithSeed(TrinketType.LIBERTY_CAP, position, rng);
201
201
  return true;
202
202
  }
203
203
 
204
- totalChance += ROCK_ALT_CHANCES.Collectible;
204
+ totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
205
205
  if (chance < totalChance) {
206
206
  if (roomType === RoomType.SECRET) {
207
207
  const wavyCapChance = getRandom(rng);
@@ -257,24 +257,24 @@ function spawnRockAltRewardSkull(position: Vector, rng: RNG): boolean {
257
257
  const chance = getRandom(rng);
258
258
  let totalChance = 0;
259
259
 
260
- totalChance += ROCK_ALT_CHANCES.Nothing;
260
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
261
261
  if (chance < totalChance) {
262
262
  return false;
263
263
  }
264
264
 
265
- totalChance += ROCK_ALT_CHANCES.BasicDrop;
265
+ totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
266
266
  if (chance < totalChance) {
267
267
  spawnCardWithSeed(Card.NULL, position, rng);
268
268
  return true;
269
269
  }
270
270
 
271
- totalChance += ROCK_ALT_CHANCES.Trinket;
271
+ totalChance += ROCK_ALT_CHANCES.TRINKET;
272
272
  if (chance < totalChance) {
273
273
  spawnHeartWithSeed(HeartSubType.BLACK, position, rng);
274
274
  return true;
275
275
  }
276
276
 
277
- totalChance += ROCK_ALT_CHANCES.Collectible;
277
+ totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
278
278
  if (chance < totalChance) {
279
279
  const ghostBabyStillInPools = isCollectibleInItemPool(
280
280
  CollectibleType.GHOST_BABY,
@@ -316,24 +316,24 @@ function spawnRockAltRewardPolyp(position: Vector, rng: RNG): boolean {
316
316
  const chance = getRandom(rng);
317
317
  let totalChance = 0;
318
318
 
319
- totalChance += ROCK_ALT_CHANCES.Nothing;
319
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
320
320
  if (chance < totalChance) {
321
321
  return false;
322
322
  }
323
323
 
324
- totalChance += ROCK_ALT_CHANCES.BasicDrop;
324
+ totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
325
325
  if (chance < totalChance) {
326
326
  spawnHeartWithSeed(HeartSubType.NULL, position, rng);
327
327
  return true;
328
328
  }
329
329
 
330
- totalChance += ROCK_ALT_CHANCES.Trinket;
330
+ totalChance += ROCK_ALT_CHANCES.TRINKET;
331
331
  if (chance < totalChance) {
332
332
  spawnTrinketWithSeed(TrinketType.UMBILICAL_CORD, position, rng);
333
333
  return true;
334
334
  }
335
335
 
336
- totalChance += ROCK_ALT_CHANCES.Collectible;
336
+ totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
337
337
  if (chance < totalChance) {
338
338
  const placentaStillInPools = isCollectibleInItemPool(
339
339
  CollectibleType.PLACENTA,
@@ -386,7 +386,7 @@ function spawnRockAltRewardBucket(
386
386
  const chance = getRandom(rng);
387
387
  let totalChance = 0;
388
388
 
389
- totalChance += ROCK_ALT_CHANCES.Nothing;
389
+ totalChance += ROCK_ALT_CHANCES.NOTHING;
390
390
  if (chance < totalChance) {
391
391
  return false;
392
392
  }
@@ -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
  }