Skip to content

Commit 7242aa5

Browse files
committed
players.xml: "innateitems" and "innatetrinkets" attributes
1 parent 65dc710 commit 7242aa5

6 files changed

Lines changed: 182 additions & 9 deletions

File tree

changelog.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ Added:
155155
* Added HistoryHUD and HistoryHUDItem classes
156156
* Backported new enums for GameStateFlag
157157
* Added "isActiveEnemy" attribute for EntityNPCs in entities2.xml (set to "true" or "false")
158+
* Added "innateitems" and "innatetrinkets" attributes for players.xml (comma-separated list of IDs or names)
158159
* Added SplitTearType.SWORD_BEAM
159160
* The "Error" and "Modeling Clay" trinkets now use the sprite of the item they are mimicking when smelted, like how they do in later versions of Repentance+.
160161
* Removed unused class constructors in Renderer.

libzhl/functions/EntityPlayer.zhl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,10 @@ static cleanup PosVel Entity_Player::GetMultiShotPositionVelocity(int loopIndex,
7474
Weapon_MultiShotParams* Entity_Player::GetMultiShotParams(Weapon_MultiShotParams* params, int weaponType);
7575

7676
"558bec83e4f883ec0c53568bf18b0d":
77-
__thiscall Entity_Player* Entity_Player::InitTwin(int playerType); //replace with ePlayerType later
77+
__thiscall Entity_Player* Entity_Player::InitTwin(int playerType);
78+
79+
"558bec6aff68????????64a1????????5083ec54a1????????33c58945??535657508d45??64a3????????8bd9895d??8b45??85c0":
80+
__thiscall void Entity_Player::ChangePlayerType(int playerType, bool unk);
7881

7982
"558bec83e4f881ec540100008bd1":
8083
__thiscall Entity* Entity_Player::ThrowHeldEntity(Vector* Velocity);

repentogon/LuaInterfaces/CustomCallbacks.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,19 @@ void CustomCallbacks::TriggerCollectibleAdded(Entity_Player& player, int collect
5757
.call(1);
5858
}
5959
}
60+
static bool s_RestoringFamiliarStates = false;
6061
HOOK_METHOD(Entity_Familiar, WispInit, () -> void) {
6162
super();
6263

63-
if (this->_variant == 237 && this->_subtype > 0 && this->_player) {
64+
if (!s_RestoringFamiliarStates && this->_variant == 237 && this->_player && ItemConfig::IsValidCollectible(this->_subtype)) {
6465
CustomCallbacks::TriggerCollectibleAdded(*this->_player, this->_subtype, false, true);
6566
}
6667
}
68+
HOOK_METHOD(Entity_Player, RestoreGameState_PostLevelInit, (GameStatePlayer* saveState) -> void) {
69+
s_RestoringFamiliarStates = true;
70+
super(saveState);
71+
s_RestoringFamiliarStates = false;
72+
}
6773

6874
//PRE/POST_ADD_COLLECTIBLE (1004/1005)
6975
void ProcessPostAddCollectible(int type, int charge, bool firsttime, int slot, int vardata, Entity_Player* player) {

repentogon/Patches/ItemSpoofSystem.cpp

Lines changed: 163 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
#include "HookSystem.h"
55
#include "EntityPlus.h"
66

7+
8+
// Context handling
9+
710
struct ItemSpoofCallContext
811
{
912
bool isLuaRequest = false;
@@ -24,16 +27,18 @@ ItemSpoofCallContext GetContextAndReset()
2427
return context;
2528
}
2629

27-
static std::bitset<CollectibleType::NUM_COLLECTIBLES> s_reworkedCollectibles;
28-
static std::bitset<ePlayerType::NUM_PLAYER_TYPES> s_reworkedBirthrights;
29-
static std::bitset<TrinketType::NUM_TRINKETS> s_reworkedTrinkets;
30-
31-
void ItemSpoofSystem::StartLuaRequest(bool ignoreSpoof)
32-
{
30+
void ItemSpoofSystem::StartLuaRequest(bool ignoreSpoof) {
3331
s_ItemSpoofCallContext.isLuaRequest = true;
3432
s_ItemSpoofCallContext.ignoreSpoof = ignoreSpoof;
3533
}
3634

35+
36+
// "Reworked" Items
37+
38+
static std::bitset<CollectibleType::NUM_COLLECTIBLES> s_reworkedCollectibles;
39+
static std::bitset<ePlayerType::NUM_PLAYER_TYPES> s_reworkedBirthrights;
40+
static std::bitset<TrinketType::NUM_TRINKETS> s_reworkedTrinkets;
41+
3742
void ItemSpoofSystem::ReworkCollectible(int collectible)
3843
{
3944
assert(CollectibleType::COLLECTIBLE_NULL < collectible && collectible < CollectibleType::NUM_COLLECTIBLES);
@@ -82,6 +87,84 @@ static bool is_reworked_trinket(int trinket)
8287
return s_reworkedTrinkets.test(trinket);
8388
}
8489

90+
91+
// XML Innate Items
92+
93+
static std::unordered_map<int, std::unordered_map<int, int>> s_playerTypeInnateCollectibles;
94+
static std::unordered_map<int, std::unordered_map<int, int>> s_playerTypeInnateTrinkets;
95+
96+
const std::unordered_map<int, int>* GetPlayerTypeInnateCollectibles(int playerType) {
97+
if (auto it = s_playerTypeInnateCollectibles.find(playerType); it != s_playerTypeInnateCollectibles.end())
98+
return &it->second;
99+
return nullptr;
100+
}
101+
102+
int GetPlayerTypeInnateCollectibleCount(Entity_Player* player, int collectible) {
103+
if (const auto* ptypeCollectibles = GetPlayerTypeInnateCollectibles(player->_playerType)) {
104+
if (auto it = ptypeCollectibles->find(collectible); it != ptypeCollectibles->end()) {
105+
return it->second;
106+
}
107+
}
108+
return 0;
109+
}
110+
111+
const std::unordered_map<int, int>* GetPlayerTypeInnateTrinkets(int playerType) {
112+
if (auto it = s_playerTypeInnateTrinkets.find(playerType); it != s_playerTypeInnateTrinkets.end())
113+
return &it->second;
114+
return nullptr;
115+
}
116+
117+
int GetPlayerTypeInnateTrinketCount(Entity_Player* player, int collectible) {
118+
if (const auto* ptypeTrinkets = GetPlayerTypeInnateTrinkets(player->_playerType)) {
119+
if (auto it = ptypeTrinkets->find(collectible); it != ptypeTrinkets->end()) {
120+
return it->second;
121+
}
122+
}
123+
return 0;
124+
}
125+
126+
std::vector<int> ParseCommaSeparatedIdList(const std::string& str) {
127+
std::vector<int> ids;
128+
129+
std::stringstream ss(str);
130+
std::string item;
131+
132+
while (std::getline(ss, item, ',')) {
133+
int id = std::atoi(item.c_str());
134+
if (id > 0) {
135+
ids.push_back(id);
136+
}
137+
}
138+
139+
return ids;
140+
}
141+
142+
HOOK_METHOD_PRIORITY(ModManager, LoadConfigs, -9999, () -> void) {
143+
RegisterCustomXMLAttr(XMLStuff.PlayerData, "innateitems", XMLStuff.ItemData);
144+
RegisterCustomXMLAttr(XMLStuff.PlayerData, "innatetrinkets", XMLStuff.TrinketData);
145+
146+
super();
147+
148+
s_playerTypeInnateCollectibles.clear();
149+
s_playerTypeInnateTrinkets.clear();
150+
151+
for (const auto& [playerType, playerData] : XMLStuff.PlayerData->nodes) {
152+
if (auto it = playerData.find("innateitems"); it != playerData.end()) {
153+
for (const int id : ParseCommaSeparatedIdList(it->second)) {
154+
s_playerTypeInnateCollectibles[playerType][id]++;
155+
}
156+
}
157+
if (auto it = playerData.find("innatetrinkets"); it != playerData.end()) {
158+
for (const int id : ParseCommaSeparatedIdList(it->second)) {
159+
s_playerTypeInnateTrinkets[playerType][id]++;
160+
}
161+
}
162+
}
163+
}
164+
165+
166+
// Core ItemSpoofSystem hooks
167+
85168
HOOK_METHOD_PRIORITY(Entity_Player, HasCollectible, -9999, (int collectible, bool ignoreModifiers) -> bool)
86169
{
87170
const auto context = GetContextAndReset();
@@ -105,6 +188,10 @@ HOOK_METHOD_PRIORITY(Entity_Player, HasCollectible, -9999, (int collectible, boo
105188
return true;
106189
}
107190
}
191+
192+
if (GetPlayerTypeInnateCollectibleCount(this, collectible) > 0) {
193+
return true;
194+
}
108195
}
109196

110197
return super(collectible, ignoreModifiers);
@@ -132,6 +219,8 @@ HOOK_METHOD_PRIORITY(Entity_Player, GetCollectibleNum, -9999, (int collectible,
132219
}
133220
innateCount += spoofs.GetInnateCount(collectible);
134221
}
222+
223+
innateCount += GetPlayerTypeInnateCollectibleCount(this, collectible);
135224
}
136225

137226
return innateCount + super(collectible, ignoreModifiers);
@@ -160,6 +249,10 @@ HOOK_METHOD_PRIORITY(Entity_Player, HasTrinket, -9999, (unsigned int trinket, bo
160249
return true;
161250
}
162251
}
252+
253+
if (GetPlayerTypeInnateTrinketCount(this, trinket) > 0) {
254+
return true;
255+
}
163256
}
164257

165258
return super(trinket, ignoreModifiers);
@@ -187,6 +280,8 @@ HOOK_METHOD_PRIORITY(Entity_Player, GetTrinketMultiplier, -9999, (unsigned int t
187280
}
188281
innateMult += spoofs.GetInnateCount(trinket & TRINKET_ID_MASK) + spoofs.GetInnateCount(trinket | TRINKET_GOLDEN_FLAG) * 2;
189282
}
283+
284+
innateMult += GetPlayerTypeInnateTrinketCount(this, trinket);
190285
}
191286

