isaacscript-common 6.10.2 → 6.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. package/dist/callbacks/postFlip.lua +2 -2
  2. package/dist/callbacks/postPlayerFatalDamage.lua +1 -1
  3. package/dist/callbacks/postPlayerInitFirst.d.ts +2 -0
  4. package/dist/callbacks/postPlayerInitFirst.d.ts.map +1 -0
  5. package/dist/callbacks/postPlayerInitFirst.lua +42 -0
  6. package/dist/callbacks/postPlayerInitLate.lua +5 -5
  7. package/dist/callbacks/postPlayerReorderedCallbacks.d.ts +2 -0
  8. package/dist/callbacks/postPlayerReorderedCallbacks.d.ts.map +1 -0
  9. package/dist/callbacks/{postPlayerReordered.lua → postPlayerReorderedCallbacks.lua} +11 -37
  10. package/dist/callbacks/subscriptions/postFirstFlip.d.ts +1 -1
  11. package/dist/callbacks/subscriptions/postFirstFlip.d.ts.map +1 -1
  12. package/dist/callbacks/subscriptions/postFirstFlip.lua +2 -2
  13. package/dist/callbacks/subscriptions/postFlip.d.ts +1 -1
  14. package/dist/callbacks/subscriptions/postFlip.d.ts.map +1 -1
  15. package/dist/callbacks/subscriptions/postFlip.lua +2 -2
  16. package/dist/callbacks/subscriptions/{postPlayerInitReordered.d.ts → postPlayerInitFirst.d.ts} +2 -2
  17. package/dist/callbacks/subscriptions/postPlayerInitFirst.d.ts.map +1 -0
  18. package/dist/callbacks/subscriptions/{postPlayerInitReordered.lua → postPlayerInitFirst.lua} +3 -3
  19. package/dist/enums/ModCallbackCustom.d.ts +19 -17
  20. package/dist/enums/ModCallbackCustom.d.ts.map +1 -1
  21. package/dist/enums/ModCallbackCustom.lua +3 -3
  22. package/dist/enums/private/StageTravelState.d.ts +9 -0
  23. package/dist/enums/private/StageTravelState.d.ts.map +1 -0
  24. package/dist/enums/private/StageTravelState.lua +15 -0
  25. package/dist/enums/private/TrapdoorAnimation.d.ts +6 -0
  26. package/dist/enums/private/TrapdoorAnimation.d.ts.map +1 -0
  27. package/dist/enums/private/TrapdoorAnimation.lua +6 -0
  28. package/dist/features/customGridEntity.d.ts +8 -5
  29. package/dist/features/customGridEntity.d.ts.map +1 -1
  30. package/dist/features/customGridEntity.lua +66 -17
  31. package/dist/features/customStage/exports.d.ts.map +1 -1
  32. package/dist/features/customStage/exports.lua +0 -13
  33. package/dist/features/customStage/init.d.ts.map +1 -1
  34. package/dist/features/customStage/init.lua +24 -2
  35. package/dist/features/customStage/streakText.d.ts +6 -0
  36. package/dist/features/customStage/streakText.d.ts.map +1 -1
  37. package/dist/features/customStage/streakText.lua +16 -12
  38. package/dist/features/customStage/versusScreen.d.ts +6 -0
  39. package/dist/features/customStage/versusScreen.d.ts.map +1 -1
  40. package/dist/features/customStage/versusScreen.lua +10 -5
  41. package/dist/features/customTrapdoor/blackSprite.d.ts +2 -0
  42. package/dist/features/customTrapdoor/blackSprite.d.ts.map +1 -0
  43. package/dist/features/customTrapdoor/blackSprite.lua +19 -0
  44. package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts +15 -0
  45. package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts.map +1 -0
  46. package/dist/features/customTrapdoor/customTrapdoorConstants.lua +16 -0
  47. package/dist/features/customTrapdoor/exports.d.ts +29 -0
  48. package/dist/features/customTrapdoor/exports.d.ts.map +1 -0
  49. package/dist/features/customTrapdoor/exports.lua +93 -0
  50. package/dist/features/customTrapdoor/init.d.ts +3 -0
  51. package/dist/features/customTrapdoor/init.d.ts.map +1 -0
  52. package/dist/features/customTrapdoor/init.lua +173 -0
  53. package/dist/features/customTrapdoor/openClose.d.ts +5 -0
  54. package/dist/features/customTrapdoor/openClose.d.ts.map +1 -0
  55. package/dist/features/customTrapdoor/openClose.lua +60 -0
  56. package/dist/features/customTrapdoor/touched.d.ts +4 -0
  57. package/dist/features/customTrapdoor/touched.d.ts.map +1 -0
  58. package/dist/features/customTrapdoor/touched.lua +141 -0
  59. package/dist/features/customTrapdoor/v.d.ts +18 -0
  60. package/dist/features/customTrapdoor/v.d.ts.map +1 -0
  61. package/dist/features/customTrapdoor/v.lua +17 -0
  62. package/dist/features/deployJSONRoom.d.ts.map +1 -1
  63. package/dist/features/deployJSONRoom.lua +1 -1
  64. package/dist/features/extraConsoleCommands/init.d.ts.map +1 -1
  65. package/dist/features/extraConsoleCommands/init.lua +3 -1
  66. package/dist/features/extraConsoleCommands/listCommands.lua +2 -2
  67. package/dist/features/taintedLazarusPlayers.d.ts.map +1 -1
  68. package/dist/features/taintedLazarusPlayers.lua +13 -21
  69. package/dist/functions/{character.d.ts → characters.d.ts} +3 -1
  70. package/dist/functions/characters.d.ts.map +1 -0
  71. package/dist/functions/{character.lua → characters.lua} +12 -0
  72. package/dist/functions/deepCopyTests.lua +35 -45
  73. package/dist/functions/jsonHelpers.d.ts +6 -0
  74. package/dist/functions/jsonHelpers.d.ts.map +1 -1
  75. package/dist/functions/jsonHelpers.lua +9 -3
  76. package/dist/functions/log.lua +3 -3
  77. package/dist/functions/playerIndex.d.ts +11 -2
  78. package/dist/functions/playerIndex.d.ts.map +1 -1
  79. package/dist/functions/playerIndex.lua +20 -8
  80. package/dist/functions/players.lua +4 -4
  81. package/dist/functions/revive.lua +2 -2
  82. package/dist/functions/table.d.ts +1 -1
  83. package/dist/functions/table.d.ts.map +1 -1
  84. package/dist/index.d.ts +3 -2
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.lua +10 -2
  87. package/dist/initCustomCallbacks.d.ts.map +1 -1
  88. package/dist/initCustomCallbacks.lua +5 -2
  89. package/dist/initFeatures.d.ts +1 -2
  90. package/dist/initFeatures.d.ts.map +1 -1
  91. package/dist/initFeatures.lua +10 -2
  92. package/dist/interfaces/AddCallbackParameterCustom.d.ts +2 -2
  93. package/dist/interfaces/AddCallbackParameterCustom.d.ts.map +1 -1
  94. package/dist/interfaces/CustomGridEntityData.d.ts +6 -2
  95. package/dist/interfaces/CustomGridEntityData.d.ts.map +1 -1
  96. package/dist/interfaces/private/CustomTrapdoorDescription.d.ts +7 -0
  97. package/dist/interfaces/private/CustomTrapdoorDescription.d.ts.map +1 -0
  98. package/dist/interfaces/private/CustomTrapdoorDescription.lua +2 -0
  99. package/dist/lib/jsonLua.lua +390 -0
  100. package/dist/objects/callbackRegisterFunctions.d.ts.map +1 -1
  101. package/dist/objects/callbackRegisterFunctions.lua +3 -3
  102. package/dist/objects/characterDamageMultipliers.d.ts +6 -0
  103. package/dist/objects/characterDamageMultipliers.d.ts.map +1 -0
  104. package/dist/objects/characterDamageMultipliers.lua +49 -0
  105. package/dist/upgradeMod.d.ts.map +1 -1
  106. package/dist/upgradeMod.lua +2 -4
  107. package/package.json +2 -2
  108. package/src/callbacks/customRevive.ts +3 -3
  109. package/src/callbacks/itemPickup.ts +3 -3
  110. package/src/callbacks/postAmbush.ts +3 -3
  111. package/src/callbacks/postEsauJr.ts +3 -3
  112. package/src/callbacks/postFlip.ts +6 -5
  113. package/src/callbacks/postGridEntity.ts +5 -5
  114. package/src/callbacks/postPlayerCollectible.ts +2 -2
  115. package/src/callbacks/postPlayerFatalDamage.ts +5 -0
  116. package/src/callbacks/postPlayerInitFirst.ts +57 -0
  117. package/src/callbacks/postPlayerInitLate.ts +9 -5
  118. package/src/callbacks/{postPlayerReordered.ts → postPlayerReorderedCallbacks.ts} +9 -29
  119. package/src/callbacks/postSlotInitUpdate.ts +5 -2
  120. package/src/callbacks/postSlotRender.ts +2 -2
  121. package/src/callbacks/reorderedCallbacks.ts +1 -1
  122. package/src/callbacks/subscriptions/postFirstFlip.ts +6 -3
  123. package/src/callbacks/subscriptions/postFlip.ts +6 -3
  124. package/src/callbacks/subscriptions/{postPlayerInitReordered.ts → postPlayerInitFirst.ts} +6 -6
  125. package/src/enums/ModCallbackCustom.ts +19 -17
  126. package/src/enums/private/StageTravelState.ts +8 -0
  127. package/src/enums/private/TrapdoorAnimation.ts +5 -0
  128. package/src/features/customGridEntity.ts +93 -12
  129. package/src/features/customStage/exports.ts +3 -22
  130. package/src/features/customStage/init.ts +30 -1
  131. package/src/features/customStage/streakText.ts +13 -5
  132. package/src/features/customStage/versusScreen.ts +20 -12
  133. package/src/features/customTrapdoor/blackSprite.ts +16 -0
  134. package/src/features/customTrapdoor/customTrapdoorConstants.ts +23 -0
  135. package/src/features/customTrapdoor/exports.ts +99 -0
  136. package/src/features/customTrapdoor/init.ts +215 -0
  137. package/src/features/customTrapdoor/openClose.ts +103 -0
  138. package/src/features/customTrapdoor/touched.ts +175 -0
  139. package/src/features/customTrapdoor/v.ts +26 -0
  140. package/src/features/deployJSONRoom.ts +6 -1
  141. package/src/features/extraConsoleCommands/init.ts +5 -2
  142. package/src/features/extraConsoleCommands/listCommands.ts +1 -1
  143. package/src/features/saveDataManager/main.ts +1 -1
  144. package/src/features/taintedLazarusPlayers.ts +32 -31
  145. package/src/functions/{character.ts → characters.ts} +13 -0
  146. package/src/functions/deepCopy.ts +2 -2
  147. package/src/functions/deepCopyTests.ts +44 -48
  148. package/src/functions/entities.ts +1 -1
  149. package/src/functions/jsonHelpers.ts +9 -3
  150. package/src/functions/playerIndex.ts +18 -2
  151. package/src/functions/players.ts +1 -1
  152. package/src/functions/revive.ts +1 -1
  153. package/src/functions/rng.ts +1 -1
  154. package/src/functions/table.ts +2 -2
  155. package/src/index.ts +6 -2
  156. package/src/initCustomCallbacks.ts +3 -1
  157. package/src/initFeatures.ts +9 -2
  158. package/src/interfaces/AddCallbackParameterCustom.ts +2 -2
  159. package/src/interfaces/CustomGridEntityData.ts +7 -2
  160. package/src/interfaces/private/CustomTrapdoorDescription.ts +7 -0
  161. package/src/lib/jsonLua.d.ts +10 -0
  162. package/src/lib/jsonLua.lua +390 -0
  163. package/src/objects/callbackRegisterFunctions.ts +2 -3
  164. package/src/objects/characterDamageMultipliers.ts +49 -0
  165. package/src/upgradeMod.ts +2 -3
  166. package/dist/callbacks/postPlayerReordered.d.ts +0 -2
  167. package/dist/callbacks/postPlayerReordered.d.ts.map +0 -1
  168. package/dist/callbacks/subscriptions/postPlayerInitReordered.d.ts.map +0 -1
  169. package/dist/functions/character.d.ts.map +0 -1
