isaacscript-common 6.9.0 → 6.10.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/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 +73 -37
- package/dist/functions/deepCopyTests.d.ts.map +1 -1
- package/dist/functions/deepCopyTests.lua +85 -18
- 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 -6
- 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 +95 -41
- package/src/functions/deepCopyTests.ts +161 -28
- 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 +7 -4
- package/src/functions/mergeTests.ts +24 -32
- 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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TSTLClass.d.ts","sourceRoot":"","sources":["../../../src/types/private/TSTLClass.ts"],"names":[],"mappings":";AAAA,oBAAY,SAAS,GAAG,
|
|
1
|
+
{"version":3,"file":"TSTLClass.d.ts","sourceRoot":"","sources":["../../../src/types/private/TSTLClass.ts"],"names":[],"mappings":";AAAA,oBAAY,SAAS,GAAG,MAAM,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG;IACnD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "isaacscript-common",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.10.2",
|
|
4
4
|
"description": "Helper functions and features for IsaacScript mods.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"isaac",
|
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
"main": "dist/index",
|
|
23
23
|
"types": "dist/index.d.ts",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"isaac-typescript-definitions": "^3.0.
|
|
25
|
+
"isaac-typescript-definitions": "^3.0.31"
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -165,10 +165,6 @@ export function removeUrnRewards(
|
|
|
165
165
|
return;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
// Spiders
|
|
169
|
-
const spiders = getNPCs(EntityType.SPIDER);
|
|
170
|
-
removeEntitiesSpawnedFromGridEntity(spiders, gridEntity);
|
|
171
|
-
|
|
172
168
|
// Coins
|
|
173
169
|
const coins = getCoins();
|
|
174
170
|
removeEntitiesSpawnedFromGridEntity(coins, gridEntity);
|
|
@@ -180,6 +176,10 @@ export function removeUrnRewards(
|
|
|
180
176
|
// Swallowed Penny
|
|
181
177
|
const swallowedPennies = getTrinkets(TrinketType.SWALLOWED_PENNY);
|
|
182
178
|
removeEntitiesSpawnedFromGridEntity(swallowedPennies, gridEntity);
|
|
179
|
+
|
|
180
|
+
// Spiders
|
|
181
|
+
const spiders = getNPCs(EntityType.SPIDER);
|
|
182
|
+
removeEntitiesSpawnedFromGridEntity(spiders, gridEntity);
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
function removeEntitiesSpawnedFromGridEntity(
|
|
@@ -19,12 +19,12 @@ import { getRooms } from "../../functions/rooms";
|
|
|
19
19
|
import { getGotoCommand, setStage } from "../../functions/stage";
|
|
20
20
|
import { runNextRoom } from "../runNextRoom";
|
|
21
21
|
import { getRandomCustomStageRoom } from "./customStageUtils";
|
|
22
|
+
import { topStreakTextStart } from "./streakText";
|
|
22
23
|
import v, {
|
|
23
24
|
customBossPNGPaths,
|
|
24
25
|
customStageCachedRoomData,
|
|
25
26
|
customStagesMap,
|
|
26
27
|
} from "./v";
|
|
27
|
-
import { playVersusScreenAnimation } from "./versusScreen";
|
|
28
28
|
|
|
29
29
|
const DEFAULT_BASE_STAGE = LevelStage.BASEMENT_2;
|
|
30
30
|
const DEFAULT_BASE_STAGE_TYPE = StageType.ORIGINAL;
|
|
@@ -39,7 +39,7 @@ export function setCustomStage(name: string, verbose = false): void {
|
|
|
39
39
|
const customStage = customStagesMap.get(name);
|
|
40
40
|
if (customStage === undefined) {
|
|
41
41
|
error(
|
|
42
|
-
`Failed to set the custom stage of "${name}" because it was not found in the custom stages map. (
|
|
42
|
+
`Failed to set the custom stage of "${name}" because it was not found in the custom stages map. (Try restarting IsaacScript / recompiling the mod, and try again. If that does not work, you probably forgot to define it in your "tsconfig.json" file.) See the website for more details on how to set up custom stages.`,
|
|
43
43
|
);
|
|
44
44
|
}
|
|
45
45
|
|
|
@@ -152,6 +152,8 @@ function postRoomTransition() {
|
|
|
152
152
|
// After the room transition, the players will be placed next to a door, but they should be in the
|
|
153
153
|
// center of the room to emulate what happens on a vanilla stage.
|
|
154
154
|
movePlayersToCenter();
|
|
155
|
+
|
|
156
|
+
topStreakTextStart();
|
|
155
157
|
}
|
|
156
158
|
|
|
157
159
|
export function setCustomStageDebug(): void {
|
|
@@ -161,7 +163,7 @@ export function setCustomStageDebug(): void {
|
|
|
161
163
|
return;
|
|
162
164
|
}
|
|
163
165
|
|
|
164
|
-
|
|
166
|
+
topStreakTextStart();
|
|
165
167
|
}
|
|
166
168
|
|
|
167
169
|
/**
|
|
@@ -24,7 +24,11 @@ import {
|
|
|
24
24
|
} from "./customStageGridEntities";
|
|
25
25
|
import * as metadataJSON from "./metadata.json"; // This will correspond to "metadata.lua" at run-time.
|
|
26
26
|
import { setShadows } from "./shadows";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
streakTextGetShaderParams,
|
|
29
|
+
streakTextPostGameStarted,
|
|
30
|
+
streakTextPostRender,
|
|
31
|
+
} from "./streakText";
|
|
28
32
|
import v, { customStagesMap } from "./v";
|
|
29
33
|
import {
|
|
30
34
|
playVersusScreenAnimation,
|
|
@@ -36,20 +40,17 @@ export function customStageInit(mod: ModUpgraded): void {
|
|
|
36
40
|
initRoomTypeMaps();
|
|
37
41
|
|
|
38
42
|
mod.AddCallback(ModCallback.POST_RENDER, postRender); // 2
|
|
39
|
-
|
|
43
|
+
mod.AddCallback(ModCallback.POST_GAME_STARTED, postGameStarted); // 15
|
|
40
44
|
mod.AddCallback(ModCallback.GET_SHADER_PARAMS, getShaderParams); // 21
|
|
41
|
-
|
|
42
45
|
mod.AddCallbackCustom(
|
|
43
46
|
ModCallbackCustom.POST_GRID_ENTITY_BROKEN,
|
|
44
47
|
postGridEntityBrokenRockAlt,
|
|
45
48
|
GridEntityType.ROCK_ALT,
|
|
46
49
|
);
|
|
47
|
-
|
|
48
50
|
mod.AddCallbackCustom(
|
|
49
51
|
ModCallbackCustom.POST_GRID_ENTITY_INIT,
|
|
50
52
|
postGridEntityBrokenInit,
|
|
51
53
|
);
|
|
52
|
-
|
|
53
54
|
mod.AddCallbackCustom(
|
|
54
55
|
ModCallbackCustom.POST_NEW_ROOM_REORDERED,
|
|
55
56
|
postNewRoomReordered,
|
|
@@ -123,15 +124,26 @@ function postRender() {
|
|
|
123
124
|
return;
|
|
124
125
|
}
|
|
125
126
|
|
|
126
|
-
streakTextPostRender(
|
|
127
|
+
streakTextPostRender();
|
|
127
128
|
versusScreenPostRender();
|
|
128
129
|
}
|
|
129
130
|
|
|
131
|
+
// ModCallback.POST_GAME_STARTED (15)
|
|
132
|
+
function postGameStarted() {
|
|
133
|
+
// We don't early return here because we need to unconditionally reset the sprites.
|
|
134
|
+
streakTextPostGameStarted();
|
|
135
|
+
}
|
|
136
|
+
|
|
130
137
|
// ModCallback.GET_SHADER_PARAMS (22)
|
|
131
138
|
function getShaderParams(
|
|
132
139
|
shaderName: string,
|
|
133
140
|
): Record<string, unknown> | undefined {
|
|
134
|
-
|
|
141
|
+
const customStage = v.run.currentCustomStage;
|
|
142
|
+
if (customStage === null) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
streakTextGetShaderParams(customStage, shaderName);
|
|
135
147
|
return undefined;
|
|
136
148
|
}
|
|
137
149
|
|
|
@@ -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>();
|