192287
int vanillaMult = super(trinket);
@@ -228,12 +323,42 @@ HOOK_METHOD_PRIORITY(Entity_Player, UpdateEffects, -9999, () -> void)
228323
{
229324
if (EntityPlayerPlus* playerPlus = GetEntityPlayerPlus(this))
230325
{
326+
playerPlus->itemSpoofs.CheckPlayerType(*this, false);
231327
playerPlus->itemSpoofs.GetCollectibleSpoof().UpdateTemporaryTimers(*this);
232328
playerPlus->itemSpoofs.GetTrinketSpoof().UpdateTemporaryTimers(*this);
233329
}
234330
super();
235331
}
236332

333+
// Detect PlayerType changes where they can happen.
334+
HOOK_METHOD_PRIORITY(Entity_Player, ChangePlayerType, 9999, (int playerType, bool unk) -> void) {
335+
super(playerType, unk);
336+
if (EntityPlayerPlus* playerPlus = GetEntityPlayerPlus(this)) {
337+
playerPlus->itemSpoofs.CheckPlayerType(*this, false);
338+
}
339+
}
340+
HOOK_METHOD_PRIORITY(Entity_Player, InitPostLevelInitStats, 9999, () -> void) {
341+
if (EntityPlayerPlus* playerPlus = GetEntityPlayerPlus(this)) {
342+
playerPlus->itemSpoofs.CheckPlayerType(*this, false);
343+
}
344+
super();
345+
}
346+
HOOK_METHOD_PRIORITY(Entity_Player, TriggerDeath, 9999, (bool checkOnly) -> bool) {
347+
bool res = super(checkOnly);
348+
if (!checkOnly) {
349+
if (EntityPlayerPlus* playerPlus = GetEntityPlayerPlus(this)) {
350+
playerPlus->itemSpoofs.CheckPlayerType(*this, false);
351+
}
352+
}
353+
return res;
354+
}
355+
HOOK_METHOD_PRIORITY(Entity_Player, RestoreGameState, -9999, (GameStatePlayer* saveState) -> void) {
356+
super(saveState);
357+
if (EntityPlayerPlus* playerPlus = GetEntityPlayerPlus(this)) {
358+
playerPlus->itemSpoofs.CheckPlayerType(*this, true);
359+
}
360+
}
361+
237362

