Skip to content

Commit deadcec

Browse files
committed
#20 Make the last created checkpoint temporally active
Signed-off-by: Kevadroz <[email protected]>
1 parent 8c6cd37 commit deadcec

File tree

5 files changed

+102
-33
lines changed

5 files changed

+102
-33
lines changed

changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
## Unreleased
2+
- Make the last placed persistent checkpoint be "ghost" active until switching to another checkpoint or placing a regular checkpoint \[[Issue #20](https://github.com/Kevadroz/PracticeCheckpointPermanence/issues/20)\]
23
- Added an option to change the position of the practice buttons to above and below the vanilla ones \[[Issue #16](https://github.com/Kevadroz/PracticeCheckpointPermanence/issues/16)\]
34
- The "Reset Attempts" now also resets the attempts when restarting from the last checkpoint in the Level End Screen \[[Issue #18](https://github.com/Kevadroz/PracticeCheckpointPermanence/issues/18)\]
45
- Added an option to change the opacity of the active checkpoint \[[Issue #19](https://github.com/Kevadroz/PracticeCheckpointPermanence/issues/19)\]

src/Hooks/PlayLayer.cpp

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -93,14 +93,19 @@ void ModPlayLayer::processCreateObjectsFromSetup() {
9393

9494
void ModPlayLayer::resetLevel() {
9595
PersistentCheckpoint* checkpoint = nullptr;
96-
if (m_isPracticeMode && m_fields->m_activeCheckpoint != 0 &&
97-
m_checkpointArray->count() == 0) {
98-
checkpoint = reinterpret_cast<PersistentCheckpoint*>(
99-
m_fields->m_persistentCheckpointArray->objectAtIndex(
100-
m_fields->m_activeCheckpoint - 1
101-
)
102-
);
103-
m_checkpointArray->addObject(checkpoint->m_checkpoint);
96+
if (m_isPracticeMode) {
97+
unsigned int loadIndex = 0;
98+
if (m_fields->m_ghostActiveCheckpoint > 0)
99+
loadIndex = m_fields->m_ghostActiveCheckpoint;
100+
else if (m_checkpointArray->count() == 0)
101+
loadIndex = m_fields->m_activeCheckpoint;
102+
103+
if (loadIndex != 0) {
104+
checkpoint = reinterpret_cast<PersistentCheckpoint*>(
105+
m_fields->m_persistentCheckpointArray->objectAtIndex(loadIndex - 1)
106+
);
107+
m_checkpointArray->addObject(checkpoint->m_checkpoint);
108+
}
104109
}
105110

106111
PlayLayer::resetLevel();
@@ -153,13 +158,22 @@ void ModPlayLayer::togglePracticeMode(bool enabled) {
153158
updateModUI();
154159
}
155160

161+
void ModPlayLayer::storeCheckpoint(CheckpointObject* p0) {
162+
PlayLayer::storeCheckpoint(p0);
163+
164+
if (m_fields->m_ghostActiveCheckpoint > 0) {
165+
m_fields->m_ghostActiveCheckpoint = 0;
166+
static_cast<ModUILayer*>(m_uiLayer)->updateSwitcher();
167+
}
168+
}
169+
156170
void ModPlayLayer::registerKeybindListeners() {
157171
#ifndef GEODE_IS_IOS
158172
this->template addEventListener<InvokeBindFilter>(
159173
[this](InvokeBindEvent* event) {
160-
if (m_isPracticeMode && event->isDown()) {
174+
if (m_isPracticeMode && event->isDown())
161175
markPersistentCheckpoint();
162-
}
176+
163177
return ListenerResult::Propagate;
164178
},
165179
"create_checkpoint"_spr
@@ -168,9 +182,10 @@ void ModPlayLayer::registerKeybindListeners() {
168182
this->template addEventListener<InvokeBindFilter>(
169183
[this](InvokeBindEvent* event) {
170184
if (m_isPracticeMode && event->isDown()) {
171-
if (m_fields->m_activeCheckpoint != 0) {
185+
if (m_fields->m_ghostActiveCheckpoint != 0)
186+
removeGhostPersistentCheckpoint();
187+
else if (m_fields->m_activeCheckpoint != 0)
172188
removeCurrentPersistentCheckpoint();
173-
}
174189
}
175190
return ListenerResult::Propagate;
176191
},
@@ -179,39 +194,39 @@ void ModPlayLayer::registerKeybindListeners() {
179194

180195
this->template addEventListener<InvokeBindFilter>(
181196
[this](InvokeBindEvent* event) {
182-
if (event->isDown()) {
197+
if (event->isDown())
183198
previousCheckpoint();
184-
}
199+
185200
return ListenerResult::Propagate;
186201
},
187202
"previous_checkpoint"_spr
188203
);
189204

190205
this->template addEventListener<InvokeBindFilter>(
191206
[this](InvokeBindEvent* event) {
192-
if (event->isDown()) {
207+
if (event->isDown())
193208
nextCheckpoint();
194-
}
209+
195210
return ListenerResult::Propagate;
196211
},
197212
"next_checkpoint"_spr
198213
);
199214

200215
this->template addEventListener<InvokeBindFilter>(
201216
[this](InvokeBindEvent* event) {
202-
if (event->isDown()) {
217+
if (event->isDown())
203218
previousSaveLayer();
204-
}
219+
205220
return ListenerResult::Propagate;
206221
},
207222
"previous_layer"_spr
208223
);
209224

210225
this->template addEventListener<InvokeBindFilter>(
211226
[this](InvokeBindEvent* event) {
212-
if (event->isDown()) {
227+
if (event->isDown())
213228
nextSaveLayer();
214-
}
229+
215230
return ListenerResult::Propagate;
216231
},
217232
"next_layer"_spr

src/Hooks/PlayLayer.hpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
#include <Geode/modify/PlayLayer.hpp>
66
#include <functional>
77
#include <optional>
8-
#include <variant>
98
#include <sabe.persistenceapi/include/PersistenceAPI.hpp>
9+
#include <variant>
1010
#ifndef GEODE_IS_IOS
1111
#include <geode.custom-keybinds/include/Keybinds.hpp>
1212
#endif
@@ -61,6 +61,7 @@ class $modify(ModPlayLayer, PlayLayer) {
6161
unsigned int m_activeCheckpoint = 0;
6262
unsigned int m_activeSaveLayer = 0;
6363
unsigned int m_saveLayerCount = 0;
64+
unsigned int m_ghostActiveCheckpoint = 0;
6465

6566
std::optional<size_t> m_levelStringHash;
6667

@@ -79,6 +80,8 @@ class $modify(ModPlayLayer, PlayLayer) {
7980

8081
void togglePracticeMode(bool enabled);
8182

83+
void storeCheckpoint(CheckpointObject* p0);
84+
8285
// Custom
8386
void registerKeybindListeners();
8487
void updateModUI();
@@ -99,9 +102,10 @@ class $modify(ModPlayLayer, PlayLayer) {
99102
void
100103
switchCurrentCheckpoint(unsigned int, bool ignoreLastCheckpoint = false);
101104
void markPersistentCheckpoint();
102-
void storePersistentCheckpoint(PersistentCheckpoint* checkpoint);
105+
unsigned int storePersistentCheckpoint(PersistentCheckpoint* checkpoint);
103106
void removePersistentCheckpoint(PersistentCheckpoint* checkpoint);
104107
void removeCurrentPersistentCheckpoint();
108+
void removeGhostPersistentCheckpoint();
105109
void swapPersistentCheckpoints(unsigned int left, unsigned int right);
106110

107111
// Save Layers
@@ -113,7 +117,9 @@ class $modify(ModPlayLayer, PlayLayer) {
113117
void updateSaveLayerCount();
114118

115119
static void onModify(auto& self) {
116-
if (!self.setHookPriorityPost("PlayLayer::setupHasCompleted", Priority::VeryLate))
120+
if (!self.setHookPriorityPost(
121+
"PlayLayer::setupHasCompleted", Priority::VeryLate
122+
))
117123
log::warn("Failed to set PlayLayer::setupHasCompleted hook priority!");
118124
}
119125
};

src/Hooks/PlayLayerCheckpoints.cpp

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ void ModPlayLayer::switchCurrentCheckpoint(
4343
)
4444
->toggleActive(true);
4545

46+
m_fields->m_ghostActiveCheckpoint = 0;
4647
m_fields->m_activeCheckpoint = nextCheckpoint;
4748

4849
if (Mod::get()->getSettingValue<bool>("reset-attempts"))
@@ -70,7 +71,7 @@ void ModPlayLayer::markPersistentCheckpoint() {
7071
m_effectManager->m_persistentItemCountMap,
7172
m_effectManager->m_persistentTimerItemSet
7273
);
73-
storePersistentCheckpoint(checkpoint);
74+
m_fields->m_ghostActiveCheckpoint = storePersistentCheckpoint(checkpoint) + 1;
7475
serializeCheckpoints();
7576

7677
if (m_fields->m_persistentCheckpointArray->count() == 1)
@@ -79,7 +80,8 @@ void ModPlayLayer::markPersistentCheckpoint() {
7980
updateModUI();
8081
}
8182

82-
void ModPlayLayer::storePersistentCheckpoint(PersistentCheckpoint* checkpoint) {
83+
unsigned int
84+
ModPlayLayer::storePersistentCheckpoint(PersistentCheckpoint* checkpoint) {
8385
CCArray* array = m_fields->m_persistentCheckpointArray;
8486

8587
unsigned int index = 0;
@@ -100,6 +102,8 @@ void ModPlayLayer::storePersistentCheckpoint(PersistentCheckpoint* checkpoint) {
100102
array->insertObject(checkpoint, index);
101103
else
102104
array->addObject(checkpoint);
105+
106+
return index;
103107
}
104108

105109
void ModPlayLayer::removePersistentCheckpoint(
@@ -110,18 +114,33 @@ void ModPlayLayer::removePersistentCheckpoint(
110114
return;
111115
}
112116

113-
bool switchCheckpoint = m_fields->m_activeCheckpoint > 0 &&
114-
m_fields->m_persistentCheckpointArray->objectAtIndex(
115-
m_fields->m_activeCheckpoint - 1
116-
) == checkpoint;
117+
assert(m_fields->m_persistentCheckpointArray->containsObject(checkpoint));
118+
119+
unsigned int removeIndex =
120+
m_fields->m_persistentCheckpointArray->indexOfObject(checkpoint);
121+
122+
bool updateActiveCheckpoint =
123+
m_fields->m_activeCheckpoint > 0 &
124+
removeIndex <= m_fields->m_activeCheckpoint - 1;
125+
bool switchCheckpoint =
126+
m_fields->m_activeCheckpoint > 0 && updateActiveCheckpoint;
117127

118128
checkpoint->m_checkpoint->m_physicalCheckpointObject->removeFromParent();
119-
m_fields->m_persistentCheckpointArray->removeObject(checkpoint);
129+
m_fields->m_persistentCheckpointArray->removeObjectAtIndex(removeIndex);
130+
131+
if (removeIndex + 1 == m_fields->m_ghostActiveCheckpoint)
132+
m_fields->m_ghostActiveCheckpoint = 0;
133+
else if (m_fields->m_ghostActiveCheckpoint > 0)
134+
m_fields->m_ghostActiveCheckpoint--;
120135

121136
if (switchCheckpoint)
122137
switchCurrentCheckpoint(m_fields->m_activeCheckpoint - 1, true);
123-
else
138+
else {
139+
if (updateActiveCheckpoint)
140+
m_fields->m_activeCheckpoint--;
141+
124142
updateModUI();
143+
}
125144

126145
serializeCheckpoints();
127146
}
@@ -143,15 +162,35 @@ void ModPlayLayer::removeCurrentPersistentCheckpoint() {
143162
}
144163
}
145164

165+
void ModPlayLayer::removeGhostPersistentCheckpoint() {
166+
assert(m_fields->m_loadError == LoadError::None);
167+
168+
if (m_fields->m_ghostActiveCheckpoint > 0) {
169+
PersistentCheckpoint* checkpoint =
170+
reinterpret_cast<PersistentCheckpoint*>(
171+
m_fields->m_persistentCheckpointArray->objectAtIndex(
172+
m_fields->m_ghostActiveCheckpoint - 1
173+
)
174+
);
175+
removePersistentCheckpoint(checkpoint);
176+
}
177+
}
178+
146179
void ModPlayLayer::swapPersistentCheckpoints(
147180
unsigned int left, unsigned int right
148181
) {
149182
m_fields->m_persistentCheckpointArray->exchangeObjectAtIndex(left, right);
183+
150184
if (m_fields->m_activeCheckpoint == left + 1)
151185
m_fields->m_activeCheckpoint = right + 1;
152186
else if (m_fields->m_activeCheckpoint == right + 1)
153187
m_fields->m_activeCheckpoint = left + 1;
154188

189+
if (m_fields->m_ghostActiveCheckpoint == left + 1)
190+
m_fields->m_ghostActiveCheckpoint = right + 1;
191+
else if (m_fields->m_ghostActiveCheckpoint == right + 1)
192+
m_fields->m_ghostActiveCheckpoint = left + 1;
193+
155194
serializeCheckpoints();
156195
updateModUI();
157196
}

src/Hooks/UILayer.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,20 @@ void ModUILayer::updateSwitcher() {
119119

120120
std::string checkpointString;
121121
switch (loadError) {
122-
case None:
122+
case None: {
123+
std::string ghostCheckpointString =
124+
playLayer->m_fields->m_ghostActiveCheckpoint == 0
125+
? ""
126+
: fmt::format(
127+
" ({})", playLayer->m_fields->m_ghostActiveCheckpoint
128+
);
123129
checkpointString = fmt::format(
124-
"{}/{}", playLayer->m_fields->m_activeCheckpoint,
130+
"{}{}/{}", playLayer->m_fields->m_activeCheckpoint,
131+
ghostCheckpointString,
125132
playLayer->m_fields->m_persistentCheckpointArray->count()
126133
);
127134
break;
135+
}
128136
case Crash:
129137
checkpointString = "BAD DATA";
130138
break;

0 commit comments

Comments
 (0)