mirror of
https://github.com/Dingo-Shenanigans/ReSkate.git
synced 2026-10-23 07:45:53 +02:00
Merge branch 'main' of https://github.com/Dingo-Shenanigans/ReSkate
This commit is contained in:
commit
8f2c456a39
12 files changed
+300
-48
No files matched your search
+75
-15
@@ -120,6 +120,33 @@ bool deck_before(const std::string& a, const std::string& b) {
|
||||
};
|
||||
return place(a) != place(b) ? place(a) < place(b) : a < b;
|
||||
}
|
||||
// The decks a pack holds beside its own (mod_catalog.h), by name, in the menus' order.
|
||||
std::vector<std::pair<std::string, fs::path>> decks_inside(const fs::path& mod) {
|
||||
std::vector<std::pair<std::string, fs::path>> found;
|
||||
std::error_code error;
|
||||
for (fs::directory_iterator at(mod / L"decks", error), end; !error && at != end; at.increment(error)) {
|
||||
if (!at->is_directory(error) || !fs::is_regular_file(at->path() / L"layout.toc", error)) continue;
|
||||
auto name = lower(at->path().filename().string());
|
||||
if (deck_name(name)) found.emplace_back(std::move(name), at->path());
|
||||
}
|
||||
std::sort(found.begin(), found.end(), [](const auto& a, const auto& b) { return deck_before(a.first, b.first); });
|
||||
return found;
|
||||
}
|
||||
// Every deck in the Mods folder with the folder of the mod that brings it, in the menus' order,
|
||||
// whatever mods.json says of that mod: the decks inside a pack, or else the one a folder is named for.
|
||||
std::vector<std::pair<std::string, fs::path>> installed_decks() {
|
||||
std::vector<std::pair<std::string, fs::path>> decks;
|
||||
std::error_code error;
|
||||
for (fs::directory_iterator at(engine_data_root() / L"Mods", error), end; !error && at != end; at.increment(error)) {
|
||||
if (!at->is_directory(error)) continue;
|
||||
auto inside = decks_inside(at->path());
|
||||
for (auto& one : inside) decks.emplace_back(std::move(one.first), at->path());
|
||||
if (!inside.empty()) continue;
|
||||
if (auto deck = board_break_deck_of(at->path().filename().string()); !deck.empty()) decks.emplace_back(std::move(deck), at->path());
|
||||
}
|
||||
std::stable_sort(decks.begin(), decks.end(), [](const auto& a, const auto& b) { return deck_before(a.first, b.first); });
|
||||
return decks;
|
||||
}
|
||||
// The choice made in this run, and what the last live apply gave the game (none since launch: the
|
||||
// launch's catalogue says).
|
||||
struct BoardBreakApplied {
|
||||
@@ -133,13 +160,22 @@ std::optional<BoardBreakApplied> applied_board_break;
|
||||
} // namespace
|
||||
|
||||
std::string board_break_deck_of(std::string_view mod) {
|
||||
// Team-Board_Break_Wide and Board_Break_Wide are both the deck "wide".
|
||||
constexpr std::string_view prefix = "board_break_";
|
||||
auto name = lower(mod);
|
||||
// A pack's deck is listed as <pack's folder>#<deck>; no folder has a # in its name.
|
||||
if (const auto mark = name.rfind('#'); mark != std::string::npos) {
|
||||
name.erase(0, mark + 1);
|
||||
return deck_name(name) ? name : std::string{};
|
||||
}
|
||||
// Team-Board_Break_Wide and Board_Break_Wide are both the deck "wide".
|
||||
if (const auto team = name.rfind('-'); team != std::string::npos) name.erase(0, team + 1);
|
||||
if (!name.starts_with(prefix) || !deck_name(std::string_view(name).substr(prefix.size()))) return {};
|
||||
return name.substr(prefix.size());
|
||||
}
|
||||
std::string board_break_pack_of(std::string_view mod) {
|
||||
const auto mark = mod.rfind('#');
|
||||
return mark == std::string_view::npos ? std::string{} : std::string(mod.substr(0, mark));
|
||||
}
|
||||
std::string wanted_board_break_deck() noexcept {
|
||||
try {
|
||||
{
|
||||
@@ -178,23 +214,16 @@ bool save_board_break_deck(std::string_view deck) noexcept {
|
||||
std::vector<std::filesystem::path> board_break_folders() noexcept {
|
||||
std::vector<fs::path> found;
|
||||
try {
|
||||
std::vector<std::pair<std::string, fs::path>> decks;
|
||||
std::error_code error;
|
||||
for (fs::directory_iterator at(engine_data_root() / L"Mods", error), end; !error && at != end; at.increment(error))
|
||||
if (at->is_directory(error))
|
||||
if (auto deck = board_break_deck_of(at->path().filename().string()); !deck.empty()) decks.emplace_back(std::move(deck), at->path());
|
||||
std::sort(decks.begin(), decks.end(), [](const auto& a, const auto& b) { return deck_before(a.first, b.first); });
|
||||
for (auto& deck : decks) found.push_back(std::move(deck.second));
|
||||
for (auto& deck : installed_decks())
|
||||
if (std::find(found.begin(), found.end(), deck.second) == found.end()) found.push_back(std::move(deck.second));
|
||||
} catch (...) {}
|
||||
return found;
|
||||
}
|
||||
std::vector<std::string> installed_board_break_decks() noexcept {
|
||||
std::vector<std::string> found;
|
||||
try {
|
||||
for (const auto& folder : board_break_folders()) {
|
||||
auto deck = board_break_deck_of(folder.filename().string());
|
||||
if (found.empty() || found.back() != deck) found.push_back(std::move(deck));
|
||||
}
|
||||
for (auto& deck : installed_decks())
|
||||
if (found.empty() || found.back() != deck.first) found.push_back(std::move(deck.first));
|
||||
} catch (...) {}
|
||||
return found;
|
||||
}
|
||||
@@ -275,6 +304,37 @@ Catalog load_catalog(const std::filesystem::path& data_root, const MergeObserver
|
||||
try {
|
||||
for (const auto& name : list.missing)
|
||||
result.notes.push_back("mods.json names a folder that is not present: " + name);
|
||||
// Board Break: a deck pack holds several decks in one mod folder (mod_catalog.h). Each deck
|
||||
// inside becomes a mod of its own for the run, listed after the pack as <folder>#<deck>, so
|
||||
// that a pack's decks are chosen, set aside and switched like decks in folders of their own.
|
||||
// It carries the pack's build stamp; mods.json and the mod list only know the pack. The
|
||||
// pack's own layout is no deck here, whatever its folder is named: it is the deck a ReSkate
|
||||
// from before deck packs breaks, and is set aside for the decks inside.
|
||||
std::vector<std::string> packs;
|
||||
for (std::size_t i = 0; i < list.entries.size(); ++i) {
|
||||
auto inside = decks_inside(list.entries[i].mod.directory);
|
||||
if (inside.empty()) continue;
|
||||
const auto pack = list.entries[i].mod;
|
||||
packs.push_back(pack.name);
|
||||
const bool enabled = list.entries[i].enabled;
|
||||
for (auto& [deck, directory] : inside) {
|
||||
ModEntry part;
|
||||
part.enabled = enabled;
|
||||
part.mod.name = pack.name + "#" + deck;
|
||||
part.mod.title = pack.title + " (" + deck + ")";
|
||||
part.mod.author = pack.author;
|
||||
part.mod.version = pack.version;
|
||||
part.mod.directory = std::move(directory);
|
||||
part.mod.provides_layout = true;
|
||||
part.mod.studio_marker = pack.studio_marker;
|
||||
part.mod.skate_sha256 = pack.skate_sha256;
|
||||
part.mod.outdated = pack.outdated;
|
||||
list.entries.insert(list.entries.begin() + static_cast<std::ptrdiff_t>(++i), std::move(part));
|
||||
}
|
||||
}
|
||||
const auto deck_of = [&](const Mod& mod) {
|
||||
return std::find(packs.begin(), packs.end(), mod.name) != packs.end() ? std::string{} : board_break_deck_of(mod.name);
|
||||
};
|
||||
// Board Break (Extension/BoardBreak/board_break.h). The deck that can break is a mod's own
|
||||
// copy of the game's one deck model, and any mod that reshapes the deck replaces that same
|
||||
// model: the one higher in the load order has it. So the deck the player chose goes first
|
||||
@@ -287,7 +347,7 @@ Catalog load_catalog(const std::filesystem::path& data_root, const MergeObserver
|
||||
{
|
||||
const auto enabled_deck = [&](const std::string& deck) {
|
||||
return std::find_if(list.entries.begin(), list.entries.end(), [&](const auto& entry) {
|
||||
const auto found = board_break_deck_of(entry.mod.name);
|
||||
const auto found = deck_of(entry.mod);
|
||||
return entry.enabled && !found.empty() && (deck.empty() || found == deck);
|
||||
});
|
||||
};
|
||||
@@ -298,7 +358,7 @@ Catalog load_catalog(const std::filesystem::path& data_root, const MergeObserver
|
||||
std::rotate(list.entries.begin(), first, first + 1);
|
||||
result.notes.push_back("Board Break: " + list.entries.front().mod.name + " loads first, so its deck is the one in the game");
|
||||
for (auto entry = list.entries.begin() + 1; entry != list.entries.end(); ++entry) {
|
||||
if (!entry->enabled || (board_break_deck_of(entry->mod.name).empty() && !changes_the_deck(entry->mod.directory))) continue;
|
||||
if (!entry->enabled || (deck_of(entry->mod).empty() && !changes_the_deck(entry->mod.directory))) continue;
|
||||
entry->enabled = false;
|
||||
result.set_aside.push_back(entry->mod.name);
|
||||
result.notes.push_back("Board Break: " + entry->mod.name + " brings a deck model too and is set aside for this run");
|
||||
@@ -392,7 +452,7 @@ Catalog load_catalog(const std::filesystem::path& data_root, const MergeObserver
|
||||
// Board Break's deck is the one in the game when its mod is among those that load and no mod
|
||||
// above it brings a deck model of its own.
|
||||
for (const auto& mod : result.mods) {
|
||||
if (!(result.board_break_deck = board_break_deck_of(mod.name)).empty()) break;
|
||||
if (!(result.board_break_deck = deck_of(mod)).empty()) break;
|
||||
if (changes_the_deck(mod.directory)) break;
|
||||
}
|
||||
// Any Mods folder gets a patch, even an empty one or one of disabled
|
||||
|
||||
+21
-10
@@ -49,22 +49,33 @@ struct Catalog {
|
||||
};
|
||||
|
||||
// Board Break's decks (Extension/BoardBreak/board_break.h). The game has one deck model for every
|
||||
// board, so each deck that can break is a content mod of its own that replaces it: any mod whose
|
||||
// folder is named Board_Break_<Name> (also behind a team name, as a mod manager installs it:
|
||||
// Team-Board_Break_<Name>). The name, in small letters, is what the deck goes by; the deck the
|
||||
// player chose is the one that loads. These are the decks the feature was made with, in the order
|
||||
// the menus list them, and the console also knows them by their place here (deck 1 is the first).
|
||||
// Any other deck comes after them, by its name: a new shape needs no change to ReSkate.
|
||||
// board, so each deck that can break is a copy of it in a content mod, and the deck the player
|
||||
// chose is the one that loads. A deck comes in one of two ways:
|
||||
// - a mod whose folder is named Board_Break_<Name> (also behind a team name, as a mod manager
|
||||
// installs it: Team-Board_Break_<Name>) holds the deck <name>;
|
||||
// - a deck pack, a mod of any name with a folder decks/<name>/ for each deck it brings, laid out
|
||||
// like a mod folder (layout.toc, Win32/...). One package can bring every deck that way, where
|
||||
// the launcher installs one mod folder per package. For the run each deck inside is a mod of
|
||||
// its own, listed as <pack's folder>#<name>. The pack's own layout is not one of its decks,
|
||||
// whatever the folder is named, and is set aside like any other deck model. It is what an
|
||||
// older ReSkate loads from the pack: in a folder named Board_Break_<Name> the deck 2.0.6
|
||||
// breaks, which knows a deck by its folder's name only; under another name the game's own.
|
||||
// A deck goes by its name in small letters. These are the decks the feature was made with, in the
|
||||
// order the menus list them, and the console also knows them by their place here (deck 1 is the
|
||||
// first). Any other deck comes after them, by its name: a new shape needs no change to ReSkate.
|
||||
inline constexpr std::array<std::string_view, 8> board_break_decks{"standard", "wide", "concave", "powell",
|
||||
"slim", "egg", "square", "shaped"};
|
||||
// The name of the deck a mod folder holds, or nothing for any other mod.
|
||||
std::string board_break_deck_of(std::string_view mod_folder);
|
||||
// The name of the deck a mod holds going by the mod's name (a folder's, or a pack's deck's), or
|
||||
// nothing; and the folder name of the pack that one of a pack's decks is listed under, or nothing.
|
||||
std::string board_break_deck_of(std::string_view mod);
|
||||
std::string board_break_pack_of(std::string_view mod);
|
||||
// The deck the player chose ("standard" when nothing was chosen): read whenever the mods are put
|
||||
// together, at launch and at every live apply. Saving a choice makes it this run's at once.
|
||||
std::string wanted_board_break_deck() noexcept;
|
||||
bool save_board_break_deck(std::string_view deck) noexcept;
|
||||
// The decks whose mod's folder is in Mods/, whatever mods.json says of them, in the menus' order,
|
||||
// and those folders in the same order.
|
||||
// The decks that are in the Mods folder, whatever mods.json says of their mods, in the menus'
|
||||
// order; and the folders of the mods that bring them (a pack's once), for what a pack carries
|
||||
// beside its decks.
|
||||
std::vector<std::string> installed_board_break_decks() noexcept;
|
||||
std::vector<std::filesystem::path> board_break_folders() noexcept;
|
||||
// What the mods in effect give the game: the launch's catalogue, or the one the last live apply
|
||||
|
||||
Reference in new issue
Block a user