Working on translations
This commit is contained in:
1 parent
9d8ca865c7
commit
e2d9105b02
18 files changed
+271
-88
No files matched your search
+1
-1
@@ -316,7 +316,7 @@ namespace ZL
|
||||
gameState.chatPreviewMsg[i].clear();
|
||||
gameState.chatHistory[i].clear();
|
||||
}
|
||||
gameState.inventory.items.clear();
|
||||
gameState.inventory.clear();
|
||||
gameState.questJournal.loadFromFile("resources/config/quests.json", CONST_ZIP_FILE);
|
||||
createLocations();
|
||||
};
|
||||
|
||||
@@ -21,6 +21,39 @@ Language languageFromCode(const std::string& code) {
|
||||
return Language::Russian;
|
||||
}
|
||||
|
||||
std::string localizedConfigPath(const std::string& basePath, Language lang) {
|
||||
static const std::string marker = "/config/";
|
||||
const size_t pos = basePath.find(marker);
|
||||
if (pos == std::string::npos) return basePath;
|
||||
return basePath.substr(0, pos) + "/config_" + languageToCode(lang) + "/" + basePath.substr(pos + marker.size());
|
||||
}
|
||||
|
||||
std::string readLocalizedConfigFile(const std::string& basePath, const std::string& zipFile) {
|
||||
const std::string localizedPath = localizedConfigPath(basePath, g_currentLanguage);
|
||||
if (localizedPath != basePath) {
|
||||
std::string content;
|
||||
if (zipFile.empty()) {
|
||||
content = readTextFile(localizedPath);
|
||||
} else {
|
||||
try {
|
||||
const auto buf = readFileFromZIP(localizedPath, zipFile);
|
||||
content.assign(buf.begin(), buf.end());
|
||||
} catch (const std::exception&) {
|
||||
content.clear();
|
||||
}
|
||||
}
|
||||
if (!content.empty()) return content;
|
||||
}
|
||||
|
||||
if (zipFile.empty()) return readTextFile(basePath);
|
||||
try {
|
||||
const auto buf = readFileFromZIP(basePath, zipFile);
|
||||
return std::string(buf.begin(), buf.end());
|
||||
} catch (const std::exception&) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
void loadLanguageSetting() {
|
||||
const std::string content = readTextFile("settings.json");
|
||||
if (content.empty()) return;
|
||||
|
||||
@@ -14,6 +14,15 @@ extern Language g_currentLanguage;
|
||||
std::string languageToCode(Language lang);
|
||||
Language languageFromCode(const std::string& code);
|
||||
|
||||
// "resources/config/items.json" + English -> "resources/config_en/items.json".
|
||||
// Returns basePath unchanged if it doesn't contain a "/config/" segment.
|
||||
std::string localizedConfigPath(const std::string& basePath, Language lang);
|
||||
|
||||
// Reads basePath, preferring the config_<lang> variant for g_currentLanguage;
|
||||
// falls back to basePath if the localized file/zip-entry is missing or empty.
|
||||
// zipFile empty => loose files from disk; non-empty => entries inside that zip.
|
||||
std::string readLocalizedConfigFile(const std::string& basePath, const std::string& zipFile);
|
||||
|
||||
// Reads the "language" field from settings.json (if present) and updates
|
||||
// g_currentLanguage. Safe to call even if settings.json doesn't exist yet.
|
||||
// Must run before any location/dialogue content is loaded, since some
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "MenuManager.h"
|
||||
#include "Game.h"
|
||||
#include "Localization.h"
|
||||
#include "items/ItemRegistry.h"
|
||||
#include "render/TextRenderer.h"
|
||||
#include "utils/Utils.h"
|
||||
#include <iostream>
|
||||
@@ -414,12 +415,19 @@ namespace ZL {
|
||||
|
||||
uiManager.setTextButtonCallback("languageRussianButton", [this](const std::string&) {
|
||||
ZL::g_currentLanguage = ZL::Language::Russian;
|
||||
reloadLocalizedGameContent();
|
||||
});
|
||||
uiManager.setTextButtonCallback("languageEnglishButton", [this](const std::string&) {
|
||||
ZL::g_currentLanguage = ZL::Language::English;
|
||||
reloadLocalizedGameContent();
|
||||
});
|
||||
}
|
||||
|
||||
void MenuManager::reloadLocalizedGameContent() {
|
||||
ItemRegistry::instance().loadFromJson("resources/config/items.json", CONST_ZIP_FILE);
|
||||
gameState_.questJournal.reloadDefinitionsPreservingState("resources/config/quests.json", CONST_ZIP_FILE);
|
||||
}
|
||||
|
||||
void MenuManager::showLoadGameScreen()
|
||||
{
|
||||
uiManager.pushMenuFromSavedRoot(loadSavedGameScreenRoot);
|
||||
|
||||
@@ -118,6 +118,7 @@ namespace ZL {
|
||||
void enterGameplay(bool isFromLoad = false);
|
||||
void applyHudForCurrentState();
|
||||
void refreshQuestJournalUi();
|
||||
void reloadLocalizedGameContent();
|
||||
void selectQuestByIndex(int index);
|
||||
void refreshItemPickupHud();
|
||||
void setupStep5Callbacks();
|
||||
|
||||
@@ -124,7 +124,7 @@ namespace ZL {
|
||||
api.set_function("pickup_item", [inventory](const std::string& itemId) {
|
||||
const Item* item = ItemRegistry::instance().findById(itemId);
|
||||
if (item) {
|
||||
inventory->addItem(*item);
|
||||
inventory->addItem(item->id);
|
||||
std::cout << "[script] pickup_item: " << item->name << std::endl;
|
||||
} else {
|
||||
std::cerr << "[script] pickup_item: item '" << itemId << "' not found in ItemRegistry\n";
|
||||
|
||||
+29
-30
@@ -1,56 +1,55 @@
|
||||
#include "Item.h"
|
||||
#include "ItemRegistry.h"
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
namespace ZL {
|
||||
|
||||
void Inventory::addItem(const Item& item) {
|
||||
items.push_back(item);
|
||||
std::cout << "Item added to inventory: " << item.name << std::endl;
|
||||
if (onItemAdded) onItemAdded(item.id);
|
||||
void Inventory::addItem(const std::string& itemId) {
|
||||
itemIds.push_back(itemId);
|
||||
std::cout << "Item added to inventory: " << itemId << std::endl;
|
||||
if (onItemAdded) onItemAdded(itemId);
|
||||
}
|
||||
|
||||
void Inventory::removeItem(const std::string& itemId) {
|
||||
auto it = std::find_if(items.begin(), items.end(),
|
||||
[&itemId](const Item& item) { return item.id == itemId; });
|
||||
if (it != items.end()) {
|
||||
std::cout << "Item removed from inventory: " << it->name << std::endl;
|
||||
auto it = std::find(itemIds.begin(), itemIds.end(), itemId);
|
||||
if (it != itemIds.end()) {
|
||||
std::cout << "Item removed from inventory: " << itemId << std::endl;
|
||||
if (onItemRemoved) onItemRemoved(itemId);
|
||||
items.erase(it);
|
||||
itemIds.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
bool Inventory::hasItem(const std::string& itemId) const {
|
||||
return std::find_if(items.begin(), items.end(),
|
||||
[&itemId](const Item& item) { return item.id == itemId; }) != items.end();
|
||||
return std::find(itemIds.begin(), itemIds.end(), itemId) != itemIds.end();
|
||||
}
|
||||
|
||||
std::vector<Item> Inventory::getItems() const {
|
||||
std::vector<Item> result;
|
||||
result.reserve(itemIds.size());
|
||||
for (const auto& id : itemIds) {
|
||||
const Item* def = ItemRegistry::instance().findById(id);
|
||||
if (def) result.push_back(*def);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void Inventory::save(nlohmann::json& out) const {
|
||||
nlohmann::json arr = nlohmann::json::array();
|
||||
for (const auto& item : items) {
|
||||
arr.push_back({
|
||||
{"id", item.id},
|
||||
{"name", item.name},
|
||||
{"description", item.description},
|
||||
{"icon", item.icon},
|
||||
{"selectedIcon", item.selectedIcon}
|
||||
});
|
||||
}
|
||||
out["items"] = std::move(arr);
|
||||
out["items"] = itemIds;
|
||||
}
|
||||
|
||||
void Inventory::load(const nlohmann::json& in) {
|
||||
items.clear();
|
||||
itemIds.clear();
|
||||
if (!in.contains("items") || !in["items"].is_array()) return;
|
||||
for (const auto& j : in["items"]) {
|
||||
Item item;
|
||||
item.id = j.value("id", "");
|
||||
item.name = j.value("name", "");
|
||||
item.description = j.value("description", "");
|
||||
item.icon = j.value("icon", "");
|
||||
item.selectedIcon = j.value("selectedIcon", "");
|
||||
items.push_back(std::move(item));
|
||||
if (j.is_string()) {
|
||||
itemIds.push_back(j.get<std::string>());
|
||||
} else if (j.is_object()) {
|
||||
// Backward-compat with old save files that stored full item structs.
|
||||
const std::string id = j.value("id", "");
|
||||
if (!id.empty()) itemIds.push_back(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-5
@@ -22,13 +22,14 @@ namespace ZL {
|
||||
|
||||
class Inventory : public ISaveable {
|
||||
public:
|
||||
std::vector<Item> items;
|
||||
void addItem(const Item& item);
|
||||
void addItem(const std::string& itemId);
|
||||
void removeItem(const std::string& itemId);
|
||||
const std::vector<Item>& getItems() const { return items; }
|
||||
// Resolves current items from ItemRegistry by id, so returned data
|
||||
// always reflects the currently loaded (localized) definitions.
|
||||
std::vector<Item> getItems() const;
|
||||
bool hasItem(const std::string& itemId) const;
|
||||
void clear() { items.clear(); }
|
||||
size_t getCount() const { return items.size(); }
|
||||
void clear() { itemIds.clear(); }
|
||||
size_t getCount() const { return itemIds.size(); }
|
||||
|
||||
void save(nlohmann::json& out) const override;
|
||||
void load(const nlohmann::json& in) override;
|
||||
@@ -36,6 +37,9 @@ namespace ZL {
|
||||
// Called whenever an item is added/removed; receives the item id.
|
||||
std::function<void(const std::string&)> onItemAdded;
|
||||
std::function<void(const std::string&)> onItemRemoved;
|
||||
|
||||
private:
|
||||
std::vector<std::string> itemIds;
|
||||
};
|
||||
|
||||
} // namespace ZL
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "ItemRegistry.h"
|
||||
#include "utils/Utils.h"
|
||||
#include "Localization.h"
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include <iostream>
|
||||
|
||||
@@ -11,20 +12,9 @@ ItemRegistry& ItemRegistry::instance() {
|
||||
}
|
||||
|
||||
void ItemRegistry::loadFromJson(const std::string& jsonPath, const std::string& zipPath) {
|
||||
std::string content;
|
||||
try {
|
||||
if (zipPath.empty()) {
|
||||
content = readTextFile(jsonPath);
|
||||
} else {
|
||||
auto buf = readFileFromZIP(jsonPath, zipPath);
|
||||
if (buf.empty()) {
|
||||
std::cerr << "[ItemRegistry] Failed to read " << jsonPath << " from zip\n";
|
||||
return;
|
||||
}
|
||||
content.assign(buf.begin(), buf.end());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "[ItemRegistry] Failed to open " << jsonPath << ": " << e.what() << "\n";
|
||||
const std::string content = readLocalizedConfigFile(jsonPath, zipPath);
|
||||
if (content.empty()) {
|
||||
std::cerr << "[ItemRegistry] Failed to read " << jsonPath << "\n";
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+17
-19
@@ -1,6 +1,7 @@
|
||||
#include "quest/QuestJournal.h"
|
||||
#include "external/nlohmann/json.hpp"
|
||||
#include "utils/Utils.h"
|
||||
#include "Localization.h"
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
@@ -26,26 +27,9 @@ static QuestStatus parseQuestStatus(const std::string& value) {
|
||||
}
|
||||
|
||||
bool QuestJournal::loadFromFile(const std::string& path, const std::string& zipFile) {
|
||||
quests.clear();
|
||||
questOrder.clear();
|
||||
|
||||
std::string content;
|
||||
try {
|
||||
if (zipFile.empty()) {
|
||||
content = ZL::readTextFile(path);
|
||||
}
|
||||
else {
|
||||
auto data = ZL::readFileFromZIP(path, zipFile);
|
||||
content.assign(data.begin(), data.end());
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
std::cerr << "[quest] Failed to read " << path << ": " << e.what() << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string content = ZL::readLocalizedConfigFile(path, zipFile);
|
||||
if (content.empty()) {
|
||||
std::cerr << "[quest] Empty quest file: " << path << std::endl;
|
||||
std::cerr << "[quest] Failed to read " << path << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -63,6 +47,12 @@ bool QuestJournal::loadFromFile(const std::string& path, const std::string& zipF
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only clear existing definitions/state once the new file is confirmed
|
||||
// valid, so a failed reload (e.g. a broken localized file) doesn't wipe
|
||||
// the currently loaded quest journal.
|
||||
quests.clear();
|
||||
questOrder.clear();
|
||||
|
||||
for (const auto& item : root["quests"]) {
|
||||
QuestDefinition def;
|
||||
def.id = item.value("id", "");
|
||||
@@ -101,6 +91,14 @@ bool QuestJournal::loadFromFile(const std::string& path, const std::string& zipF
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QuestJournal::reloadDefinitionsPreservingState(const std::string& path, const std::string& zipFile) {
|
||||
json snapshot;
|
||||
save(snapshot);
|
||||
if (!loadFromFile(path, zipFile)) return false;
|
||||
load(snapshot);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QuestJournal::setStatus(const std::string& questId, QuestStatus status) {
|
||||
QuestState* quest = findQuest(questId);
|
||||
if (!quest) return false;
|
||||
|
||||
@@ -14,6 +14,11 @@ class QuestJournal : public ZL::ISaveable {
|
||||
public:
|
||||
bool loadFromFile(const std::string& path, const std::string& zipFile = "");
|
||||
|
||||
// Reloads quest definitions (e.g. after a language switch) while
|
||||
// preserving current progress: snapshots status/objective state, reloads
|
||||
// definitions from path, then re-applies the snapshot.
|
||||
bool reloadDefinitionsPreservingState(const std::string& path, const std::string& zipFile = "");
|
||||
|
||||
void save(nlohmann::json& out) const override;
|
||||
void load(const nlohmann::json& in) override;
|
||||
|
||||
|
||||
Reference in new issue
Block a user