238363
// Serialization & Data Structure Details
239364

@@ -592,4 +717,36 @@ namespace ItemSpoofSystem
592717
}
593718
}
594719

720+
void PlayerItemSpoofs::CheckPlayerType(Entity_Player& player, bool restoringGameState) {
721+
const int prevPlayerType = lastPlayerType_;
722+
const int currentPlayerType = player._playerType;
723+
if (prevPlayerType != currentPlayerType) {
724+
lastPlayerType_ = currentPlayerType;
725+
if (restoringGameState) {
726+
return;
727+
}
728+
if (const auto* oldCols = GetPlayerTypeInnateCollectibles(prevPlayerType)) {
729+
for (const auto& [id, count] : *oldCols) {
730+
collectibleSpoof_.TriggerRemoved(player, id, count, false);
731+
}
732+
}
733+
if (const auto* newCols = GetPlayerTypeInnateCollectibles(currentPlayerType)) {
734+
for (const auto& [id, count] : *newCols) {
735+
collectibleSpoof_.TriggerAdded(player, id, count, false, false);
736+
}
737+
}
738+
if (const auto* oldTrinkets = GetPlayerTypeInnateTrinkets(prevPlayerType)) {
739+
for (const auto& [id, count] : *oldTrinkets) {
740+
trinketSpoof_.TriggerRemoved(player, id, count, false);
741+
}
742+
}
743+
if (const auto* newTrinkets = GetPlayerTypeInnateTrinkets(currentPlayerType)) {
744+
for (const auto& [id, count] : *newTrinkets) {
745+
trinketSpoof_.TriggerAdded(player, id, count, false, false);
746+
}
747+
}
748+
player.EvaluateItems();
749+
}
750+
}
751+
595752
} // namespace ItemSpoofSystem

