Skip to content

Commit e4aa11f

Browse files
Merge pull request #797 from TeamREPENTOGON/persistent_entity_mod_data
Add EntitySaveStateManager
2 parents 8d7f0d1 + 0ca0757 commit e4aa11f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+4690
-162
lines changed

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
v1.1.x -
22
Added:
3+
* Added EntitySaveStateManager class to support persistent, modded save data for entities.
34
* EntityLaser
45
- SetInitSound(SoundEffect sound)
56
- Get/SetNumChainedLasers

libzhl/IsaacRepentance_static.cpp

Lines changed: 22 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -602,50 +602,6 @@ void Entity_Bomb::UpdateDirtColor() {
602602
}
603603
}
604604

605-
void Entity_Pickup::InitFlipState(CollectibleType collectType, bool setupCollectibleGraphics) {
606-
607-
if (_variant == PICKUP_COLLECTIBLE && CanReroll() && !_dead) {
608-
609-
EntitySaveState* emptySaveState = new EntitySaveState();
610-
611-
_flipSaveState.SetP(emptySaveState);
612-
EntitySaveState* flipState = _flipSaveState.saveState;
613-
614-
flipState->type = _type, flipState->variant = _variant;
615-
616-
RNG rng = RNG();
617-
rng.SetSeed(_initSeed, 39);
618-
unsigned int seed = rng.Next();
619-
620-
flipState->_initSeed = seed;
621-
622-
int collectibleID = (collectType != COLLECTIBLE_NULL) ? collectType : g_Game->_itemPool.GetSeededCollectible(flipState->_initSeed, true, g_Game->_room->_descriptor); //to-do: add valid itemconfig check
623-
624-
flipState->subtype = collectibleID;
625-
626-
_altPedestalANM2.Reset();
627-
if (setupCollectibleGraphics) {
628-
629-
630-
ANM2 copySprite = ANM2();
631-
copySprite.construct_from_copy(&_sprite);
632-
633-
Isaac::SwapANM2(&_altPedestalANM2, &copySprite);
634-
635-
Entity_Pickup::SetupCollectibleGraphics(&_altPedestalANM2, 1, (CollectibleType)flipState->subtype, flipState->_initSeed, false);
636-
637-
_altPedestalANM2.LoadGraphics(true);
638-
639-
_altPedestalANM2.Play(_sprite.GetAnimationData(0)->GetName().c_str(), true);
640-
_altPedestalANM2.Update();
641-
}
642-
}
643-
644-
645-
646-
return;
647-
}
648-
649605
void DestinationQuad::RotateRadians(const Vector& pivot, float radians)
650606
{
651607
if (radians == 0.0)
@@ -680,4 +636,26 @@ void DestinationQuad::RotateRadians(const Vector& pivot, float radians)
680636
_topRight += pivot;
681637
_bottomLeft += pivot;
682638
_bottomRight += pivot;
639+
}
640+
641+
ModReference* LuaEngine::GetModRefByTable(int tblIdx)
642+
{
643+
lua_State* L = this->_state;
644+
int tblAbsIdx = lua_absindex(L, tblIdx);
645+
std::list<ModReference>& mods = g_Mods;
646+
647+
for (ModReference& mod : mods)
648+
{
649+
int modRefTbl = mod._luaTableRef->_ref;
650+
lua_rawgeti(L, LUA_REGISTRYINDEX, modRefTbl);
651+
bool eq = lua_rawequal(L, tblAbsIdx, -1);
652+
lua_pop(L, 1);
653+
654+
if (eq)
655+
{
656+
return &mod;
657+
}
658+
}
659+
660+
return nullptr;
683661
}

