isaacscript-common 6.16.2 → 6.18.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/enums/ModCallbackCustom.d.ts +1 -1
- package/dist/features/customGridEntity.d.ts +3 -3
- package/dist/features/customGridEntity.d.ts.map +1 -1
- package/dist/features/customGridEntity.lua +18 -11
- package/dist/features/extraConsoleCommands/listCommands.lua +2 -2
- package/dist/features/persistentEntities.d.ts.map +1 -1
- package/dist/features/persistentEntities.lua +15 -5
- package/dist/features/pickupIndex.d.ts.map +1 -1
- package/dist/features/pickupIndex.lua +5 -6
- package/dist/features/roomHistory.d.ts +11 -4
- package/dist/features/roomHistory.d.ts.map +1 -1
- package/dist/features/roomHistory.lua +18 -5
- package/dist/functions/log.d.ts +1 -1
- package/dist/functions/log.d.ts.map +1 -1
- package/dist/functions/log.lua +33 -28
- package/dist/functions/npcs.d.ts +9 -0
- package/dist/functions/npcs.d.ts.map +1 -1
- package/dist/functions/npcs.lua +16 -3
- package/dist/functions/playerHealth.d.ts.map +1 -1
- package/dist/functions/playerHealth.lua +29 -12
- package/dist/functions/rockAlt.d.ts +4 -4
- package/dist/functions/rockAlt.d.ts.map +1 -1
- package/dist/functions/rockAlt.lua +38 -87
- package/dist/functions/saveFile.d.ts +5 -0
- package/dist/functions/saveFile.d.ts.map +1 -1
- package/dist/functions/saveFile.lua +112 -3
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.lua +1 -9
- package/dist/interfaces/{CustomGridEntityData.d.ts → GridEntityCustomData.d.ts} +2 -2
- package/dist/interfaces/{CustomGridEntityData.d.ts.map → GridEntityCustomData.d.ts.map} +1 -1
- package/dist/interfaces/{CustomGridEntityData.lua → GridEntityCustomData.lua} +0 -0
- package/dist/interfaces/PlayerHealth.d.ts +4 -1
- package/dist/interfaces/PlayerHealth.d.ts.map +1 -1
- package/dist/interfaces/RoomDescription.d.ts +1 -0
- package/dist/interfaces/RoomDescription.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/enums/ModCallbackCustom.ts +1 -1
- package/src/features/customGridEntity.ts +30 -17
- package/src/features/extraConsoleCommands/listCommands.ts +2 -2
- package/src/features/persistentEntities.ts +19 -5
- package/src/features/pickupIndex.ts +9 -15
- package/src/features/roomHistory.ts +24 -4
- package/src/functions/log.ts +35 -30
- package/src/functions/npcs.ts +27 -2
- package/src/functions/playerHealth.ts +25 -12
- package/src/functions/rockAlt.ts +50 -132
- package/src/functions/saveFile.ts +146 -4
- package/src/index.ts +1 -2
- package/src/interfaces/{CustomGridEntityData.ts → GridEntityCustomData.ts} +1 -1
- package/src/interfaces/PlayerHealth.ts +12 -1
- package/src/interfaces/RoomDescription.ts +1 -0
- package/dist/functions/itemPool.d.ts +0 -10
- package/dist/functions/itemPool.d.ts.map +0 -1
- package/dist/functions/itemPool.lua +0 -116
- package/src/functions/itemPool.ts +0 -153
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
ActiveSlot,
|
|
3
3
|
CollectibleType,
|
|
4
4
|
DamageFlag,
|
|
5
|
+
EntityFlag,
|
|
5
6
|
EntityType,
|
|
6
7
|
GridCollisionClass,
|
|
7
8
|
GridEntityType,
|
|
@@ -15,7 +16,7 @@ import { ModUpgraded } from "../classes/ModUpgraded";
|
|
|
15
16
|
import { DecorationVariant } from "../enums/DecorationVariant";
|
|
16
17
|
import { ModCallbackCustom } from "../enums/ModCallbackCustom";
|
|
17
18
|
import { errorIfFeaturesNotInitialized } from "../featuresInitialized";
|
|
18
|
-
import {
|
|
19
|
+
import { spawn } from "../functions/entities";
|
|
19
20
|
import { hasFlag } from "../functions/flag";
|
|
20
21
|
import {
|
|
21
22
|
removeGridEntity,
|
|
@@ -23,22 +24,23 @@ import {
|
|
|
23
24
|
} from "../functions/gridEntities";
|
|
24
25
|
import { getRoomListIndex } from "../functions/roomData";
|
|
25
26
|
import { isVector } from "../functions/vector";
|
|
26
|
-
import {
|
|
27
|
+
import { GridEntityCustomData } from "../interfaces/GridEntityCustomData";
|
|
27
28
|
import { runNextGameFrame } from "./runInNFrames";
|
|
28
29
|
import { saveDataManager } from "./saveDataManager/exports";
|
|
29
30
|
|
|
30
31
|
const FEATURE_NAME = "customGridEntity";
|
|
32
|
+
const GENERIC_PROP_SIZE_MULTIPLIER = 0.66;
|
|
31
33
|
|
|
32
34
|
const v = {
|
|
33
35
|
level: {
|
|
34
36
|
/** Indexed by room list index and grid index. */
|
|
35
|
-
customGridEntities: new DefaultMap<int, Map<int,
|
|
37
|
+
customGridEntities: new DefaultMap<int, Map<int, GridEntityCustomData>>(
|
|
36
38
|
() => new Map(),
|
|
37
39
|
),
|
|
38
40
|
},
|
|
39
41
|
|
|
40
42
|
room: {
|
|
41
|
-
|
|
43
|
+
genericPropPtrHashes: new Set<PtrHash>(),
|
|
42
44
|
manuallyUsingShovel: false,
|
|
43
45
|
},
|
|
44
46
|
};
|
|
@@ -49,8 +51,8 @@ export function customGridEntityInit(mod: ModUpgraded): void {
|
|
|
49
51
|
|
|
50
52
|
mod.AddCallback(
|
|
51
53
|
ModCallback.ENTITY_TAKE_DMG,
|
|
52
|
-
|
|
53
|
-
EntityType.
|
|
54
|
+
entityTakeDmgGenericProp,
|
|
55
|
+
EntityType.GENERIC_PROP,
|
|
54
56
|
); // 11
|
|
55
57
|
|
|
56
58
|
mod.AddCallback(
|
|
@@ -66,8 +68,8 @@ export function customGridEntityInit(mod: ModUpgraded): void {
|
|
|
66
68
|
}
|
|
67
69
|
|
|
68
70
|
// ModCallback.ENTITY_TAKE_DMG (11)
|
|
69
|
-
// EntityType.
|
|
70
|
-
function
|
|
71
|
+
// EntityType.GENERIC_PROP (960)
|
|
72
|
+
function entityTakeDmgGenericProp(
|
|
71
73
|
tookDamage: Entity,
|
|
72
74
|
_damageAmount: float,
|
|
73
75
|
damageFlags: BitFlags<DamageFlag>,
|
|
@@ -75,7 +77,7 @@ function entityTakeDmgDummy(
|
|
|
75
77
|
_damageCountdownFrames: int,
|
|
76
78
|
): boolean | undefined {
|
|
77
79
|
const ptrHash = GetPtrHash(tookDamage);
|
|
78
|
-
if (!v.room.
|
|
80
|
+
if (!v.room.genericPropPtrHashes.has(ptrHash)) {
|
|
79
81
|
return undefined;
|
|
80
82
|
}
|
|
81
83
|
|
|
@@ -105,8 +107,8 @@ function entityTakeDmgDummy(
|
|
|
105
107
|
|
|
106
108
|
postGridEntityCustomBrokenFire(gridEntity, data.gridEntityTypeCustom);
|
|
107
109
|
|
|
108
|
-
// Even though the custom grid entity is now
|
|
109
|
-
// could intend for it to persist with different graphics.
|
|
110
|
+
// Even though the custom grid entity is now broken, we do not want to remove it, as the end-user
|
|
111
|
+
// could intend for it to persist with different graphics (or take multiple hits to be destroyed).
|
|
110
112
|
return false;
|
|
111
113
|
}
|
|
112
114
|
|
|
@@ -261,6 +263,7 @@ export function spawnCustomGridEntity(
|
|
|
261
263
|
if (decoration === undefined) {
|
|
262
264
|
error("Failed to spawn a decoration for a custom grid entity.");
|
|
263
265
|
}
|
|
266
|
+
decoration.CollisionClass = gridCollisionClass;
|
|
264
267
|
|
|
265
268
|
const sprite = decoration.GetSprite();
|
|
266
269
|
sprite.Load(anm2Path, true);
|
|
@@ -270,7 +273,7 @@ export function spawnCustomGridEntity(
|
|
|
270
273
|
: defaultAnimation;
|
|
271
274
|
sprite.Play(animationToPlay, true);
|
|
272
275
|
|
|
273
|
-
const customGridEntityData:
|
|
276
|
+
const customGridEntityData: GridEntityCustomData = {
|
|
274
277
|
gridEntityTypeCustom,
|
|
275
278
|
roomListIndex,
|
|
276
279
|
gridIndex,
|
|
@@ -287,8 +290,18 @@ export function spawnCustomGridEntity(
|
|
|
287
290
|
// the monitoring for explosions in the `ENTITY_TAKE_DMG` callback.
|
|
288
291
|
if (breakable) {
|
|
289
292
|
const position = room.GetGridPosition(gridIndex);
|
|
290
|
-
const
|
|
291
|
-
|
|
293
|
+
const entity = spawn(EntityType.GENERIC_PROP, 0, 0, position);
|
|
294
|
+
entity.ClearEntityFlags(EntityFlag.APPEAR);
|
|
295
|
+
entity.Visible = false;
|
|
296
|
+
|
|
297
|
+
// By default, it is larger than a grid tile, so make it a bit smaller.
|
|
298
|
+
entity.SizeMulti = Vector(
|
|
299
|
+
GENERIC_PROP_SIZE_MULTIPLIER,
|
|
300
|
+
GENERIC_PROP_SIZE_MULTIPLIER,
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
const ptrHash = GetPtrHash(entity);
|
|
304
|
+
v.room.genericPropPtrHashes.add(ptrHash);
|
|
292
305
|
}
|
|
293
306
|
|
|
294
307
|
return decoration;
|
|
@@ -309,7 +322,7 @@ export function spawnCustomGridEntity(
|
|
|
309
322
|
* @returns The grid entity that was removed. Returns undefined if no grid entity was found at the
|
|
310
323
|
* given location or if the given grid entity was not a custom grid entity.
|
|
311
324
|
*/
|
|
312
|
-
export function
|
|
325
|
+
export function removeCustomGridEntity(
|
|
313
326
|
gridIndexOrPositionOrGridEntity: int | Vector | GridEntity,
|
|
314
327
|
updateRoom = true,
|
|
315
328
|
): GridEntity | undefined {
|
|
@@ -356,7 +369,7 @@ export function removeCustomGrid(
|
|
|
356
369
|
* containing the raw decoration grid entity and the associated entity data.
|
|
357
370
|
*/
|
|
358
371
|
export function getCustomGridEntities(): Array<
|
|
359
|
-
[gridEntity: GridEntity, data:
|
|
372
|
+
[gridEntity: GridEntity, data: GridEntityCustomData]
|
|
360
373
|
> {
|
|
361
374
|
const roomListIndex = getRoomListIndex();
|
|
362
375
|
const roomCustomGridEntities = v.level.customGridEntities.get(roomListIndex);
|
|
@@ -365,7 +378,7 @@ export function getCustomGridEntities(): Array<
|
|
|
365
378
|
}
|
|
366
379
|
|
|
367
380
|
const room = game.GetRoom();
|
|
368
|
-
const customGridEntities: Array<[GridEntity,
|
|
381
|
+
const customGridEntities: Array<[GridEntity, GridEntityCustomData]> = [];
|
|
369
382
|
for (const [gridIndex, data] of roomCustomGridEntities.entries()) {
|
|
370
383
|
const gridEntity = room.GetGridEntity(gridIndex);
|
|
371
384
|
if (gridEntity !== undefined) {
|
|
@@ -64,7 +64,7 @@ import { getEnumValues } from "../../functions/enums";
|
|
|
64
64
|
import { addFlag } from "../../functions/flag";
|
|
65
65
|
import { spawnGridEntity } from "../../functions/gridEntities";
|
|
66
66
|
import {
|
|
67
|
-
|
|
67
|
+
logPlayerEffects,
|
|
68
68
|
logRoom,
|
|
69
69
|
logSeedEffects,
|
|
70
70
|
logSounds,
|
|
@@ -515,7 +515,7 @@ export function dungeon(): void {
|
|
|
515
515
|
/** Logs the player's current temporary effects to the "log.txt" file. */
|
|
516
516
|
export function effects(): void {
|
|
517
517
|
const player = Isaac.GetPlayer();
|
|
518
|
-
|
|
518
|
+
logPlayerEffects(player);
|
|
519
519
|
printConsole('Logged the player\'s effects to the "log.txt" file.');
|
|
520
520
|
}
|
|
521
521
|
|
|
@@ -8,7 +8,7 @@ import { ModCallbackCustom } from "../enums/ModCallbackCustom";
|
|
|
8
8
|
import { errorIfFeaturesNotInitialized } from "../featuresInitialized";
|
|
9
9
|
import { spawn } from "../functions/entities";
|
|
10
10
|
import { getRoomListIndex } from "../functions/roomData";
|
|
11
|
-
import { getLatestRoomDescription } from "./roomHistory";
|
|
11
|
+
import { getLatestRoomDescription, isLeavingRoom } from "./roomHistory";
|
|
12
12
|
import { saveDataManager } from "./saveDataManager/exports";
|
|
13
13
|
|
|
14
14
|
interface PersistentEntityDescription {
|
|
@@ -56,6 +56,10 @@ export function persistentEntitiesInit(mod: ModUpgraded): void {
|
|
|
56
56
|
|
|
57
57
|
// ModCallback.POST_ENTITY_REMOVE (67)
|
|
58
58
|
function postEntityRemove(entity: Entity) {
|
|
59
|
+
checkDespawningFromPlayerLeavingRoom(entity);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function checkDespawningFromPlayerLeavingRoom(entity: Entity) {
|
|
59
63
|
const ptrHash = GetPtrHash(entity);
|
|
60
64
|
const tuple = v.room.spawnedPersistentEntities.get(ptrHash);
|
|
61
65
|
if (tuple === undefined) {
|
|
@@ -63,10 +67,20 @@ function postEntityRemove(entity: Entity) {
|
|
|
63
67
|
}
|
|
64
68
|
const index = tuple[0];
|
|
65
69
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
if (!isLeavingRoom()) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
trackDespawningPickupPosition(entity, index);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The persistent entity is despawning because the player is in the process of leaving the room.
|
|
79
|
+
* Keep track of the position for later.
|
|
80
|
+
*/
|
|
81
|
+
function trackDespawningPickupPosition(entity: Entity, index: int) {
|
|
82
|
+
// (The "latest" room description is really the previous room, because the `POST_NEW_ROOM`
|
|
83
|
+
// callback was not fired yet.)
|
|
70
84
|
const previousRoomDescription = getLatestRoomDescription();
|
|
71
85
|
const previousRoomListIndex = previousRoomDescription.roomListIndex;
|
|
72
86
|
const persistentEntityDescription: PersistentEntityDescription = {
|
|
@@ -14,7 +14,7 @@ import { getRoomListIndex } from "../functions/roomData";
|
|
|
14
14
|
import { onAscent } from "../functions/stage";
|
|
15
15
|
import { vectorEquals } from "../functions/vector";
|
|
16
16
|
import { PickupIndex } from "../types/PickupIndex";
|
|
17
|
-
import { getLatestRoomDescription } from "./roomHistory";
|
|
17
|
+
import { getLatestRoomDescription, isLeavingRoom } from "./roomHistory";
|
|
18
18
|
import { saveDataManager } from "./saveDataManager/exports";
|
|
19
19
|
|
|
20
20
|
interface PickupDescription {
|
|
@@ -27,7 +27,6 @@ const FEATURE_NAME = "pickupIndex";
|
|
|
27
27
|
const v = {
|
|
28
28
|
run: {
|
|
29
29
|
pickupCounter: 0 as PickupIndex,
|
|
30
|
-
currentRoomListIndex: 0,
|
|
31
30
|
|
|
32
31
|
pickupDataTreasureRooms: new Map<PickupIndex, PickupDescription>(),
|
|
33
32
|
pickupDataBossRooms: new Map<PickupIndex, PickupDescription>(),
|
|
@@ -81,9 +80,6 @@ function postPickupInit(pickup: EntityPickup) {
|
|
|
81
80
|
|
|
82
81
|
v.run.pickupCounter++;
|
|
83
82
|
v.room.pickupIndexes.set(ptrHash, v.run.pickupCounter);
|
|
84
|
-
|
|
85
|
-
// Additionally, keep track of which room we are storing the pointer hashes for.
|
|
86
|
-
v.run.currentRoomListIndex = getRoomListIndex();
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
// ModCallback.POST_ENTITY_REMOVE (67)
|
|
@@ -99,10 +95,7 @@ function checkDespawningFromPlayerLeavingRoom(entity: Entity) {
|
|
|
99
95
|
return;
|
|
100
96
|
}
|
|
101
97
|
|
|
102
|
-
|
|
103
|
-
if (roomListIndex === v.run.currentRoomListIndex) {
|
|
104
|
-
// This is a pickup that is despawning in the current room. For example, it could be a heart
|
|
105
|
-
// pickup that the player picked up. Thus, we do not need to keep track of it's metadata.
|
|
98
|
+
if (!isLeavingRoom()) {
|
|
106
99
|
return;
|
|
107
100
|
}
|
|
108
101
|
|
|
@@ -111,13 +104,14 @@ function checkDespawningFromPlayerLeavingRoom(entity: Entity) {
|
|
|
111
104
|
|
|
112
105
|
/**
|
|
113
106
|
* This is a pickup that is despawning because the player is in the process of leaving the room.
|
|
114
|
-
* Keep track of the metadata for later.
|
|
115
|
-
* though the `POST_NEW_ROOM` callback was not fired yet, we have already traveled to the next room.
|
|
107
|
+
* Keep track of the metadata for later.
|
|
116
108
|
*/
|
|
117
109
|
function trackDespawningPickupMetadata(
|
|
118
110
|
entity: Entity,
|
|
119
111
|
pickupIndex: PickupIndex,
|
|
120
112
|
) {
|
|
113
|
+
// The "latest" room description is really the previous room, because the `POST_NEW_ROOM` callback
|
|
114
|
+
// was not fired yet.
|
|
121
115
|
const previousRoomDescription = getLatestRoomDescription();
|
|
122
116
|
const previousRoomListIndex = previousRoomDescription.roomListIndex;
|
|
123
117
|
const pickupDescriptions = v.level.pickupData.getAndSetDefault(
|
|
@@ -176,10 +170,10 @@ function postNewRoomReordered() {
|
|
|
176
170
|
}
|
|
177
171
|
|
|
178
172
|
if (pickupIndex === undefined) {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
173
|
+
// At this point, if we do not already have an existing pickup index, we need to create a new
|
|
174
|
+
// one in order to cover the cases where mods spawn items in the `POST_NEW_ROOM` callback.
|
|
175
|
+
v.run.pickupCounter++;
|
|
176
|
+
pickupIndex = v.run.pickupCounter;
|
|
183
177
|
}
|
|
184
178
|
|
|
185
179
|
const ptrHash = GetPtrHash(pickup);
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
getRoomStageID,
|
|
11
11
|
getRoomSubType,
|
|
12
12
|
getRoomVariant,
|
|
13
|
+
getRoomVisitedCount,
|
|
13
14
|
} from "../functions/roomData";
|
|
14
15
|
import { RoomDescription } from "../interfaces/RoomDescription";
|
|
15
16
|
import { saveDataManager } from "./saveDataManager/exports";
|
|
@@ -45,6 +46,7 @@ function postNewRoomEarly() {
|
|
|
45
46
|
const roomName = getRoomName();
|
|
46
47
|
const roomGridIndex = getRoomGridIndex();
|
|
47
48
|
const roomListIndex = getRoomListIndex();
|
|
49
|
+
const roomVisitedCount = getRoomVisitedCount();
|
|
48
50
|
|
|
49
51
|
const roomDescription: RoomDescription = {
|
|
50
52
|
stage,
|
|
@@ -56,6 +58,7 @@ function postNewRoomEarly() {
|
|
|
56
58
|
roomName,
|
|
57
59
|
roomGridIndex,
|
|
58
60
|
roomListIndex,
|
|
61
|
+
roomVisitedCount,
|
|
59
62
|
};
|
|
60
63
|
v.run.roomHistory.push(roomDescription);
|
|
61
64
|
}
|
|
@@ -96,10 +99,7 @@ export function getPreviousRoomDescription(): RoomDescription {
|
|
|
96
99
|
* Helper function to get information about the most recent room that is stored in the room history
|
|
97
100
|
* array.
|
|
98
101
|
*
|
|
99
|
-
* This is useful in the `POST_ENTITY_REMOVE` callback
|
|
100
|
-
* player having left the room, the current room will have changed already, but the `POST_NEW_ROOM`
|
|
101
|
-
* callback will not have fired yet, and there will not be an entry in the room history array for
|
|
102
|
-
* the current room.
|
|
102
|
+
* This is useful in the `POST_ENTITY_REMOVE` callback; see the `isLeavingRoom` function.
|
|
103
103
|
*/
|
|
104
104
|
export function getLatestRoomDescription(): RoomDescription {
|
|
105
105
|
const latestRoomDescription = getLastElement(v.run.roomHistory);
|
|
@@ -111,3 +111,23 @@ export function getLatestRoomDescription(): RoomDescription {
|
|
|
111
111
|
|
|
112
112
|
return latestRoomDescription;
|
|
113
113
|
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Helper function to detect if the game is in the state where the room index has changed to a new
|
|
117
|
+
* room, but the entities from the previous room are currently in the process of despawning. (At
|
|
118
|
+
* this point, the `POST_NEW_ROOM` callback will not have fired yet, and there will not be an entry
|
|
119
|
+
* in the room history array for the current room.)
|
|
120
|
+
*
|
|
121
|
+
* This function is intended to be used in the `POST_ENTITY_REMOVE` callback to detect when an
|
|
122
|
+
* entity is pseudo-persistent entity such as a pickup is despawning.
|
|
123
|
+
*/
|
|
124
|
+
export function isLeavingRoom(): boolean {
|
|
125
|
+
const roomListIndex = getRoomListIndex();
|
|
126
|
+
const roomVisitedCount = getRoomVisitedCount();
|
|
127
|
+
const latestRoomDescription = getLatestRoomDescription();
|
|
128
|
+
|
|
129
|
+
return (
|
|
130
|
+
roomListIndex !== latestRoomDescription.roomListIndex ||
|
|
131
|
+
roomVisitedCount !== latestRoomDescription.roomVisitedCount
|
|
132
|
+
);
|
|
133
|
+
}
|
package/src/functions/log.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
GameStateFlag,
|
|
7
7
|
GridEntityType,
|
|
8
8
|
GridRoom,
|
|
9
|
+
HeartSubType,
|
|
9
10
|
LevelStateFlag,
|
|
10
11
|
ProjectileFlag,
|
|
11
12
|
SeedEffect,
|
|
@@ -202,34 +203,6 @@ export function logDamageFlags(
|
|
|
202
203
|
logFlags(flags, DamageFlag, "damage");
|
|
203
204
|
}
|
|
204
205
|
|
|
205
|
-
export function logEffects(this: void, player: EntityPlayer): void {
|
|
206
|
-
const effects = getEffectsList(player);
|
|
207
|
-
|
|
208
|
-
log("Logging player effects:");
|
|
209
|
-
|
|
210
|
-
if (effects.length === 0) {
|
|
211
|
-
log(" n/a (no effects)");
|
|
212
|
-
return;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
effects.forEach((effect, i) => {
|
|
216
|
-
let effectDescription: string;
|
|
217
|
-
if (effect.Item.IsCollectible()) {
|
|
218
|
-
const collectibleName = getCollectibleName(effect.Item.ID);
|
|
219
|
-
effectDescription = `Collectible: ${collectibleName}`;
|
|
220
|
-
} else if (effect.Item.IsTrinket()) {
|
|
221
|
-
const trinketName = getTrinketName(effect.Item.ID);
|
|
222
|
-
effectDescription = `Trinket: ${trinketName}`;
|
|
223
|
-
} else if (effect.Item.IsNull()) {
|
|
224
|
-
effectDescription = `Null item: ${effect.Item.ID}`;
|
|
225
|
-
} else {
|
|
226
|
-
effectDescription = `Unknown type of effect: ${effect.Item.ID}`;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
log(` ${i + 1}) ${effectDescription} (x${effect.Count})`);
|
|
230
|
-
});
|
|
231
|
-
}
|
|
232
|
-
|
|
233
206
|
/** Helper function for logging an array of specific entities. */
|
|
234
207
|
export function logEntities(this: void, entities: Entity[]): void {
|
|
235
208
|
for (const entity of entities) {
|
|
@@ -504,6 +477,34 @@ export function logMap(this: void, map: Map<AnyNotNil, unknown>): void {
|
|
|
504
477
|
log(` The size of the map was: ${map.size}`);
|
|
505
478
|
}
|
|
506
479
|
|
|
480
|
+
export function logPlayerEffects(this: void, player: EntityPlayer): void {
|
|
481
|
+
const effects = getEffectsList(player);
|
|
482
|
+
|
|
483
|
+
log("Logging player effects:");
|
|
484
|
+
|
|
485
|
+
if (effects.length === 0) {
|
|
486
|
+
log(" n/a (no effects)");
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
effects.forEach((effect, i) => {
|
|
491
|
+
let effectDescription: string;
|
|
492
|
+
if (effect.Item.IsCollectible()) {
|
|
493
|
+
const collectibleName = getCollectibleName(effect.Item.ID);
|
|
494
|
+
effectDescription = `Collectible: ${collectibleName}`;
|
|
495
|
+
} else if (effect.Item.IsTrinket()) {
|
|
496
|
+
const trinketName = getTrinketName(effect.Item.ID);
|
|
497
|
+
effectDescription = `Trinket: ${trinketName}`;
|
|
498
|
+
} else if (effect.Item.IsNull()) {
|
|
499
|
+
effectDescription = `Null item: ${effect.Item.ID}`;
|
|
500
|
+
} else {
|
|
501
|
+
effectDescription = `Unknown type of effect: ${effect.Item.ID}`;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
log(` ${i + 1}) ${effectDescription} (x${effect.Count})`);
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
507
508
|
export function logPlayerHealth(this: void, player: EntityPlayer): void {
|
|
508
509
|
const playerName = getPlayerName(player);
|
|
509
510
|
const playerHealth = getPlayerHealth(player);
|
|
@@ -519,7 +520,11 @@ export function logPlayerHealth(this: void, player: EntityPlayer): void {
|
|
|
519
520
|
log(` Broken hearts: ${playerHealth.brokenHearts}`);
|
|
520
521
|
log(` Soul charges: ${playerHealth.soulCharges}`);
|
|
521
522
|
log(` Blood charges: ${playerHealth.bloodCharges}`);
|
|
522
|
-
log(
|
|
523
|
+
log(" Soul heart types: [");
|
|
524
|
+
for (const soulHeartType of playerHealth.soulHeartTypes) {
|
|
525
|
+
log(` HeartSubType.${HeartSubType[soulHeartType]}`);
|
|
526
|
+
}
|
|
527
|
+
log(" ]");
|
|
523
528
|
}
|
|
524
529
|
|
|
525
530
|
/**
|
|
@@ -764,7 +769,6 @@ export function setLogFunctionsGlobal(): void {
|
|
|
764
769
|
globals["logArray"] = logArray;
|
|
765
770
|
globals["logColor"] = logColor;
|
|
766
771
|
globals["logDamageFlags"] = logDamageFlags;
|
|
767
|
-
globals["logEffects"] = logEffects;
|
|
768
772
|
globals["logEntities"] = logEntities;
|
|
769
773
|
globals["logEntity"] = logEntity;
|
|
770
774
|
globals["logEntityID"] = logEntityID;
|
|
@@ -777,6 +781,7 @@ export function setLogFunctionsGlobal(): void {
|
|
|
777
781
|
globals["logKColor"] = logKColor;
|
|
778
782
|
globals["logLevelStateFlags"] = logLevelStateFlags;
|
|
779
783
|
globals["logMap"] = logMap;
|
|
784
|
+
globals["logPlayerEffects"] = logPlayerEffects;
|
|
780
785
|
globals["logPlayerHealth"] = logPlayerHealth;
|
|
781
786
|
globals["logProjectileFlags"] = logProjectileFlags;
|
|
782
787
|
globals["logRoom"] = logRoom;
|
package/src/functions/npcs.ts
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
DarkEsauVariant,
|
|
7
7
|
DeathVariant,
|
|
8
8
|
EntityType,
|
|
9
|
+
HopperVariant,
|
|
9
10
|
MamaGurdyVariant,
|
|
10
11
|
MotherSubType,
|
|
11
12
|
MotherVariant,
|
|
@@ -83,17 +84,39 @@ export function isAliveExceptionNPC(npc: EntityNPC): boolean {
|
|
|
83
84
|
return true;
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
// EntityType.HOPPER (29)
|
|
88
|
+
// HopperVariant.EGGY (2)
|
|
89
|
+
if (isDyingEggyWithNoSpidersLeft(npc)) {
|
|
87
90
|
return true;
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
|
|
93
|
+
// EntityType.DADDY_LONG_LEGS (101)
|
|
94
|
+
if (isDaddyLongLegsChildStompEntity(npc)) {
|
|
95
|
+
return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// EntityType.RAGLING (256)
|
|
99
|
+
if (isRaglingDeathPatch(npc)) {
|
|
91
100
|
return true;
|
|
92
101
|
}
|
|
93
102
|
|
|
94
103
|
return false;
|
|
95
104
|
}
|
|
96
105
|
|
|
106
|
+
/**
|
|
107
|
+
* Helper function to distinguish between a normal Daddy Long Legs / Triachnid and the child entity
|
|
108
|
+
* that is spawned when the boss does the multi-stomp attack.
|
|
109
|
+
*
|
|
110
|
+
* When this attack occurs, four extra copies of Daddy Long Legs will be spawned with the same
|
|
111
|
+
* entity type, variant, and sub-type. The `Entity.Parent` property will be undefined in this case,
|
|
112
|
+
* so the way to tell them apart is to check for a non-undefined `Entity.SpawnerEntity` property.
|
|
113
|
+
*/
|
|
114
|
+
export function isDaddyLongLegsChildStompEntity(npc: EntityNPC): boolean {
|
|
115
|
+
return (
|
|
116
|
+
npc.Type === EntityType.DADDY_LONG_LEGS && npc.SpawnerEntity !== undefined
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
97
120
|
/**
|
|
98
121
|
* Helper function to detect the custom death state of an Eggy. Eggies are never actually marked
|
|
99
122
|
* dead by the game. Instead, when Eggies take fatal damage, they go into NpcState.STATE_SUICIDE and
|
|
@@ -101,6 +124,8 @@ export function isAliveExceptionNPC(npc: EntityNPC): boolean {
|
|
|
101
124
|
*/
|
|
102
125
|
export function isDyingEggyWithNoSpidersLeft(npc: EntityNPC): boolean {
|
|
103
126
|
return (
|
|
127
|
+
npc.Type === EntityType.HOPPER &&
|
|
128
|
+
npc.Variant === (HopperVariant.EGGY as int) &&
|
|
104
129
|
npc.State === NpcState.SUICIDE &&
|
|
105
130
|
npc.StateFrame >= EGGY_STATE_FRAME_OF_FINAL_SPIDER
|
|
106
131
|
);
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "isaac-typescript-definitions";
|
|
7
7
|
import { MAX_PLAYER_HEART_CONTAINERS } from "../constants";
|
|
8
8
|
import { HealthType } from "../enums/HealthType";
|
|
9
|
-
import { PlayerHealth } from "../interfaces/PlayerHealth";
|
|
9
|
+
import { PlayerHealth, SoulHeartType } from "../interfaces/PlayerHealth";
|
|
10
10
|
import { getTotalCharge } from "./charge";
|
|
11
11
|
import { getEnumValues } from "./enums";
|
|
12
12
|
import {
|
|
@@ -79,7 +79,6 @@ export function addPlayerHealthType(
|
|
|
79
79
|
*/
|
|
80
80
|
export function getPlayerHealth(player: EntityPlayer): PlayerHealth {
|
|
81
81
|
const character = player.GetPlayerType();
|
|
82
|
-
const soulHeartTypes: HeartSubType[] = [];
|
|
83
82
|
let maxHearts = player.GetMaxHearts();
|
|
84
83
|
let hearts = getPlayerHearts(player); // We use the helper function to remove rotten hearts
|
|
85
84
|
let soulHearts = player.GetSoulHearts();
|
|
@@ -115,6 +114,7 @@ export function getPlayerHealth(player: EntityPlayer): PlayerHealth {
|
|
|
115
114
|
// track which soul heart we're currently at.
|
|
116
115
|
let currentSoulHeart = 0;
|
|
117
116
|
|
|
117
|
+
const soulHeartTypes: SoulHeartType[] = [];
|
|
118
118
|
for (let i = 0; i < extraHearts; i++) {
|
|
119
119
|
let isBoneHeart = player.IsBoneHeart(i);
|
|
120
120
|
if (character === PlayerType.THE_FORGOTTEN && subPlayer !== undefined) {
|
|
@@ -333,24 +333,37 @@ export function setPlayerHealth(
|
|
|
333
333
|
|
|
334
334
|
// Add the soul / black / bone hearts.
|
|
335
335
|
let soulHeartsRemaining = playerHealth.soulHearts;
|
|
336
|
-
playerHealth.soulHeartTypes.forEach((
|
|
336
|
+
playerHealth.soulHeartTypes.forEach((soulHeartType, i) => {
|
|
337
337
|
const isHalf =
|
|
338
338
|
playerHealth.soulHearts + playerHealth.boneHearts * 2 < (i + 1) * 2;
|
|
339
339
|
let addAmount = 2;
|
|
340
|
-
if (
|
|
340
|
+
if (
|
|
341
|
+
isHalf ||
|
|
342
|
+
soulHeartType === HeartSubType.BONE ||
|
|
343
|
+
soulHeartsRemaining < 2
|
|
344
|
+
) {
|
|
341
345
|
// Fix the bug where a half soul heart to the left of a bone heart will be treated as a full
|
|
342
346
|
// soul heart.
|
|
343
347
|
addAmount = 1;
|
|
344
348
|
}
|
|
345
349
|
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
350
|
+
switch (soulHeartType) {
|
|
351
|
+
case HeartSubType.SOUL: {
|
|
352
|
+
player.AddSoulHearts(addAmount);
|
|
353
|
+
soulHeartsRemaining -= addAmount;
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
case HeartSubType.BLACK: {
|
|
358
|
+
player.AddBlackHearts(addAmount);
|
|
359
|
+
soulHeartsRemaining -= addAmount;
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
case HeartSubType.BONE: {
|
|
364
|
+
player.AddBoneHearts(addAmount);
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
354
367
|
}
|
|
355
368
|
});
|
|
356
369
|
|