repentogon/Patches/ItemSpoofSystem.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,10 +419,16 @@ namespace ItemSpoofSystem
419419
TrinketSpoof& GetTrinketSpoof() {
420420
return trinketSpoof_;
421421
}
422+
423+
// Check for a PlayerType change, and if a change is detected, "trigger" the add/remove of PlayerType-based innate items.
424+
// Note that this is not required for the PlayerType-based innates to function correctly - this only handles stuff like TriggerCollectibleRemoved.
425+
// If `restoringGameState`, update the lastPlayerType_ without triggering anything.
426+
void CheckPlayerType(Entity_Player& player, bool restoringGameState);
422427

423428
private:
424429
CollectibleSpoof collectibleSpoof_;
425430
TrinketSpoof trinketSpoof_;
431+
int lastPlayerType_ = -1;
426432
};
427433

428434
} // namespace ItemSpoofSystem

repentogon/Patches/XMLData.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1163,7 +1163,7 @@ void ProcessXmlNode(xml_node<char>* node,bool force = false) {
11631163
XMLStuff.PlayerData->byorder[XMLStuff.PlayerData->nodes.size()] = id;
11641164
XMLStuff.PlayerData->bymod[lastmodid].push_back(id);
11651165
XMLStuff.ModData->players[lastmodid] += 1;
1166-
}
1166+
}
11671167
break;
11681168
case 3: //pocketitems
11691169
id = 1;

0 commit comments

Comments
 (0)