libzhl/functions/ASM.zhl

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,4 +201,29 @@ asm EntityPlayer_RecomputeWispCollectibles_TriggerCollectibleRemoved "e8????????
201201
// ExtraRenderSteps
202202
asm Manager_Init_PreInitCursor "0f84????????c785????????000000006a20";
203203
asm Manager_Render_PreCursorRender "80bf????????000f84????????8b47??83f80274";
204-
asm Manager_Render_OnCursorRender "e8????????a1????????80b8????????000f84????????b9";
204+
asm Manager_Render_OnCursorRender "e8????????a1????????80b8????????000f84????????b9";
205+
206+
// EntitySaveStateManagement
207+
asm KAGE_ReferenceCount_EntitySaveState_DecrementReference_SaveStateDestructor "e8????????6a7857";
208+
asm Game_RestoreState_PostBackwardsStageDescRestore "8987????????8b4e??c70600000000";
209+
asm Level_Init_PostMyosotisEffect "8b35????????8d87????????8987";
210+
asm Level_RestoreGameState_PreRoomLoad "8983????????e8????????e8";
211+
asm Level_place_rooms_backwards_Boss_AssignEntitySaveStateVector "ff75??ff36ff76??e8????????8b55";
212+
asm Level_place_rooms_backwards_Treasure_AssignEntitySaveStateVector "ff75??8bcaff36";
213+
asm Level_generate_dark_closet_PostGenerateCollectibleSaveState "c745??010000008bc2";
214+
asm Room_SaveState_ClearSavedEntities "8b4e??83c174";
215+
asm Room_RestoreState_ClearSavedEntities "8b78??8b70??3bf7";
216+
asm PlayerManager_RestoreGameState_AssignBackupFamiliarData "ff75??ffb3";
217+
asm EntityPlayer_UseActiveItem_MovingBox_ClearVector "8d8f????????e8????????6a006848020000";
218+
asm EntityPlayer_StoreGameState_FamiliarStoreState "8b8e????????3b8e";
219+
asm EntityPlayer_StoreGameState_AssignUnlistedFamiliarData "ff75??ff70??ff30e8????????e9";
220+
asm EntityPickup_InitFlipState_PostSaveStateConstructor "8db7????????508bcee8????????8b0e";
221+
asm EntityPickup_TryFlip_PostRestoreFlipState "837d??0075??33c0";
222+
asm EntityNPC_ai_mothers_shadow_ChangeMineshaftRoom "8b41??6aff";
223+
224+
// EntityLifecycle
225+
asm EntityList_destructor_RemoveEntityMainEL "8b0c??8b01ff50??463b77??72??33f6";
226+
asm EntityList_destructor_RemoveEntityPersistentEL "8b0c??8b01ff50??463b77??72??8d9f";
227+
asm EntityList_Reset_RemoveNonPersistentEntity "8b0c??8b01ff50??463b73";
228+
asm EntityList_Update_RemoveEntityMainEL "8b028bcaff50??8b47";
229+
asm EntityList_Update_RemoveEntityPersistentEL "8a82????????8bca";

