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.
- package/dist/callbacks/postFlip.lua +2 -2
- package/dist/callbacks/postPlayerFatalDamage.lua +1 -1
- package/dist/callbacks/postPlayerInitFirst.d.ts +2 -0
- package/dist/callbacks/postPlayerInitFirst.d.ts.map +1 -0
- package/dist/callbacks/postPlayerInitFirst.lua +42 -0
- package/dist/callbacks/postPlayerInitLate.lua +5 -5
- package/dist/callbacks/postPlayerReorderedCallbacks.d.ts +2 -0
- package/dist/callbacks/postPlayerReorderedCallbacks.d.ts.map +1 -0
- package/dist/callbacks/{postPlayerReordered.lua → postPlayerReorderedCallbacks.lua} +11 -37
- package/dist/callbacks/subscriptions/postFirstFlip.d.ts +1 -1
- package/dist/callbacks/subscriptions/postFirstFlip.d.ts.map +1 -1
- package/dist/callbacks/subscriptions/postFirstFlip.lua +2 -2
- package/dist/callbacks/subscriptions/postFlip.d.ts +1 -1
- package/dist/callbacks/subscriptions/postFlip.d.ts.map +1 -1
- package/dist/callbacks/subscriptions/postFlip.lua +2 -2
- package/dist/callbacks/subscriptions/{postPlayerInitReordered.d.ts → postPlayerInitFirst.d.ts} +2 -2
- package/dist/callbacks/subscriptions/postPlayerInitFirst.d.ts.map +1 -0
- package/dist/callbacks/subscriptions/{postPlayerInitReordered.lua → postPlayerInitFirst.lua} +3 -3
- package/dist/enums/ModCallbackCustom.d.ts +19 -17
- package/dist/enums/ModCallbackCustom.d.ts.map +1 -1
- package/dist/enums/ModCallbackCustom.lua +3 -3
- package/dist/enums/private/StageTravelState.d.ts +9 -0
- package/dist/enums/private/StageTravelState.d.ts.map +1 -0
- package/dist/enums/private/StageTravelState.lua +15 -0
- package/dist/enums/private/TrapdoorAnimation.d.ts +6 -0
- package/dist/enums/private/TrapdoorAnimation.d.ts.map +1 -0
- package/dist/enums/private/TrapdoorAnimation.lua +6 -0
- package/dist/features/customGridEntity.d.ts +8 -5
- package/dist/features/customGridEntity.d.ts.map +1 -1
- package/dist/features/customGridEntity.lua +66 -17
- package/dist/features/customStage/exports.d.ts.map +1 -1
- package/dist/features/customStage/exports.lua +0 -13
- package/dist/features/customStage/init.d.ts.map +1 -1
- package/dist/features/customStage/init.lua +24 -2
- package/dist/features/customStage/streakText.d.ts +6 -0
- package/dist/features/customStage/streakText.d.ts.map +1 -1
- package/dist/features/customStage/streakText.lua +16 -12
- package/dist/features/customStage/versusScreen.d.ts +6 -0
- package/dist/features/customStage/versusScreen.d.ts.map +1 -1
- package/dist/features/customStage/versusScreen.lua +10 -5
- package/dist/features/customTrapdoor/blackSprite.d.ts +2 -0
- package/dist/features/customTrapdoor/blackSprite.d.ts.map +1 -0
- package/dist/features/customTrapdoor/blackSprite.lua +19 -0
- package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts +15 -0
- package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts.map +1 -0
- package/dist/features/customTrapdoor/customTrapdoorConstants.lua +16 -0
- package/dist/features/customTrapdoor/exports.d.ts +29 -0
- package/dist/features/customTrapdoor/exports.d.ts.map +1 -0
- package/dist/features/customTrapdoor/exports.lua +93 -0
- package/dist/features/customTrapdoor/init.d.ts +3 -0
- package/dist/features/customTrapdoor/init.d.ts.map +1 -0
- package/dist/features/customTrapdoor/init.lua +173 -0
- package/dist/features/customTrapdoor/openClose.d.ts +5 -0
- package/dist/features/customTrapdoor/openClose.d.ts.map +1 -0
- package/dist/features/customTrapdoor/openClose.lua +60 -0
- package/dist/features/customTrapdoor/touched.d.ts +4 -0
- package/dist/features/customTrapdoor/touched.d.ts.map +1 -0
- package/dist/features/customTrapdoor/touched.lua +141 -0
- package/dist/features/customTrapdoor/v.d.ts +18 -0
- package/dist/features/customTrapdoor/v.d.ts.map +1 -0
- package/dist/features/customTrapdoor/v.lua +17 -0
- package/dist/features/deployJSONRoom.d.ts.map +1 -1
- package/dist/features/deployJSONRoom.lua +1 -1
- package/dist/features/extraConsoleCommands/init.d.ts.map +1 -1
- package/dist/features/extraConsoleCommands/init.lua +3 -1
- package/dist/features/extraConsoleCommands/listCommands.lua +2 -2
- package/dist/features/taintedLazarusPlayers.d.ts.map +1 -1
- package/dist/features/taintedLazarusPlayers.lua +13 -21
- package/dist/functions/{character.d.ts → characters.d.ts} +3 -1
- package/dist/functions/characters.d.ts.map +1 -0
- package/dist/functions/{character.lua → characters.lua} +12 -0
- package/dist/functions/deepCopyTests.lua +35 -45
- package/dist/functions/jsonHelpers.d.ts +6 -0
- package/dist/functions/jsonHelpers.d.ts.map +1 -1
- package/dist/functions/jsonHelpers.lua +9 -3
- package/dist/functions/log.lua +3 -3
- package/dist/functions/playerIndex.d.ts +11 -2
- package/dist/functions/playerIndex.d.ts.map +1 -1
- package/dist/functions/playerIndex.lua +20 -8
- package/dist/functions/players.lua +4 -4
- package/dist/functions/revive.lua +2 -2
- package/dist/functions/table.d.ts +1 -1
- package/dist/functions/table.d.ts.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.lua +10 -2
- package/dist/initCustomCallbacks.d.ts.map +1 -1
- package/dist/initCustomCallbacks.lua +5 -2
- package/dist/initFeatures.d.ts +1 -2
- package/dist/initFeatures.d.ts.map +1 -1
- package/dist/initFeatures.lua +10 -2
- package/dist/interfaces/AddCallbackParameterCustom.d.ts +2 -2
- package/dist/interfaces/AddCallbackParameterCustom.d.ts.map +1 -1
- package/dist/interfaces/CustomGridEntityData.d.ts +6 -2
- package/dist/interfaces/CustomGridEntityData.d.ts.map +1 -1
- package/dist/interfaces/private/CustomTrapdoorDescription.d.ts +7 -0
- package/dist/interfaces/private/CustomTrapdoorDescription.d.ts.map +1 -0
- package/dist/interfaces/private/CustomTrapdoorDescription.lua +2 -0
- package/dist/lib/jsonLua.lua +390 -0
- package/dist/objects/callbackRegisterFunctions.d.ts.map +1 -1
- package/dist/objects/callbackRegisterFunctions.lua +3 -3
- package/dist/objects/characterDamageMultipliers.d.ts +6 -0
- package/dist/objects/characterDamageMultipliers.d.ts.map +1 -0
- package/dist/objects/characterDamageMultipliers.lua +49 -0
- package/dist/upgradeMod.d.ts.map +1 -1
- package/dist/upgradeMod.lua +2 -4
- package/package.json +2 -2
- package/src/callbacks/customRevive.ts +3 -3
- package/src/callbacks/itemPickup.ts +3 -3
- package/src/callbacks/postAmbush.ts +3 -3
- package/src/callbacks/postEsauJr.ts +3 -3
- package/src/callbacks/postFlip.ts +6 -5
- package/src/callbacks/postGridEntity.ts +5 -5
- package/src/callbacks/postPlayerCollectible.ts +2 -2
- package/src/callbacks/postPlayerFatalDamage.ts +5 -0
- package/src/callbacks/postPlayerInitFirst.ts +57 -0
- package/src/callbacks/postPlayerInitLate.ts +9 -5
- package/src/callbacks/{postPlayerReordered.ts → postPlayerReorderedCallbacks.ts} +9 -29
- package/src/callbacks/postSlotInitUpdate.ts +5 -2
- package/src/callbacks/postSlotRender.ts +2 -2
- package/src/callbacks/reorderedCallbacks.ts +1 -1
- package/src/callbacks/subscriptions/postFirstFlip.ts +6 -3
- package/src/callbacks/subscriptions/postFlip.ts +6 -3
- package/src/callbacks/subscriptions/{postPlayerInitReordered.ts → postPlayerInitFirst.ts} +6 -6
- package/src/enums/ModCallbackCustom.ts +19 -17
- package/src/enums/private/StageTravelState.ts +8 -0
- package/src/enums/private/TrapdoorAnimation.ts +5 -0
- package/src/features/customGridEntity.ts +93 -12
- package/src/features/customStage/exports.ts +3 -22
- package/src/features/customStage/init.ts +30 -1
- package/src/features/customStage/streakText.ts +13 -5
- package/src/features/customStage/versusScreen.ts +20 -12
- package/src/features/customTrapdoor/blackSprite.ts +16 -0
- package/src/features/customTrapdoor/customTrapdoorConstants.ts +23 -0
- package/src/features/customTrapdoor/exports.ts +99 -0
- package/src/features/customTrapdoor/init.ts +215 -0
- package/src/features/customTrapdoor/openClose.ts +103 -0
- package/src/features/customTrapdoor/touched.ts +175 -0
- package/src/features/customTrapdoor/v.ts +26 -0
- package/src/features/deployJSONRoom.ts +6 -1
- package/src/features/extraConsoleCommands/init.ts +5 -2
- package/src/features/extraConsoleCommands/listCommands.ts +1 -1
- package/src/features/saveDataManager/main.ts +1 -1
- package/src/features/taintedLazarusPlayers.ts +32 -31
- package/src/functions/{character.ts → characters.ts} +13 -0
- package/src/functions/deepCopy.ts +2 -2
- package/src/functions/deepCopyTests.ts +44 -48
- package/src/functions/entities.ts +1 -1
- package/src/functions/jsonHelpers.ts +9 -3
- package/src/functions/playerIndex.ts +18 -2
- package/src/functions/players.ts +1 -1
- package/src/functions/revive.ts +1 -1
- package/src/functions/rng.ts +1 -1
- package/src/functions/table.ts +2 -2
- package/src/index.ts +6 -2
- package/src/initCustomCallbacks.ts +3 -1
- package/src/initFeatures.ts +9 -2
- package/src/interfaces/AddCallbackParameterCustom.ts +2 -2
- package/src/interfaces/CustomGridEntityData.ts +7 -2
- package/src/interfaces/private/CustomTrapdoorDescription.ts +7 -0
- package/src/lib/jsonLua.d.ts +10 -0
- package/src/lib/jsonLua.lua +390 -0
- package/src/objects/callbackRegisterFunctions.ts +2 -3
- package/src/objects/characterDamageMultipliers.ts +49 -0
- package/src/upgradeMod.ts +2 -3
- package/dist/callbacks/postPlayerReordered.d.ts +0 -2
- package/dist/callbacks/postPlayerReordered.d.ts.map +0 -1
- package/dist/callbacks/subscriptions/postPlayerInitReordered.d.ts.map +0 -1
- 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(
|
|
353
|
-
|
|
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/
|
|
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";
|
|
@@ -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
|
|
13
|
-
queuedDeadTaintedLazarus: [] as
|
|
14
|
+
queuedTaintedLazarus: [] as EntityPlayer[],
|
|
15
|
+
queuedDeadTaintedLazarus: [] as EntityPlayer[],
|
|
14
16
|
|
|
15
17
|
/**
|
|
16
|
-
* The
|
|
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
|
|
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,
|
|
29
|
+
subPlayerMap: new Map<PtrHash, EntityPlayer>(),
|
|
23
30
|
},
|
|
24
31
|
};
|
|
25
32
|
|
|
26
33
|
/** @internal */
|
|
27
|
-
export function taintedLazarusPlayersInit(mod:
|
|
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(
|
|
48
|
+
v.run.queuedTaintedLazarus.push(player);
|
|
40
49
|
} else if (character === PlayerType.LAZARUS_2_B) {
|
|
41
|
-
v.run.queuedDeadTaintedLazarus.push(
|
|
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
|
|
65
|
-
const
|
|
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
|
-
|
|
82
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
36
|
-
|
|
35
|
+
copiedSerializedMapHasStringKey();
|
|
36
|
+
copiedSerializedMapHasNumberKey();
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
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
|
|
473
|
+
const serializedOldMap = deepCopy(
|
|
474
474
|
oldMap,
|
|
475
475
|
SerializationType.SERIALIZE,
|
|
476
|
-
"
|
|
476
|
+
"copiedSerializedMapHasStringKey-serialize",
|
|
477
477
|
);
|
|
478
478
|
|
|
479
479
|
const newTable = deepCopy(
|
|
480
|
-
|
|
480
|
+
serializedOldMap,
|
|
481
481
|
SerializationType.DESERIALIZE,
|
|
482
|
-
"
|
|
482
|
+
"copiedSerializedMapHasStringKey-deserialize",
|
|
483
483
|
);
|
|
484
484
|
|
|
485
485
|
const newMap = newTable as Map<string, number>;
|
|
486
|
-
if (!
|
|
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 ${
|
|
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
|
|
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
|
|
500
|
+
const serializedOldMap = deepCopy(
|
|
502
501
|
oldMap,
|
|
503
502
|
SerializationType.SERIALIZE,
|
|
504
|
-
"
|
|
503
|
+
"copiedSerializedMapHasNumberKey-serialize",
|
|
505
504
|
);
|
|
506
505
|
|
|
507
506
|
const newTable = deepCopy(
|
|
508
|
-
|
|
507
|
+
serializedOldMap,
|
|
509
508
|
SerializationType.DESERIALIZE,
|
|
510
|
-
"
|
|
509
|
+
"copiedSerializedMapHasNumberKey-deserialize",
|
|
511
510
|
);
|
|
512
511
|
|
|
513
512
|
const newMap = newTable as Map<number, number>;
|
|
514
|
-
if (!
|
|
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 ${
|
|
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
|
|
521
|
+
function copiedSerializedDefaultMapHasStringKey() {
|
|
524
522
|
const mapKey = "123";
|
|
525
|
-
const
|
|
526
|
-
|
|
523
|
+
const oldDefaultMap = new DefaultMap<string, number>(456);
|
|
524
|
+
oldDefaultMap.getAndSetDefault(mapKey);
|
|
527
525
|
|
|
528
|
-
const
|
|
529
|
-
|
|
526
|
+
const serializedOldDefaultMap = deepCopy(
|
|
527
|
+
oldDefaultMap,
|
|
530
528
|
SerializationType.SERIALIZE,
|
|
531
|
-
"
|
|
529
|
+
"copiedSerializedDefaultMapHasStringKey-serialize",
|
|
532
530
|
);
|
|
533
531
|
|
|
534
532
|
const newTable = deepCopy(
|
|
535
|
-
|
|
533
|
+
serializedOldDefaultMap,
|
|
536
534
|
SerializationType.DESERIALIZE,
|
|
537
|
-
"
|
|
535
|
+
"copiedSerializedDefaultMapHasStringKey-deserialize",
|
|
538
536
|
);
|
|
539
537
|
|
|
540
|
-
const
|
|
541
|
-
if (!
|
|
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
|
|
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
|
|
547
|
+
function copiedSerializedDefaultMapHasNumberKey() {
|
|
551
548
|
const mapKey = 123;
|
|
552
|
-
const
|
|
553
|
-
|
|
549
|
+
const oldDefaultMap = new DefaultMap<number, number>(456);
|
|
550
|
+
oldDefaultMap.getAndSetDefault(mapKey);
|
|
554
551
|
|
|
555
|
-
const
|
|
556
|
-
|
|
552
|
+
const serializedOldDefaultMap = deepCopy(
|
|
553
|
+
oldDefaultMap,
|
|
557
554
|
SerializationType.SERIALIZE,
|
|
558
|
-
"
|
|
555
|
+
"copiedSerializedDefaultMapHasNumberKey-serialize",
|
|
559
556
|
);
|
|
560
557
|
|
|
561
558
|
const newTable = deepCopy(
|
|
562
|
-
|
|
559
|
+
serializedOldDefaultMap,
|
|
563
560
|
SerializationType.DESERIALIZE,
|
|
564
|
-
"
|
|
561
|
+
"copiedSerializedDefaultMapHasNumberKey-deserialize",
|
|
565
562
|
);
|
|
566
563
|
|
|
567
|
-
const
|
|
568
|
-
if (!
|
|
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 ${
|
|
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
|
|
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
|
|
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
|
|
5
|
+
return jsonLua.decode(jsonString) as LuaMap;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function tryEncode(this: void, luaTable: unknown) {
|
|
9
|
-
return
|
|
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);
|