@@ -0,0 +1,175 @@
1
+ import {
2
+ EntityCollisionClass,
3
+ EntityPartition,
4
+ PlayerType,
5
+ } from "isaac-typescript-definitions";
6
+ import { VectorZero } from "../../constants";
7
+ import { StageTravelState } from "../../enums/private/StageTravelState";
8
+ import {
9
+ getOtherPlayers,
10
+ getPlayers,
11
+ isChildPlayer,
12
+ } from "../../functions/playerIndex";
13
+ import { isCharacter } from "../../functions/players";
14
+ import { CustomTrapdoorDescription } from "../../interfaces/private/CustomTrapdoorDescription";
15
+ import { disableAllInputs } from "../disableInputs";
16
+ import { isPlayerUsingPony } from "../ponyDetection";
17
+ import { runInNGameFrames, runNextGameFrame } from "../runInNFrames";
18
+ import {
19
+ ANIMATIONS_THAT_PREVENT_STAGE_TRAVEL,
20
+ CUSTOM_TRAPDOOR_FEATURE_NAME,
21
+ OTHER_PLAYER_TRAPDOOR_JUMP_DELAY_GAME_FRAMES,
22
+ OTHER_PLAYER_TRAPDOOR_JUMP_DURATION_GAME_FRAMES,
23
+ TRAPDOOR_TOUCH_DISTANCE,
24
+ } from "./customTrapdoorConstants";
25
+ import v from "./v";
26
+
27
+ export function checkCustomTrapdoorPlayerTouched(
28
+ gridEntity: GridEntity,
29
+ trapdoorDescription: CustomTrapdoorDescription,
30
+ ): void {
31
+ if (v.run.state !== StageTravelState.NONE) {
32
+ return;
33
+ }
34
+
35
+ if (!trapdoorDescription.open) {
36
+ return;
37
+ }
38
+
39
+ const playersTouching = Isaac.FindInRadius(
40
+ gridEntity.Position,
41
+ TRAPDOOR_TOUCH_DISTANCE,
42
+ EntityPartition.PLAYER,
43
+ );
44
+ for (const playerEntity of playersTouching) {
45
+ const player = playerEntity.ToPlayer();
46
+ if (player === undefined) {
47
+ continue;
48
+ }
49
+
50
+ if (
51
+ // We don't want a Pony dash to transition to a new floor or a crawl space.
52
+ !isPlayerUsingPony(player) &&
53
+ !isChildPlayer(player) &&
54
+ canPlayerInteractWithTrapdoor(player)
55
+ ) {
56
+ playerTouchedCustomTrapdoor(gridEntity, trapdoorDescription, player);
57
+ return; // Prevent two players from touching the same entity.
58
+ }
59
+ }
60
+ }
61
+
62
+ function canPlayerInteractWithTrapdoor(player: EntityPlayer) {
63
+ // Players cannot interact with stage travel entities when items are queued or while playing
64
+ // certain animations.
65
+ const sprite = player.GetSprite();
66
+ const animation = sprite.GetAnimation();
67
+ return (
68
+ !player.IsHoldingItem() &&
69
+ !ANIMATIONS_THAT_PREVENT_STAGE_TRAVEL.has(animation)
70
+ );
71
+ }
72
+
73
+ function playerTouchedCustomTrapdoor(
74
+ gridEntity: GridEntity,
75
+ trapdoorDescription: CustomTrapdoorDescription,
76
+ player: EntityPlayer,
77
+ ) {
78
+ v.run.state = StageTravelState.PLAYERS_JUMPING_DOWN;
79
+ v.run.destination = trapdoorDescription.destination;
80
+
81
+ disableAllInputs(CUSTOM_TRAPDOOR_FEATURE_NAME);
82
+ setPlayerAttributes(player, gridEntity.Position);
83
+ dropTaintedForgotten(player);
84
+
85
+ player.PlayExtraAnimation("Trapdoor");
86
+
87
+ const otherPlayers = getOtherPlayers(player);
88
+ otherPlayers.forEach((otherPlayer, i) => {
89
+ const gameFramesToWaitBeforeJumping =
90
+ OTHER_PLAYER_TRAPDOOR_JUMP_DELAY_GAME_FRAMES * (i + 1);
91
+ const otherPlayerPtr = EntityPtr(otherPlayer);
92
+ runInNGameFrames(() => {
93
+ startDelayedJump(otherPlayerPtr, gridEntity.Position);
94
+ }, gameFramesToWaitBeforeJumping);
95
+ });
96
+ }
97
+
98
+ function startDelayedJump(entityPtr: EntityPtr, trapdoorPosition: Vector) {
99
+ const entity = entityPtr.Ref;
100
+ if (entity === undefined) {
101
+ return;
102
+ }
103
+
104
+ const player = entity.ToPlayer();
105
+ if (player === undefined) {
106
+ return;
107
+ }
108
+
109
+ player.PlayExtraAnimation("Trapdoor");
110
+
111
+ adjustPlayerVelocityToTrapdoor(entityPtr, player.Position, trapdoorPosition);
112
+ }
113
+
114
+ function adjustPlayerVelocityToTrapdoor(
115
+ entityPtr: EntityPtr,
116
+ startPos: Vector,
117
+ endPos: Vector,
118
+ ) {
119
+ const entity = entityPtr.Ref;
120
+ if (entity === undefined) {
121
+ return;
122
+ }
123
+
124
+ const player = entity.ToPlayer();
125
+ if (player === undefined) {
126
+ return;
127
+ }
128
+
129
+ const sprite = player.GetSprite();
130
+ if (sprite.IsFinished("Trapdoor")) {
131
+ return;
132
+ }
133
+
134
+ const frame = sprite.GetFrame();
135
+ if (frame > OTHER_PLAYER_TRAPDOOR_JUMP_DURATION_GAME_FRAMES) {
136
+ // We have already arrived at the trapdoor.
137
+ return;
138
+ }
139
+
140
+ const totalDifference = endPos.sub(startPos);
141
+ const differencePerFrame = totalDifference.div(
142
+ OTHER_PLAYER_TRAPDOOR_JUMP_DURATION_GAME_FRAMES,
143
+ );
144
+ const differenceForThisFrame = differencePerFrame.mul(frame + 1);
145
+ const targetPosition = startPos.add(differenceForThisFrame);
146
+ const calculatedVelocity = player.Position.sub(targetPosition);
147
+
148
+ player.Velocity = calculatedVelocity;
149
+
150
+ runNextGameFrame(() => {
151
+ adjustPlayerVelocityToTrapdoor(entityPtr, startPos, endPos);
152
+ });
153
+ }
154
+
155
+ function setPlayerAttributes(trapdoorPlayer: EntityPlayer, position: Vector) {
156
+ // Snap the player to the exact position of the trapdoor so that they cleanly jump down the hole.
157
+ trapdoorPlayer.Position = position;
158
+
159
+ for (const player of getPlayers()) {
160
+ // Freeze all players.
161
+ player.Velocity = VectorZero;
162
+
163
+ // We don't want enemy attacks to move the players.
164
+ player.EntityCollisionClass = EntityCollisionClass.NONE;
165
+ }
166
+ }
167
+
168
+ function dropTaintedForgotten(player: EntityPlayer) {
169
+ if (isCharacter(player, PlayerType.THE_FORGOTTEN_B)) {
170
+ const taintedSoul = player.GetOtherTwin();
171
+ if (taintedSoul !== undefined) {
172
+ taintedSoul.ThrowHeldEntity(VectorZero);
173
+ }
174
+ }
175
+ }
@@ -0,0 +1,26 @@
1
+ import { LevelStage, StageType } from "isaac-typescript-definitions";
2
+ import { DefaultMap } from "../../classes/DefaultMap";
3
+ import { StageTravelState } from "../../enums/private/StageTravelState";
4
+ import { CustomTrapdoorDescription } from "../../interfaces/private/CustomTrapdoorDescription";
5
+
6
+ const v = {
7
+ run: {
8
+ state: StageTravelState.NONE,
9
+
10
+ /** The render frame that this state was reached. */
11
+ stateRenderFrame: null as int | null,
12
+
13
+ destination: null as
14
+ | [stage: LevelStage, stageType: StageType]
15
+ | string
16
+ | null,
17
+ },
18
+
19
+ level: {
20
+ /** Indexed by room list index and grid index. */
21
+ trapdoors: new DefaultMap<int, Map<int, CustomTrapdoorDescription>>(
22
+ () => new Map(),
23
+ ),
24
+ },
25
+ };
26
+ export default v;
@@ -4,6 +4,7 @@
4
4
  // it from scratch based on the JSON data.
