Skip to content

Commit d43db5a

Browse files
committed
v1.1
1 parent 7351aa0 commit d43db5a

6 files changed

Lines changed: 109 additions & 63 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# GatoBot
2-
**GatoBot is a frame-based Geometry Dash bot with an internal renderer.**
2+
**GatoBot is a physics-based Geometry Dash bot with an internal renderer.**
33

44
This bot is **heavily** inspired by [ReplayBot](https://github.com/matcool/ReplayBot), with a goal to make a more accurate version.
55

src/GatoBot.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,16 @@ void GatoBot::toggleGameFPSCap(bool toggled) {
5353
if(!toggled) bytes = {0xEB, 0x24};
5454

5555
patchMemory(patchLoc, bytes);
56+
}
5657

57-
/*auto dir = CCDirector::sharedDirector();
58+
float GatoBot::getTimeForXPos(gd::PlayLayer* pLayer) {
59+
float ret;
60+
float xPos = pLayer->m_pPlayer1->getPositionX();
61+
__asm movss xmm1, xPos;
62+
reinterpret_cast<void(__thiscall*)(gd::PlayLayer*, bool)>(gd::base + 0x208800)(pLayer, pLayer->m_isTestMode); // PlayLayer::timeForXPos2
63+
__asm movss ret, xmm0; // return value
5864

59-
if(!toggled) {
60-
lastSPF = dir->getAnimationInterval();
61-
dir->setAnimationInterval(0);
62-
}
63-
else dir->setAnimationInterval(lastSPF);*/
65+
return ret;
6466
}
6567

6668
// https://github.com/matcool/small-gd-mods/blob/main/src/menu-shaders.cpp#L19

src/GatoBot.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ struct LevelFrameData {
7979
};
8080

8181
struct CheckpointData {
82+
gd::CheckpointObject* object;
8283
LevelFrameData frame;
8384
};
8485

@@ -154,8 +155,10 @@ class GatoBot {
154155
static GatoBot* sharedState();
155156

156157
bool FFmpegInstalled();
158+
float getTimeForXPos(gd::PlayLayer*);
157159

158160
void handleFrame(gd::PlayLayer*);
161+
void handleCheckpoint(gd::PlayLayer*);
159162

160163
void saveReplay(std::string& filepath);
161164
void loadReplay(std::string data);

src/GatoBotMenu.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ void GatoBotMenu::FLAlert_Clicked(gd::FLAlertLayer* alert, bool btn2) {
171171
}
172172
if(alert->getTag() == 4 && !btn2) {
173173
// download ffmpeg
174-
CCApplication::sharedApplication()->openURL("https://www.gyan.dev/ffmpeg/builds/");
174+
CCApplication::sharedApplication()->openURL("https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip");
175175
}
176176
}
177177

src/Record.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ void GatoBot::handleFrame(gd::PlayLayer* pLayer) {
4949
levelFrames.push_back(frame);
5050
}
5151

