isaacscript-common 6.20.1 → 6.22.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/constants.d.ts +6 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.lua +4 -0
- package/dist/constantsFirstLast.d.ts +2 -2
- package/dist/constantsFirstLast.lua +2 -2
- package/dist/enums/RockAltType.d.ts +12 -1
- package/dist/enums/RockAltType.d.ts.map +1 -1
- package/dist/enums/RockAltType.lua +4 -2
- package/dist/enums/private/StageTravelState.d.ts +4 -3
- package/dist/enums/private/StageTravelState.d.ts.map +1 -1
- package/dist/enums/private/StageTravelState.lua +6 -4
- package/dist/features/customStage/backdrop.d.ts.map +1 -1
- package/dist/features/customStage/backdrop.lua +3 -2
- package/dist/features/customStage/customStageConstants.d.ts +11 -0
- package/dist/features/customStage/customStageConstants.d.ts.map +1 -1
- package/dist/features/customStage/customStageConstants.lua +10 -0
- package/dist/features/customStage/customStageGridEntities.d.ts +1 -0
- package/dist/features/customStage/customStageGridEntities.d.ts.map +1 -1
- package/dist/features/customStage/customStageGridEntities.lua +65 -23
- package/dist/features/customStage/exports.d.ts +17 -3
- package/dist/features/customStage/exports.d.ts.map +1 -1
- package/dist/features/customStage/exports.lua +65 -46
- package/dist/features/customStage/init.d.ts.map +1 -1
- package/dist/features/customStage/init.lua +7 -12
- package/dist/features/customStage/shadows.d.ts.map +1 -1
- package/dist/features/customStage/shadows.lua +2 -1
- package/dist/features/customStage/streakText.d.ts +0 -7
- package/dist/features/customStage/streakText.d.ts.map +1 -1
- package/dist/features/customStage/streakText.lua +52 -85
- package/dist/features/customStage/v.d.ts +13 -0
- package/dist/features/customStage/v.d.ts.map +1 -1
- package/dist/features/customStage/v.lua +6 -1
- package/dist/features/customStage/versusScreen.d.ts.map +1 -1
- package/dist/features/customStage/versusScreen.lua +32 -1
- package/dist/features/customTrapdoor/blackSprite.d.ts.map +1 -1
- package/dist/features/customTrapdoor/blackSprite.lua +2 -1
- package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts +4 -0
- package/dist/features/customTrapdoor/customTrapdoorConstants.d.ts.map +1 -1
- package/dist/features/customTrapdoor/exports.d.ts +11 -7
- package/dist/features/customTrapdoor/exports.d.ts.map +1 -1
- package/dist/features/customTrapdoor/exports.lua +6 -5
- package/dist/features/customTrapdoor/init.d.ts.map +1 -1
- package/dist/features/customTrapdoor/init.lua +35 -20
- package/dist/features/customTrapdoor/touched.lua +1 -1
- package/dist/features/customTrapdoor/v.d.ts +2 -2
- package/dist/features/customTrapdoor/v.d.ts.map +1 -1
- package/dist/features/extraConsoleCommands/commandsSubroutines.d.ts.map +1 -1
- package/dist/features/extraConsoleCommands/commandsSubroutines.lua +3 -3
- package/dist/features/playerInventory.d.ts +7 -0
- package/dist/features/playerInventory.d.ts.map +1 -1
- package/dist/features/playerInventory.lua +7 -0
- package/dist/features/saveDataManager/exports.d.ts +3 -0
- package/dist/features/saveDataManager/exports.d.ts.map +1 -1
- package/dist/features/saveDataManager/exports.lua +3 -0
- package/dist/functions/collectibleSet.d.ts +1 -1
- package/dist/functions/collectibleSet.lua +1 -1
- package/dist/functions/doors.d.ts +10 -0
- package/dist/functions/doors.d.ts.map +1 -1
- package/dist/functions/doors.lua +6 -0
- package/dist/functions/levelGrid.lua +1 -1
- package/dist/functions/log.d.ts +1 -15
- package/dist/functions/log.d.ts.map +1 -1
- package/dist/functions/log.lua +3 -218
- package/dist/functions/logEntities.d.ts +16 -0
- package/dist/functions/logEntities.d.ts.map +1 -0
- package/dist/functions/logEntities.lua +220 -0
- package/dist/functions/rockAlt.d.ts +6 -5
- package/dist/functions/rockAlt.d.ts.map +1 -1
- package/dist/functions/rockAlt.lua +147 -18
- package/dist/functions/roomData.d.ts +1 -1
- package/dist/functions/roomData.d.ts.map +1 -1
- package/dist/functions/roomTransition.d.ts +26 -0
- package/dist/functions/roomTransition.d.ts.map +1 -0
- package/dist/functions/roomTransition.lua +75 -0
- package/dist/functions/rooms.d.ts +36 -35
- package/dist/functions/rooms.d.ts.map +1 -1
- package/dist/functions/rooms.lua +94 -99
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.lua +8 -0
- package/dist/interfaces/private/CustomTrapdoorDescription.d.ts +2 -2
- package/dist/interfaces/private/CustomTrapdoorDescription.d.ts.map +1 -1
- package/dist/objects/backdropTypeToRockAltType.lua +3 -3
- package/dist/types/TrapdoorDestination.d.ts +3 -0
- package/dist/types/TrapdoorDestination.d.ts.map +1 -0
- package/dist/types/TrapdoorDestination.lua +2 -0
- package/package.json +2 -2
- package/src/constants.ts +6 -0
- package/src/constantsFirstLast.ts +2 -2
- package/src/enums/RockAltType.ts +14 -1
- package/src/enums/private/StageTravelState.ts +2 -1
- package/src/features/customStage/backdrop.ts +2 -1
- package/src/features/customStage/customStageConstants.ts +16 -0
- package/src/features/customStage/customStageGridEntities.ts +61 -0
- package/src/features/customStage/exports.ts +81 -42
- package/src/features/customStage/init.ts +7 -18
- package/src/features/customStage/shadows.ts +2 -1
- package/src/features/customStage/streakText.ts +59 -96
- package/src/features/customStage/v.ts +17 -0
- package/src/features/customStage/versusScreen.ts +29 -0
- package/src/features/customTrapdoor/blackSprite.ts +6 -1
- package/src/features/customTrapdoor/customTrapdoorConstants.ts +4 -0
- package/src/features/customTrapdoor/exports.ts +8 -6
- package/src/features/customTrapdoor/init.ts +55 -23
- package/src/features/customTrapdoor/touched.ts +4 -1
- package/src/features/customTrapdoor/v.ts +2 -5
- package/src/features/extraConsoleCommands/commandsSubroutines.ts +4 -1
- package/src/features/playerInventory.ts +7 -0
- package/src/features/saveDataManager/exports.ts +3 -0
- package/src/functions/collectibleSet.ts +1 -1
- package/src/functions/doors.ts +10 -0
- package/src/functions/levelGrid.ts +1 -1
- package/src/functions/log.ts +1 -279
- package/src/functions/logEntities.ts +276 -0
- package/src/functions/rockAlt.ts +147 -19
- package/src/functions/roomData.ts +1 -1
- package/src/functions/roomTransition.ts +78 -0
- package/src/functions/rooms.ts +104 -107
- package/src/index.ts +1 -0
- package/src/interfaces/private/CustomTrapdoorDescription.ts +2 -2
- package/src/objects/backdropTypeToRockAltType.ts +3 -3
- package/src/types/TrapdoorDestination.ts +5 -0
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EffectVariant,
|
|
3
|
+
EntityType,
|
|
4
|
+
GridEntityType,
|
|
5
|
+
} from "isaac-typescript-definitions";
|
|
6
|
+
import { getEntities, getEntityID } from "./entities";
|
|
7
|
+
import { getGridEntities, getGridEntityID } from "./gridEntities";
|
|
8
|
+
import { log } from "./log";
|
|
9
|
+
|
|
10
|
+
const IGNORE_EFFECT_VARIANTS: ReadonlySet<EffectVariant> = new Set([
|
|
11
|
+
EffectVariant.BLOOD_EXPLOSION, // 2
|
|
12
|
+
EffectVariant.BLOOD_PARTICLE, // 5
|
|
13
|
+
EffectVariant.TINY_BUG, // 21
|
|
14
|
+
EffectVariant.TINY_FLY, // 33
|
|
15
|
+
EffectVariant.WATER_DROPLET, // 41
|
|
16
|
+
EffectVariant.WALL_BUG, // 68
|
|
17
|
+
EffectVariant.FALLING_EMBER, // 87
|
|
18
|
+
EffectVariant.LIGHT, // 121
|
|
19
|
+
EffectVariant.TADPOLE, // 158
|
|
20
|
+
]);
|
|
21
|
+
|
|
22
|
+
/** Helper function for printing out every entity (or filtered entity) in the current room. */
|
|
23
|
+
export function logAllEntities(
|
|
24
|
+
this: void,
|
|
25
|
+
includeBackgroundEffects: boolean,
|
|
26
|
+
entityTypeFilter?: EntityType,
|
|
27
|
+
): void {
|
|
28
|
+
let msg = "Entities in the room";
|
|
29
|
+
if (entityTypeFilter !== undefined) {
|
|
30
|
+
msg += ` (filtered to entity type ${entityTypeFilter})`;
|
|
31
|
+
} else if (!includeBackgroundEffects) {
|
|
32
|
+
msg += " (not including background effects)";
|
|
33
|
+
}
|
|
34
|
+
msg += ":\n";
|
|
35
|
+
|
|
36
|
+
const entities = getEntities();
|
|
37
|
+
let numMatchedEntities = 0;
|
|
38
|
+
entities.forEach((entity, i) => {
|
|
39
|
+
// If a filter was specified, exclude all entities outside of the filter.
|
|
40
|
+
if (entityTypeFilter !== undefined && entity.Type !== entityTypeFilter) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const effect = entity.ToEffect();
|
|
45
|
+
if (
|
|
46
|
+
!includeBackgroundEffects &&
|
|
47
|
+
effect !== undefined &&
|
|
48
|
+
IGNORE_EFFECT_VARIANTS.has(effect.Variant)
|
|
49
|
+
) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
msg += getEntityLogLine(entity, i + 1);
|
|
54
|
+
numMatchedEntities++;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (numMatchedEntities === 0) {
|
|
58
|
+
msg += "(no entities matched)\n";
|
|
59
|
+
} else {
|
|
60
|
+
msg += `(${numMatchedEntities} total ${
|
|
61
|
+
numMatchedEntities === 1 ? "entity" : "entities"
|
|
62
|
+
})\n`;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
log(msg);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Helper function for printing out every grid entity (or filtered grid entity) in the current room.
|
|
70
|
+
*/
|
|
71
|
+
export function logAllGridEntities(
|
|
72
|
+
this: void,
|
|
73
|
+
includeWalls: boolean,
|
|
74
|
+
gridEntityTypeFilter?: GridEntityType,
|
|
75
|
+
): void {
|
|
76
|
+
let msg = "Grid entities in the room";
|
|
77
|
+
if (gridEntityTypeFilter !== undefined) {
|
|
78
|
+
msg += ` (filtered to grid entity type ${gridEntityTypeFilter})`;
|
|
79
|
+
} else if (!includeWalls) {
|
|
80
|
+
msg += " (not including walls)";
|
|
81
|
+
}
|
|
82
|
+
msg += ":\n";
|
|
83
|
+
|
|
84
|
+
const gridEntities = getGridEntities();
|
|
85
|
+
let numMatchedEntities = 0;
|
|
86
|
+
gridEntities.forEach((gridEntity) => {
|
|
87
|
+
const gridEntityIndex = gridEntity.GetGridIndex();
|
|
88
|
+
const gridEntityType = gridEntity.GetType();
|
|
89
|
+
|
|
90
|
+
// If a filter was specified, exclude all entities outside of the filter.
|
|
91
|
+
if (
|
|
92
|
+
gridEntityTypeFilter !== undefined &&
|
|
93
|
+
gridEntityType !== gridEntityTypeFilter
|
|
94
|
+
) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (
|
|
99
|
+
!includeWalls &&
|
|
100
|
+
gridEntityType === GridEntityType.WALL &&
|
|
101
|
+
gridEntityTypeFilter !== GridEntityType.WALL
|
|
102
|
+
) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
msg += getGridEntityLogLine(gridEntity, gridEntityIndex);
|
|
107
|
+
|
|
108
|
+
numMatchedEntities++;
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (numMatchedEntities === 0) {
|
|
112
|
+
msg += "(no grid entities matched)\n";
|
|
113
|
+
} else {
|
|
114
|
+
msg += `(${numMatchedEntities} total grid ${
|
|
115
|
+
numMatchedEntities === 1 ? "entity" : "entities"
|
|
116
|
+
})\n`;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
log(msg);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Helper function for logging an array of specific entities. */
|
|
123
|
+
export function logEntities(this: void, entities: Entity[]): void {
|
|
124
|
+
for (const entity of entities) {
|
|
125
|
+
logEntity(entity);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/** Helper function to log information about a specific entity. */
|
|
130
|
+
export function logEntity(this: void, entity: Entity): void {
|
|
131
|
+
const msg = getEntityLogLine(entity);
|
|
132
|
+
log(msg);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getEntityLogLine(entity: Entity, num?: int): string {
|
|
136
|
+
let msg = num === undefined ? "" : `${num}) `;
|
|
137
|
+
|
|
138
|
+
msg += getEntityID(entity);
|
|
139
|
+
|
|
140
|
+
const bomb = entity.ToBomb();
|
|
141
|
+
if (bomb !== undefined) {
|
|
142
|
+
msg += " (bomb)";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const effect = entity.ToEffect();
|
|
146
|
+
if (effect !== undefined) {
|
|
147
|
+
msg += ` (effect) (State: ${effect.State})`;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const familiar = entity.ToFamiliar();
|
|
151
|
+
if (familiar !== undefined) {
|
|
152
|
+
msg += ` (familiar) (State: ${familiar.State})`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const knife = entity.ToKnife();
|
|
156
|
+
if (knife !== undefined) {
|
|
157
|
+
msg += " (knife)";
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const laser = entity.ToLaser();
|
|
161
|
+
if (laser !== undefined) {
|
|
162
|
+
msg += " (laser)";
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const npc = entity.ToNPC();
|
|
166
|
+
if (npc !== undefined) {
|
|
167
|
+
msg += ` (NPC) (State: ${npc.State})`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const pickup = entity.ToPickup();
|
|
171
|
+
if (pickup !== undefined) {
|
|
172
|
+
msg += ` (pickup) (State: ${pickup.State})`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const player = entity.ToPlayer();
|
|
176
|
+
if (player !== undefined) {
|
|
177
|
+
msg += " (player)";
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const projectile = entity.ToProjectile();
|
|
181
|
+
if (projectile !== undefined) {
|
|
182
|
+
msg += " (projectile)";
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const tear = entity.ToTear();
|
|
186
|
+
if (tear !== undefined) {
|
|
187
|
+
msg += " (tear)";
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
msg += "\n";
|
|
191
|
+
msg += ` - Index: ${entity.Index}\n`;
|
|
192
|
+
msg += ` - InitSeed: ${entity.InitSeed}\n`;
|
|
193
|
+
msg += ` - DropSeed: ${entity.DropSeed}\n`;
|
|
194
|
+
msg += ` - Position: (${entity.Position.X}, ${entity.Position.Y})\n`;
|
|
195
|
+
msg += ` - Velocity: (${entity.Velocity.X}, ${entity.Velocity.Y})\n`;
|
|
196
|
+
msg += ` - HP: ${entity.HitPoints} / ${entity.MaxHitPoints}\n`;
|
|
197
|
+
msg += ` - Parent: ${entity.Parent}\n`;
|
|
198
|
+
msg += ` - Child: ${entity.Child}\n`;
|
|
199
|
+
msg += ` - SpawnerEntity: ${entity.SpawnerEntity}\n`;
|
|
200
|
+
msg += ` - SpawnerType / SpawnerVariant: ${entity.SpawnerType}.${entity.SpawnerVariant}\n`;
|
|
201
|
+
if (npc !== undefined) {
|
|
202
|
+
msg += ` - CanShutDoors: ${npc.CanShutDoors}\n`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return msg;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** Helper function for logging an array of specific grid entities. */
|
|
209
|
+
export function logGridEntities(this: void, gridEntities: GridEntity[]): void {
|
|
210
|
+
for (const gridEntity of gridEntities) {
|
|
211
|
+
logGridEntity(gridEntity);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Helper function for log information about a specific grid entity. */
|
|
216
|
+
export function logGridEntity(this: void, gridEntity: GridEntity): void {
|
|
217
|
+
const msg = getGridEntityLogLine(gridEntity);
|
|
218
|
+
log(msg);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function getGridEntityLogLine(gridEntity: GridEntity, num?: int): string {
|
|
222
|
+
const gridEntityDesc = gridEntity.GetSaveState();
|
|
223
|
+
|
|
224
|
+
let msg = num === undefined ? "" : `${num}) `;
|
|
225
|
+
|
|
226
|
+
msg += getGridEntityID(gridEntity);
|
|
227
|
+
|
|
228
|
+
const door = gridEntity.ToDoor();
|
|
229
|
+
if (door !== undefined) {
|
|
230
|
+
msg += " (door)";
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const pit = gridEntity.ToPit();
|
|
234
|
+
if (pit !== undefined) {
|
|
235
|
+
msg += " (pit)";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const poop = gridEntity.ToPoop();
|
|
239
|
+
if (poop !== undefined) {
|
|
240
|
+
msg += " (poop)";
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const pressurePlate = gridEntity.ToPressurePlate();
|
|
244
|
+
if (pressurePlate !== undefined) {
|
|
245
|
+
msg += " (pressurePlate)";
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const rock = gridEntity.ToRock();
|
|
249
|
+
if (rock !== undefined) {
|
|
250
|
+
msg += " (rock)";
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const spikes = gridEntity.ToSpikes();
|
|
254
|
+
if (spikes !== undefined) {
|
|
255
|
+
msg += " (spikes)";
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
const tnt = gridEntity.ToTNT();
|
|
259
|
+
if (tnt !== undefined) {
|
|
260
|
+
msg += " (TNT)";
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
msg += ` - State: ${gridEntity.State}\n`;
|
|
264
|
+
msg += ` - VarData: ${gridEntity.VarData}\n`;
|
|
265
|
+
msg += ` - Position: (${gridEntity.Position.X}, ${gridEntity.Position.Y})\n`;
|
|
266
|
+
msg += ` - SpawnSeed: ${gridEntityDesc.SpawnSeed}\n`;
|
|
267
|
+
msg += ` - VariableSeed: ${gridEntityDesc.VariableSeed})\n`;
|
|
268
|
+
if (door !== undefined) {
|
|
269
|
+
msg += ` - Slot: ${door.Slot}\n`;
|
|
270
|
+
msg += ` - Direction: ${door.Direction}\n`;
|
|
271
|
+
msg += ` - TargetRoomIndex: ${door.TargetRoomIndex}\n`;
|
|
272
|
+
msg += ` - TargetRoomType: ${door.TargetRoomType}\n`;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return msg;
|
|
276
|
+
}
|
package/src/functions/rockAlt.ts
CHANGED
|
@@ -40,6 +40,7 @@ const ROCK_ALT_CHANCES = {
|
|
|
40
40
|
COLLECTIBLE: 0.005,
|
|
41
41
|
} as const;
|
|
42
42
|
|
|
43
|
+
const COIN_VELOCITY_MULTIPLIER = 2;
|
|
43
44
|
const POLYP_PROJECTILE_SPEED = 10;
|
|
44
45
|
const POLYP_NUM_PROJECTILES = 6;
|
|
45
46
|
|
|
@@ -76,16 +77,17 @@ export function getRockAltType(): RockAltType {
|
|
|
76
77
|
* trinket is still in the pool. Thus, it will always have a chance to spawn the respective trinket
|
|
77
78
|
* (e.g. Swallowed Penny from urns).
|
|
78
79
|
*
|
|
80
|
+
* When filled buckets are destroyed, 6 projectiles will always spawn in a random pattern (in
|
|
81
|
+
* addition to any other rewards that are spawned). This function does not account for this, so if
|
|
82
|
+
* you want to specifically emulate destroying a filled bucket, you have to account for the
|
|
83
|
+
* projectiles yourself.
|
|
84
|
+
*
|
|
79
85
|
* The logic in this function is based on the rewards listed on the wiki:
|
|
80
86
|
* https://bindingofisaacrebirth.fandom.com/wiki/Rocks
|
|
81
87
|
*
|
|
82
88
|
* @param position The place to spawn the reward.
|
|
83
89
|
* @param rockAltType The type of reward to spawn. For example, `RockAltType.URN` will have a chance
|
|
84
90
|
* at spawning coins and spiders.
|
|
85
|
-
* @param variant Optional. The variant of the grid entity to emulate. Default is 0, which
|
|
86
|
-
* corresponds to a "normal" grid entity or an empty bucket. This only matters when
|
|
87
|
-
* spawning the reward for buckets. (Empty buckets have different rewards than full
|
|
88
|
-
* buckets.)
|
|
89
91
|
* @param seedOrRNG Optional. The `Seed` or `RNG` object to use. If an `RNG` object is provided, the
|
|
90
92
|
* `RNG.Next` method will be called. Default is `getRandomSeed()`. Normally, you
|
|
91
93
|
* should pass the `InitSeed` of the grid entity that was broken.
|
|
@@ -94,7 +96,6 @@ export function getRockAltType(): RockAltType {
|
|
|
94
96
|
export function spawnRockAltReward(
|
|
95
97
|
position: Vector,
|
|
96
98
|
rockAltType: RockAltType,
|
|
97
|
-
variant = 0,
|
|
98
99
|
seedOrRNG: Seed | RNG = getRandomSeed(),
|
|
99
100
|
): boolean {
|
|
100
101
|
const rng = isRNG(seedOrRNG) ? seedOrRNG : newRNG(seedOrRNG);
|
|
@@ -116,8 +117,12 @@ export function spawnRockAltReward(
|
|
|
116
117
|
return spawnRockAltRewardPolyp(position, rng);
|
|
117
118
|
}
|
|
118
119
|
|
|
119
|
-
case RockAltType.
|
|
120
|
-
return
|
|
120
|
+
case RockAltType.BUCKET_DOWNPOUR: {
|
|
121
|
+
return spawnRockAltRewardBucketDownpour(position, rng);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case RockAltType.BUCKET_DROSS: {
|
|
125
|
+
return spawnRockAltRewardBucketDross(position, rng);
|
|
121
126
|
}
|
|
122
127
|
}
|
|
123
128
|
}
|
|
@@ -135,10 +140,9 @@ function spawnRockAltRewardUrn(position: Vector, rng: RNG): boolean {
|
|
|
135
140
|
if (chance < totalChance) {
|
|
136
141
|
const numCoinsChance = getRandom(rng);
|
|
137
142
|
const numCoins = numCoinsChance < 0.5 ? 1 : 2;
|
|
138
|
-
const length = DISTANCE_OF_GRID_TILE;
|
|
139
143
|
repeat(numCoins, () => {
|
|
140
144
|
const randomVector = getRandomVector(rng);
|
|
141
|
-
const velocity = randomVector.mul(
|
|
145
|
+
const velocity = randomVector.mul(COIN_VELOCITY_MULTIPLIER);
|
|
142
146
|
spawnCoinWithSeed(CoinSubType.NULL, position, rng, velocity);
|
|
143
147
|
});
|
|
144
148
|
return true;
|
|
@@ -165,10 +169,10 @@ function spawnRockAltRewardUrn(position: Vector, rng: RNG): boolean {
|
|
|
165
169
|
}
|
|
166
170
|
|
|
167
171
|
// Since the detrimental effect is the final option, we don't need to check the chance.
|
|
168
|
-
const
|
|
169
|
-
const
|
|
172
|
+
const numEnemiesChance = getRandom(rng);
|
|
173
|
+
const numEnemies = numEnemiesChance < 0.5 ? 1 : 2;
|
|
170
174
|
const length = DISTANCE_OF_GRID_TILE * 3;
|
|
171
|
-
repeat(
|
|
175
|
+
repeat(numEnemies, () => {
|
|
172
176
|
const randomVector = getRandomVector(rng);
|
|
173
177
|
const offset = randomVector.mul(length);
|
|
174
178
|
const targetPos = position.add(offset);
|
|
@@ -378,11 +382,79 @@ function spawnRockAltRewardPolyp(position: Vector, rng: RNG): boolean {
|
|
|
378
382
|
return true;
|
|
379
383
|
}
|
|
380
384
|
|
|
381
|
-
function
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
385
|
+
function spawnRockAltRewardBucketDownpour(position: Vector, rng: RNG): boolean {
|
|
386
|
+
const chance = getRandom(rng);
|
|
387
|
+
let totalChance = 0;
|
|
388
|
+
|
|
389
|
+
totalChance += ROCK_ALT_CHANCES.NOTHING;
|
|
390
|
+
if (chance < totalChance) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
|
|
395
|
+
if (chance < totalChance) {
|
|
396
|
+
const numCoinsChance = getRandom(rng);
|
|
397
|
+
const numCoins = numCoinsChance < 0.5 ? 1 : 2;
|
|
398
|
+
repeat(numCoins, () => {
|
|
399
|
+
const randomVector = getRandomVector(rng);
|
|
400
|
+
const velocity = randomVector.mul(COIN_VELOCITY_MULTIPLIER);
|
|
401
|
+
spawnCoinWithSeed(CoinSubType.NULL, position, rng, velocity);
|
|
402
|
+
});
|
|
403
|
+
return true;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
totalChance += ROCK_ALT_CHANCES.TRINKET;
|
|
407
|
+
if (chance < totalChance) {
|
|
408
|
+
spawnTrinketWithSeed(TrinketType.SWALLOWED_PENNY, position, rng);
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
|
|
413
|
+
if (chance < totalChance) {
|
|
414
|
+
const stillInPools = isCollectibleInItemPool(
|
|
415
|
+
CollectibleType.LEECH,
|
|
416
|
+
ItemPoolType.TREASURE,
|
|
417
|
+
);
|
|
418
|
+
if (stillInPools) {
|
|
419
|
+
spawnCollectible(CollectibleType.LEECH, position, rng);
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return false;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Since the detrimental effect is the final option, we don't need to check the chance.
|
|
427
|
+
const enemiesChance = getRandom(rng);
|
|
428
|
+
const entityType =
|
|
429
|
+
enemiesChance < 0.5 ? EntityType.SPIDER : EntityType.SMALL_LEECH;
|
|
430
|
+
|
|
431
|
+
const numEnemiesChance = getRandom(rng);
|
|
432
|
+
const numEnemies = numEnemiesChance < 0.5 ? 1 : 2;
|
|
433
|
+
const jumpDistance = DISTANCE_OF_GRID_TILE * 3;
|
|
434
|
+
repeat(numEnemies, () => {
|
|
435
|
+
const randomVector = getRandomVector(rng);
|
|
436
|
+
const offset = randomVector.mul(jumpDistance);
|
|
437
|
+
const targetPos = position.add(offset);
|
|
438
|
+
// If the room has water, Spiders will automatically be replaced with Striders.
|
|
439
|
+
const spider = EntityNPC.ThrowSpider(
|
|
440
|
+
position,
|
|
441
|
+
undefined,
|
|
442
|
+
targetPos,
|
|
443
|
+
false,
|
|
444
|
+
0,
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
// There is no `ThrowLeech` function exposed in the API, so we can piggyback off of the
|
|
448
|
+
// `ThrowSpider` method.
|
|
449
|
+
if (entityType === EntityType.SMALL_LEECH && spider.Type !== entityType) {
|
|
450
|
+
spider.Morph(entityType, 0, 0, -1);
|
|
451
|
+
}
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
return true;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function spawnRockAltRewardBucketDross(position: Vector, rng: RNG): boolean {
|
|
386
458
|
const chance = getRandom(rng);
|
|
387
459
|
let totalChance = 0;
|
|
388
460
|
|
|
@@ -391,6 +463,62 @@ function spawnRockAltRewardBucket(
|
|
|
391
463
|
return false;
|
|
392
464
|
}
|
|
393
465
|
|
|
394
|
-
|
|
395
|
-
|
|
466
|
+
totalChance += ROCK_ALT_CHANCES.BASIC_DROP;
|
|
467
|
+
if (chance < totalChance) {
|
|
468
|
+
const numCoinsChance = getRandom(rng);
|
|
469
|
+
const numCoins = numCoinsChance < 0.5 ? 1 : 2;
|
|
470
|
+
repeat(numCoins, () => {
|
|
471
|
+
const randomVector = getRandomVector(rng);
|
|
472
|
+
const velocity = randomVector.mul(COIN_VELOCITY_MULTIPLIER);
|
|
473
|
+
spawnCoinWithSeed(CoinSubType.NULL, position, rng, velocity);
|
|
474
|
+
});
|
|
475
|
+
return true;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
totalChance += ROCK_ALT_CHANCES.TRINKET;
|
|
479
|
+
if (chance < totalChance) {
|
|
480
|
+
spawnTrinketWithSeed(TrinketType.BUTT_PENNY, position, rng);
|
|
481
|
+
return true;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
totalChance += ROCK_ALT_CHANCES.COLLECTIBLE;
|
|
485
|
+
if (chance < totalChance) {
|
|
486
|
+
const stillInPools = isCollectibleInItemPool(
|
|
487
|
+
CollectibleType.POOP,
|
|
488
|
+
ItemPoolType.TREASURE,
|
|
489
|
+
);
|
|
490
|
+
if (stillInPools) {
|
|
491
|
+
spawnCollectible(CollectibleType.POOP, position, rng);
|
|
492
|
+
return true;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
// Since the detrimental effect is the final option, we don't need to check the chance.
|
|
499
|
+
const enemiesChance = getRandom(rng);
|
|
500
|
+
const entityType =
|
|
501
|
+
enemiesChance < 0.5 ? EntityType.DRIP : EntityType.SMALL_LEECH;
|
|
502
|
+
|
|
503
|
+
const numEnemiesChance = getRandom(rng);
|
|
504
|
+
const numEnemies = numEnemiesChance < 0.5 ? 1 : 2;
|
|
505
|
+
const jumpDistance = DISTANCE_OF_GRID_TILE * 3;
|
|
506
|
+
repeat(numEnemies, () => {
|
|
507
|
+
const randomVector = getRandomVector(rng);
|
|
508
|
+
const offset = randomVector.mul(jumpDistance);
|
|
509
|
+
const targetPos = position.add(offset);
|
|
510
|
+
const spider = EntityNPC.ThrowSpider(
|
|
511
|
+
position,
|
|
512
|
+
undefined,
|
|
513
|
+
targetPos,
|
|
514
|
+
false,
|
|
515
|
+
0,
|
|
516
|
+
);
|
|
517
|
+
|
|
518
|
+
// There is no `ThrowLeech` or `ThrowDrip` functions exposed in the API, so we can piggyback off
|
|
519
|
+
// of the `ThrowSpider` method.
|
|
520
|
+
spider.Morph(entityType, 0, 0, -1);
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
return true;
|
|
396
524
|
}
|
|
@@ -62,7 +62,7 @@ export function getRoomDescriptor(roomGridIndex?: int): RoomDescriptor {
|
|
|
62
62
|
* Alias for the `Level.GetCurrentRoomDesc` method. Use this to make it more clear what type of
|
|
63
63
|
* `RoomDescriptor` object that you are retrieving.
|
|
64
64
|
*/
|
|
65
|
-
export function getRoomDescriptorReadOnly():
|
|
65
|
+
export function getRoomDescriptorReadOnly(): Readonly<RoomDescriptor> {
|
|
66
66
|
const level = game.GetLevel();
|
|
67
67
|
return level.GetCurrentRoomDesc();
|
|
68
68
|
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Direction,
|
|
3
|
+
DoorSlot,
|
|
4
|
+
LevelCurse,
|
|
5
|
+
RoomTransitionAnim,
|
|
6
|
+
} from "isaac-typescript-definitions";
|
|
7
|
+
import { game } from "../cachedClasses";
|
|
8
|
+
import { runNextRoom } from "../features/runNextRoom";
|
|
9
|
+
import { hasCurse } from "./curses";
|
|
10
|
+
import { getRoomData, getRoomGridIndex } from "./roomData";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Helper function to reload the current room using `Game.StartRoomTransition`.
|
|
14
|
+
*
|
|
15
|
+
* This is useful for canceling the "goto" console command or to make the `Level.SetStage` method
|
|
16
|
+
* take effect.
|
|
17
|
+
*/
|
|
18
|
+
export function reloadRoom(): void {
|
|
19
|
+
const roomGridIndex = getRoomGridIndex();
|
|
20
|
+
teleport(
|
|
21
|
+
roomGridIndex,
|
|
22
|
+
Direction.NO_DIRECTION,
|
|
23
|
+
RoomTransitionAnim.FADE,
|
|
24
|
+
true,
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Helper function to change the current room. It can be used for both teleportation and "normal"
|
|
30
|
+
* room transitions, depending on what is passed for the `direction` and `roomTransitionAnim`
|
|
31
|
+
* arguments.
|
|
32
|
+
*
|
|
33
|
+
* Use this function instead of invoking the `Game.StartRoomTransition` method directly so that:
|
|
34
|
+
* - you do not forget to set `Level.LeaveDoor` property
|
|
35
|
+
* - to prevent crashing on invalid room grid indexes
|
|
36
|
+
* - to automatically handle Curse of the Maze
|
|
37
|
+
*
|
|
38
|
+
* @param roomGridIndex The room grid index of the destination room.
|
|
39
|
+
* @param direction Optional. Default is `Direction.NO_DIRECTION`.
|
|
40
|
+
* @param roomTransitionAnim Optional. Default is `RoomTransitionAnim.TELEPORT`.
|
|
41
|
+
* @param force Optional. Whether to temporarily disable Curse of the Maze. Default is false. If set
|
|
42
|
+
* to false, then this function may not go to the provided room grid index.
|
|
43
|
+
*/
|
|
44
|
+
export function teleport(
|
|
45
|
+
roomGridIndex: int,
|
|
46
|
+
direction = Direction.NO_DIRECTION,
|
|
47
|
+
roomTransitionAnim = RoomTransitionAnim.TELEPORT,
|
|
48
|
+
force = false,
|
|
49
|
+
): void {
|
|
50
|
+
const level = game.GetLevel();
|
|
51
|
+
|
|
52
|
+
// Before starting a room transition, we must ensure that Curse of the Maze is not in effect, or
|
|
53
|
+
// else the room transition might send us to the wrong room.
|
|
54
|
+
const shouldTempDisableCurse = force && hasCurse(LevelCurse.MAZE);
|
|
55
|
+
if (shouldTempDisableCurse) {
|
|
56
|
+
level.RemoveCurses(LevelCurse.MAZE);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const roomData = getRoomData(roomGridIndex);
|
|
60
|
+
if (roomData === undefined) {
|
|
61
|
+
error(
|
|
62
|
+
`Failed to change the room to grid index ${roomGridIndex} because that room does not exist.`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// This must be set before every `Game.StartRoomTransition` method invocation or else the function
|
|
67
|
+
// can send you to the wrong room.
|
|
68
|
+
level.LeaveDoor = DoorSlot.NO_DOOR_SLOT;
|
|
69
|
+
|
|
70
|
+
game.StartRoomTransition(roomGridIndex, direction, roomTransitionAnim);
|
|
71
|
+
|
|
72
|
+
if (shouldTempDisableCurse) {
|
|
73
|
+
runNextRoom(() => {
|
|
74
|
+
const futureLevel = game.GetLevel();
|
|
75
|
+
futureLevel.AddCurse(LevelCurse.MAZE, false);
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|