Skip to content

Commit

Permalink
Are we done yet?
Browse files Browse the repository at this point in the history
  • Loading branch information
hiimjasmine00 committed Oct 8, 2024
1 parent 28f0638 commit a700919
Show file tree
Hide file tree
Showing 11 changed files with 275 additions and 44 deletions.
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64")
set(CMAKE_CXX_VISIBILITY_PRESET hidden)

project(MoreIcons VERSION 1.2.1)
project(MoreIcons VERSION 1.2.2)

add_library(${PROJECT_NAME} SHARED
src/classes/DummyNode.cpp
src/hooks/CharacterColorPage.cpp
src/hooks/GameManager.cpp
src/hooks/GJGarageLayer.cpp
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ A mod that loads custom icons.
[YouTube Tutorial](https://youtu.be/s2AM98Yj59k)

## Adding Icons
To add a custom icon, you need an icon spritesheet (.plist) and an icon atlas (.png).
To add a custom icon, you need an icon spritesheet (.plist) and an icon atlas (.png). Tools like [GDBrowser's Icon Kit](https://gdbrowser.com/iconkit) can be used to create these spritesheets and atlases, with the "Developer Mode" option enabled in the settings.

The spritesheet should be in the format of a typical Geometry Dash icon spritesheet, with the primary sprite, secondary sprite, glow sprite, and an optional detail sprite. UFOs contain a fifth sprite for the dome.

Expand All @@ -30,6 +30,8 @@ The spritesheets and atlases should be placed in `(Geometry Dash folder)/geode/c
- swing
- jetpack

This can also be done with individual images per icon piece, with the same naming conventions as above. The sprites should be placed in `(Geometry Dash folder)/geode/config/hiimjustin000.more_icons/(gamemode)/(icon name)`, where `(icon name)` is the name of the icon.

If anything goes wrong, the mod will log warnings and errors to the console, which can be checked in `(Geometry Dash folder)/geode/logs`.

## Texture Packs
Expand All @@ -44,5 +46,9 @@ To use a custom icon, you will need to go into the icon kit. In the icon kit, th

To deselect a custom icon, use the first row of dots to go back to the default icons, and click on one of the default icons to select it.

## Credits
- [DeepResonanceX](https://gdbrowser.com/u/5668656) - Idea for the mod
- [hiimjustin000](https://gdbrowser.com/u/7466002) - Creator of the mod

# License
This mod is licensed under the [MIT License](./LICENSE).
10 changes: 8 additions & 2 deletions about.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ A mod that loads custom icons.
[YouTube Tutorial](https://youtu.be/s2AM98Yj59k)

## Adding Icons
To add a custom icon, you need an icon spritesheet (.plist) and an icon atlas (.png).
To add a custom icon, you need an icon spritesheet (.plist) and an icon atlas (.png). Tools like [GDBrowser's Icon Kit](https://gdbrowser.com/iconkit) can be used to create these spritesheets and atlases, with the "Developer Mode" option enabled in the settings.

The spritesheet should be in the format of a typical Geometry Dash icon spritesheet, with the primary sprite, secondary sprite, glow sprite, and an optional detail sprite. UFOs contain a fifth sprite for the dome.

Expand All @@ -30,6 +30,8 @@ The spritesheets and atlases should be placed in `(Geometry Dash folder)/geode/c
- swing
- jetpack

This can also be done with individual images per icon piece, with the same naming conventions as above. The sprites should be placed in `(Geometry Dash folder)/geode/config/hiimjustin000.more_icons/(gamemode)/(icon name)`, where `(icon name)` is the name of the icon.

If anything goes wrong, the mod will log warnings and errors to the console, which can be checked in `(Geometry Dash folder)/geode/logs`.

## Texture Packs
Expand All @@ -42,4 +44,8 @@ Note that this will only work if the texture pack has a `pack.json` file in the
## Using Icons
To use a custom icon, you will need to go into the icon kit. In the icon kit, there is a second row of dots that you can click on to view the extra icons. Click on the dot to view a page of extra icons, and click on one of the icons to select it.

To deselect a custom icon, use the first row of dots to go back to the default icons, and click on one of the default icons to select it.
To deselect a custom icon, use the first row of dots to go back to the default icons, and click on one of the default icons to select it.

## Credits
- [DeepResonanceX](user:5668656) - Idea for the mod
- [hiimjustin000](user:7466002) - Creator of the mod
11 changes: 8 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,25 @@
# More Icons Changelog
## v1.2.2 (2024-10-08)
- Added support for individual images per icon piece
- Added trail customization options ([#5](https://github.com/hiimjustin000/MoreIcons/issues/5))
- Fixed robots and spiders not changing in icon popups

## v1.2.1 (2024-10-08)
- Fixed custom spiders not showing up in game (#4)
- Fixed custom spiders not showing up in game ([#4](https://github.com/hiimjustin000/MoreIcons/issues/4))

## v1.2.0 (2024-10-07)
- Added support for texture packs
- Added support for custom trails
- Added support for Icon Profile in the main menu and Animated Profile Icons in the icon kit
- Fixed a bug where the robot's boost particles would not show up (#3)
- Fixed a bug where the robot's boost particles would not show up ([#3](https://github.com/hiimjustin000/MoreIcons/issues/3))

## v1.1.2 (2024-10-07)
- Added sprite name detection for spritesheets
- Added icon loading text to the loading screen

## v1.1.1 (2024-10-06)
- Fixed custom jetpacks not showing up in game
- Fixed icon sorting not working properly (#2)
- Fixed icon sorting not working properly ([#2](https://github.com/hiimjustin000/MoreIcons/issues/2))

## v1.1.0 (2024-10-06)
- Added support for Separate Dual Icons
Expand Down
2 changes: 1 addition & 1 deletion mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"win": "2.206",
"mac": "2.206"
},
"version": "v1.2.1",
"version": "v1.2.2",
"id": "hiimjustin000.more_icons",
"name": "More Icons",
"developer": "hiimjustin000",
Expand Down
100 changes: 93 additions & 7 deletions src/MoreIcons.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,17 @@ using namespace geode::prelude;
MoreIcons::load();
}

$on_mod(DataSaved) {
for (auto& [trail, info] : MoreIcons::TRAIL_INFO) {
std::fstream file(std::filesystem::path(info.texture).replace_extension(".json"), std::ios::out);
file << matjson::Value(matjson::Object{
{ "blend", info.blend },
{ "tint", info.tint },
}).dump();
file.close();
}
}

// https://github.com/GlobedGD/globed2/blob/v1.6.2/src/util/cocos.cpp#L44
namespace {
template <typename TC>
Expand Down Expand Up @@ -83,10 +94,7 @@ std::vector<std::filesystem::path> MoreIcons::getTexturePacks() {
return packs;
}

void MoreIcons::loadIcons(
const std::filesystem::path& path, std::vector<std::string>& list, std::vector<std::string>& duplicates,
std::unordered_map<std::string, std::string>& textures, IconType type, bool create
) {
void MoreIcons::loadIcons(const std::filesystem::path& path, std::vector<std::string>& list, std::vector<std::string>& duplicates, IconType type, bool create) {
auto folder = path.filename().string();
log::info("Loading {}s from {}", folder, path.string());
if (LOADING_LAYER)
Expand All @@ -98,8 +106,67 @@ void MoreIcons::loadIcons(

auto textureCache = CCTextureCache::get();
auto textureQuality = CCDirector::get()->getLoadedTextureQuality();
auto spriteFrameCache = CCSpriteFrameCache::sharedSpriteFrameCache();
for (auto& entry : naturalSort(path)) {
if (!entry.is_regular_file()) continue;
if (!entry.is_regular_file() && !entry.is_directory()) continue;

if (entry.is_directory()) {
auto entryPath = entry.path();

auto name = entryPath.stem().string();
if (std::find(list.begin(), list.end(), name) != list.end()) {
duplicates.push_back(name);
name += fmt::format("_{:02}", std::count(duplicates.begin(), duplicates.end(), name));
}

for (auto& subEntry : naturalSort(entryPath)) {
if (!subEntry.is_regular_file()) continue;

auto subEntryPath = subEntry.path();
if (subEntryPath.extension() != ".png") continue;

auto pathFilename = subEntryPath.filename().string();
auto fileQuality = kTextureQualityLow;
if (pathFilename.find("-uhd.png") != std::string::npos) {
auto hdExists = std::filesystem::exists(string::replace(subEntryPath.string(), "-uhd.png", "-hd.png"));
if (hdExists || textureQuality != kTextureQualityHigh) {
if (!hdExists) log::warn("Ignoring too high quality PNG file: {}", entryPath.filename() / pathFilename);
continue;
}
else fileQuality = kTextureQualityHigh;
}
else if (pathFilename.find("-hd.png") != std::string::npos) {
auto sdExists = std::filesystem::exists(string::replace(subEntryPath.string(), "-hd.png", ".png"));
if (sdExists || (textureQuality != kTextureQualityHigh && textureQuality != kTextureQualityMedium)) {
if (!sdExists) log::warn("Ignoring too high quality PNG file: {}", entryPath.filename() / pathFilename);
continue;
}
else fileQuality = kTextureQualityMedium;
}

auto pngPath = subEntryPath.string();
auto noGraphicPngPath = string::replace(string::replace(pngPath, "-uhd.png", ".png"), "-hd.png", ".png");
auto possibleUHD = string::replace(noGraphicPngPath, ".png", "-uhd.png");
auto possibleHD = string::replace(noGraphicPngPath, ".png", "-hd.png");
auto usedTextureQuality = kTextureQualityLow;
if (textureQuality == kTextureQualityHigh && (fileQuality == kTextureQualityHigh || std::filesystem::exists(possibleUHD))) {
pngPath = possibleUHD;
usedTextureQuality = kTextureQualityHigh;
}
else if (textureQuality == kTextureQualityMedium && (fileQuality == kTextureQualityMedium || std::filesystem::exists(possibleHD))) {
pngPath = possibleHD;
usedTextureQuality = kTextureQualityMedium;
}

auto texture = textureCache->addImage(pngPath.c_str(), false);
spriteFrameCache->addSpriteFrame(
CCSpriteFrame::createWithTexture(texture, { { 0.0f, 0.0f }, texture->getContentSize() }),
getFrameName(std::filesystem::path(noGraphicPngPath).filename().string(), name, type).c_str()
);
}
list.push_back(name);
continue;
}

auto entryPath = entry.path();
if (entryPath.extension() != ".plist") continue;
Expand Down Expand Up @@ -157,7 +224,6 @@ void MoreIcons::loadIcons(
_addSpriteFramesWithDictionary(dict, textureCache->addImage(fullTexturePath.c_str(), false));
dict->release();
list.push_back(name);
textures.emplace(name, fullTexturePath);
}

Mod::get()->setSavedValue(path.filename().string() + "s", list);
Expand All @@ -180,6 +246,22 @@ void MoreIcons::loadTrails(const std::filesystem::path& path, std::vector<std::s
auto entryPath = entry.path();
if (entryPath.extension() != ".png") continue;

auto jsonPath = std::filesystem::path(entryPath).replace_extension(".json");
matjson::Value json;
if (!std::filesystem::exists(jsonPath)) json = matjson::Object { { "blend", false }, { "tint", false } };
else {
std::ifstream file(jsonPath);
std::stringstream bufferStream;
bufferStream << file.rdbuf();
std::string error;
auto tryJson = matjson::parse(bufferStream.str(), error);
if (!error.empty()) {
log::warn("Failed to parse JSON file {}: {}", jsonPath.filename().string(), error);
json = matjson::Object { { "blend", false }, { "tint", false } };
}
else json = tryJson.value_or(matjson::Object { { "blend", false }, { "tint", false } });
}

auto name = entryPath.stem().string();
if (std::find(TRAILS.begin(), TRAILS.end(), name) != TRAILS.end()) {
duplicates.push_back(name);
Expand All @@ -189,7 +271,11 @@ void MoreIcons::loadTrails(const std::filesystem::path& path, std::vector<std::s
auto fullTexturePath = entryPath.string();
textureCache->addImage(fullTexturePath.c_str(), false);
TRAILS.push_back(name);
TRAIL_TEXTURES.emplace(name, fullTexturePath);
TRAIL_INFO.emplace(name, TrailInfo {
.texture = fullTexturePath,
.blend = json.contains("blend") && json["blend"].is_bool() ? json["blend"].as_bool() : false,
.tint = json.contains("tint") && json["tint"].is_bool() ? json["tint"].as_bool() : false,
});
}
}

Expand Down
40 changes: 20 additions & 20 deletions src/MoreIcons.hpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
struct TrailInfo {
std::string texture;
bool blend;
bool tint;
};

class MoreIcons {
public:
static inline std::vector<std::string> ICONS;
Expand All @@ -6,13 +12,11 @@ class MoreIcons {
static inline std::vector<std::string> UFOS;
static inline std::vector<std::string> WAVES;
static inline std::vector<std::string> ROBOTS;
static inline std::unordered_map<std::string, std::string> ROBOT_TEXTURES;
static inline std::vector<std::string> SPIDERS;
static inline std::unordered_map<std::string, std::string> SPIDER_TEXTURES;
static inline std::vector<std::string> SWINGS;
static inline std::vector<std::string> JETPACKS;
static inline std::vector<std::string> TRAILS;
static inline std::unordered_map<std::string, std::string> TRAIL_TEXTURES;
static inline std::unordered_map<std::string, TrailInfo> TRAIL_INFO;
static inline LoadingLayer* LOADING_LAYER = nullptr;

static bool hasIcon(const std::string& name) {
Expand Down Expand Up @@ -67,32 +71,30 @@ class MoreIcons {
WAVES.clear();
geode::Mod::get()->setSavedValue("waves", WAVES);
ROBOTS.clear();
ROBOT_TEXTURES.clear();
geode::Mod::get()->setSavedValue("robots", ROBOTS);
SPIDERS.clear();
SPIDER_TEXTURES.clear();
geode::Mod::get()->setSavedValue("spiders", SPIDERS);
SWINGS.clear();
geode::Mod::get()->setSavedValue("swings", SWINGS);
JETPACKS.clear();
geode::Mod::get()->setSavedValue("jetpacks", JETPACKS);
TRAILS.clear();
TRAIL_INFO.clear();
geode::Mod::get()->setSavedValue("trails", TRAILS);
}
static void load() {
std::unordered_map<std::string, std::string> tempMap;
auto packs = getTexturePacks();
auto packSize = packs.size();
std::vector<std::string> duplicates;
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "icon", ICONS, duplicates, tempMap, IconType::Cube, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "ship", SHIPS, duplicates, tempMap, IconType::Ship, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "ball", BALLS, duplicates, tempMap, IconType::Ball, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "ufo", UFOS, duplicates, tempMap, IconType::Ufo, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "wave", WAVES, duplicates, tempMap, IconType::Wave, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "robot", ROBOTS, duplicates, ROBOT_TEXTURES, IconType::Robot, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "spider", SPIDERS, duplicates, SPIDER_TEXTURES, IconType::Spider, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "swing", SWINGS, duplicates, tempMap, IconType::Swing, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "jetpack", JETPACKS, duplicates, tempMap, IconType::Jetpack, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "icon", ICONS, duplicates, IconType::Cube, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "ship", SHIPS, duplicates, IconType::Ship, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "ball", BALLS, duplicates, IconType::Ball, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "ufo", UFOS, duplicates, IconType::Ufo, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "wave", WAVES, duplicates, IconType::Wave, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "robot", ROBOTS, duplicates, IconType::Robot, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "spider", SPIDERS, duplicates, IconType::Spider, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "swing", SWINGS, duplicates, IconType::Swing, i == 0);
for (int i = 0; i < packSize; i++) loadIcons(packs[i] / "jetpack", JETPACKS, duplicates, IconType::Jetpack, i == 0);
duplicates.clear();
for (int i = 0; i < packSize; i++) loadTrails(packs[i] / "trail", duplicates, i == 0);
duplicates.clear();
Expand All @@ -102,7 +104,7 @@ class MoreIcons {
static std::vector<std::filesystem::path> getTexturePacks();
static void loadIcons(
const std::filesystem::path& path, std::vector<std::string>& list, std::vector<std::string>& duplicates,
std::unordered_map<std::string, std::string>& textures, IconType type, bool create
IconType type, bool create
);
static void loadTrails(const std::filesystem::path& path, std::vector<std::string>& duplicates, bool create);
static void changeSimplePlayer(SimplePlayer* player, IconType type) {
Expand Down Expand Up @@ -215,16 +217,14 @@ class MoreIcons {
}

static void useCustomRobot(GJRobotSprite* robot, const std::string& robotFile) {
if (robotFile.empty() || !MoreIcons::hasRobot(robotFile)) return;
if (!robot || robotFile.empty() || !MoreIcons::hasRobot(robotFile)) return;
robot->setBatchNode(nullptr);
robot->setTexture(cocos2d::CCTextureCache::get()->textureForKey(MoreIcons::ROBOT_TEXTURES[robotFile].c_str()));
useCustomSprite(robot, robotFile);
}

static void useCustomSpider(GJSpiderSprite* spider, const std::string& spiderFile) {
if (spiderFile.empty() || !MoreIcons::hasSpider(spiderFile)) return;
if (!spider || spiderFile.empty() || !MoreIcons::hasSpider(spiderFile)) return;
spider->setBatchNode(nullptr);
spider->setTexture(cocos2d::CCTextureCache::get()->textureForKey(MoreIcons::SPIDER_TEXTURES[spiderFile].c_str()));
useCustomSprite(spider, spiderFile);
}

Expand Down
16 changes: 16 additions & 0 deletions src/classes/DummyNode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "DummyNode.hpp"

using namespace geode::prelude;

void DummyNode::recursiveBlend(CCNode* node, ccBlendFunc blendFunc) {
if (node) {
if (auto blendNode = typeinfo_cast<CCBlendProtocol*>(node)) {
blendNode->setBlendFunc(blendFunc);
}
for (auto child : CCArrayExt<CCNode*>(node->getChildren())) {
if (auto blendNode = typeinfo_cast<CCBlendProtocol*>(child)) {
recursiveBlend(child, blendFunc);
}
}
}
}
Loading

0 comments on commit a700919

Please sign in to comment.