Skip to content

Commit 38045de

Browse files
committed
Add pause inventory and persisted transformation progress
1 parent b153812 commit 38045de

9 files changed

Lines changed: 678 additions & 29 deletions

CHANGELOG.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- Added a top-left pause-only inventory browser for collectibles, active items,
6+
trinkets, held cards and runes, identified pills, and all transformation
7+
progress.
8+
- Added native pause detection from the verified Repentance PauseScreen state.
9+
- Replaced session-only transformation reconstruction with Isaac's persisted
10+
15-entry PlayerForm counter array, preserving progress across active-item
11+
replacement and save reloads.
12+
- Added native Super Bum familiar detection for correct post-merge progress.
13+
314
## 0.5.0 - 2026-08-20
415

516
- Fixed Curse of the Blind spoiler protection by reading the native level curse mask and suppressing every collectible pedestal while the curse is active.

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,9 @@ The overlay tracks the player's position and displays the nearest eligible objec
3838
- Untouched floor cards and runes remain hidden until the game marks them touched or a player holds them in a native pocket slot.
3939
- Pills remain hidden until Isaac's native ItemPool state identifies their effect.
4040
- Curse of the Blind suppresses every collectible description using the native level curse mask. The forced-blind field and question-mark sprite are retained as additional guards.
41-
- Transformation progress is calculated from the player's real owned-collectible table instead of nearby-item observations.
42-
- Replaced active items remain counted for the current run, matching persistent transformation contribution.
43-
- Native run identity and seed tracking reset learned cards, runes, and transformation history when a new run begins.
41+
- Transformation progress is read from the player's native persisted transformation state instead of nearby-item observations.
42+
- The 14 normal transformations use Isaac's persisted native PlayerForm counters, so replaced active items and saved/reloaded runs keep their real progress. Super Bum completion is read from its native merged familiar.
43+
- Native run identity and seed tracking reset learned cards and run-only knowledge when a new run begins.
4444

4545
### Artwork and presentation
4646

@@ -65,6 +65,14 @@ The **EID ⚙** button appears in the bottom-right corner while Isaac is in a me
6565

6666
The default overlay position is 140 px from the left and 50 px from the top.
6767

68+
### Pause inventory
69+
70+
While a run is paused, an **EID Items** button appears in the top-left corner.
71+
It opens a scrollable browser containing the player's current collectibles,
72+
active item, trinkets, held cards and runes, identified pills, and progress for
73+
all transformations. Selecting an item closes the browser and renders its full
74+
EID description. The button and browser disappear as soon as gameplay resumes.
75+
6876
## Compatibility
6977

7078
The current native layout supports this App Store build:
@@ -154,9 +162,9 @@ The description importer combines upstream `ab+` base tables with `rep` override
154162

155163
## How it works
156164

157-
The native iOS game has no desktop Lua mod API. This project locates the supported Isaac executable by UUID, resolves native C++ RTTI for pickup, player, slot, effect, and grid-spike types, and reads bounded snapshots from its own process.
165+
The native iOS game has no desktop Lua mod API. This project locates the supported Isaac executable by UUID, resolves native C++ RTTI for pickup, player, familiar, slot, effect, and grid-spike types, and reads bounded snapshots from its own process.
158166

159-
Those snapshots provide nearby pickup identity, player position, card/rune knowledge, pill knowledge, owned collectibles, level curses, room effects, Sacrifice Room grid state, and run identity. The mod does not patch save data or write into Isaac's native gameplay objects.
167+
Those snapshots provide nearby pickup identity, player position, card/rune knowledge, pill knowledge, current inventory, persisted transformation counters, native pause state, level curses, room effects, Sacrifice Room grid state, and run identity. The mod does not patch save data or write into Isaac's native gameplay objects.
160168

161169
Verified offsets and their validation are documented in [Native layout](docs/NATIVE_LAYOUT.md). Device and packaging results are recorded in [Test matrix](docs/TEST_MATRIX.md), and presentation differences are tracked in [EID parity](docs/EID_PARITY.md).
162170

docs/NATIVE_LAYOUT.md

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,13 @@ allocator and does not directly dereference mutable game allocations.
3333
| Pickup forced-blind byte | `0x562` | ARM64 disassembly of native `SetForceBlind` |
3434
| Crane Game prize collectible | `0x570` | ARM64 disassembly of `Entity_Slot::SetPrizeCollectible` |
3535
| Player pocket items | `Entity_Player + 0x1c10` | Live held-card and rune identity matched pocket slots |
36+
| Player trinket slots | `Entity_Player + 0x1ab0` | Native GetTrinket call sites and both live slots |
3637
| Player collectible-count table | `Entity_Player + 0x1ab8` | Live inventory changes matched collected items |
38+
| Player transformation counters | `Entity_Player + 0x1c54` | Native 15-entry add/remove, save, and restore loops |
3739
| Game level curse mask | `Game + 0x0c` | Live Curse of the Blind state matched mask `0x40` |
3840
| Game current room | `Game + 0x21550` | Live room transitions matched room descriptor state |
3941
| Game run seed | `Game + 0x25d44` | Stable within a run and changed when starting a new run |
42+
| Game pause-menu state | `Game + 0x10dfd8` | PauseScreen state switch; valid values are `0...3` |
4043
| Room grid-entity table | `Room + 0x30` | Live Sacrifice Room spikes and activation count |
4144
| Game ItemPool | `Game + 0x242c0` | Native `GetPillEffect` call sites |
4245
| ItemPool pill effect array | `ItemPool + 0xa2c` | ARM64 `GetPillEffect` implementation |
@@ -71,8 +74,34 @@ iOS drops that create a new entity with `Touched` cleared. This learned identity
7174
lasts only for the current run. A newly discovered floor card is therefore
7275
still hidden until it is actually held by a player.
7376