libzhl/functions/Entity.zhl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ struct Entity depends (AnimationState, ANM2, RNG, Vector, EntityPtr) {
193193
bool _visible : 0x171;
194194
bool _exists : 0x172;
195195
bool _dead : 0x173;
196+
bool _removedByFactory : 0x177;
196197
int _entityGridCollisionClass : 0x184;
197198
int _entityCollisionClass : 0x188;
198199
// 0x18c is a boolean related to collisions. Seems to be essentially a "_collidesWithEntity"
@@ -279,7 +280,10 @@ struct Entity depends (AnimationState, ANM2, RNG, Vector, EntityPtr) {
279280
void SetColor(ColorMod *color, int Duration, int Priority, bool Fadeout, bool Shared);
280281

281282
skip; // SetCollisionDamage
282-
skip; // ClearReferences
283+
284+
"558bec515356578bf98b8f":
285+
void ClearReferences();
286+
283287
skip; // CanShutDoors
284288
skip; // IsBoss
285289
skip; // IsValidTarget

libzhl/functions/EntityFactory.zhl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"558bec518b45??535657":
22
__thiscall Entity* EntityFactory::Create(unsigned int type, bool force);
33

4-
struct EntityFactory depends(Entity, Entity_Familiar){
5-
Entity_Familiar _familiar[64] : 0xf8008;
4+
struct EntityFactory depends(Entity, Entity_Familiar, Entity_Pickup){
5+
Entity_Familiar _familiar[64] : 0x130008;
6+
Entity_Pickup _pickup[512] : 0x357630;
67
} : 0x695c60 ;

libzhl/functions/EntityFamiliar.zhl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ __thiscall void Entity_Familiar::delirium_morph();
6767
"558bec6aff68????????64a1????????5083ec24535657a1????????33c5508d45??64a3????????894d??8b0d":
6868
static __fastcall int Entity_Familiar::GetRandomWisp(RNG& rng);
6969

70+
"558bec6aff68????????64a1????????5081ec80000000a1????????33c58945??535657508d45??64a3????????8bf9":
71+
__thiscall void Entity_Familiar::RestoreState(FamiliarData* saveData);
72+
7073
struct Entity_Familiar depends (ColorMod, ItemConfig_Item, NPCAI_Pathfinder) : public Entity {
7174
{{
7275
inline Entity_Player** GetPlayer() { return &this->_player; }

libzhl/functions/EntityNPC.zhl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,9 @@ __thiscall void Entity_NPC::Hornfel_UpdateAI();
9494
"558bec83e4f85153568bf15783be????????0a":
9595
__thiscall void Entity_NPC::Hornfel_UpdateFrame();
9696

97+
"558bec6aff68????????64a1????????5081ec2c010000":
98+
static __cdecl void Entity_NPC::moms_heart_mausoleum_death();
99+
97100
/*
98101
// Minecart functions
99102
"558bec51538bd98b8b":

libzhl/functions/EntityPickup.zhl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,6 @@ struct Entity_Pickup depends (ANM2, KAGE_SmartPointer_EntitySaveState) : public
8989
static inline bool ShouldIgnoreModifiers() { return s_EntityPickup_IgnoreModifiers != 0; }
9090
static inline void BeginIgnoreModifiers() { s_EntityPickup_IgnoreModifiers++; }
9191
static inline void EndIgnoreModifiers() { s_EntityPickup_IgnoreModifiers--; }
92-
93-
void LIBZHL_API Entity_Pickup::InitFlipState(CollectibleType collectType, bool setupCollectibleGraphics);
9492
}}
9593

9694
__vtable {

libzhl/functions/EntityPlayer.zhl

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ __thiscall void Entity_Player::AnimatePickup(ANM2 *param_1,bool param_2,char *pa
88
"538bdc83ec0883e4f883c404558b6b??896c24??8bec6aff68????????64a1????????505383ec085657a1????????33c5508d45??64a3????????8bf9":
99
__thiscall void Entity_Player::constructor();
1010

11+
"558bec6aff68????????64a1????????5051535657a1????????33c5508d45??64a3????????8bf98b8f":
12+
__thiscall void Entity_Player::destructor();
13+
1114
"558bec83e4f8a1????????83ec1483b8????????02":
1215
__thiscall void Entity_Player::update_red_hearts();
1316

@@ -570,10 +573,19 @@ __thiscall bool Entity_Player::HasInvincibility(uint64_t damageFlags, EntityRef*
570573
"558bec6aff68????????64a1????????50b8dc190000":
571574
__thiscall void Entity_Player::UseActiveItem(short* resultFlags, CollectibleType collectible, unsigned int useFlags, int activeSlot, int varData);
572575

576+
"538bdc83ec0883e4f883c404558b6b??896c24??8bec6aff68????????64a1????????505381ec800000005657a1????????33c5508d45??64a3????????8bf9":
577+
__thiscall void Entity_Player::StoreGameState(GameStatePlayer* saveState, bool saveTemporaryFamiliars);
578+
579+
"538bdc83ec0883e4f883c404558b6b??896c24??8bec6aff68????????64a1????????505383ec40a1????????33c58945??5657508d45??64a3????????8bf9897d??83bf????????00":
580+
__thiscall void Entity_Player::RestoreGameState(GameStatePlayer* saveState);
581+
582+
"558bec83e4f883ec6453568b75":
583+
__thiscall void Entity_Player::RestoreGameState_PostLevelInit(GameStatePlayer* saveState);
584+
573585
"558bec83e4f883ec1453568bf157897424":
574586
__thiscall Entity_Pickup* Entity_Player::DropTrinket(Vector* DropPos, bool ReplaceTick);
575587

576-
struct Entity_Player depends (Vector, ANM2, ColorMod, KColor, PlayerCostumeMap, TemporaryEffects, ConsumableData, History, BagOfCraftingOutput, EntityDesc) : public Entity { {{
588+
struct Entity_Player depends (Vector, ANM2, ColorMod, KColor, PlayerCostumeMap, TemporaryEffects, ConsumableData, History, BagOfCraftingOutput, EntityDesc, GameStatePlayer) : public Entity { {{
577589
inline int* GetMaxHearts() {return &this->_maxHearts; }
578590
inline int* GetRedHearts() {return &this->_redHearts; }
579591
inline int* GetEternalHearts() {return &this->_eternalHearts; }
@@ -806,6 +818,9 @@ struct Entity_Player depends (Vector, ANM2, ColorMod, KColor, PlayerCostumeMap,
806818
int _wildCardItem : 0x2068;
807819
bool _isCoopGhost : 0x207d;
808820
PlayerHUD* _playerHUD : 0x20a8;
821+
bool _hasUnlistedRestoreState : 0x20bc; // States if the _unlistedRestoreState is usable
822+
GameStatePlayer _unlistedRestoreState : 0x20c0; // This state represents the state the player was in before being replaced by a player that was not in the player list (this is used by T.Lazarus and Esau Jr.)
823+
Entity_Player* _replacedPlayer : 0x267c; // The other player in the last PlayerManager::ReplacePlayer for this player
809824
} : 0x2ea0;
810825

811826
"68(????????)68????????f30f59c16a00":
Lines changed: 7 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"33c0c701000000008941":
22
__thiscall void EntitySaveState::constructor();
33

4-
"558bec6aff68????????64a1????????505657a1????????33c5508d45??64a3????????8bf98d77??c745??000000008b4e??85c974??8b018b40??ffd084c074??a1????????85c074??56ffd083c4048d77??c745??010000008b4e??85c974??8b018b40??ffd084c074??a1????????85c074??56ffd083c4048b4d??64890d????????595f5e8be55dc3??????????????????????????????????????558bec6aff68????????64a1????????5083ec14":
5-
__thiscall void** EntitySaveState::Clear();
4+
"558bec6aff68????????64a1????????505657a1????????33c5508d45??64a3????????8bf98d77??c745??000000008b4e??85c974??8b018b40??ffd084c074??a1????????85c074??56ffd083c4048d77??c745??010000008b4e??85c974??8b018b40??ffd084c074??a1????????85c074??56ffd083c4048b4d??64890d????????595f5e8be55dc3??????????????????????????????????????55":
5+
__thiscall void EntitySaveState::destructor();
66

7-
struct EntitySaveState depends (Vector) {
7+
struct EntitySaveState depends (Vector, KAGE_SmartPointer_EntitySaveState) {
88
int type : 0x0;
99
int variant : 0x4;
1010
int subtype : 0x8;
@@ -13,12 +13,12 @@ struct EntitySaveState depends (Vector) {
1313
unsigned int intStorage3 : 0x14;
1414
int intStorage4 : 0x18;
1515
int intStorage5 : 0x1c;
16-
int16_t gridSpawnIdx : 0x20;
16+
int16_t gridSpawnIdx : 0x20; // HIJACKED by ESSM
1717
bool boolStorage1 : 0x22;
1818
bool boolStorage2 : 0x23;
1919
unsigned int intStorage6 : 0x24;
2020
Vector targetPosition : 0x28;
21-
unsigned int _intStorage7 : 0x30;
21+
unsigned int _intStorage7 : 0x30; // HIJACKED by ESSM
2222
unsigned int _initSeed : 0x34;
2323
unsigned int _dropSeed : 0x38;
2424
int spawnerType : 0x3c;
@@ -27,71 +27,11 @@ struct EntitySaveState depends (Vector) {
2727
float floatStorage2 : 0x54;
2828
unsigned int intStorage8: 0x5c;
2929
int8_t byteStorage : 0x60;
30+
KAGE_SmartPointer_EntitySaveState entitySaveState : 0x6c;
3031

3132
{{
32-
struct Bomb;
33-
struct Pickup;
34-
struct NPC; // FIREPLACE and MOVABLE_TNT only
35-
struct Effect;
36-
struct Minecart;
37-
3833
EntitySaveState() {
3934
this->constructor();
4035
}
41-
4236
}}
43-
} : 0x78;
44-
45-
{{
46-
struct EntitySaveState::Bomb
47-
{
48-
static inline uint32_t* GetScale(EntitySaveState& saveState) { return (uint32_t*)&saveState.intStorage1; }
49-
static inline BitSet128* GetTearFlags(EntitySaveState& saveState) { return (BitSet128*)&saveState.intStorage2; }
50-
static inline bool* GetIsFetus(EntitySaveState& saveState) { return (bool*)&saveState.boolStorage2; }
51-
static inline float* GetExplosionDamage(EntitySaveState& saveState) { return (float*)&saveState.floatStorage1; }
52-
static inline float* GetRadiusMulti(EntitySaveState& saveState) { return (float*)&saveState.floatStorage2; }
53-
static inline int8_t* GetSpawnerPlayerIndex(EntitySaveState& saveState) { return (int8_t*)&saveState.byteStorage; } // -1 if not spawned by player
54-
};
55-
56-
struct EntitySaveState::Pickup
57-
{
58-
static inline int* GetCharge(EntitySaveState& saveState) { return (int*)&saveState.intStorage2; }
59-
static inline int* GetPrice(EntitySaveState& saveState) { return (int*)&saveState.intStorage3; }
60-
static inline bool* GetAutoUpdatePrice(EntitySaveState& saveState) { return (bool*)&saveState.byteStorage; }
61-
static inline int* GetShopItemId(EntitySaveState& saveState) { return (int*)&saveState.intStorage4; }
62-
static inline bool* GetTouched(EntitySaveState& saveState) { return (bool*)&saveState.boolStorage1; }
63-
static inline int* GetOptionsPickupIndex(EntitySaveState& saveState) { return (int*)&saveState._intStorage7; }
64-
static inline int* GetTimeout(EntitySaveState& saveState) { return (int*)&saveState.intStorage5; }
65-
static inline bool* GetIsBlind(EntitySaveState& saveState) { return (bool*)&saveState.boolStorage2; }
66-
static inline int* GetAlternatePedestal(EntitySaveState& saveState) { return (int*)&saveState.intStorage1; }
67-
static inline int* GetActiveVarData(EntitySaveState& saveState) { return (int*)&saveState.intStorage8; }
68-
static inline int* GetSourcePoolType(EntitySaveState& saveState) { return (int*)&saveState.floatStorage1; }
69-
static inline float* GetSpriteScale(EntitySaveState& saveState) { return (float*)&saveState.floatStorage2; }
70-
static inline std::array<int, 8>* GetCycleCollectibles(EntitySaveState& saveState) { return *(std::array<int, 8>**)((char*)&saveState + 0x64); } // 0 is used as a terminator if < 8
71-
static inline EntitySaveState* GetFlipSaveState(EntitySaveState& saveState) { return *(EntitySaveState**)((char*)&saveState + 0x6c); }
72-
};
73-
74-
struct EntitySaveState::Slot
75-
{
76-
static inline int* GetDonationValue(EntitySaveState& saveState) { return (int*)&saveState.intStorage1; }
77-
static inline int* GetTriggerTimer(EntitySaveState& saveState) { return (int*)&saveState.intStorage2; } // Only used by SHOP_RESTOCK_MACHINE
78-
static inline int* GetPrizeCollectible(EntitySaveState& saveState) { return (int*)&saveState._intStorage7; }
79-
};
80-
81-
struct EntitySaveState::NPC // FIREPLACE and MOVABLE_TNT
82-
{
83-
static inline int* GetHealth(EntitySaveState& saveState) { return (int*)&saveState.intStorage1; }
84-
};
85-
86-
struct EntitySaveState::Effect
87-
{
88-
static inline int* GetState(EntitySaveState& saveState) { return (int*)&saveState.intStorage1; } // Used by DIRT_PATCH, SPAWNER and LIL_GHOST
89-
static inline int* GetVarData0(EntitySaveState& saveState) { return (int*)&saveState.intStorage4; } // Used by SPAWNER
90-
static inline int* GetVarData1(EntitySaveState& saveState) { return (int*)&saveState.intStorage2; } // Used by SPAWNER
91-
};
92-
93-
struct EntitySaveState::Minecart
94-
{
95-
static inline float* GetAngleDegrees(EntitySaveState& saveState) { return (float*)&saveState.floatStorage1; }
96-
};
97-
}}
37+
} : 0x78; // 0x78

0 commit comments

Comments
 (0)