5
5
 
6
6
  import {
7
+ ActiveSlot,
7
8
  CollectibleType,
8
9
  EffectVariant,
9
10
  EntityCollisionClass,
@@ -16,6 +17,7 @@ import {
16
17
  PickupVariant,
17
18
  PitfallVariant,
18
19
  RoomType,
20
+ UseFlag,
19
21
  } from "isaac-typescript-definitions";
20
22
  import { game } from "../cachedClasses";
21
23
  import { DefaultMap } from "../classes/DefaultMap";
@@ -113,6 +115,9 @@ function preUseItemWeNeedToGoDeeper(
113
115
  _collectibleType: CollectibleType,
114
116
  _rng: RNG,
115
117
  player: EntityPlayer,
118
+ _useFlags: BitFlags<UseFlag>,
119
+ _activeSlot: ActiveSlot,
120
+ _customVarData: int,
116
121
  ): boolean | undefined {
117
122
  if (v.room.manuallyUsingShovel) {
118
123
  return undefined;
@@ -156,7 +161,7 @@ function preUseItemWeNeedToGoDeeper(
156
161
  fillRoomWithDecorations();
157
162
  });
158
163
 
159
- // Cancel the effect.
164
+ // Cancel the original effect.
160
165
  return true;
161
166
  }
162
167
 
@@ -8,6 +8,7 @@ import {
8
8
  } from "isaac-typescript-definitions";
9
9
  import { ModUpgraded } from "../../classes/ModUpgraded";
10
10
  import { MAX_SPEED_STAT } from "../../constants";
11
+ import { bitFlags } from "../../functions/flag";
11
12
  import { getMapPartialMatch } from "../../functions/map";
12
13
  import { printConsole } from "../../functions/utils";
13
14
  import { debugDisplayInit } from "../debugDisplay/debugDisplay";
@@ -349,8 +350,10 @@ function entityTakeDmgPlayer() {
349
350
  }
350
351
 
351
352
  // ModCallback.POST_CURSE_EVAL (12)
352
- function postCurseEval(curses: int) {
353
- return v.persistent.disableCurses ? LevelCurse.NONE : curses;
353
+ function postCurseEval(
354
+ curses: BitFlags<LevelCurse>,
355
+ ): BitFlags<LevelCurse> | undefined {
356
+ return v.persistent.disableCurses ? bitFlags(LevelCurse.NONE) : curses;
354
357
  }
355
358
 
356
359
  // ModCallback.EXECUTE_CMD (22)
@@ -50,7 +50,7 @@ import {
50
50
  } from "../../constantsFirstLast";
51
51
  import { HealthType } from "../../enums/HealthType";
52
52
  import { getCardName } from "../../functions/cards";
53
- import { getCharacterName } from "../../functions/character";
53
+ import { getCharacterName } from "../../functions/characters";
54
54
  import { addCharge } from "../../functions/charge";
55
55
  import { isValidCollectibleType } from "../../functions/collectibles";
56
56
  import { runDeepCopyTests } from "../../functions/deepCopyTests";
@@ -171,7 +171,7 @@ function clearAndCopyAllElements(
171
171
  ) {
172
172
  clearTable(oldTable);
173
173
 
174
- for (const [key, value] of pairs(newTable)) {
174
+ for (const [key, value] of newTable) {
175
175
  oldTable.set(key, value);
176
176
  }
177
177
  }
@@ -2,29 +2,39 @@
2
2
  // Lazarus.
3
3
 
4
4
  import { ModCallback, PlayerType } from "isaac-typescript-definitions";
5
+ import { ModUpgraded } from "../classes/ModUpgraded";
5
6
  import { errorIfFeaturesNotInitialized } from "../featuresInitialized";
7
+ import { logError } from "../functions/log";
6
8
  import { saveDataManager } from "./saveDataManager/exports";
7
9
 
8
10
  const FEATURE_NAME = "taintedLazarusPlayers";
9
11
 
10
12
  const v = {
11
13
  run: {
12
- queuedTaintedLazarus: [] as EntityPtr[],
13
- queuedDeadTaintedLazarus: [] as EntityPtr[],
14
+ queuedTaintedLazarus: [] as EntityPlayer[],
15
+ queuedDeadTaintedLazarus: [] as EntityPlayer[],
14
16
 
15
17
  /**
16
- * The PostPlayerInit callback fires for Dead Tainted Lazarus at the beginning of the run.
18
+ * The `POST_PLAYER_INIT` callback fires for Dead Tainted Lazarus at the beginning of the run.
17
19
  * However, the player index for the Dead Tainted Lazarus player object at that time does not
18
20
  * actually correspond to the player index for the real player once Flip has been used. Thus, we
19
21
  * revert to using PtrHash as an index for our map, which is consistent between the Dead Tainted
20
- * Lazarus object in the PostPlayerInit callback and the "real" Dead Tainted Lazarus.
22
+ * Lazarus object in the `POST_PLAYER_INIT` callback and the "real" Dead Tainted Lazarus.
23
+ *
24
+ * We use `EntityPlayer` as the value for the map instead of `EntityPtr` because using the
25
+ * pointer does not work for some reason. (When we unwrap it after one or more flips have been
26
+ * used, the pointers no longer point to the original objects, even if we manually update the
27
+ * pointers in the `POST_FLIP` callback.)
21
28
  */
22
- subPlayerMap: new Map<PtrHash, EntityPtr>(),
29
+ subPlayerMap: new Map<PtrHash, EntityPlayer>(),
23
30
  },
24
31
  };
25
32
 
26
33
  /** @internal */
27
- export function taintedLazarusPlayersInit(mod: Mod): void {
34
+ export function taintedLazarusPlayersInit(mod: ModUpgraded): void {
35
+ // `EntityPtr` is not serializable, so we cannot save data. However, this is inconsequential,
36
+ // since the `POST_PLAYER_INIT` callback will fire when a run is continued, which will repopulate
37
+ // the `subPlayerMap`.
28
38
  saveDataManager(FEATURE_NAME, v, () => false);
29
39
 
30
40
  mod.AddCallback(ModCallback.POST_PLAYER_INIT, postPlayerInit);
@@ -32,13 +42,12 @@ export function taintedLazarusPlayersInit(mod: Mod): void {
32
42
 
33
43
  // ModCallback.POST_PLAYER_INIT (9)
34
44
  function postPlayerInit(player: EntityPlayer) {
35
- const entityPtr = EntityPtr(player);
36
45
  const character = player.GetPlayerType();
37
46
 
38
47
  if (character === PlayerType.LAZARUS_B) {
39
- v.run.queuedTaintedLazarus.push(entityPtr);
48
+ v.run.queuedTaintedLazarus.push(player);
40
49
  } else if (character === PlayerType.LAZARUS_2_B) {
41
- v.run.queuedDeadTaintedLazarus.push(entityPtr);
50
+ v.run.queuedDeadTaintedLazarus.push(player);
42
51
  } else {
43
52
  return;
44
53
  }
@@ -47,6 +56,8 @@ function postPlayerInit(player: EntityPlayer) {
47
56
  }
48
57
 
49
58
  /**
59
+ * Indexes are the `PtrHash`, values are the `EntityPtr` of the *other* Lazarus.
60
+ *
50
61
  * When starting a run, the PostPlayerInit callback will fire first for Dead Tainted Lazarus, then
51
62
  * for Tainted Lazarus. When continuing a run, the PostPlayerInit callback will fire first for the
52
63
  * character that is currently active. Thus, since the order of the characters is not certain, we
@@ -61,15 +72,8 @@ function checkDequeue() {
61
72
  return;
62
73
  }
63
74
 
64
- const taintedLazarusPtr = v.run.queuedTaintedLazarus.shift();
65
- const deadTaintedLazarusPtr = v.run.queuedDeadTaintedLazarus.shift();
66
-
67
- if (taintedLazarusPtr === undefined || deadTaintedLazarusPtr === undefined) {
68
- return;
69
- }
70
-
71
- const taintedLazarus = taintedLazarusPtr.Ref;
72
- const deadTaintedLazarus = deadTaintedLazarusPtr.Ref;
75
+ const taintedLazarus = v.run.queuedTaintedLazarus.shift();
76
+ const deadTaintedLazarus = v.run.queuedDeadTaintedLazarus.shift();
73
77
 
74
78
  if (taintedLazarus === undefined || deadTaintedLazarus === undefined) {
75
79
  return;
@@ -78,8 +82,15 @@ function checkDequeue() {
78
82
  const taintedLazarusPtrHash = GetPtrHash(taintedLazarus);
79
83
  const deadTaintedLazarusPtrHash = GetPtrHash(deadTaintedLazarus);
80
84
 
81
- v.run.subPlayerMap.set(taintedLazarusPtrHash, deadTaintedLazarusPtr);
82
- v.run.subPlayerMap.set(deadTaintedLazarusPtrHash, taintedLazarusPtr);
85
+ if (taintedLazarusPtrHash === deadTaintedLazarusPtrHash) {
86
+ logError(
87
+ "Failed to cache the Tainted Lazarus player objects, since the hash for Tainted Lazarus and Dead Tainted Lazarus were the same.",
88
+ );
89
+ return;
90
+ }
91
+
92
+ v.run.subPlayerMap.set(taintedLazarusPtrHash, deadTaintedLazarus);
93
+ v.run.subPlayerMap.set(deadTaintedLazarusPtrHash, taintedLazarus);
83
94
  }
84
95
 
85
96
  /**
@@ -99,15 +110,5 @@ export function getTaintedLazarusSubPlayer(
99
110
  errorIfFeaturesNotInitialized(FEATURE_NAME);
100
111
 
101
112
  const ptrHash = GetPtrHash(player);
102
- const entityPtr = v.run.subPlayerMap.get(ptrHash);
103
- if (entityPtr === undefined) {
104
- return undefined;
105
- }
106
-
107
- const entity = entityPtr.Ref;
108
- if (entity === undefined) {
109
- return undefined;
110
- }
111
-
112
- return entity.ToPlayer();
113
+ return v.run.subPlayerMap.get(ptrHash);
113
114
  }
@@ -1,5 +1,6 @@
1
1
  import { PlayerType } from "isaac-typescript-definitions";
2
2
  import { LAST_VANILLA_CHARACTER } from "../constantsFirstLast";
3
+ import { CHARACTER_DAMAGE_MULTIPLIERS } from "../objects/characterDamageMultipliers";
3
4
  import { CHARACTER_NAMES } from "../objects/characterNames";
4
5
  import { CHARACTERS_THAT_START_WITH_AN_ACTIVE_ITEM_SET } from "../sets/charactersThatStartWithAnActiveItemSet";
5
6
  import { CHARACTERS_WITH_BLACK_HEART_FROM_ETERNAL_HEART_SET } from "../sets/charactersWithBlackHeartFromEternalHeartSet";
@@ -56,6 +57,18 @@ export function characterStartsWithActiveItem(character: PlayerType): boolean {
56
57
  return CHARACTERS_THAT_START_WITH_AN_ACTIVE_ITEM_SET.has(character);
57
58
  }
58
59
 
60
+ /** Helper function to get the numerical damage multiplier for a character. */
61
+ export function getCharacterDamageMultiplier(
62
+ character: PlayerType,
63
+ hasWhoreOfBabylon = false,
64
+ ): float {
65
+ if (character === PlayerType.EVE && hasWhoreOfBabylon) {
66
+ return 1.0;
67
+ }
68
+
69
+ return CHARACTER_DAMAGE_MULTIPLIERS[character];
70
+ }
71
+
59
72
  /**
60
73
  * - Most characters have a 56 frame death animation (i.e. the "Death" animation).
61
74
  * - The Lost and Tainted Lost have a 38 frame death animation (i.e. the "LostDeath" animation).
@@ -550,8 +550,8 @@ function getCopiedEntries(
550
550
  entries: Array<[key: AnyNotNil, value: unknown]>;
551
551
  convertedNumberKeysToStrings: boolean;
552
552
  } {
553
- // First, shallow copy the entries. We cannot use "pairs" to iterate over a Map or Set. We cannot
554
- // use "[...pairs(object)]", as it results in a run-time error.
553
+ // First, shallow copy the entries. We cannot use "pairs" to iterate over a `Map` or `Set`. We
554
+ // cannot use "[...pairs(object)]", as it results in a run-time error.
555
555
  const entries: Array<[key: AnyNotNil, value: unknown]> = [];
556
556
  if (isTSTLMap(object) || isTSTLSet(object) || isDefaultMap(object)) {
557
557
  for (const [key, value] of object.entries()) {
@@ -32,11 +32,11 @@ export function runDeepCopyTests(): void {
32
32
  copiedDefaultMapHasChildDefaultMap();
33
33
  copiedDefaultMapHasBrand();
34
34
 
35
- copiedSerializedMapHasStringKeyType();
36
- copiedSerializedMapHasNumberKeyType();
35
+ copiedSerializedMapHasStringKey();
36
+ copiedSerializedMapHasNumberKey();
37
37
 
38
- copiedSerializedDefaultMapHasStringKeyType();
39
- copiedSerializedDefaultMapHasNumberKeyType();
38
+ copiedSerializedDefaultMapHasStringKey();
39
+ copiedSerializedDefaultMapHasNumberKey();
40
40
 
41
41
  log("All deep copy tests passed!");
42
42
  }
@@ -464,112 +464,108 @@ function copiedDefaultMapHasBrand() {
464
464
  }
465
465
  }
466
466
 
467
- function copiedSerializedMapHasStringKeyType() {
467
+ function copiedSerializedMapHasStringKey() {
468
468
  const mapKey = "123";
469
469
  const mapValue = 456;
470
470
  const oldMap = new Map<string, number>();
471
471
  oldMap.set(mapKey, mapValue);
472
472
 
473
- const tempTable = deepCopy(
473
+ const serializedOldMap = deepCopy(
474
474
  oldMap,
475
475
  SerializationType.SERIALIZE,
476
- "copiedSerializedMapHasStringKeyTypeSerialize",
476
+ "copiedSerializedMapHasStringKey-serialize",
477
477
  );
478
478
 
479
479
  const newTable = deepCopy(
480
- tempTable,
480
+ serializedOldMap,
481
481
  SerializationType.DESERIALIZE,
482
- "copiedSerializedMapHasStringKeyTypeDeserialize",
482
+ "copiedSerializedMapHasStringKey-deserialize",
483
483
  );
484
484
 
485
485
  const newMap = newTable as Map<string, number>;
486
- if (![...newMap.keys()].includes(mapKey)) {
486
+ if (!newMap.has(mapKey)) {
487
+ const keyType = type(mapKey);
487
488
  error(
488
- `The copied Map did not have a key of: ${mapKey} with type ${type(
489
- mapKey,
490
- )}`,
489
+ `The copied Map did not have a key of: ${mapKey} with type ${keyType}`,
491
490
  );
492
491
  }
493
492
  }
494
493
 
495
- function copiedSerializedMapHasNumberKeyType() {
494
+ function copiedSerializedMapHasNumberKey() {
496
495
  const mapKey = 123;
497
496
  const mapValue = 456;
498
497
  const oldMap = new Map<number, number>();
499
498
  oldMap.set(mapKey, mapValue);
500
499
 
501
- const tempTable = deepCopy(
500
+ const serializedOldMap = deepCopy(
502
501
  oldMap,
503
502
  SerializationType.SERIALIZE,
504
- "copiedSerializedMapHasNumberKeyTypeSerialize",
503
+ "copiedSerializedMapHasNumberKey-serialize",
505
504
  );
506
505
 
507
506
  const newTable = deepCopy(
508
- tempTable,
507
+ serializedOldMap,
509
508
  SerializationType.DESERIALIZE,
510
- "copiedSerializedMapHasNumberKeyTypeDeserialize",
509
+ "copiedSerializedMapHasNumberKey-deserialize",
511
510
  );
512
511
 
513
512
  const newMap = newTable as Map<number, number>;
514
- if (![...newMap.keys()].includes(mapKey)) {
513
+ if (!newMap.has(mapKey)) {
514
+ const keyType = type(mapKey);
515
515
  error(
516
- `The copied Map did not have a key of: ${mapKey} with type ${type(
517
- mapKey,
518
- )}`,
516
+ `The copied Map did not have a key of: ${mapKey} with type ${keyType}`,
519
517
  );
520
518
  }
521
519
  }
522
520
 
523
- function copiedSerializedDefaultMapHasStringKeyType() {
521
+ function copiedSerializedDefaultMapHasStringKey() {
524
522
  const mapKey = "123";
525
- const oldMap = new DefaultMap<string, number>(456);
526
- oldMap.getAndSetDefault(mapKey);
523
+ const oldDefaultMap = new DefaultMap<string, number>(456);
524
+ oldDefaultMap.getAndSetDefault(mapKey);
527
525
 
528
- const tempTable = deepCopy(
529
- oldMap,
526
+ const serializedOldDefaultMap = deepCopy(
527
+ oldDefaultMap,
530
528
  SerializationType.SERIALIZE,
531
- "copiedSerializedDefaultMapHasStringKeyTypeSerialize",
529
+ "copiedSerializedDefaultMapHasStringKey-serialize",
532
530
  );
533
531
 
534
532
  const newTable = deepCopy(
535
- tempTable,
533
+ serializedOldDefaultMap,
536
534
  SerializationType.DESERIALIZE,
537
- "copiedSerializedDefaultMapHasStringKeyTypeDeserialize",
535
+ "copiedSerializedDefaultMapHasStringKey-deserialize",
538
536
  );
539
537
 
540
- const newMap = newTable as DefaultMap<string, number>;
541
- if (![...newMap.keys()].includes(mapKey)) {
538
+ const newDefaultMap = newTable as DefaultMap<string, number>;
539
+ if (!newDefaultMap.has(mapKey)) {
540
+ const keyType = type(mapKey);
542
541
  error(
543
- `The copied DefaultMap did not have a key of: ${mapKey} with type ${type(
544
- mapKey,
545
- )}`,
542
+ `The copied DefaultMap did not have a key of "${mapKey}" with type: ${keyType}`,
546
543
  );
547
544
  }
548
545
  }
549
546
 
550
- function copiedSerializedDefaultMapHasNumberKeyType() {
547
+ function copiedSerializedDefaultMapHasNumberKey() {
551
548
  const mapKey = 123;
552
- const oldMap = new DefaultMap<number, number>(456);
553
- oldMap.getAndSetDefault(mapKey);
549
+ const oldDefaultMap = new DefaultMap<number, number>(456);
550
+ oldDefaultMap.getAndSetDefault(mapKey);
554
551
 
555
- const tempTable = deepCopy(
556
- oldMap,
552
+ const serializedOldDefaultMap = deepCopy(
553
+ oldDefaultMap,
557
554
  SerializationType.SERIALIZE,
558
- "copiedSerializedDefaultMapHasNumberKeyTypeSerialize",
555
+ "copiedSerializedDefaultMapHasNumberKey-serialize",
559
556
  );
560
557
 
561
558
  const newTable = deepCopy(
562
- tempTable,
559
+ serializedOldDefaultMap,
563
560
  SerializationType.DESERIALIZE,
564
- "copiedSerializedDefaultMapHasNumberKeyTypeDeserialize",
561
+ "copiedSerializedDefaultMapHasNumberKey-deserialize",
565
562
  );
566
563
 
567
- const newMap = newTable as DefaultMap<number, number>;
568
- if (![...newMap.keys()].includes(mapKey)) {
564
+ const newDefaultMap = newTable as DefaultMap<number, number>;
565
+ if (!newDefaultMap.has(mapKey)) {
566
+ const keyType = type(mapKey);
569
567
  error(
570
- `The copied DefaultMap did not have a key of: ${mapKey} with type ${type(
571
- mapKey,
572
- )}`,
568
+ `The copied DefaultMap did not have a key of: ${mapKey} with type ${keyType}`,
573
569
  );
574
570
  }
575
571
  }
@@ -181,7 +181,7 @@ function setPrimitiveEntityFields(
181
181
  error('Failed to get the "__propget" table for an entity.');
182
182
  }
183
183
 
184
- for (const [key] of pairs(propGetTable)) {
184
+ for (const [key] of propGetTable) {
185
185
  // The values of this table are functions. Thus, we use the key to index the original entity.
186
186
  const indexKey = key as keyof typeof entity;
187
187
  const value = entity[indexKey];
@@ -1,12 +1,12 @@
1
- import * as json from "json";
1
+ import * as jsonLua from "../lib/jsonLua";
2
2
  import { logError } from "./log";
3
3
 
4
4
  function tryDecode(this: void, jsonString: string) {
5
- return json.decode(jsonString) as LuaMap;
5
+ return jsonLua.decode(jsonString) as LuaMap;
6
6
  }
7
7
 
8
8
  function tryEncode(this: void, luaTable: unknown) {
9
- return json.encode(luaTable);
9
+ return jsonLua.encode(luaTable);
10
10
  }
11
11
 
12
12
  /**
@@ -16,6 +16,9 @@ function tryEncode(this: void, luaTable: unknown) {
16
16
  * fails, it will return a blank Lua table instead of throwing an error. (This allows execution to
17
17
  * continue in cases where users have no current save data or have manually removed their existing
18
18
  * save data.)
19
+ *
20
+ * Under the hood, this uses a custom JSON parser that was measured to be 11.8 times faster than the
21
+ * vanilla JSON parser.
19
22
  */
20
23
  export function jsonDecode(jsonString: string): LuaMap<AnyNotNil, unknown> {
21
24
  const [ok, luaTableOrErrMsg] = pcall(tryDecode, jsonString);
@@ -34,6 +37,9 @@ export function jsonDecode(jsonString: string): LuaMap<AnyNotNil, unknown> {
34
37
  * In most cases, this function will be used for writing data to a "save#.dat" file. If encoding
35
38
  * fails, it will throw an error to prevent writing a blank string or corrupted data to a user's
36
39
  * "save#.dat" file.
40
+ *
41
+ * Under the hood, this uses a custom JSON parser that was measured to be 11.8 times faster than the
42
+ * vanilla JSON parser.
37
43
  */
38
44
  export function jsonEncode(luaTable: unknown): string {
39
45
  const [ok, jsonStringOrErrMsg] = pcall(tryEncode, luaTable);