74-
Transformation progress comes from the player's native collectible-count
75-
table. Active collectibles are remembered after replacement for the duration
76-
of the run, matching the way transformation contribution persists. A native
77-
run seed plus a debounced empty-player state resets this history when a new run
78-
starts.
77+
The native `Entity_Player::_playerForms` array has 15 persisted `int32`
78+
counters starting at `+0x1c54`. Isaac copies the complete 60-byte array into
79+
and out of its player save state. EID transformation IDs are mapped to the
80+
native PlayerForm order rather than treated as the same enum:
81+
82+
| EID | Native | Form |
83+
| ---: | ---: | --- |
84+
| 1 | 0 | Guppy |
85+
| 2 | 2 | Fun Guy |
86+
| 3 | 1 | Lord of the Flies |
87+
| 4 | 7 | Conjoined |
88+
| 5 | 5 | Spun |
89+
| 6 | 6 | Yes Mother? |
90+
| 7 | 9 | Oh Crap |
91+
| 8 | 4 | Bob |
92+
| 9 | 8 | Leviathan |
93+
| 10 | 3 | Seraphim |
94+
| 12 | 10 | Bookworm |
95+
| 13 | 12 | Spider Baby |
96+
| 14 | 11 | Adult |
97+
| 15 | 13 | Stompy |
98+
99+
Super Bum is a familiar merge rather than a native PlayerForm. Its partial
100+
progress uses the three source collectible identities, and completion is
101+
verified from the live `Entity_Familiar` type `3`, variant `102`, so the display
102+
remains `3/3` after the source familiars merge. In co-op, each player has an
103+
independent counter array; the overlay displays the highest valid per-player
104+
counter instead of incorrectly summing two players.
105+
106+
The pause browser reads PauseScreen state `1...3` as paused and `0` as live
107+
gameplay. An invalid read fails closed and leaves the button hidden.

docs/TEST_MATRIX.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
| Untouched-card identity guard | Pass: untouched cards stay hidden; held cards are learned from the four native player pocket slots |
3434
| Native item artwork | Pass: exact Isaac collectible/trinket paths and subtype-aware pocket-item artwork rendered beside text |
3535
| Native card/rune/pill artwork | Pass: subtype-aware card and rune frames plus original EID pill-atlas frames |
36-
| Transformation progress | Pass: native owned-collectible counts, duplicate handling, active-item run history, and run-seed reset |
36+
| Transformation progress | Pass: live persisted PlayerForm counters survived a save reload and reported Conjoined `1/3` plus Bookworm `1/3`; Super Bum uses verified familiar type/variant state |
37+
| Pause detection | Pass: live PauseScreen state changed `0 -> 1 -> 0` on the supported device build |
38+
| Pause inventory | Pass: top-left button rendered 3 current items and all 15 transformations; item 312 selection opened its description; UI hid on resume |
3739
| Dice Room descriptions | Pass: live native face detection and localized effect description |
3840
| Sacrifice Room descriptions | Pass: native grid-spike detection and live next-payout counter |
3941
| Description removal transition | Pass: title, icon, and body now disappear together after collection |

include/EIDNativeProbe.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,20 @@ typedef NS_ENUM(NSInteger, EIDPickupVariant) {
2525
@property(nonatomic, copy, readonly) NSString *status;
2626
@property(nonatomic, readonly, getter=isSupportedBuild) BOOL supportedBuild;
2727
@property(atomic, readonly, getter=isGameplayActive) BOOL gameplayActive;
28+
@property(atomic, readonly, getter=isPauseStateAvailable) BOOL pauseStateAvailable;
29+
@property(atomic, readonly, getter=isPaused) BOOL paused;
2830
@property(atomic, readonly) uint32_t runSeed;
2931
@property(atomic, readonly) NSUInteger runCounter;
3032
@property(atomic, readonly, getter=isOwnedCollectibleStateAvailable) BOOL ownedCollectibleStateAvailable;
33+
@property(atomic, readonly, getter=isInventoryStateAvailable) BOOL inventoryStateAvailable;
34+
@property(atomic, readonly, getter=isTransformationStateAvailable) BOOL transformationStateAvailable;
35+
@property(atomic, readonly, getter=isSuperBumActive) BOOL superBumActive;
3136
- (void)start;
3237
- (NSArray<EIDPickupIdentity *> *)currentDescribablePickups;
38+
- (NSArray<EIDPickupIdentity *> *)currentInventoryItems;
3339
- (NSInteger)ownedCollectibleCountForID:(NSInteger)collectibleID;
3440
- (NSInteger)transformationCollectibleCountForID:(NSInteger)collectibleID;
41+
- (NSInteger)nativeTransformationCounterForFormID:(NSInteger)formID;
3542
// Compatibility API used by early integrations and exported diagnostic helpers.
3643
- (NSArray<NSNumber *> *)currentCollectibleIDs;
3744
@end
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#import <Foundation/Foundation.h>
2+
3+
@class EIDNativeProbe;
4+
5+
NS_ASSUME_NONNULL_BEGIN
6+
7+
@interface EIDTransformationProgress : NSObject
8+
@property(nonatomic, weak, nullable) EIDNativeProbe *probe;
9+
@property(nonatomic, readonly) NSInteger required;
10+
+ (instancetype)shared;
11+
- (NSArray<NSNumber *> *)allTransformationIdentifiers;
12+
- (NSArray<NSNumber *> *)transformationsForCollectible:(NSInteger)collectible;
13+
- (NSInteger)progressForTransformation:(NSInteger)transformation;
14+
- (NSString *)localizedNameForTransformation:(NSInteger)transformation;
15+
- (NSString *)englishNameForTransformation:(NSInteger)transformation;
16+
@end
17+
18+
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)