isaacscript-common 6.9.0 → 6.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/features/customStage/customStageGridEntities.lua +2 -2
- package/dist/features/customStage/exports.d.ts.map +1 -1
- package/dist/features/customStage/exports.lua +5 -4
- package/dist/features/customStage/init.d.ts.map +1 -1
- package/dist/features/customStage/init.lua +12 -3
- package/dist/features/customStage/streakText.d.ts +4 -2
- package/dist/features/customStage/streakText.d.ts.map +1 -1
- package/dist/features/customStage/streakText.lua +216 -15
- package/dist/features/customStage/v.d.ts +4 -2
- package/dist/features/customStage/v.d.ts.map +1 -1
- package/dist/features/customStage/v.lua +9 -1
- package/dist/features/customStage/versusScreen.d.ts +1 -1
- package/dist/features/customStage/versusScreen.d.ts.map +1 -1
- package/dist/features/customStage/versusScreen.lua +2 -5
- package/dist/features/extraConsoleCommands/init.lua +1 -0
- package/dist/features/extraConsoleCommands/listCommands.d.ts +8 -0
- package/dist/features/extraConsoleCommands/listCommands.d.ts.map +1 -1
- package/dist/features/extraConsoleCommands/listCommands.lua +13 -0
- package/dist/features/saveDataManager/exports.d.ts.map +1 -1
- package/dist/features/saveDataManager/exports.lua +2 -4
- package/dist/features/saveDataManager/load.d.ts +1 -1
- package/dist/features/saveDataManager/load.d.ts.map +1 -1
- package/dist/features/saveDataManager/main.d.ts.map +1 -1
- package/dist/features/saveDataManager/main.lua +0 -1
- package/dist/features/saveDataManager/maps.d.ts +3 -3
- package/dist/features/saveDataManager/maps.d.ts.map +1 -1
- package/dist/features/saveDataManager/maps.lua +1 -4
- package/dist/features/saveDataManager/merge.d.ts +2 -2
- package/dist/features/saveDataManager/merge.d.ts.map +1 -1
- package/dist/features/saveDataManager/merge.lua +3 -3
- package/dist/features/saveDataManager/save.d.ts +1 -1
- package/dist/features/saveDataManager/save.d.ts.map +1 -1
- package/dist/features/saveDataManager/save.lua +1 -2
- package/dist/features/saveDataManager/{serializationBrand.d.ts → serializationBrands.d.ts} +1 -1
- package/dist/features/saveDataManager/serializationBrands.d.ts.map +1 -0
- package/dist/features/saveDataManager/{serializationBrand.lua → serializationBrands.lua} +0 -0
- package/dist/functions/array.d.ts.map +1 -1
- package/dist/functions/color.d.ts +1 -1
- package/dist/functions/color.d.ts.map +1 -1
- package/dist/functions/deepCopy.d.ts +1 -1
- package/dist/functions/deepCopy.lua +27 -27
- package/dist/functions/deepCopyTests.lua +3 -3
- package/dist/functions/entities.d.ts +1 -1
- package/dist/functions/entities.d.ts.map +1 -1
- package/dist/functions/input.d.ts.map +1 -1
- package/dist/functions/input.lua +8 -10
- package/dist/functions/jsonHelpers.d.ts +1 -1
- package/dist/functions/jsonHelpers.d.ts.map +1 -1
- package/dist/functions/kColor.d.ts +1 -1
- package/dist/functions/kColor.d.ts.map +1 -1
- package/dist/functions/log.d.ts +1 -1
- package/dist/functions/log.d.ts.map +1 -1
- package/dist/functions/log.lua +3 -1
- package/dist/functions/mergeTests.d.ts +2 -2
- package/dist/functions/mergeTests.lua +2 -2
- package/dist/functions/playerIndex.d.ts +1 -1
- package/dist/functions/playerIndex.lua +1 -1
- package/dist/functions/rng.d.ts +1 -1
- package/dist/functions/rng.d.ts.map +1 -1
- package/dist/functions/table.d.ts +9 -9
- package/dist/functions/table.d.ts.map +1 -1
- package/dist/functions/table.lua +23 -21
- package/dist/functions/types.d.ts +1 -1
- package/dist/functions/types.d.ts.map +1 -1
- package/dist/functions/vector.d.ts +1 -1
- package/dist/functions/vector.d.ts.map +1 -1
- package/dist/interfaces/SaveData.d.ts +1 -1
- package/dist/interfaces/private/TSTLClassMetatable.d.ts +1 -1
- package/dist/interfaces/private/TSTLClassMetatable.d.ts.map +1 -1
- package/dist/lualib_bundle.lua +38 -7
- package/dist/types/PlayerIndex.d.ts +3 -2
- package/dist/types/PlayerIndex.d.ts.map +1 -1
- package/dist/types/private/IsaacAPIClass.d.ts +1 -1
- package/dist/types/private/IsaacAPIClass.d.ts.map +1 -1
- package/dist/types/private/SerializedIsaacAPIClass.d.ts +1 -1
- package/dist/types/private/SerializedIsaacAPIClass.d.ts.map +1 -1
- package/dist/types/private/TSTLClass.d.ts +1 -1
- package/dist/types/private/TSTLClass.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/features/customStage/customStageGridEntities.ts +4 -4
- package/src/features/customStage/exports.ts +5 -3
- package/src/features/customStage/init.ts +19 -7
- package/src/features/customStage/streakText.ts +284 -19
- package/src/features/customStage/v.ts +6 -1
- package/src/features/customStage/versusScreen.ts +2 -5
- package/src/features/extraConsoleCommands/init.ts +1 -0
- package/src/features/extraConsoleCommands/listCommands.ts +14 -0
- package/src/features/saveDataManager/exports.ts +3 -8
- package/src/features/saveDataManager/load.ts +2 -3
- package/src/features/saveDataManager/main.ts +4 -5
- package/src/features/saveDataManager/maps.ts +3 -3
- package/src/features/saveDataManager/merge.ts +11 -11
- package/src/features/saveDataManager/save.ts +6 -6
- package/src/features/saveDataManager/{serializationBrand.ts → serializationBrands.ts} +0 -0
- package/src/functions/array.ts +2 -4
- package/src/functions/color.ts +3 -3
- package/src/functions/deepCopy.ts +39 -36
- package/src/functions/deepCopyTests.ts +11 -11
- package/src/functions/entities.ts +7 -7
- package/src/functions/input.ts +7 -8
- package/src/functions/isaacAPIClass.ts +3 -3
- package/src/functions/jsonHelpers.ts +3 -3
- package/src/functions/kColor.ts +3 -3
- package/src/functions/log.ts +5 -4
- package/src/functions/mergeTests.ts +24 -24
- package/src/functions/playerIndex.ts +1 -1
- package/src/functions/rng.ts +3 -3
- package/src/functions/table.ts +25 -23
- package/src/functions/tstlClass.ts +1 -1
- package/src/functions/types.ts +3 -1
- package/src/functions/vector.ts +3 -3
- package/src/interfaces/SaveData.ts +1 -1
- package/src/interfaces/private/TSTLClassMetatable.ts +1 -1
- package/src/types/PlayerIndex.ts +3 -2
- package/src/types/private/IsaacAPIClass.ts +1 -1
- package/src/types/private/SerializedIsaacAPIClass.ts +1 -1
- package/src/types/private/TSTLClass.ts +1 -1
- package/dist/features/saveDataManager/serializationBrand.d.ts.map +0 -1
|
@@ -1,43 +1,308 @@
|
|
|
1
|
+
import { ButtonAction, ControllerIndex } from "isaac-typescript-definitions";
|
|
1
2
|
import { fonts, game } from "../../cachedClasses";
|
|
2
|
-
import { KColorDefault } from "../../constants";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { KColorDefault, VectorOne } from "../../constants";
|
|
4
|
+
import { getEnumValues } from "../../functions/enums";
|
|
5
|
+
import {
|
|
6
|
+
getScreenBottomCenterPos,
|
|
7
|
+
getScreenTopCenterPos,
|
|
8
|
+
} from "../../functions/ui";
|
|
5
9
|
import { CustomStage } from "../../interfaces/CustomStage";
|
|
6
10
|
import v from "./v";
|
|
7
11
|
|
|
12
|
+
enum UIStreakAnimation {
|
|
13
|
+
TEXT = "Text",
|
|
14
|
+
TEXT_IN = "TextIn",
|
|
15
|
+
TEXT_OUT = "TextOut",
|
|
16
|
+
TEXT_STAY = "TextStay",
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
/** This must match the name of the shader in "shaders.xml". */
|
|
9
20
|
const EMPTY_SHADER_NAME = "IsaacScript-RenderAboveHUD";
|
|
10
21
|
|
|
11
|
-
/**
|
|
12
|
-
|
|
22
|
+
/**
|
|
23
|
+
* The frame of the "Text" animation that corresponds to when it reaches the center of the screen
|
|
24
|
+
* and stays put.
|
|
25
|
+
*/
|
|
26
|
+
const TEXT_STAY_FRAME = 8;
|
|
27
|
+
|
|
28
|
+
/** The frame of the "Text" animation that corresponds to when it starts to move right. */
|
|
29
|
+
const TEXT_OUT_FRAME = 60;
|
|
30
|
+
|
|
31
|
+
/** This matches the offset that the vanilla game uses; determined via trial and error. */
|
|
32
|
+
const STREAK_SPRITE_TOP_OFFSET = Vector(0, 48.25);
|
|
33
|
+
|
|
34
|
+
/** This matches the offset that the vanilla game uses; determined via trial and error. */
|
|
35
|
+
const STREAK_SPRITE_BOTTOM_OFFSET = Vector(0, -48.25);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The offset from the bottom of the sprite that the rendered text should go; determined via trial
|
|
39
|
+
* and error.
|
|
40
|
+
*/
|
|
41
|
+
const STREAK_TEXT_BOTTOM_Y_OFFSET = -9;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Corresponds to the vanilla value; determined via trial and error.
|
|
45
|
+
*
|
|
46
|
+
* 8 is too little and 9 has the vanilla text come out just slightly ahead, so we go with 9.
|
|
47
|
+
*/
|
|
48
|
+
const NUM_RENDER_FRAMES_MAP_HELD_BEFORE_STREAK_TEXT = 11;
|
|
49
|
+
|
|
50
|
+
/** Corresponds to the vanilla value; determined through trial and error. */
|
|
51
|
+
const TEXT_PLAYBACK_SPEED = 0.5;
|
|
52
|
+
|
|
53
|
+
/** Taken from StageAPI. */
|
|
54
|
+
const TEXT_IN_ADJUSTMENTS = [-800, -639, -450, -250, -70, 10, 6, 3];
|
|
55
|
+
|
|
56
|
+
/** Taken from StageAPI. */
|
|
57
|
+
const TEXT_OUT_ADJUSTMENTS = [0, -5, -10, -15, -20, 144, 308, 472, 636, 800];
|
|
58
|
+
|
|
59
|
+
/** Taken from StageAPI. */
|
|
60
|
+
const TEXT_IN_SCALES = [
|
|
61
|
+
Vector(3, 0.2),
|
|
62
|
+
Vector(2.6, 0.36),
|
|
63
|
+
Vector(2.2, 0.52),
|
|
64
|
+
Vector(1.8, 0.68),
|
|
65
|
+
Vector(1.4, 0.84),
|
|
66
|
+
Vector(0.95, 1.05),
|
|
67
|
+
Vector(0.97, 1.03),
|
|
68
|
+
Vector(0.98, 1.02),
|
|
69
|
+
];
|
|
70
|
+
|
|
71
|
+
/** Taken from StageAPI. */
|
|
72
|
+
const TEXT_OUT_SCALES = [
|
|
73
|
+
Vector(1, 1),
|
|
74
|
+
Vector(0.99, 1.03),
|
|
75
|
+
Vector(0.98, 1.05),
|
|
76
|
+
Vector(0.96, 1.08),
|
|
77
|
+
Vector(0.95, 1.1),
|
|
78
|
+
Vector(1.36, 0.92),
|
|
79
|
+
Vector(1.77, 0.74),
|
|
80
|
+
Vector(2.18, 0.56),
|
|
81
|
+
Vector(2.59, 0.38),
|
|
82
|
+
Vector(3, 0.2),
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* We do not actually need to render this sprite at all. It's only purpose is to be an invisible
|
|
87
|
+
* reference for when and where we need to render the stage's name. Thus, we specify "false" for
|
|
88
|
+
* "loadGraphics", but still manage playing its animations in the code below.
|
|
89
|
+
*/
|
|
90
|
+
const topStreakSprite = Sprite();
|
|
91
|
+
topStreakSprite.Load("resources/gfx/ui/ui_streak.anm2", false);
|
|
92
|
+
topStreakSprite.PlaybackSpeed = TEXT_PLAYBACK_SPEED;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* We do not actually need to render this sprite at all. It's only purpose is to be an invisible
|
|
96
|
+
* reference for when and where we need to render the stage's name. Thus, we specify "false" for
|
|
97
|
+
* "loadGraphics", but still manage playing its animations in the code below.
|
|
98
|
+
*/
|
|
99
|
+
const bottomStreakSprite = Sprite();
|
|
100
|
+
bottomStreakSprite.Load("resources/gfx/ui/ui_streak.anm2", false);
|
|
101
|
+
bottomStreakSprite.PlaybackSpeed = TEXT_PLAYBACK_SPEED;
|
|
13
102
|
|
|
14
103
|
// ModCallback.POST_RENDER (2)
|
|
15
|
-
export function streakTextPostRender(
|
|
16
|
-
|
|
104
|
+
export function streakTextPostRender(): void {
|
|
105
|
+
// The top streak only plays when the player arrives on the floor (or continues a game from the
|
|
106
|
+
// main menu.)
|
|
107
|
+
checkEndTopStreakText();
|
|
108
|
+
|
|
109
|
+
// The bottom streak only plays when the player holds down the map button.
|
|
110
|
+
trackMapInputPressed();
|
|
111
|
+
checkStartBottomStreakText();
|
|
112
|
+
checkEndBottomStreakText();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function checkEndTopStreakText() {
|
|
116
|
+
if (
|
|
117
|
+
v.run.topStreakTextStartedRenderFrame === null ||
|
|
118
|
+
!topStreakSprite.IsPlaying(UIStreakAnimation.TEXT_STAY)
|
|
119
|
+
) {
|
|
17
120
|
return;
|
|
18
121
|
}
|
|
19
122
|
|
|
20
|
-
const
|
|
21
|
-
|
|
123
|
+
const renderFrameCount = Isaac.GetFrameCount();
|
|
124
|
+
const elapsedFrames =
|
|
125
|
+
renderFrameCount - v.run.topStreakTextStartedRenderFrame;
|
|
126
|
+
if (elapsedFrames >= 115) {
|
|
127
|
+
playTextOut(topStreakSprite);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function trackMapInputPressed() {
|
|
132
|
+
for (const controllerIndex of getEnumValues(ControllerIndex)) {
|
|
133
|
+
const gameFrameCount = game.GetFrameCount();
|
|
134
|
+
const oldPushedMapFrame =
|
|
135
|
+
v.run.controllerIndexPushingMapRenderFrame.get(controllerIndex);
|
|
136
|
+
const isPushingMap = Input.IsActionPressed(
|
|
137
|
+
ButtonAction.MAP,
|
|
138
|
+
controllerIndex,
|
|
139
|
+
);
|
|
140
|
+
|
|
141
|
+
if (isPushingMap) {
|
|
142
|
+
if (oldPushedMapFrame === undefined) {
|
|
143
|
+
v.run.controllerIndexPushingMapRenderFrame.set(
|
|
144
|
+
controllerIndex,
|
|
145
|
+
gameFrameCount,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
v.run.controllerIndexPushingMapRenderFrame.delete(controllerIndex);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* If the map input has been pressed down for long enough, play the animation where the level streak
|
|
156
|
+
* slides in from the left.
|
|
157
|
+
*/
|
|
158
|
+
function checkStartBottomStreakText() {
|
|
159
|
+
if (bottomStreakSprite.IsPlaying()) {
|
|
22
160
|
return;
|
|
23
161
|
}
|
|
24
162
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
163
|
+
const pushedMapFrames = [
|
|
164
|
+
...v.run.controllerIndexPushingMapRenderFrame.values(),
|
|
165
|
+
];
|
|
166
|
+
if (pushedMapFrames.length === 0) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const earliestFrame = Math.min(...pushedMapFrames);
|
|
171
|
+
const gameFrameCount = game.GetFrameCount();
|
|
172
|
+
const elapsedFrames = gameFrameCount - earliestFrame;
|
|
173
|
+
if (elapsedFrames >= NUM_RENDER_FRAMES_MAP_HELD_BEFORE_STREAK_TEXT) {
|
|
174
|
+
bottomStreakSprite.Play(UIStreakAnimation.TEXT, true);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* If the map input has been released, play the animation where the level streak slides out to the
|
|
180
|
+
* right.
|
|
181
|
+
*/
|
|
182
|
+
function checkEndBottomStreakText() {
|
|
183
|
+
if (!bottomStreakSprite.IsPlaying(UIStreakAnimation.TEXT_STAY)) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const pushedMapFrames = [
|
|
188
|
+
...v.run.controllerIndexPushingMapRenderFrame.values(),
|
|
189
|
+
];
|
|
190
|
+
if (pushedMapFrames.length === 0) {
|
|
191
|
+
playTextOut(bottomStreakSprite);
|
|
192
|
+
}
|
|
31
193
|
}
|
|
32
194
|
|
|
33
|
-
// ModCallback.
|
|
34
|
-
|
|
195
|
+
// ModCallback.POST_GAME_STARTED (15)
|
|
196
|
+
export function streakTextPostGameStarted(): void {
|
|
197
|
+
topStreakSprite.Stop();
|
|
198
|
+
bottomStreakSprite.Stop();
|
|
199
|
+
}
|
|
35
200
|
|
|
36
201
|
// ModCallback.GET_SHADER_PARAMS (22)
|
|
37
|
-
export function streakTextGetShaderParams(
|
|
202
|
+
export function streakTextGetShaderParams(
|
|
203
|
+
customStage: CustomStage,
|
|
204
|
+
shaderName: string,
|
|
205
|
+
): void {
|
|
38
206
|
if (shaderName !== EMPTY_SHADER_NAME) {
|
|
39
207
|
return;
|
|
40
208
|
}
|
|
41
209
|
|
|
42
|
-
|
|
210
|
+
const topCenterPos = getScreenTopCenterPos();
|
|
211
|
+
const topStreakPosition = topCenterPos.add(STREAK_SPRITE_TOP_OFFSET);
|
|
212
|
+
renderSprite(customStage, topStreakSprite, topStreakPosition);
|
|
213
|
+
|
|
214
|
+
const bottomCenterPos = getScreenBottomCenterPos();
|
|
215
|
+
const bottomStreakPosition = bottomCenterPos.add(STREAK_SPRITE_BOTTOM_OFFSET);
|
|
216
|
+
renderSprite(customStage, bottomStreakSprite, bottomStreakPosition);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function renderSprite(
|
|
220
|
+
customStage: CustomStage,
|
|
221
|
+
sprite: Sprite,
|
|
222
|
+
position: Vector,
|
|
223
|
+
) {
|
|
224
|
+
sprite.Update();
|
|
225
|
+
if (!sprite.IsPlaying()) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const animation = sprite.GetAnimation() as UIStreakAnimation;
|
|
230
|
+
const frame = sprite.GetFrame();
|
|
231
|
+
if (animation === UIStreakAnimation.TEXT && frame === TEXT_STAY_FRAME) {
|
|
232
|
+
sprite.Play(UIStreakAnimation.TEXT_STAY, true);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const isPaused = game.IsPaused();
|
|
236
|
+
if (isPaused) {
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const font = fonts.upheaval;
|
|
241
|
+
const { name } = customStage;
|
|
242
|
+
const length = font.GetStringWidthUTF8(name);
|
|
243
|
+
const centeredX = position.X - length / 2;
|
|
244
|
+
|
|
245
|
+
let adjustment = 0;
|
|
246
|
+
let scale = VectorOne;
|
|
247
|
+
switch (animation) {
|
|
248
|
+
case UIStreakAnimation.TEXT: {
|
|
249
|
+
if (frame < TEXT_STAY_FRAME) {
|
|
250
|
+
adjustment = TEXT_IN_ADJUSTMENTS[frame] ?? 0;
|
|
251
|
+
scale = TEXT_IN_SCALES[frame] ?? VectorOne;
|
|
252
|
+
} else {
|
|
253
|
+
const adjustedFrame = frame - TEXT_OUT_FRAME;
|
|
254
|
+
adjustment = TEXT_OUT_ADJUSTMENTS[adjustedFrame] ?? 0;
|
|
255
|
+
scale = TEXT_OUT_SCALES[adjustedFrame] ?? VectorOne;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
case UIStreakAnimation.TEXT_IN: {
|
|
262
|
+
adjustment = TEXT_IN_ADJUSTMENTS[frame] ?? 0;
|
|
263
|
+
scale = TEXT_IN_SCALES[frame] ?? VectorOne;
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
case UIStreakAnimation.TEXT_OUT: {
|
|
268
|
+
adjustment = TEXT_OUT_ADJUSTMENTS[frame] ?? 0;
|
|
269
|
+
scale = TEXT_OUT_SCALES[frame] ?? VectorOne;
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
default: {
|
|
274
|
+
break;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const adjustedX = centeredX + adjustment;
|
|
279
|
+
const adjustedY = position.Y + STREAK_TEXT_BOTTOM_Y_OFFSET;
|
|
280
|
+
|
|
281
|
+
sprite.RenderLayer(0, position);
|
|
282
|
+
font.DrawStringScaled(
|
|
283
|
+
name,
|
|
284
|
+
adjustedX,
|
|
285
|
+
adjustedY,
|
|
286
|
+
scale.X,
|
|
287
|
+
scale.Y,
|
|
288
|
+
KColorDefault,
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function playTextOut(sprite: Sprite) {
|
|
293
|
+
sprite.Play(UIStreakAnimation.TEXT, true);
|
|
294
|
+
// We adjust by 2 to roughly align with the speed of the vanilla animation.
|
|
295
|
+
sprite.SetFrame(TEXT_OUT_FRAME - 2);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function topStreakTextStart(): void {
|
|
299
|
+
const level = game.GetLevel();
|
|
300
|
+
const renderFrameCount = Isaac.GetFrameCount();
|
|
301
|
+
|
|
302
|
+
// Show the vanilla streak text, which will have a blank name because of the -1 floor.
|
|
303
|
+
level.ShowName(false);
|
|
304
|
+
|
|
305
|
+
// Initiate the animation for the custom text.
|
|
306
|
+
v.run.topStreakTextStartedRenderFrame = renderFrameCount;
|
|
307
|
+
topStreakSprite.Play(UIStreakAnimation.TEXT, true);
|
|
43
308
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
|
+
import { ControllerIndex } from "isaac-typescript-definitions";
|
|
1
2
|
import { CustomStage } from "../../interfaces/CustomStage";
|
|
2
3
|
|
|
3
4
|
const v = {
|
|
4
5
|
run: {
|
|
5
6
|
currentCustomStage: null as CustomStage | null,
|
|
6
|
-
showingStreakText: false,
|
|
7
7
|
showingBossVersusScreen: false,
|
|
8
|
+
|
|
9
|
+
/** Values are the render frame that the controller first pressed the map button. */
|
|
10
|
+
controllerIndexPushingMapRenderFrame: new Map<ControllerIndex, int>(),
|
|
11
|
+
|
|
12
|
+
topStreakTextStartedRenderFrame: null as int | null,
|
|
8
13
|
},
|
|
9
14
|
|
|
10
15
|
room: {
|
|
@@ -91,15 +91,12 @@ versusScreenBackgroundSprite.Load("gfx/ui/boss/versusscreen.anm2", true);
|
|
|
91
91
|
const versusScreenDirtSpotSprite = Sprite();
|
|
92
92
|
versusScreenDirtSpotSprite.Load("gfx/ui/boss/versusscreen.anm2", true);
|
|
93
93
|
|
|
94
|
-
export function playVersusScreenAnimation(
|
|
95
|
-
customStage: CustomStage,
|
|
96
|
-
force = false,
|
|
97
|
-
): void {
|
|
94
|
+
export function playVersusScreenAnimation(customStage: CustomStage): void {
|
|
98
95
|
const room = game.GetRoom();
|
|
99
96
|
const roomType = room.GetType();
|
|
100
97
|
const hud = game.GetHUD();
|
|
101
98
|
|
|
102
|
-
if (roomType !== RoomType.BOSS
|
|
99
|
+
if (roomType !== RoomType.BOSS) {
|
|
103
100
|
return;
|
|
104
101
|
}
|
|
105
102
|
|
|
@@ -218,6 +218,7 @@ function initMap() {
|
|
|
218
218
|
);
|
|
219
219
|
extraConsoleCommandsFunctionMap.set("room", commands.roomCommand);
|
|
220
220
|
extraConsoleCommandsFunctionMap.set("rottenHearts", commands.rottenHearts);
|
|
221
|
+
extraConsoleCommandsFunctionMap.set("runTests", commands.runTests);
|
|
221
222
|
extraConsoleCommandsFunctionMap.set("s", commands.s);
|
|
222
223
|
extraConsoleCommandsFunctionMap.set("sacrifice", commands.sacrifice);
|
|
223
224
|
extraConsoleCommandsFunctionMap.set("secret", commands.secret);
|
|
@@ -53,6 +53,7 @@ import { getCardName } from "../../functions/cards";
|
|
|
53
53
|
import { getCharacterName } from "../../functions/character";
|
|
54
54
|
import { addCharge } from "../../functions/charge";
|
|
55
55
|
import { isValidCollectibleType } from "../../functions/collectibles";
|
|
56
|
+
import { runDeepCopyTests } from "../../functions/deepCopyTests";
|
|
56
57
|
import { getNPCs } from "../../functions/entitiesSpecific";
|
|
57
58
|
import { getEnumValues } from "../../functions/enums";
|
|
58
59
|
import { addFlag } from "../../functions/flag";
|
|
@@ -64,6 +65,7 @@ import {
|
|
|
64
65
|
logSounds,
|
|
65
66
|
} from "../../functions/log";
|
|
66
67
|
import { getMapPartialMatch } from "../../functions/map";
|
|
68
|
+
import { runMergeTests } from "../../functions/mergeTests";
|
|
67
69
|
import {
|
|
68
70
|
spawnCard,
|
|
69
71
|
spawnPill,
|
|
@@ -988,6 +990,18 @@ export function rottenHearts(params: string): void {
|
|
|
988
990
|
addHeart(params, HealthType.ROTTEN);
|
|
989
991
|
}
|
|
990
992
|
|
|
993
|
+
/**
|
|
994
|
+
* Run the suite of tests that prove that the "deepCopy" helper function and the "merge" function
|
|
995
|
+
* work properly. For more information, see the `runDeepCopyTests` and the `runMergeTests`
|
|
996
|
+
* functions.
|
|
997
|
+
*
|
|
998
|
+
* In general, running the tests is only useful if you are troubleshooting the save data manager.
|
|
999
|
+
*/
|
|
1000
|
+
export function runTests(): void {
|
|
1001
|
+
runDeepCopyTests();
|
|
1002
|
+
runMergeTests();
|
|
1003
|
+
}
|
|
1004
|
+
|
|
991
1005
|
/**
|
|
992
1006
|
* Alias for the "stage" command.
|
|
993
1007
|
*
|
|
@@ -132,12 +132,7 @@ export function saveDataManager(
|
|
|
132
132
|
|
|
133
133
|
// Make a copy of the initial save data so that we can use it to restore the default values later
|
|
134
134
|
// on.
|
|
135
|
-
const
|
|
136
|
-
const saveDataCopy = deepCopy(
|
|
137
|
-
saveDataTable,
|
|
138
|
-
SerializationType.NONE,
|
|
139
|
-
key,
|
|
140
|
-
) as SaveData;
|
|
135
|
+
const saveDataCopy = deepCopy(v, SerializationType.NONE, key) as SaveData;
|
|
141
136
|
saveDataDefaultsMap.set(key, saveDataCopy);
|
|
142
137
|
|
|
143
138
|
// Store the conditional function for later, if present.
|
|
@@ -170,10 +165,10 @@ export function saveDataManagerSave(): void {
|
|
|
170
165
|
}
|
|
171
166
|
|
|
172
167
|
/** "g" stands for "globals". */
|
|
173
|
-
declare let g:
|
|
168
|
+
declare let g: LuaMap<string, SaveData>; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
174
169
|
|
|
175
170
|
/** "gd" stands for "globals defaults". */
|
|
176
|
-
declare let gd:
|
|
171
|
+
declare let gd: LuaMap<string, SaveData>; // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
177
172
|
|
|
178
173
|
/**
|
|
179
174
|
* - Sets the global variable of "g" equal to all of the save data variables for this mod.
|
|
@@ -13,7 +13,7 @@ const DEFAULT_MOD_DATA = "{}";
|
|
|
13
13
|
|
|
14
14
|
export function loadFromDisk(
|
|
15
15
|
mod: Mod,
|
|
16
|
-
oldSaveData:
|
|
16
|
+
oldSaveData: LuaMap<string, SaveData>,
|
|
17
17
|
): void {
|
|
18
18
|
if (!mod.HasData()) {
|
|
19
19
|
// There is no "save#.dat" file for this save slot.
|
|
@@ -47,7 +47,6 @@ export function loadFromDisk(
|
|
|
47
47
|
|
|
48
48
|
// Ignore elements that represent subscriptions that no longer exist in the current save data.
|
|
49
49
|
const oldSaveDataForSubscriber = oldSaveData.get(key);
|
|
50
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
51
50
|
if (oldSaveDataForSubscriber === undefined) {
|
|
52
51
|
return;
|
|
53
52
|
}
|
|
@@ -60,7 +59,7 @@ export function loadFromDisk(
|
|
|
60
59
|
// We do not want to blow away the child tables of the existing map, because save data could
|
|
61
60
|
// contain out-of-date fields. Instead, merge it one field at a time in a recursive way (and
|
|
62
61
|
// convert Lua tables back to TypeScriptToLua Maps, if necessary).
|
|
63
|
-
merge(oldSaveDataForSubscriber as
|
|
62
|
+
merge(oldSaveDataForSubscriber as LuaMap<AnyNotNil, unknown>, value, key);
|
|
64
63
|
},
|
|
65
64
|
SAVE_DATA_MANAGER_DEBUG,
|
|
66
65
|
);
|
|
@@ -129,7 +129,6 @@ export function restoreDefaultSaveData(
|
|
|
129
129
|
|
|
130
130
|
// Get the default values for this feature.
|
|
131
131
|
const saveDataDefaults = saveDataDefaultsMap.get(subscriberName);
|
|
132
|
-
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
133
132
|
if (saveDataDefaults === undefined) {
|
|
134
133
|
logError(
|
|
135
134
|
`Failed to find the default copy of the save data for subscriber: ${subscriberName}`,
|
|
@@ -151,13 +150,13 @@ export function restoreDefaultSaveData(
|
|
|
151
150
|
childTableDefaults,
|
|
152
151
|
SerializationType.NONE,
|
|
153
152
|
`${subscriberName} --> ${saveDataKey}`,
|
|
154
|
-
) as
|
|
153
|
+
) as LuaMap<AnyNotNil, unknown>;
|
|
155
154
|
|
|
156
155
|
// We do not want to blow away the existing child table because we don't want to break any
|
|
157
156
|
// existing references. Instead, empty the table and copy all of the elements from the copy of the
|
|
158
157
|
// defaults table.
|
|
159
158
|
clearAndCopyAllElements(
|
|
160
|
-
childTable as unknown as
|
|
159
|
+
childTable as unknown as LuaMap<AnyNotNil, unknown>,
|
|
161
160
|
childTableDefaultsCopy,
|
|
162
161
|
);
|
|
163
162
|
}
|
|
@@ -167,8 +166,8 @@ export function restoreDefaultSaveData(
|
|
|
167
166
|
* table to the old table.
|
|
168
167
|
*/
|
|
169
168
|
function clearAndCopyAllElements(
|
|
170
|
-
oldTable:
|
|
171
|
-
newTable:
|
|
169
|
+
oldTable: LuaMap<AnyNotNil, unknown>,
|
|
170
|
+
newTable: LuaMap<AnyNotNil, unknown>,
|
|
172
171
|
) {
|
|
173
172
|
clearTable(oldTable);
|
|
174
173
|
|
|
@@ -5,7 +5,7 @@ import { SaveData } from "../../interfaces/SaveData";
|
|
|
5
5
|
* Maps for the master map so that we can access the variables via the in-game console when
|
|
6
6
|
* debugging. (TSTL Maps don't expose the map keys as normal keys.)
|
|
7
7
|
*/
|
|
8
|
-
export const saveDataMap = new
|
|
8
|
+
export const saveDataMap = new LuaMap<string, SaveData>();
|
|
9
9
|
|
|
10
|
-
export const saveDataDefaultsMap = new
|
|
11
|
-
export const saveDataConditionalFuncMap = new
|
|
10
|
+
export const saveDataDefaultsMap = new LuaMap<string, SaveData>();
|
|
11
|
+
export const saveDataConditionalFuncMap = new LuaMap<string, () => boolean>();
|
|
@@ -12,7 +12,7 @@ import { isDefaultMap, isTSTLMap, isTSTLSet } from "../../functions/tstlClass";
|
|
|
12
12
|
import { isTable } from "../../functions/types";
|
|
13
13
|
import { getTraversalDescription } from "../../functions/utils";
|
|
14
14
|
import { SAVE_DATA_MANAGER_DEBUG } from "./saveDataManagerConstants";
|
|
15
|
-
import { isSerializationBrand } from "./
|
|
15
|
+
import { isSerializationBrand } from "./serializationBrands";
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* `merge` takes the values from a new table and recursively merges them into an old object (while
|
|
@@ -20,7 +20,7 @@ import { isSerializationBrand } from "./serializationBrand";
|
|
|
20
20
|
*
|
|
21
21
|
* It supports the following object types:
|
|
22
22
|
*
|
|
23
|
-
* -
|
|
23
|
+
* - Basic TSTL objects / tables
|
|
24
24
|
* - TSTL `Map`
|
|
25
25
|
* - TSTL `Set`
|
|
26
26
|
* - TSTL classes
|
|
@@ -38,10 +38,10 @@ import { isSerializationBrand } from "./serializationBrand";
|
|
|
38
38
|
*/
|
|
39
39
|
export function merge(
|
|
40
40
|
oldObject:
|
|
41
|
-
|
|
|
41
|
+
| LuaMap<AnyNotNil, unknown>
|
|
42
42
|
| Map<AnyNotNil, unknown>
|
|
43
43
|
| Set<AnyNotNil>,
|
|
44
|
-
newTable:
|
|
44
|
+
newTable: LuaMap<AnyNotNil, unknown>,
|
|
45
45
|
traversalDescription: string,
|
|
46
46
|
): void {
|
|
47
47
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
|
@@ -73,8 +73,8 @@ export function merge(
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function mergeArray(
|
|
76
|
-
oldArray:
|
|
77
|
-
newArray:
|
|
76
|
+
oldArray: LuaMap<AnyNotNil, unknown>,
|
|
77
|
+
newArray: LuaMap<AnyNotNil, unknown>,
|
|
78
78
|
) {
|
|
79
79
|
// Assume that we should blow away all array values with whatever is present in the incoming
|
|
80
80
|
// array.
|
|
@@ -90,7 +90,7 @@ function mergeArray(
|
|
|
90
90
|
|
|
91
91
|
function mergeTSTLObject(
|
|
92
92
|
oldObject: Map<AnyNotNil, unknown> | Set<AnyNotNil>,
|
|
93
|
-
newTable:
|
|
93
|
+
newTable: LuaMap<AnyNotNil, unknown>,
|
|
94
94
|
traversalDescription: string,
|
|
95
95
|
) {
|
|
96
96
|
// We blow away the old object and recursively copy over all of the incoming values.
|
|
@@ -140,8 +140,8 @@ function mergeTSTLObject(
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
function mergeTable(
|
|
143
|
-
oldTable:
|
|
144
|
-
newTable:
|
|
143
|
+
oldTable: LuaMap<AnyNotNil, unknown>,
|
|
144
|
+
newTable: LuaMap<AnyNotNil, unknown>,
|
|
145
145
|
traversalDescription: string,
|
|
146
146
|
) {
|
|
147
147
|
iterateTableInOrder(
|
|
@@ -170,12 +170,12 @@ function mergeTable(
|
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
if (isTable(value)) {
|
|
173
|
-
let oldValue = oldTable.get(key) as
|
|
173
|
+
let oldValue = oldTable.get(key) as LuaMap<AnyNotNil, unknown>;
|
|
174
174
|
if (!isTable(oldValue)) {
|
|
175
175
|
// The child table does not exist on the old table. However, we still need to copy over
|
|
176
176
|
// the new table, because we need to handle data types like "Foo | null". Thus, set up a
|
|
177
177
|
// blank sub-table on the old table, and continue to recursively merge..
|
|
178
|
-
oldValue = new
|
|
178
|
+
oldValue = new LuaMap();
|
|
179
179
|
oldTable.set(key, oldValue);
|
|
180
180
|
}
|
|
181
181
|
|
|
@@ -11,8 +11,8 @@ import {
|
|
|
11
11
|
|
|
12
12
|
export function saveToDisk(
|
|
13
13
|
mod: Mod,
|
|
14
|
-
saveDataMap:
|
|
15
|
-
saveDataConditionalFuncMap:
|
|
14
|
+
saveDataMap: LuaMap<string, SaveData>,
|
|
15
|
+
saveDataConditionalFuncMap: LuaMap<string, () => boolean>,
|
|
16
16
|
): void {
|
|
17
17
|
const allSaveData = getAllSaveDataToWriteToDisk(
|
|
18
18
|
saveDataMap,
|
|
@@ -26,10 +26,10 @@ export function saveToDisk(
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function getAllSaveDataToWriteToDisk(
|
|
29
|
-
saveDataMap:
|
|
30
|
-
saveDataConditionalFuncMap:
|
|
29
|
+
saveDataMap: LuaMap<string, SaveData>,
|
|
30
|
+
saveDataConditionalFuncMap: LuaMap<string, () => boolean>,
|
|
31
31
|
) {
|
|
32
|
-
const allSaveData = new
|
|
32
|
+
const allSaveData = new LuaMap<AnyNotNil, unknown>();
|
|
33
33
|
|
|
34
34
|
iterateTableInOrder(
|
|
35
35
|
saveDataMap,
|
|
@@ -60,7 +60,7 @@ function getAllSaveDataToWriteToDisk(
|
|
|
60
60
|
// that is unnecessary. Make a copy of the data and recursively convert all TypeScriptToLua
|
|
61
61
|
// Maps into Lua tables.
|
|
62
62
|
const saveDataCopy = deepCopy(
|
|
63
|
-
saveDataWithoutRoom as
|
|
63
|
+
saveDataWithoutRoom as LuaMap,
|
|
64
64
|
SerializationType.SERIALIZE,
|
|
65
65
|
subscriberName,
|
|
66
66
|
);
|
|
File without changes
|
package/src/functions/array.ts
CHANGED
|
@@ -443,9 +443,7 @@ export function isArray(object: unknown): object is unknown[] {
|
|
|
443
443
|
return false;
|
|
444
444
|
}
|
|
445
445
|
|
|
446
|
-
// Third, handle the case of an "empty" table.
|
|
447
|
-
// determine this, since that will not actually return the number of keys. (It will only work
|
|
448
|
-
// properly for tables that are being used as arrays, which is not necessarily the case here.)
|
|
446
|
+
// Third, handle the case of an "empty" table.
|
|
449
447
|
const tableLength = Object.keys(object).length;
|
|
450
448
|
if (tableLength === 0) {
|
|
451
449
|
return true;
|
|
@@ -453,7 +451,7 @@ export function isArray(object: unknown): object is unknown[] {
|
|
|
453
451
|
|
|
454
452
|
// Third, check for non-contiguous elements. (Lua tables start at an index of 1.)
|
|
455
453
|
for (let i = 1; i <= tableLength; i++) {
|
|
456
|
-
const element = object.get(i)
|
|
454
|
+
const element = object.get(i);
|
|
457
455
|
if (element === undefined) {
|
|
458
456
|
return false;
|
|
459
457
|
}
|
package/src/functions/color.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { isaacAPIClassEquals, isIsaacAPIClassOfType } from "./isaacAPIClass";
|
|
|
4
4
|
import { copyValuesToTable, getNumbersFromTable, tableHasKeys } from "./table";
|
|
5
5
|
import { isTable } from "./types";
|
|
6
6
|
|
|
7
|
-
type SerializedColor =
|
|
7
|
+
type SerializedColor = LuaMap<string, unknown> & {
|
|
8
8
|
readonly __serializedColorBrand: symbol;
|
|
9
9
|
};
|
|
10
10
|
|
|
@@ -65,7 +65,7 @@ export function copyColor(
|
|
|
65
65
|
);
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
const colorTable = new
|
|
68
|
+
const colorTable = new LuaMap<string, unknown>();
|
|
69
69
|
copyValuesToTable(color, KEYS, colorTable);
|
|
70
70
|
colorTable.set(SerializationBrand.COLOR, "");
|
|
71
71
|
return colorTable as SerializedColor;
|
|
@@ -79,7 +79,7 @@ export function copyColor(
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
const [r, g, b, a, ro, go, bo] = getNumbersFromTable(
|
|
82
|
-
color as
|
|
82
|
+
color as LuaMap<string, unknown>,
|
|
83
83
|
OBJECT_NAME,
|
|
84
84
|
...KEYS,
|
|
85
85
|
);
|