Skip to content

Commit f2aa6f2

Browse files
committed
GatoBot v1.2
crazy right
1 parent 1b10b82 commit f2aa6f2

10 files changed

Lines changed: 170 additions & 99 deletions

File tree

.vscode/settings.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@
9191
"variant": "cpp",
9292
"any": "cpp",
9393
"cwctype": "cpp",
94-
"span": "cpp"
95-
}
94+
"span": "cpp",
95+
"ranges": "cpp",
96+
"valarray": "cpp"
97+
},
98+
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
9699
}

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,18 @@
33

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

6+
### Features
7+
- **Internal renderer** (up to 8K resolution)
8+
- Mega Hack replay support (Frame Fixes accuracy only)
9+
- High accuracy
10+
611
> Video soon
712
813
![image](https://user-images.githubusercontent.com/57293929/213887369-1842a66b-067e-4d11-8d09-a4f8dda6a936.png)
914
![image](https://user-images.githubusercontent.com/57293929/213887317-d469f53c-ed6e-4a9b-a426-2600db0331e4.png)
15+
16+
### Special thanks
17+
- [HJFod](https://github.com/hjfod) (Libraries)
18+
- [KontrollFreek](https://github.com/KontrollFreek) (Geode icon)
19+
- [TGDPS members](https://discord.gg/AqqDEteRtT) (Beta testing (especially Impostor69 and Hykre))
20+
- [meramie](https://www.youtube.com/channel/UCwcP_WpIrMj423GcZvA2hiQ) (Low-End hardware testing)

geode/about.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,13 @@
33

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

6+
### Features
7+
- **Internal renderer** (up to 8K resolution)
8+
- Mega Hack replay support (Frame Fixes accuracy only)
9+
- High accuracy
10+
611
### Special thanks
712
- [HJFod](https://github.com/hjfod) (Libraries)
813
- [KontrollFreek](https://github.com/KontrollFreek) (Geode icon)
9-
- [TGDPS members](https://discord.gg/AqqDEteRtT) (Beta testing)
14+
- [TGDPS members](https://discord.gg/AqqDEteRtT) (Beta testing (especially Impostor69 and Hykre))
15+
- [meramie](https://www.youtube.com/channel/UCwcP_WpIrMj423GcZvA2hiQ) (Low-End hardware testing)

geode/mod.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"geode": "1.0.0-beta.10",
33
"version": "v1.0.0",
4-
"id": "cattodev.gbgeodebootstrap",
4+
"id": "cattodev.gatobot",
55
"name": "GatoBot",
66
"developer": "CattoDev",
7-
"description": "GatoBot bootstrapper for Geode"
7+
"description": "GatoBot Geode port"
88
}

src/GatoBot.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,16 @@
1212
enum ButtonType { None, Pressed, Released };
1313
enum BotStatus { Disabled, Recording, Replaying, Rendering };
1414
enum ReplayType { GatoBotR, MegaHack };
15+
enum ReplayLoadStatus { Success, MissingFrames, Failed };
1516

1617
struct PlayerData {
1718
cocos2d::CCPoint position;
1819
double yVel;
1920
bool isHolding;
2021
ButtonType action = None;
2122

23+
PlayerData() {} // ?
24+
2225
static PlayerData fromPlayer(gd::PlayerObject* player) {
2326
PlayerData data;
2427
data.position = MBO(CCPoint, player, 0x67C);
@@ -28,6 +31,7 @@ struct PlayerData {
2831
return data;
2932
}
3033
void applyToPlayer(gd::PlayerObject* player);
34+
static PlayerData fromJson(nlohmann::json);
3135
};
3236

3337
struct LevelFrameData {
@@ -129,7 +133,7 @@ class GatoBot {
129133
void handleClick(gd::GJBaseGameLayer*, bool, ButtonType);
130134

131135
void saveReplay(std::string& filepath);
132-
void loadReplay(std::string data, ReplayType);
136+
ReplayLoadStatus loadReplay(std::string data, ReplayType);
133137
void updateRender();
134138
void updateStatusLabel();
135139
LevelFrameData frameFromString(std::string data);
@@ -141,8 +145,8 @@ class GatoBot {
141145
void retryLevel();
142146
void setSongPitch(float);
143147

144-
void toggleRecord(float a = 0, float b = 0);
145-
void toggleReplay(float a = 0, float b = 0);
148+
void toggleRecord(int a = 0, float b = 0);
149+
void toggleReplay(int a = 0, float b = 0);
146150
void toggleRender();
147151

148152
void toggleGameFPSCap(bool);

src/Record.cpp

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include <sstream>
55
#include <nfd.h>
66

7-
void GatoBot::toggleRecord(float newSPF, float speed) {
7+
/*void GatoBot::toggleRecord(float newSPF, float speed) {
88
if(status == Recording) {
99
status = Disabled;
1010
@@ -33,6 +33,39 @@ void GatoBot::toggleRecord(float newSPF, float speed) {
3333
status = Recording;
3434
}
3535
36+
updateStatusLabel();
37+
}*/
38+
39+
void GatoBot::toggleRecord(int FPS, float speed) {
40+
if(status == Recording) {
41+
status = Disabled;
42+
43+
// reset fps
44+
auto dir = CCDirector::sharedDirector();
45+
dir->setAnimationInterval(lastSPF);
46+
dir->getScheduler()->setTimeScale(1);
47+
setSongPitch(1);
48+
}
49+
else {
50+
levelFrames.clear();
51+
52+
if(FPS > 0 && speed > 0) {
53+
auto dir = CCDirector::sharedDirector();
54+
55+
float newSPF = 1.f / (FPS * speed);
56+
lastSPF = dir->getAnimationInterval();
57+
58+
settings.targetSPF = newSPF;
59+
settings.targetSpeed = speed;
60+
settings.targetFPS = FPS;
61+
62+
dir->setAnimationInterval(newSPF);
63+
dir->getScheduler()->setTimeScale(speed);
64+
}
65+
66+
status = Recording;
67+
}
68+
3669
updateStatusLabel();
3770
}
3871

src/RecordPopup.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,10 @@ void RecordPopup::onStart(CCObject*) {
130130
int fpsVal = std::stoi(fpsInput->getString());
131131
float speedVal = std::stof(speedInput->getString());
132132

133-
if(isReplay) bot->toggleReplay(1.f / (fpsVal * speedVal), speedVal);
134-
else bot->toggleRecord(1.f / (fpsVal * speedVal), speedVal);
133+
//if(isReplay) bot->toggleReplay(1.f / (fpsVal * speedVal), speedVal);
134+
//else bot->toggleRecord(1.f / (fpsVal * speedVal), speedVal);
135+
if(isReplay) bot->toggleReplay(fpsVal, speedVal);
136+
else bot->toggleRecord(fpsVal, speedVal);
135137

136138
onCancel(nullptr);
137139
parentMenu->keyBackClicked();

src/Replay.cpp

Lines changed: 81 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,19 @@
22

33
#include <nfd.h>
44

5-
#undef snprintf
6-
#include <json.hpp>
5+
using namespace nlohmann; // json
76

8-
void GatoBot::toggleReplay(float newSPF, float speed) {
7+
// for sorting
8+
bool compareFramesForPrac(const LevelFrameData& a, const LevelFrameData& b) {
9+
return a.frame < b.frame;
10+
}
11+
12+
bool compareMHevents(const json& a, const json& b) {
13+
return a["frame"] < b["frame"];
14+
}
15+
16+
// toggle
17+
void GatoBot::toggleReplay(int FPS, float speed) {
918
if(status == Replaying) {
1019
status = Disabled;
1120

@@ -16,15 +25,16 @@ void GatoBot::toggleReplay(float newSPF, float speed) {
1625
setSongPitch(1);
1726
}
1827
else {
19-
if(newSPF > 0 && speed > 0) {
28+
if(FPS > 0 && speed > 0) {
2029
auto dir = CCDirector::sharedDirector();
2130

31+
float newSPF = 1.f / (FPS * speed);
2232
lastSPF = dir->getAnimationInterval();
2333

2434
settings.targetSPF = newSPF;
2535
settings.targetSpeed = speed;
36+
settings.targetFPS = FPS;
2637

27-
// Speedhack (Classic Mode)
2838
dir->setAnimationInterval(newSPF);
2939
dir->getScheduler()->setTimeScale(speed);
3040
}
@@ -35,11 +45,6 @@ void GatoBot::toggleReplay(float newSPF, float speed) {
3545
updateStatusLabel();
3646
}
3747

38-
// for sorting
39-
bool compareFramesForPrac(const LevelFrameData& a, const LevelFrameData& b) {
40-
return a.frame < b.frame;
41-
}
42-
4348
// split
4449
std::vector<std::string> _splitString(std::string stringData, char* delimiter) {
4550
size_t index = 0;
@@ -66,8 +71,7 @@ std::vector<std::string> _splitString(std::string stringData, char* delimiter) {
6671
void GatoBot::loadNewReplay() {
6772
// get file path
6873
nfdchar_t *outPath = NULL;
69-
//nfdresult_t res = NFD_OpenDialog({"gatobot,mhr.json"}, nullptr, &outPath);
70-
nfdresult_t res = NFD_OpenDialog({"gatobot"}, nullptr, &outPath);
74+
nfdresult_t res = NFD_OpenDialog({"gatobot,mhr.json"}, nullptr, &outPath);
7175

7276
if(res != NFD_OKAY) {
7377
free(outPath);
@@ -86,15 +90,29 @@ void GatoBot::loadNewReplay() {
8690
ReplayType rType = ReplayType::GatoBotR;
8791

8892
// MH replay
89-
//if(strstr(outPath, "mhr.json") != NULL) rType = ReplayType::MegaHack;
93+
if(strstr(outPath, "mhr.json") != NULL) rType = ReplayType::MegaHack;
9094

9195
free(outPath);
9296

9397
if(data.length() > 0) {
94-
loadReplay(data, rType);
95-
auto alert = gd::FLAlertLayer::create(nullptr, "Success", "OK", nullptr, "Replay loaded!");
96-
alert->m_pTargetLayer = (CCNode*)botMenu;
97-
alert->show();
98+
auto ret = loadReplay(data, rType);
99+
100+
if(ret == Success) {
101+
auto alert = gd::FLAlertLayer::create(nullptr, "Success", "OK", nullptr, "Replay loaded!");
102+
alert->m_pTargetLayer = (CCNode*)botMenu;
103+
alert->show();
104+
}
105+
if(ret == MissingFrames) {
106+
auto alert = gd::FLAlertLayer::create(nullptr, "Warning", "OK", nullptr, 360, "This replay seems to have missing frames.\n<cy>Make sure the Mega Hack replay is recorded with the \"Frame Fixes\" accuracy.</c>");
107+
alert->m_pTargetLayer = (CCNode*)botMenu;
108+
alert->show();
109+
}
110+
if(ret == Failed) {
111+
// error
112+
auto alert = gd::FLAlertLayer::create(nullptr, "Error", "OK", nullptr, "Failed to load replay!");
113+
alert->m_pTargetLayer = (CCNode*)botMenu;
114+
alert->show();
115+
}
98116
}
99117
else {
100118
// error
@@ -104,7 +122,9 @@ void GatoBot::loadNewReplay() {
104122
}
105123
}
106124

107-
void GatoBot::loadReplay(std::string replayDataCompressed, ReplayType rType = ReplayType::GatoBotR) {
125+
ReplayLoadStatus GatoBot::loadReplay(std::string replayDataCompressed, ReplayType rType = ReplayType::GatoBotR) {
126+
ReplayLoadStatus retCode = Success;
127+
108128
// remove currently loaded frames
109129
levelFrames.clear();
110130

@@ -139,68 +159,58 @@ void GatoBot::loadReplay(std::string replayDataCompressed, ReplayType rType = Re
139159
}
140160
// parse MegaHack replay
141161
if(rType == ReplayType::MegaHack) {
142-
using namespace nlohmann;
143-
144162
json data = json::parse(replayData);
145163

146164
auto events = data["events"];
147165

148-
bool isHolding = false;
149-
int curF = 0;
166+
// sort just in case
167+
std::sort(events.begin(), events.end(), compareMHevents);
168+
169+
// allocate (ig?)
170+
levelFrames.resize(events.back()["frame"] + 1);
150171

172+
// apply to frames
151173
for (json::iterator it = events.begin(); it != events.end(); it++) {
152-
// - parse event
153174
const auto item = it.value();
175+
const int curFrame = item["frame"];
154176

155-
item["frame"].get_to(curF);
156-
157-
// player data
158-
PlayerData pData;
177+
if(!item.contains("p2")) {
178+
auto pData = PlayerData::fromJson(item);
159179

160-
item["x"].get_to(pData.position.x);
161-
item["y"].get_to(pData.position.y);
162-
item["a"].get_to(pData.yVel);
180+
const auto oldData = levelFrames[curFrame].player1;
163181

164-
// click
165-
if(item.contains("down")) {
166-
item["down"].get_to(isHolding);
182+
if(oldData.action != None)
183+
pData.action = oldData.action;
167184

168-
pData.action = isHolding ? ButtonType::Pressed : ButtonType::Released;
169-
170-
pData.isHolding = isHolding;
185+
levelFrames[curFrame].player1 = pData;
171186
}
172187
else {
173-
pData.action = ButtonType::None;
174-
}
188+
auto pData = PlayerData::fromJson(item);
175189

176-
// new frame
177-
if(curF + 1 > levelFrames.size()) {
178-
LevelFrameData frame;
179-
frame.frame = curF;
190+
const auto oldData = levelFrames[curFrame].player2;
180191

181-
if(item.contains("p2"))
182-
frame.player2 = pData;
192+
if(oldData.action != None)
193+
pData.action = oldData.action;
183194

184-
else frame.player1 = pData;
185-
186-
levelFrames.push_back(frame);
195+
levelFrames[curFrame].player2 = pData;
187196
}
188-
else {
189-
LevelFrameData frame = levelFrames[curF];
190197

191-
if(item.contains("p2")) {
192-
frame.player2 = pData;
193-
}
194-
195-
else frame.player1 = pData;
198+
levelFrames[curFrame].frame = curFrame;
199+
}
196200

197-
levelFrames[curF] = frame;
201+
// warning if not frame fixes accuracy
202+
for(size_t i = 0; i < levelFrames.size(); i++) {
203+
if(levelFrames[i].frame != i) {
204+
retCode = MissingFrames;
205+
break;
198206
}
199207
}
200208
}
201209

202210
// sort clicks if some shit went wrong and they got shuffled
203211
std::sort(levelFrames.begin(), levelFrames.end(), compareFramesForPrac);
212+
213+
return retCode;
204214
}
205215

206216
LevelFrameData GatoBot::frameFromString(std::string frameData) {
@@ -245,4 +255,20 @@ LevelFrameData GatoBot::frameFromString(std::string frameData) {
245255
void PlayerData::applyToPlayer(gd::PlayerObject* player) {
246256
MBO(CCPoint, player, 0x67C) = position;
247257
MBO(double, player, 0x628) = yVel;
258+
}
259+
260+
PlayerData PlayerData::fromJson(json jsonData) {
261+
PlayerData data;
262+
263+
data.position = CCPoint(
264+
jsonData["x"], jsonData["y"]
265+
);
266+
267+
data.yVel = jsonData["a"];
268+
269+
if(jsonData.contains("down")) {
270+
data.action = jsonData["down"] ? Pressed : Released;
271+
}
272+
273+
return data;
248274
}

0 commit comments

Comments
 (0)