isaacscript-common 8.4.6 → 8.7.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 +2 -2
- package/dist/features/characterStats.d.ts.map +1 -1
- package/dist/features/characterStats.lua +1 -2
- package/dist/features/firstLast.d.ts +6 -3
- package/dist/features/firstLast.d.ts.map +1 -1
- package/dist/features/firstLast.lua +6 -3
- package/dist/features/playerInventory.d.ts +11 -3
- package/dist/features/playerInventory.d.ts.map +1 -1
- package/dist/features/playerInventory.lua +24 -11
- package/dist/functions/bosses.d.ts +8 -2
- package/dist/functions/bosses.d.ts.map +1 -1
- package/dist/functions/bosses.lua +14 -6
- package/dist/functions/collectibleCacheFlag.d.ts +1 -1
- package/dist/functions/collectibleCacheFlag.d.ts.map +1 -1
- package/dist/functions/collectibleCacheFlag.lua +1 -2
- package/dist/functions/collectibleSet.lua +32 -32
- package/dist/functions/collectibleTag.d.ts +1 -1
- package/dist/functions/collectibleTag.d.ts.map +1 -1
- package/dist/functions/collectibleTag.lua +1 -3
- package/dist/functions/collectibles.d.ts +10 -1
- package/dist/functions/collectibles.d.ts.map +1 -1
- package/dist/functions/collectibles.lua +17 -1
- package/dist/functions/eden.d.ts +1 -1
- package/dist/functions/eden.d.ts.map +1 -1
- package/dist/functions/eden.lua +1 -2
- package/dist/functions/flying.d.ts +1 -1
- package/dist/functions/flying.d.ts.map +1 -1
- package/dist/functions/flying.lua +5 -2
- package/dist/functions/input.d.ts +2 -2
- package/dist/functions/input.d.ts.map +1 -1
- package/dist/functions/input.lua +2 -4
- package/dist/functions/playerHealth.d.ts +93 -0
- package/dist/functions/playerHealth.d.ts.map +1 -1
- package/dist/functions/playerHealth.lua +216 -33
- package/dist/functions/players.d.ts +1 -111
- package/dist/functions/players.d.ts.map +1 -1
- package/dist/functions/players.lua +20 -279
- package/dist/functions/revive.d.ts.map +1 -1
- package/dist/functions/revive.lua +2 -1
- package/dist/functions/stats.d.ts +16 -0
- package/dist/functions/stats.d.ts.map +1 -1
- package/dist/functions/stats.lua +78 -0
- package/dist/functions/transformations.d.ts +1 -1
- package/dist/functions/transformations.d.ts.map +1 -1
- package/dist/functions/trinketCacheFlag.d.ts +1 -1
- package/dist/functions/trinketCacheFlag.d.ts.map +1 -1
- package/dist/functions/trinketCacheFlag.lua +1 -3
- package/dist/functions/trinketSet.lua +32 -32
- package/dist/functions/trinkets.d.ts +14 -0
- package/dist/functions/trinkets.d.ts.map +1 -1
- package/dist/functions/trinkets.lua +26 -0
- package/dist/index.d.ts +63 -36
- package/dist/sets/bossSets.d.ts +1 -0
- package/dist/sets/bossSets.d.ts.map +1 -1
- package/dist/sets/bossSets.lua +23 -0
- package/package.json +1 -1
- package/src/enums/ModCallbackCustom.ts +2 -2
- package/src/features/characterStats.ts +1 -2
- package/src/features/firstLast.ts +6 -3
- package/src/features/playerInventory.ts +37 -8
- package/src/functions/bosses.ts +18 -6
- package/src/functions/collectibleCacheFlag.ts +3 -3
- package/src/functions/collectibleSet.ts +32 -32
- package/src/functions/collectibleTag.ts +2 -3
- package/src/functions/collectibles.ts +25 -1
- package/src/functions/eden.ts +3 -3
- package/src/functions/flying.ts +4 -4
- package/src/functions/input.ts +4 -5
- package/src/functions/playerHealth.ts +269 -7
- package/src/functions/players.ts +1 -348
- package/src/functions/revive.ts +2 -6
- package/src/functions/stats.ts +75 -0
- package/src/functions/transformations.ts +1 -1
- package/src/functions/trinketCacheFlag.ts +2 -3
- package/src/functions/trinketSet.ts +32 -32
- package/src/functions/trinkets.ts +34 -0
- package/src/sets/bossSets.ts +33 -0
package/src/functions/players.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ActiveSlot,
|
|
3
|
-
CacheFlag,
|
|
4
3
|
Challenge,
|
|
5
4
|
CollectibleType,
|
|
6
5
|
FamiliarVariant,
|
|
@@ -11,14 +10,8 @@ import {
|
|
|
11
10
|
TrinketType,
|
|
12
11
|
} from "isaac-typescript-definitions";
|
|
13
12
|
import { game, itemConfig } from "../core/cachedClasses";
|
|
14
|
-
import { HealthType } from "../enums/HealthType";
|
|
15
13
|
import { getLastElement, sumArray } from "./array";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
getCharacterMaxHeartContainers,
|
|
19
|
-
getCharacterName,
|
|
20
|
-
isVanillaCharacter,
|
|
21
|
-
} from "./characters";
|
|
14
|
+
import { getCharacterName, isVanillaCharacter } from "./characters";
|
|
22
15
|
import { getCollectibleMaxCharges } from "./collectibles";
|
|
23
16
|
import { getCollectibleArray } from "./collectibleSet";
|
|
24
17
|
import { getEnumValues } from "./enums";
|
|
@@ -27,19 +20,9 @@ import {
|
|
|
27
20
|
getPlayerIndexVanilla,
|
|
28
21
|
getPlayers,
|
|
29
22
|
} from "./playerIndex";
|
|
30
|
-
import { addTearsStat } from "./tears";
|
|
31
23
|
import { isNumber } from "./types";
|
|
32
24
|
import { repeat } from "./utils";
|
|
33
25
|
|
|
34
|
-
const STAT_CACHE_FLAGS_SET: ReadonlySet<CacheFlag> = new Set([
|
|
35
|
-
CacheFlag.DAMAGE, // 1 << 0
|
|
36
|
-
CacheFlag.FIRE_DELAY, // 1 << 1
|
|
37
|
-
CacheFlag.SHOT_SPEED, // 1 << 2
|
|
38
|
-
CacheFlag.RANGE, // 1 << 3
|
|
39
|
-
CacheFlag.SPEED, // 1 << 4
|
|
40
|
-
CacheFlag.LUCK, // 1 << 10
|
|
41
|
-
]);
|
|
42
|
-
|
|
43
26
|
export function addCollectibleCostume(
|
|
44
27
|
player: EntityPlayer,
|
|
45
28
|
collectibleType: CollectibleType,
|
|
@@ -52,71 +35,6 @@ export function addCollectibleCostume(
|
|
|
52
35
|
player.AddCostume(itemConfigItem, false);
|
|
53
36
|
}
|
|
54
37
|
|
|
55
|
-
/**
|
|
56
|
-
* Helper function to add a stat to a player based on the `CacheFlag` provided. Call this function
|
|
57
|
-
* from the `EVALUATE_CACHE` callback.
|
|
58
|
-
*
|
|
59
|
-
* Note that for `CacheFlag.FIRE_DELAY`, the "amount" argument will be interpreted as the tear stat
|
|
60
|
-
* to add (and not the amount to mutate `EntityPlayer.MaxFireDelay` by).
|
|
61
|
-
*
|
|
62
|
-
* This function supports the following cache flags:
|
|
63
|
-
* - CacheFlag.DAMAGE (1 << 0)
|
|
64
|
-
* - CacheFlag.FIRE_DELAY (1 << 1)
|
|
65
|
-
* - CacheFlag.SHOT_SPEED (1 << 2)
|
|
66
|
-
* - CacheFlag.RANGE (1 << 3)
|
|
67
|
-
* - CacheFlag.SPEED (1 << 4)
|
|
68
|
-
* - CacheFlag.LUCK (1 << 10)
|
|
69
|
-
*/
|
|
70
|
-
export function addStat(
|
|
71
|
-
player: EntityPlayer,
|
|
72
|
-
cacheFlag: CacheFlag,
|
|
73
|
-
amount: number,
|
|
74
|
-
): void {
|
|
75
|
-
if (!STAT_CACHE_FLAGS_SET.has(cacheFlag)) {
|
|
76
|
-
error(
|
|
77
|
-
`You cannot add a stat to a player with the cache flag of: ${cacheFlag}`,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
switch (cacheFlag) {
|
|
82
|
-
// 1 << 0
|
|
83
|
-
case CacheFlag.DAMAGE: {
|
|
84
|
-
player.Damage += amount;
|
|
85
|
-
break;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
// 1 << 1
|
|
89
|
-
case CacheFlag.FIRE_DELAY: {
|
|
90
|
-
addTearsStat(player, amount);
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 1 << 2
|
|
95
|
-
case CacheFlag.SHOT_SPEED: {
|
|
96
|
-
player.ShotSpeed += amount;
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
// 1 << 3
|
|
101
|
-
case CacheFlag.RANGE: {
|
|
102
|
-
player.TearHeight += amount;
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// 1 << 4
|
|
107
|
-
case CacheFlag.SPEED: {
|
|
108
|
-
player.MoveSpeed += amount;
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// 1 << 10
|
|
113
|
-
case CacheFlag.LUCK: {
|
|
114
|
-
player.Luck += amount;
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
38
|
export function addTrinketCostume(
|
|
121
39
|
player: EntityPlayer,
|
|
122
40
|
trinketType: TrinketType,
|
|
@@ -173,45 +91,6 @@ export function canPlayerCrushRocks(player: EntityPlayer): boolean {
|
|
|
173
91
|
);
|
|
174
92
|
}
|
|
175
93
|
|
|
176
|
-
/**
|
|
177
|
-
* Returns whether or not all of the player's soul-heart-type hearts are black hearts.
|
|
178
|
-
*
|
|
179
|
-
* Note that this function does not consider red heart containers.
|
|
180
|
-
*
|
|
181
|
-
* For example:
|
|
182
|
-
*
|
|
183
|
-
* - If the player has one black heart, this function would return true.
|
|
184
|
-
* - If the player has one soul heart and two black hearts, this function would return false.
|
|
185
|
-
* - If the player has no black hearts, this function will return false.
|
|
186
|
-
* - If the player has one red heart container and three black hearts, this function would return
|
|
187
|
-
* true.
|
|
188
|
-
*/
|
|
189
|
-
export function doesPlayerHaveAllBlackHearts(player: EntityPlayer): boolean {
|
|
190
|
-
const soulHearts = getPlayerSoulHearts(player);
|
|
191
|
-
const blackHearts = getPlayerBlackHearts(player);
|
|
192
|
-
|
|
193
|
-
return blackHearts > 0 && soulHearts === 0;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* Returns whether or not all of the player's soul-heart-type hearts are soul hearts.
|
|
198
|
-
*
|
|
199
|
-
* Note that this function does not consider red heart containers.
|
|
200
|
-
*
|
|
201
|
-
* For example:
|
|
202
|
-
*
|
|
203
|
-
* - If the player has two soul hearts and one black heart, this function would return false.
|
|
204
|
-
* - If the player has no soul hearts, this function will return false.
|
|
205
|
-
* - If the player has one red heart container and three soul hearts, this function would return
|
|
206
|
-
* true.
|
|
207
|
-
*/
|
|
208
|
-
export function doesPlayerHaveAllSoulHearts(player: EntityPlayer): boolean {
|
|
209
|
-
const soulHearts = getPlayerSoulHearts(player);
|
|
210
|
-
const blackHearts = getPlayerBlackHearts(player);
|
|
211
|
-
|
|
212
|
-
return soulHearts > 0 && blackHearts === 0;
|
|
213
|
-
}
|
|
214
|
-
|
|
215
94
|
/**
|
|
216
95
|
* Helper function to find the active slot that the player has the corresponding collectible type
|
|
217
96
|
* in. Returns undefined if the player does not have the collectible in any active slot.
|
|
@@ -324,39 +203,6 @@ export function getNewestPlayer(): EntityPlayer {
|
|
|
324
203
|
return newestPlayer;
|
|
325
204
|
}
|
|
326
205
|
|
|
327
|
-
/**
|
|
328
|
-
* Returns the number of slots that the player has remaining for new heart containers, accounting
|
|
329
|
-
* for broken hearts. For example, if the player is Judas and has 1 red heart containers and 2 full
|
|
330
|
-
* soul hearts and 3 broken hearts, then this function would return 6 (i.e. 12 - 1 - 2 - 3).
|
|
331
|
-
*/
|
|
332
|
-
export function getPlayerAvailableHeartSlots(player: EntityPlayer): int {
|
|
333
|
-
const maxHeartContainers = getPlayerMaxHeartContainers(player);
|
|
334
|
-
const effectiveMaxHearts = player.GetEffectiveMaxHearts();
|
|
335
|
-
const normalAndBoneHeartContainers = effectiveMaxHearts / 2;
|
|
336
|
-
const soulHearts = player.GetSoulHearts();
|
|
337
|
-
const soulHeartContainers = math.ceil(soulHearts / 2);
|
|
338
|
-
const totalHeartContainers =
|
|
339
|
-
normalAndBoneHeartContainers + soulHeartContainers;
|
|
340
|
-
const brokenHearts = player.GetBrokenHearts();
|
|
341
|
-
const totalOccupiedHeartSlots = totalHeartContainers + brokenHearts;
|
|
342
|
-
|
|
343
|
-
return maxHeartContainers - totalOccupiedHeartSlots;
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Returns the number of black hearts that the player has, excluding any soul hearts. For example,
|
|
348
|
-
* if the player has one full black heart, one full soul heart, and one half black heart, this
|
|
349
|
-
* function returns 3.
|
|
350
|
-
*
|
|
351
|
-
* This is different from the `EntityPlayer.GetBlackHearts` method, since that returns a bitmask.
|
|
352
|
-
*/
|
|
353
|
-
export function getPlayerBlackHearts(player: EntityPlayer): int {
|
|
354
|
-
const blackHeartsBitmask = player.GetBlackHearts();
|
|
355
|
-
const blackHeartBits = countSetBits(blackHeartsBitmask);
|
|
356
|
-
|
|
357
|
-
return blackHeartBits * 2;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
206
|
/**
|
|
361
207
|
* Iterates over all players and checks if any are close enough to the specified position.
|
|
362
208
|
*
|
|
@@ -445,124 +291,6 @@ export function getPlayerFromTear(entity: Entity): EntityPlayer | undefined {
|
|
|
445
291
|
return undefined;
|
|
446
292
|
}
|
|
447
293
|
|
|
448
|
-
/**
|
|
449
|
-
* Returns the number of red hearts that the player has, excluding any rotten hearts. For example,
|
|
450
|
-
* if the player has one full black heart, one full soul heart, and one half black heart, this
|
|
451
|
-
* function returns 3.
|
|
452
|
-
*
|
|
453
|
-
* This is different from the `EntityPlayer.GetHearts` method, since that returns a value that
|
|
454
|
-
* includes rotten hearts.
|
|
455
|
-
*/
|
|
456
|
-
export function getPlayerHearts(player: EntityPlayer): int {
|
|
457
|
-
const rottenHearts = player.GetRottenHearts();
|
|
458
|
-
const hearts = player.GetHearts();
|
|
459
|
-
|
|
460
|
-
return hearts - rottenHearts * 2;
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Helper function that returns the type of the rightmost heart. This does not including golden
|
|
465
|
-
* hearts or broken hearts, since they cannot be damaged directly.
|
|
466
|
-
*/
|
|
467
|
-
export function getPlayerLastHeart(player: EntityPlayer): HealthType {
|
|
468
|
-
const hearts = player.GetHearts();
|
|
469
|
-
const effectiveMaxHearts = player.GetEffectiveMaxHearts();
|
|
470
|
-
const soulHearts = player.GetSoulHearts();
|
|
471
|
-
const blackHearts = player.GetBlackHearts();
|
|
472
|
-
const eternalHearts = player.GetEternalHearts();
|
|
473
|
-
const boneHearts = player.GetBoneHearts();
|
|
474
|
-
const rottenHearts = player.GetRottenHearts();
|
|
475
|
-
|
|
476
|
-
const soulHeartSlots = soulHearts / 2;
|
|
477
|
-
const lastHeartIndex = boneHearts + soulHeartSlots - 1;
|
|
478
|
-
const isLastHeartBone = player.IsBoneHeart(lastHeartIndex);
|
|
479
|
-
|
|
480
|
-
if (isLastHeartBone) {
|
|
481
|
-
const isLastContainerEmpty = hearts <= effectiveMaxHearts - 2;
|
|
482
|
-
if (isLastContainerEmpty) {
|
|
483
|
-
return HealthType.BONE;
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
if (rottenHearts > 0) {
|
|
487
|
-
return HealthType.ROTTEN;
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
if (eternalHearts > 0) {
|
|
491
|
-
return HealthType.ETERNAL;
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
return HealthType.RED;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
if (soulHearts > 0) {
|
|
498
|
-
const numBits = getNumBitsOfN(blackHearts);
|
|
499
|
-
const finalBit = getKBitOfN(numBits - 1, blackHearts);
|
|
500
|
-
const isBlack = finalBit === 1;
|
|
501
|
-
|
|
502
|
-
if (isBlack) {
|
|
503
|
-
return HealthType.BLACK;
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// If it is not a black heart, it must be a soul heart.
|
|
507
|
-
return HealthType.SOUL;
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
if (eternalHearts > 0) {
|
|
511
|
-
return HealthType.ETERNAL;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
if (rottenHearts > 0) {
|
|
515
|
-
return HealthType.ROTTEN;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
return HealthType.RED;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
/**
|
|
522
|
-
* Returns the maximum heart containers that the provided player can have. Normally, this is 12, but
|
|
523
|
-
* it can change depending on the character (e.g. Keeper) and other things (e.g. Mother's Kiss).
|
|
524
|
-
* This function does not account for Broken Hearts; use the `getPlayerAvailableHeartSlots` helper
|
|
525
|
-
* function for that.
|
|
526
|
-
*/
|
|
527
|
-
export function getPlayerMaxHeartContainers(player: EntityPlayer): int {
|
|
528
|
-
const character = player.GetPlayerType();
|
|
529
|
-
const characterMaxHeartContainers = getCharacterMaxHeartContainers(character);
|
|
530
|
-
|
|
531
|
-
// 1
|
|
532
|
-
// Magdalene can increase her maximum heart containers with Birthright.
|
|
533
|
-
if (
|
|
534
|
-
character === PlayerType.MAGDALENE &&
|
|
535
|
-
player.HasCollectible(CollectibleType.BIRTHRIGHT)
|
|
536
|
-
) {
|
|
537
|
-
const extraMaxHeartContainersFromBirthright = 6;
|
|
538
|
-
return characterMaxHeartContainers + extraMaxHeartContainersFromBirthright;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
// 14, 33
|
|
542
|
-
// Keeper and Tainted Keeper can increase their coin containers with Mother's Kiss and Greed's
|
|
543
|
-
// Gullet.
|
|
544
|
-
if (isKeeper(player)) {
|
|
545
|
-
const numMothersKisses = player.GetTrinketMultiplier(
|
|
546
|
-
TrinketType.MOTHERS_KISS,
|
|
547
|
-
);
|
|
548
|
-
const hasGreedsGullet = player.HasCollectible(
|
|
549
|
-
CollectibleType.GREEDS_GULLET,
|
|
550
|
-
);
|
|
551
|
-
const coins = player.GetNumCoins();
|
|
552
|
-
const greedsGulletCoinContainers = hasGreedsGullet
|
|
553
|
-
? Math.floor(coins / 25)
|
|
554
|
-
: 0;
|
|
555
|
-
|
|
556
|
-
return (
|
|
557
|
-
characterMaxHeartContainers +
|
|
558
|
-
numMothersKisses +
|
|
559
|
-
greedsGulletCoinContainers
|
|
560
|
-
);
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
return characterMaxHeartContainers;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
294
|
/**
|
|
567
295
|
* Helper function to get the proper name of the player. Use this instead of the
|
|
568
296
|
* `EntityPlayer.GetName` method because it accounts for Blue Baby, Lazarus II, and Tainted
|
|
@@ -596,21 +324,6 @@ export function getPlayerNumHitsRemaining(player: EntityPlayer): int {
|
|
|
596
324
|
return hearts + soulHearts + boneHearts + eternalHearts - rottenHearts;
|
|
597
325
|
}
|
|
598
326
|
|
|
599
|
-
/**
|
|
600
|
-
* Returns the number of soul hearts that the player has, excluding any black hearts. For example,
|
|
601
|
-
* if the player has one full black heart, one full soul heart, and one half black heart, this
|
|
602
|
-
* function returns 2.
|
|
603
|
-
*
|
|
604
|
-
* This is different from the `EntityPlayer.GetSoulHearts` method, since that returns the combined
|
|
605
|
-
* number of soul hearts and black hearts.
|
|
606
|
-
*/
|
|
607
|
-
export function getPlayerSoulHearts(player: EntityPlayer): int {
|
|
608
|
-
const soulHearts = player.GetSoulHearts();
|
|
609
|
-
const blackHearts = getPlayerBlackHearts(player);
|
|
610
|
-
|
|
611
|
-
return soulHearts - blackHearts;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
327
|
/**
|
|
615
328
|
* Helper function to get all of the players that are a certain character.
|
|
616
329
|
*
|
|
@@ -658,26 +371,6 @@ export function getPlayersWithTrinket(
|
|
|
658
371
|
);
|
|
659
372
|
}
|
|
660
373
|
|
|
661
|
-
/**
|
|
662
|
-
* Helper function to determine how many heart containers that Tainted Magdalene has that will not
|
|
663
|
-
* be automatically depleted over time. By default, this is 2, but this function will return 4 so
|
|
664
|
-
* that it is consistent with the `player.GetHearts` and `player.GetMaxHearts` methods.
|
|
665
|
-
*
|
|
666
|
-
* If Tainted Magdalene has Birthright, she will gained an additional non-temporary heart container.
|
|
667
|
-
*
|
|
668
|
-
* This function does not validate whether or not the provided player is Tainted Magdalene; that
|
|
669
|
-
* should be accomplished before invoking this function.
|
|
670
|
-
*/
|
|
671
|
-
export function getTaintedMagdaleneNonTemporaryMaxHearts(
|
|
672
|
-
player: EntityPlayer,
|
|
673
|
-
): int {
|
|
674
|
-
const maxHearts = player.GetMaxHearts();
|
|
675
|
-
const hasBirthright = player.HasCollectible(CollectibleType.BIRTHRIGHT);
|
|
676
|
-
const maxNonTemporaryMaxHearts = hasBirthright ? 6 : 4;
|
|
677
|
-
|
|
678
|
-
return Math.min(maxHearts, maxNonTemporaryMaxHearts);
|
|
679
|
-
}
|
|
680
|
-
|
|
681
374
|
/**
|
|
682
375
|
* Returns the total number of collectibles amongst all players. For example, if player 1 has 1 Sad
|
|
683
376
|
* Onion and player 2 has 2 Sad Onions, then this function would return 3.
|
|
@@ -1099,43 +792,3 @@ export function useActiveItemTemp(
|
|
|
1099
792
|
): void {
|
|
1100
793
|
player.UseActiveItem(collectibleType, false, false, true, false, -1);
|
|
1101
794
|
}
|
|
1102
|
-
|
|
1103
|
-
/**
|
|
1104
|
-
* Helper function to see if a certain damage amount would deal "permanent" damage to Tainted
|
|
1105
|
-
* Magdalene.
|
|
1106
|
-
*
|
|
1107
|
-
* Tainted Magdalene has "permanent" health and "temporary" health. When standing still and doing
|
|
1108
|
-
* nothing, all of Tainted Magdalene's temporary health will eventually go away.
|
|
1109
|
-
*
|
|
1110
|
-
* Before using this function, it is expected that you check to see if the player is Tainted
|
|
1111
|
-
* Magdalene first, or else it will give a nonsensical result.
|
|
1112
|
-
*/
|
|
1113
|
-
export function wouldDamageTaintedMagdaleneNonTemporaryHeartContainers(
|
|
1114
|
-
player: EntityPlayer,
|
|
1115
|
-
damageAmount: float,
|
|
1116
|
-
): boolean {
|
|
1117
|
-
// Regardless of the damage amount, damage to a player cannot remove a soul heart and a red heart
|
|
1118
|
-
// at the same time.
|
|
1119
|
-
const soulHearts = player.GetSoulHearts();
|
|
1120
|
-
if (soulHearts > 0) {
|
|
1121
|
-
return false;
|
|
1122
|
-
}
|
|
1123
|
-
|
|
1124
|
-
// Regardless of the damage amount, damage to a player cannot remove a bone heart and a red heart
|
|
1125
|
-
// at the same time.
|
|
1126
|
-
const boneHearts = player.GetBoneHearts();
|
|
1127
|
-
if (boneHearts > 0) {
|
|
1128
|
-
return false;
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
// Account for rotten hearts eating away at more red hearts than usual.
|
|
1132
|
-
const hearts = player.GetHearts();
|
|
1133
|
-
const rottenHearts = player.GetRottenHearts();
|
|
1134
|
-
const effectiveDamageAmount =
|
|
1135
|
-
damageAmount + Math.min(rottenHearts, damageAmount);
|
|
1136
|
-
|
|
1137
|
-
const heartsAfterDamage = hearts - effectiveDamageAmount;
|
|
1138
|
-
const nonTemporaryMaxHearts =
|
|
1139
|
-
getTaintedMagdaleneNonTemporaryMaxHearts(player);
|
|
1140
|
-
return heartsAfterDamage < nonTemporaryMaxHearts;
|
|
1141
|
-
}
|
package/src/functions/revive.ts
CHANGED
|
@@ -11,12 +11,8 @@ import {
|
|
|
11
11
|
TAINTED_SAMSON_BERSERK_CHARGE_FROM_TAKING_DAMAGE,
|
|
12
12
|
} from "../core/constants";
|
|
13
13
|
import { getCharacterDeathAnimationName } from "./characters";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
getPlayerNumHitsRemaining,
|
|
17
|
-
hasLostCurse,
|
|
18
|
-
isKeeper,
|
|
19
|
-
} from "./players";
|
|
14
|
+
import { getPlayerMaxHeartContainers } from "./playerHealth";
|
|
15
|
+
import { getPlayerNumHitsRemaining, hasLostCurse, isKeeper } from "./players";
|
|
20
16
|
import { getLastFrameOfAnimation } from "./sprites";
|
|
21
17
|
import { giveTrinketsBack, temporarilyRemoveTrinket } from "./trinketGive";
|
|
22
18
|
|
package/src/functions/stats.ts
CHANGED
|
@@ -1,5 +1,80 @@
|
|
|
1
1
|
import { CacheFlag } from "isaac-typescript-definitions";
|
|
2
2
|
import { DEFAULT_PLAYER_STAT_MAP } from "../maps/defaultPlayerStatMap";
|
|
3
|
+
import { addTearsStat } from "./tears";
|
|
4
|
+
|
|
5
|
+
const STAT_CACHE_FLAGS_SET: ReadonlySet<CacheFlag> = new Set([
|
|
6
|
+
CacheFlag.DAMAGE, // 1 << 0
|
|
7
|
+
CacheFlag.FIRE_DELAY, // 1 << 1
|
|
8
|
+
CacheFlag.SHOT_SPEED, // 1 << 2
|
|
9
|
+
CacheFlag.RANGE, // 1 << 3
|
|
10
|
+
CacheFlag.SPEED, // 1 << 4
|
|
11
|
+
CacheFlag.LUCK, // 1 << 10
|
|
12
|
+
]);
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Helper function to add a stat to a player based on the `CacheFlag` provided. Call this function
|
|
16
|
+
* from the `EVALUATE_CACHE` callback.
|
|
17
|
+
*
|
|
18
|
+
* Note that for `CacheFlag.FIRE_DELAY`, the "amount" argument will be interpreted as the tear stat
|
|
19
|
+
* to add (and not the amount to mutate `EntityPlayer.MaxFireDelay` by).
|
|
20
|
+
*
|
|
21
|
+
* This function supports the following cache flags:
|
|
22
|
+
* - CacheFlag.DAMAGE (1 << 0)
|
|
23
|
+
* - CacheFlag.FIRE_DELAY (1 << 1)
|
|
24
|
+
* - CacheFlag.SHOT_SPEED (1 << 2)
|
|
25
|
+
* - CacheFlag.RANGE (1 << 3)
|
|
26
|
+
* - CacheFlag.SPEED (1 << 4)
|
|
27
|
+
* - CacheFlag.LUCK (1 << 10)
|
|
28
|
+
*/
|
|
29
|
+
export function addStat(
|
|
30
|
+
player: EntityPlayer,
|
|
31
|
+
cacheFlag: CacheFlag,
|
|
32
|
+
amount: number,
|
|
33
|
+
): void {
|
|
34
|
+
if (!STAT_CACHE_FLAGS_SET.has(cacheFlag)) {
|
|
35
|
+
error(
|
|
36
|
+
`You cannot add a stat to a player with the cache flag of: ${cacheFlag}`,
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
switch (cacheFlag) {
|
|
41
|
+
// 1 << 0
|
|
42
|
+
case CacheFlag.DAMAGE: {
|
|
43
|
+
player.Damage += amount;
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 1 << 1
|
|
48
|
+
case CacheFlag.FIRE_DELAY: {
|
|
49
|
+
addTearsStat(player, amount);
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 1 << 2
|
|
54
|
+
case CacheFlag.SHOT_SPEED: {
|
|
55
|
+
player.ShotSpeed += amount;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 1 << 3
|
|
60
|
+
case CacheFlag.RANGE: {
|
|
61
|
+
player.TearHeight += amount;
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 1 << 4
|
|
66
|
+
case CacheFlag.SPEED: {
|
|
67
|
+
player.MoveSpeed += amount;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 1 << 10
|
|
72
|
+
case CacheFlag.LUCK: {
|
|
73
|
+
player.Luck += amount;
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
3
78
|
|
|
4
79
|
/**
|
|
5
80
|
* Returns the starting stat that Isaac (the default character) starts with. For example, if you
|
|
@@ -47,7 +47,7 @@ const TRANSFORMATIONS_THAT_GRANT_FLYING: ReadonlySet<PlayerForm> = new Set([
|
|
|
47
47
|
*/
|
|
48
48
|
export function getCollectibleTypesForTransformation(
|
|
49
49
|
playerForm: PlayerForm,
|
|
50
|
-
):
|
|
50
|
+
): ReadonlySet<CollectibleType> {
|
|
51
51
|
const itemConfigTag = TRANSFORMATION_TO_TAG_MAP.get(playerForm);
|
|
52
52
|
if (itemConfigTag === undefined) {
|
|
53
53
|
error(
|
|
@@ -3,7 +3,6 @@ import { itemConfig } from "../core/cachedClasses";
|
|
|
3
3
|
import { getTrinketTypes } from "../features/firstLast";
|
|
4
4
|
import { getEnumValues } from "./enums";
|
|
5
5
|
import { hasFlag } from "./flag";
|
|
6
|
-
import { copySet } from "./set";
|
|
7
6
|
|
|
8
7
|
const CACHE_FLAG_TO_TRINKETS_MAP = new Map<CacheFlag, Set<TrinketType>>();
|
|
9
8
|
|
|
@@ -57,7 +56,7 @@ export function getPlayerTrinketsForCacheFlag(
|
|
|
57
56
|
*/
|
|
58
57
|
export function getTrinketsForCacheFlag(
|
|
59
58
|
cacheFlag: CacheFlag,
|
|
60
|
-
):
|
|
59
|
+
): ReadonlySet<TrinketType> {
|
|
61
60
|
lazyInitCacheFlagMap();
|
|
62
61
|
|
|
63
62
|
const trinketsSet = CACHE_FLAG_TO_TRINKETS_MAP.get(cacheFlag);
|
|
@@ -65,7 +64,7 @@ export function getTrinketsForCacheFlag(
|
|
|
65
64
|
return new Set();
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
return
|
|
67
|
+
return trinketsSet;
|
|
69
68
|
}
|
|
70
69
|
|
|
71
70
|
/** Helper function to check in the item config if a given trinket has a given cache flag. */
|
|
@@ -3,17 +3,17 @@ import { itemConfig } from "../core/cachedClasses";
|
|
|
3
3
|
import { getModdedTrinketTypes } from "../features/firstLast";
|
|
4
4
|
import { getVanillaTrinketTypes } from "./trinkets";
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
6
|
+
const ALL_TRINKET_TYPES_ARRAY: TrinketType[] = [];
|
|
7
|
+
const ALL_TRINKET_TYPES_SET = new Set<TrinketType>();
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
const
|
|
9
|
+
const VANILLA_TRINKET_TYPES_ARRAY: TrinketType[] = [];
|
|
10
|
+
const VANILLA_TRINKET_TYPES_SET = new Set<TrinketType>();
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
const
|
|
12
|
+
const MODDED_TRINKET_TYPES_ARRAY: TrinketType[] = [];
|
|
13
|
+
const MODDED_TRINKET_TYPES_SET = new Set<TrinketType>();
|
|
14
14
|
|
|
15
|
-
function
|
|
16
|
-
if (
|
|
15
|
+
function lazyInitVanillaTrinketTypes() {
|
|
16
|
+
if (VANILLA_TRINKET_TYPES_ARRAY.length > 0) {
|
|
17
17
|
return;
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -22,22 +22,22 @@ function lazyInitVanillaTrinkets() {
|
|
|
22
22
|
// Vanilla trinket types are contiguous, but we check every value just in case.
|
|
23
23
|
const itemConfigItem = itemConfig.GetTrinket(trinketType);
|
|
24
24
|
if (itemConfigItem !== undefined) {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
VANILLA_TRINKET_TYPES_ARRAY.push(trinketType);
|
|
26
|
+
VANILLA_TRINKET_TYPES_SET.add(trinketType);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function
|
|
32
|
-
if (
|
|
31
|
+
function lazyInitModdedTrinketTypes() {
|
|
32
|
+
if (MODDED_TRINKET_TYPES_ARRAY.length > 0) {
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
lazyInitVanillaTrinketTypes();
|
|
37
37
|
|
|
38
|
-
for (const trinketType of
|
|
39
|
-
|
|
40
|
-
|
|
38
|
+
for (const trinketType of VANILLA_TRINKET_TYPES_ARRAY) {
|
|
39
|
+
ALL_TRINKET_TYPES_ARRAY.push(trinketType);
|
|
40
|
+
ALL_TRINKET_TYPES_SET.add(trinketType);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const moddedTrinketTypes = getModdedTrinketTypes();
|
|
@@ -45,11 +45,11 @@ function lazyInitModdedTrinkets() {
|
|
|
45
45
|
// Modded trinket types are contiguous, but we check every value just in case.
|
|
46
46
|
const itemConfigItem = itemConfig.GetTrinket(trinketType);
|
|
47
47
|
if (itemConfigItem !== undefined) {
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
MODDED_TRINKET_TYPES_ARRAY.push(trinketType);
|
|
49
|
+
MODDED_TRINKET_TYPES_SET.add(trinketType);
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
51
|
+
ALL_TRINKET_TYPES_ARRAY.push(trinketType);
|
|
52
|
+
ALL_TRINKET_TYPES_SET.add(trinketType);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
}
|
|
@@ -64,8 +64,8 @@ function lazyInitModdedTrinkets() {
|
|
|
64
64
|
* all trinkets will necessarily be present when a mod first loads (due to mod load order).
|
|
65
65
|
*/
|
|
66
66
|
export function getModdedTrinketArray(): readonly TrinketType[] {
|
|
67
|
-
|
|
68
|
-
return
|
|
67
|
+
lazyInitModdedTrinketTypes();
|
|
68
|
+
return MODDED_TRINKET_TYPES_ARRAY;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
/**
|
|
@@ -78,8 +78,8 @@ export function getModdedTrinketArray(): readonly TrinketType[] {
|
|
|
78
78
|
* all trinkets will necessarily be present when a mod first loads (due to mod load order).
|
|
79
79
|
*/
|
|
80
80
|
export function getModdedTrinketSet(): ReadonlySet<TrinketType> {
|
|
81
|
-
|
|
82
|
-
return
|
|
81
|
+
lazyInitModdedTrinketTypes();
|
|
82
|
+
return MODDED_TRINKET_TYPES_SET;
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
/**
|
|
@@ -92,8 +92,8 @@ export function getModdedTrinketSet(): ReadonlySet<TrinketType> {
|
|
|
92
92
|
* all trinkets will necessarily be present when a mod first loads (due to mod load order).
|
|
93
93
|
*/
|
|
94
94
|
export function getTrinketArray(): readonly TrinketType[] {
|
|
95
|
-
|
|
96
|
-
return
|
|
95
|
+
lazyInitModdedTrinketTypes();
|
|
96
|
+
return ALL_TRINKET_TYPES_ARRAY;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
/**
|
|
@@ -106,8 +106,8 @@ export function getTrinketArray(): readonly TrinketType[] {
|
|
|
106
106
|
* all trinkets will necessarily be present when a mod first loads (due to mod load order).
|
|
107
107
|
*/
|
|
108
108
|
export function getTrinketSet(): ReadonlySet<TrinketType> {
|
|
109
|
-
|
|
110
|
-
return
|
|
109
|
+
lazyInitModdedTrinketTypes();
|
|
110
|
+
return ALL_TRINKET_TYPES_SET;
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
@@ -117,8 +117,8 @@ export function getTrinketSet(): ReadonlySet<TrinketType> {
|
|
|
117
117
|
* use the `getVanillaTrinketSet` helper function instead.
|
|
118
118
|
*/
|
|
119
119
|
export function getVanillaTrinketArray(): readonly TrinketType[] {
|
|
120
|
-
|
|
121
|
-
return
|
|
120
|
+
lazyInitVanillaTrinketTypes();
|
|
121
|
+
return VANILLA_TRINKET_TYPES_ARRAY;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
/**
|
|
@@ -128,6 +128,6 @@ export function getVanillaTrinketArray(): readonly TrinketType[] {
|
|
|
128
128
|
* use the `getVanillaTrinketArray` helper function instead.
|
|
129
129
|
*/
|
|
130
130
|
export function getVanillaTrinketSet(): ReadonlySet<TrinketType> {
|
|
131
|
-
|
|
132
|
-
return
|
|
131
|
+
lazyInitVanillaTrinketTypes();
|
|
132
|
+
return VANILLA_TRINKET_TYPES_SET;
|
|
133
133
|
}
|