52+
void GatoBot::handleCheckpoint(gd::PlayLayer* pLayer) {
53+
if(status == Recording && !MBO(bool, pLayer, 0x39C)) {
54+
auto player1 = PlayerData::fromPlayer(pLayer->m_pPlayer1);
55+
auto player2 = PlayerData::fromPlayer(pLayer->m_pPlayer2);
56+
57+
auto obj = (gd::CheckpointObject*)pLayer->m_checkpoints->lastObject();
58+
59+
LevelFrameData frame = {currentFrame, player1, player2};
60+
CheckpointData checkpoint = {obj, frame};
61+
62+
practiceCheckpoints.push_back(checkpoint);
63+
}
64+
}
65+
5266
void GatoBot::saveCurrentReplay() {
5367
nfdchar_t filterList[] = {"gatobot"};
5468

src/main.cpp

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ bool __fastcall PlayLayer_initH(gd::PlayLayer* self, uintptr_t, gd::GJGameLevel*
1313
bot->player1holding = false;
1414
bot->player2holding = false;
1515
bot->currentFrame = 0;
16-
16+
bot->practiceCheckpoints.clear();
1717
bot->queuedBtnP1 = None;
1818
bot->queuedBtnP2 = None;
1919

@@ -72,6 +72,8 @@ void __fastcall PlayLayer_updateH(gd::PlayLayer* self, uintptr_t, float dt) {
7272
{
7373
LevelFrameData frame = bot->levelFrames[bot->currentFrame];
7474

75+
//std::cout << "alignment: " << bot->currentFrame << " :: " << frame.frame << "\n";
76+
7577
frame.player1.applyToPlayer(self->m_pPlayer1);
7678
frame.player2.applyToPlayer(self->m_pPlayer2);
7779

@@ -140,73 +142,79 @@ void __fastcall PauseLayer_customSetupH(gd::PauseLayer* self, uintptr_t) {
140142
}
141143

142144
void(__thiscall* GJBaseGameLayer_pushButtonO)(gd::GJBaseGameLayer*, int, bool);
143-
void __fastcall GJBaseGameLayer_pushButtonH(gd::GJBaseGameLayer* self, uintptr_t, int button, bool player1) {
144-
GJBaseGameLayer_pushButtonO(self, button, player1);
145+
void __fastcall GJBaseGameLayer_pushButtonH(gd::GJBaseGameLayer* self, uintptr_t, int button, bool rightSide) {
146+
GJBaseGameLayer_pushButtonO(self, button, rightSide);
145147

146148
auto bot = GatoBot::sharedState();
147149

148150
if(bot->status == Recording && !MBO(bool, self, 0x39C)) {
149-
if(player1) bot->queuedBtnP1 = Pressed;
150-
else bot->queuedBtnP2 = Pressed;
151+
bool twoPlayer = MBO(bool, self->m_pLevelSettings, 0xFA);
152+
153+
if(MBO(bool, self, 0x2A9) && !rightSide && twoPlayer)
154+
bot->queuedBtnP2 = Pressed;
155+
156+
else bot->queuedBtnP1 = Pressed;
151157
}
152158
}
153159

154160
void(__thiscall* GJBaseGameLayer_releaseButtonO)(gd::GJBaseGameLayer*, int, bool);
155-
void __fastcall GJBaseGameLayer_releaseButtonH(gd::GJBaseGameLayer* self, uintptr_t, int button, bool player1) {
156-
GJBaseGameLayer_releaseButtonO(self, button, player1);
161+
void __fastcall GJBaseGameLayer_releaseButtonH(gd::GJBaseGameLayer* self, uintptr_t, int button, bool rightSide) {
162+
GJBaseGameLayer_releaseButtonO(self, button, rightSide);
157163

158164
auto bot = GatoBot::sharedState();
159165

160166
if(bot->status == Recording && !MBO(bool, self, 0x39C)) {
161-
if(player1) bot->queuedBtnP1 = Released;
162-
else bot->queuedBtnP2 = Released;
167+
bool twoPlayer = MBO(bool, self->m_pLevelSettings, 0xFA);
168+
169+
if(MBO(bool, self, 0x2A9) && !rightSide && twoPlayer)
170+
bot->queuedBtnP2 = Released;
171+
172+
else bot->queuedBtnP1 = Released;
163173
}
164174
}
165175

166-
gd::GameObject*(__thiscall* PlayLayer_createCheckpointO)(gd::PlayLayer*);
167-
gd::GameObject* __fastcall PlayLayer_createCheckpointH(gd::PlayLayer* self, uintptr_t) {
168-
auto ret = PlayLayer_createCheckpointO(self);
176+
void(__thiscall* UILayer_onCheckO)(gd::UILayer*, CCObject*);
177+
void __fastcall UILayer_onCheckH(gd::UILayer* self, uintptr_t, CCObject* pSender) {
178+
UILayer_onCheckO(self, pSender);
169179

170-
auto bot = GatoBot::sharedState();
171-
if(bot->status == Recording) {
172-
auto player1 = PlayerData::fromPlayer(self->m_pPlayer1);
173-
auto player2 = PlayerData::fromPlayer(self->m_pPlayer2);
180+
GatoBot::sharedState()->handleCheckpoint(gd::PlayLayer::get());
181+
}
174182

175-
LevelFrameData frame = {bot->currentFrame, player1, player2};
176-
CheckpointData checkpoint = {frame};
183+
void midhookFuckery() {
184+
GatoBot::sharedState()->handleCheckpoint(gd::PlayLayer::get());
185+
}
177186

178-
bot->practiceCheckpoints.push_back(checkpoint);
187+
// midfunc moment
188+
void(*PlayerObject_tryPlaceCheckpointO)();
189+
__declspec(naked) void PlayerObject_tryPlaceCheckpointH() {
190+
__asm {
191+
call midhookFuckery;
192+
jmp PlayerObject_tryPlaceCheckpointO;
179193
}
194+
}
180195

181-
return ret;
182-
}
183-
184-
void(__thiscall* PlayLayer_removePendingCheckpointO)(gd::PlayLayer*);
185-
void __fastcall PlayLayer_removePendingCheckpointH(gd::PlayLayer* self, uintptr_t) {
186-
PlayLayer_removePendingCheckpointO(self);
196+
void(__thiscall* PlayLayer_removeLastCheckpointO)(gd::PlayLayer*);
197+
void __fastcall PlayLayer_removeLastCheckpointH(gd::PlayLayer* self, uintptr_t) {
198+
PlayLayer_removeLastCheckpointO(self);
187199

188200
auto bot = GatoBot::sharedState();
189201

190202
if(bot->practiceCheckpoints.size() > 0)
191203
bot->practiceCheckpoints.pop_back();
192204
}
193205

194-
float getTimeForPos(gd::PlayLayer* self) {
195-
float ret;
196-
float xPos = self->m_pPlayer1->getPositionX();
197-
__asm movss xmm1, xPos;
198-
reinterpret_cast<void(__thiscall*)(gd::PlayLayer*, bool)>(gd::base + 0x208800)(self, self->m_isTestMode); // PlayLayer::timeForXPos2
199-
__asm movss ret, xmm0; // return value
200-
201-
return ret;
202-
}
203-
204206
void(__thiscall* PlayLayer_resetLevelO)(gd::PlayLayer*);
205207
void __fastcall PlayLayer_resetLevelH(gd::PlayLayer* self, uintptr_t) {
206-
PlayLayer_resetLevelO(self);
207-
208208
auto bot = GatoBot::sharedState();
209209

210+
// disable practice mode
211+
if(bot->status == Replaying || bot->status == Rendering && self->m_isPracticeMode) {
212+
self->togglePracticeMode(false);
213+
bot->practiceCheckpoints.clear();
214+
}
215+
216+
PlayLayer_resetLevelO(self);
217+
210218
if(bot->status == Recording) {
211219
if(bot->levelFrames.size() > 0) {
212220
// practice mode shenanigans
@@ -221,39 +229,52 @@ void __fastcall PlayLayer_resetLevelH(gd::PlayLayer* self, uintptr_t) {
221229
checkpoint.frame.player2.applyToPlayer(self->m_pPlayer2);
222230
}
223231
else bot->currentFrame = 0;
224-
232+
225233
// remove all clicks and frames after checkpoint
226234
if(self->m_isPracticeMode && bot->currentFrame > 0) {
227235
bot->levelFrames.resize(bot->currentFrame);
228-
}
236+
}
229237
else {
230238
bot->levelFrames.clear();
231239
}
232-
240+
233241
// jump?
234242
if(bot->levelFrames.back().player1.isHolding != MBO(bool, self->m_pPlayer1, 0x611)) {
235-
if(MBO(bool, self->m_pPlayer1, 0x611)) bot->levelFrames.back().player1.action = Pressed;
236-
else bot->levelFrames.back().player1.action = Released;
243+
if(MBO(bool, self->m_pPlayer1, 0x611)) {
244+
bot->levelFrames.back().player1.action = Pressed;
245+
bot->levelFrames.back().player1.isHolding = true;
246+
}
247+
else {
248+
bot->levelFrames.back().player1.action = Released;
249+
bot->levelFrames.back().player1.isHolding = false;
250+
}
237251
}
238252

239253
if(bot->levelFrames.back().player2.isHolding != MBO(bool, self->m_pPlayer2, 0x611)) {
240-
if(MBO(bool, self->m_pPlayer2, 0x611)) bot->levelFrames.back().player2.action = Pressed;
241-
else bot->levelFrames.back().player2.action = Released;
254+
if(MBO(bool, self->m_pPlayer2, 0x611)) {
255+
bot->levelFrames.back().player2.action = Pressed;
256+
bot->levelFrames.back().player2.isHolding = true;
257+
}
258+
else {
259+
bot->levelFrames.back().player2.action = Released;
260+
bot->levelFrames.back().player2.isHolding = false;
261+
}
242262
}
243263

244264
bot->queuedBtnP1 = None;
245265
bot->queuedBtnP2 = None;
246266
}
267+
}
247268

269+
if(bot->status == Recording || bot->status == Replaying)
248270
bot->setSongPitch(CCDirector::sharedDirector()->getScheduler()->getTimeScale());
249-
}
250271

251272
if(!self->m_isPracticeMode) {
252273
bot->currentFrame = 0;
253274
bot->timeFromStart = 0;
254275

255276
// update music offset
256-
bot->currentMusicOffset = getTimeForPos(self) + MBO(float, self->m_pLevelSettings, 0xFC); // timeForXPos + songOffset
277+
bot->currentMusicOffset = bot->getTimeForXPos(self) + MBO(float, self->m_pLevelSettings, 0xFC); // timeForXPos + songOffset
257278
}
258279
}
259280

@@ -304,7 +325,6 @@ void __fastcall CCScheduler_updateH(CCScheduler* self, uintptr_t, float dt) {
304325
}
305326

306327
if(bot->status == Rendering && !bot->gamePaused) {
307-
308328
if(!bot->currentFrameHasData) {
309329
float deltaTime = 1.f / static_cast<float>(bot->settings.targetGameFPS); // constant delta time
310330

@@ -314,7 +334,7 @@ void __fastcall CCScheduler_updateH(CCScheduler* self, uintptr_t, float dt) {
314334
if(fmod->isBackgroundMusicPlaying() && !pLayer->m_hasCompletedLevel) {
315335
// what the fuck why are the args backwards
316336
auto channel = fmod->m_pGlobalChannel;
317-
int musicTime = static_cast<int>((getTimeForPos(pLayer) + MBO(float, pLayer->m_pLevelSettings, 0xFC)) * 1000);
337+
int musicTime = static_cast<int>((bot->getTimeForXPos(pLayer) + MBO(float, pLayer->m_pLevelSettings, 0xFC)) * 1000);
318338
__asm {
319339
push 0x1;
320340
push musicTime;
@@ -399,18 +419,25 @@ void loadHooks() {
399419
reinterpret_cast<void**>(&GJBaseGameLayer_releaseButtonO)
400420
);
401421

402-
// PlayLayer::createCheckpoint
422+
// UILayer::onCheck
423+
MH_CreateHook(
424+
reinterpret_cast<void*>(gd::base + 0x25fb60),
425+
reinterpret_cast<void*>(&UILayer_onCheckH),
426+
reinterpret_cast<void**>(&UILayer_onCheckO)
427+
);
428+
429+
// PlayLayer::tryPlaceCheckpoint (MIDHOOK)
403430
MH_CreateHook(
404-
reinterpret_cast<void*>(gd::base + 0x20b050),
405-
reinterpret_cast<void*>(&PlayLayer_createCheckpointH),
406-
reinterpret_cast<void**>(&PlayLayer_createCheckpointO)
431+
reinterpret_cast<void*>(gd::base + 0x20b487),
432+
reinterpret_cast<void*>(&PlayerObject_tryPlaceCheckpointH),
433+
reinterpret_cast<void**>(&PlayerObject_tryPlaceCheckpointO)
407434
);
408435

409-
// PlayLayer::removePendingCheckpoint
436+
// PlayLayer::removeLastCheckpoint
410437
MH_CreateHook(
411438
reinterpret_cast<void*>(gd::base + 0x20b830),
412-
reinterpret_cast<void*>(&PlayLayer_removePendingCheckpointH),
413-
reinterpret_cast<void**>(&PlayLayer_removePendingCheckpointO)
439+
reinterpret_cast<void*>(&PlayLayer_removeLastCheckpointH),
440+
reinterpret_cast<void**>(&PlayLayer_removeLastCheckpointO)
414441
);
415442

416443
// PlayLayer::resetLevel

0 commit comments

Comments
 (0)