diff options
Diffstat (limited to 'src/resources')
27 files changed, 584 insertions, 191 deletions
diff --git a/src/resources/abilitydb.cpp b/src/resources/abilitydb.cpp index 311ee9eb..cb596ea8 100644 --- a/src/resources/abilitydb.cpp +++ b/src/resources/abilitydb.cpp @@ -41,7 +41,7 @@ static AbilityInfo::TargetMode targetModeFromString(const std::string& str) if (str == "direction") return AbilityInfo::TARGET_DIRECTION; - logger->log("AbilityDB: Warning, unknown target mode \"%s\"", str.c_str() ); + Log::info("AbilityDB: Warning, unknown target mode \"%s\"", str.c_str() ); return AbilityInfo::TARGET_BEING; } @@ -68,7 +68,7 @@ void AbilityDB::readAbilityNode(XML::Node node, const std::string &filename) info->rechargeCurrent = 0; if (mAbilityInfos.find(id) != mAbilityInfos.end()) - logger->log("AbilityDB: Duplicate ability ID %d in %s, ignoring", id, filename.c_str()); + Log::info("AbilityDB: Duplicate ability ID %d in %s, ignoring", id, filename.c_str()); else mAbilityInfos[id] = info; } diff --git a/src/resources/animation.cpp b/src/resources/animation.cpp index 91c22236..c529400f 100644 --- a/src/resources/animation.cpp +++ b/src/resources/animation.cpp @@ -84,7 +84,7 @@ Animation Animation::fromXML(XML::Node node, const std::string &dyePalettes) if (index < 0) { - logger->log("No valid value for 'index'"); + Log::info("No valid value for 'index'"); continue; } @@ -92,7 +92,7 @@ Animation Animation::fromXML(XML::Node node, const std::string &dyePalettes) if (!img) { - logger->log("No image at index %d", index); + Log::info("No image at index %d", index); continue; } @@ -105,7 +105,7 @@ Animation Animation::fromXML(XML::Node node, const std::string &dyePalettes) if (start < 0 || end < 0) { - logger->log("No valid value for 'start' or 'end'"); + Log::info("No valid value for 'start' or 'end'"); continue; } @@ -115,7 +115,7 @@ Animation Animation::fromXML(XML::Node node, const std::string &dyePalettes) if (!img) { - logger->log("No image at index %d", start); + Log::info("No image at index %d", start); continue; } diff --git a/src/resources/attributes.cpp b/src/resources/attributes.cpp index 7ec6b516..5b1018ee 100644 --- a/src/resources/attributes.cpp +++ b/src/resources/attributes.cpp @@ -240,22 +240,22 @@ namespace Attributes { int id = node.getProperty("id", 0); if (!id) { - logger->log("Attributes: Invalid or missing stat ID in " - DEFAULT_ATTRIBUTESDB_FILE "!"); + Log::info("Attributes: Invalid or missing stat ID in " + DEFAULT_ATTRIBUTESDB_FILE "!"); return; } if (attributes.find(id) != attributes.end()) { - logger->log("Attributes: Redefinition of stat ID %d", id); + Log::info("Attributes: Redefinition of stat ID %d", id); } std::string name = node.getProperty("name", ""); if (name.empty()) { - logger->log("Attributes: Invalid or missing stat name in " - DEFAULT_ATTRIBUTESDB_FILE "!"); + Log::info("Attributes: Invalid or missing stat name in " + DEFAULT_ATTRIBUTESDB_FILE "!"); return; } @@ -280,10 +280,10 @@ namespace Attributes { { if (name.empty()) { - logger->log("Attribute modifier in attribute %u:%s: " - "Empty name definition " - "on empty tag definition, skipping.", - a.id, a.name.c_str()); + Log::info("Attribute modifier in attribute %u:%s: " + "Empty name definition " + "on empty tag definition, skipping.", + a.id, a.name.c_str()); --count; continue; } @@ -296,10 +296,10 @@ namespace Attributes { { if (name.empty()) { - logger->log("Attribute modifier in attribute %u:%s: " - "Empty name definition " - "on empty effect definition, skipping.", - a.id, a.name.c_str()); + Log::info("Attribute modifier in attribute %u:%s: " + "Empty name definition " + "on empty effect definition, skipping.", + a.id, a.name.c_str()); --count; continue; } @@ -308,7 +308,7 @@ namespace Attributes { } tags.insert(std::make_pair(tag, effect)); } - logger->log("Found %d tags for attribute %d.", count, id); + Log::info("Found %d tags for attribute %d.", count, id); } /** @@ -321,8 +321,8 @@ namespace Attributes { DEFAULT_MIN_PTS); attributeMaximum = node.getProperty("maximum", DEFAULT_MAX_PTS); - logger->log("Loaded points: start: %i, min: %i, max: %i.", - creationPoints, attributeMinimum, attributeMaximum); + Log::info("Loaded points: start: %i, min: %i, max: %i.", + creationPoints, attributeMinimum, attributeMaximum); } /** @@ -330,8 +330,8 @@ namespace Attributes { */ void checkStatus() { - logger->log("Found %d tags for %d attributes.", int(tags.size()), - int(attributes.size())); + Log::info("Found %d tags for %d attributes.", int(tags.size()), + int(attributes.size())); if (attributes.size() == 0) { @@ -346,9 +346,9 @@ namespace Attributes { if (averageValue > attributeMaximum || averageValue < attributeMinimum || creationPoints < 1) { - logger->log("Attributes: Character's point values make " - "the character's creation impossible. " - "Switch back to defaults."); + Log::info("Attributes: Character's point values make " + "the character's creation impossible. " + "Switch back to defaults."); creationPoints = DEFAULT_POINTS; attributeMinimum = DEFAULT_MIN_PTS; attributeMaximum = DEFAULT_MAX_PTS; diff --git a/src/resources/beinginfo.cpp b/src/resources/beinginfo.cpp index 20c24d4f..f2edf1d8 100644 --- a/src/resources/beinginfo.cpp +++ b/src/resources/beinginfo.cpp @@ -73,8 +73,8 @@ void BeingInfo::setTargetCursorSize(const std::string &size) const auto cursorSize = targetCursorSizeFromString(size); if (!cursorSize) { - logger->log("Unknown targetCursor value \"%s\" for %s", - size.c_str(), name.c_str()); + Log::info("Unknown targetCursor value \"%s\" for %s", + size.c_str(), name.c_str()); } targetCursorSize = cursorSize.value_or(ActorSprite::TC_MEDIUM); } @@ -84,8 +84,8 @@ void BeingInfo::setHoverCursor(const std::string &cursorName) const auto cursor = cursorFromString(cursorName); if (!cursor) { - logger->log("Unknown hoverCursor value \"%s\" for %s", - cursorName.c_str(), name.c_str()); + Log::info("Unknown hoverCursor value \"%s\" for %s", + cursorName.c_str(), name.c_str()); } hoverCursor = cursor.value_or(Cursor::Pointer); } diff --git a/src/resources/chardb.cpp b/src/resources/chardb.cpp index 9001b6c2..97f86d3a 100644 --- a/src/resources/chardb.cpp +++ b/src/resources/chardb.cpp @@ -54,7 +54,7 @@ void CharDB::load() if (!root || root.name() != "chars") { - logger->log("CharDB: Failed to parse charcreation.xml."); + Log::info("CharDB: Failed to parse charcreation.xml."); return; } @@ -86,7 +86,7 @@ void CharDB::load() void CharDB::unload() { - logger->log("Unloading chars database..."); + Log::info("Unloading chars database..."); mLoaded = false; } diff --git a/src/resources/dye.cpp b/src/resources/dye.cpp index 0ecc9fd6..136c9334 100644 --- a/src/resources/dye.cpp +++ b/src/resources/dye.cpp @@ -63,8 +63,8 @@ DyePalette::DyePalette(const std::string &description) } else { - logger->log("Error, invalid embedded palette: %s", - description.c_str()); + Log::info("Error, invalid embedded palette: %s", + description.c_str()); return; } @@ -82,7 +82,7 @@ DyePalette::DyePalette(const std::string &description) ++pos; } - logger->log("Error, invalid embedded palette: %s", description.c_str()); + Log::info("Error, invalid embedded palette: %s", description.c_str()); } void DyePalette::getColor(int intensity, int color[3]) const @@ -195,7 +195,7 @@ Dye::Dye(const std::string &description) if (next_pos <= pos + 3 || description[pos + 1] != ':') { - logger->log("Error, invalid dye: %s", description.c_str()); + Log::info("Error, invalid dye: %s", description.c_str()); return; } @@ -211,7 +211,7 @@ Dye::Dye(const std::string &description) case 'C': i = 5; break; case 'W': i = 6; break; default: - logger->log("Error, invalid dye: %s", description.c_str()); + Log::info("Error, invalid dye: %s", description.c_str()); return; } mDyePalettes[i] = new DyePalette(description.substr(pos + 2, @@ -289,7 +289,7 @@ void Dye::instantiate(std::string &target, const std::string &palettes) } else { - logger->log("Error, invalid dye placeholder: %s", target.c_str()); + Log::info("Error, invalid dye placeholder: %s", target.c_str()); return; } s << target[next_pos]; diff --git a/src/resources/dye.h b/src/resources/dye.h index 0fe68f07..1730d2fd 100644 --- a/src/resources/dye.h +++ b/src/resources/dye.h @@ -35,7 +35,7 @@ class DyePalette * The string is either a file name or a sequence of hexadecimal RGB * values separated by ',' and starting with '#'. */ - DyePalette(const std::string &pallete); + DyePalette(const std::string &description); /** * Gets a pixel color depending on its intensity. First color is diff --git a/src/resources/emotedb.cpp b/src/resources/emotedb.cpp index d29483d1..c0f5f777 100644 --- a/src/resources/emotedb.cpp +++ b/src/resources/emotedb.cpp @@ -51,7 +51,7 @@ void EmoteDB::readEmoteNode(XML::Node node, const std::string &filename) const int id = node.getProperty("id", -1); if (id == -1) { - logger->log("Emote Database: Emote with missing ID in %s!", filename.c_str()); + Log::info("Emote Database: Emote with missing ID in %s!", filename.c_str()); return; } @@ -63,8 +63,8 @@ void EmoteDB::readEmoteNode(XML::Node node, const std::string &filename) if (emote.effectId == -1) { - logger->log("Emote Database: Warning: Emote %s has no attached effect in %s!", - emote.name.c_str(), filename.c_str()); + Log::info("Emote Database: Warning: Emote %s has no attached effect in %s!", + emote.name.c_str(), filename.c_str()); return; } @@ -74,8 +74,8 @@ void EmoteDB::readEmoteNode(XML::Node node, const std::string &filename) if (imageName.empty() || width <= 0 || height <= 0) { - logger->log("Emote Database: Warning: Emote %s has bad imageset values in %s", - emote.name.c_str(), filename.c_str()); + Log::info("Emote Database: Warning: Emote %s has bad imageset values in %s", + emote.name.c_str(), filename.c_str()); return; } @@ -85,8 +85,8 @@ void EmoteDB::readEmoteNode(XML::Node node, const std::string &filename) if (!emote.is || emote.is->size() == 0) { - logger->log("Emote Database: Error loading imageset for emote %s in %s", - emote.name.c_str(), filename.c_str()); + Log::info("Emote Database: Error loading imageset for emote %s in %s", + emote.name.c_str(), filename.c_str()); return; } @@ -119,7 +119,7 @@ const Emote &EmoteDB::get(int id) if (i == mEmotes.end()) { - logger->log("EmoteDB: Warning, unknown emote ID %d requested", id); + Log::info("EmoteDB: Warning, unknown emote ID %d requested", id); return mUnknown; } diff --git a/src/resources/hairdb.cpp b/src/resources/hairdb.cpp index 6b88a4df..312188d6 100644 --- a/src/resources/hairdb.cpp +++ b/src/resources/hairdb.cpp @@ -40,7 +40,7 @@ void HairDB::readHairColorNode(XML::Node node, const std::string &filename) int id = node.getProperty("id", 0); if (mHairColors.find(id) != mHairColors.end()) - logger->log("HairDb: Redefinition of color Id %d in %s", id, filename.c_str()); + Log::info("HairDb: Redefinition of color Id %d in %s", id, filename.c_str()); mHairColors[id] = node.getProperty("value", COLOR_WHITE); } @@ -55,7 +55,7 @@ void HairDB::unload() if (!mLoaded) return; - logger->log("Unloading hair style and color database..."); + Log::info("Unloading hair style and color database..."); mHairColors.clear(); mHairStyles.clear(); @@ -71,7 +71,7 @@ void HairDB::addHairStyle(int id) id = -id; if (mHairStyles.find(id) != mHairStyles.end()) - logger->log("Warning: Redefinition of hairstyle id %i:", id); + Log::warn("Redefinition of hairstyle id %i:", id); mHairStyles.insert(id); } @@ -81,13 +81,13 @@ const std::string &HairDB::getHairColor(int id) const if (!mLoaded) { // no idea if this can happen, but that check existed before - logger->log("WARNING: HairDB::getHairColor() called before settings were loaded!"); + Log::warn("HairDB::getHairColor() called before settings were loaded!"); } auto it = mHairColors.find(id); if (it != mHairColors.end()) return it->second; - logger->log("HairDb: Error, unknown color Id# %d", id); + Log::info("HairDb: Error, unknown color Id# %d", id); return mHairColors.at(0); } diff --git a/src/resources/image.cpp b/src/resources/image.cpp index b36aea01..11d5c275 100644 --- a/src/resources/image.cpp +++ b/src/resources/image.cpp @@ -54,8 +54,7 @@ Image::Image(SDL_Texture *texture, int width, int height): if (!texture) { - logger->log( - "Image::Image(SDL_Texture*, ...): Couldn't load invalid Surface!"); + Log::info("Image::Image(SDL_Texture*, ...): Couldn't load invalid Surface!"); } } @@ -72,8 +71,7 @@ Image::Image(GLuint glimage, int width, int height, int texWidth, int texHeight) if (glimage == 0) { - logger->log( - "Image::Image(GLuint, ...): Couldn't load invalid Surface!"); + Log::info("Image::Image(GLuint, ...): Couldn't load invalid Surface!"); } } #endif @@ -101,7 +99,7 @@ Resource *Image::load(SDL_RWops *rw) if (!tmpImage) { - logger->log("Error, image load failed: %s", IMG_GetError()); + Log::info("Error, image load failed: %s", IMG_GetError()); return nullptr; } @@ -117,20 +115,20 @@ Resource *Image::load(SDL_RWops *rw, const Dye &dye) if (!surf) { - logger->log("Error, image load failed: %s", IMG_GetError()); + Log::info("Error, image load failed: %s", IMG_GetError()); return nullptr; } if (surf->format->format != SDL_PIXELFORMAT_RGBA32) { - logger->log("Warning: image format is %s, not SDL_PIXELFORMAT_RGBA32. Converting...", - SDL_GetPixelFormatName(surf->format->format)); + Log::warn("Image format is %s, not SDL_PIXELFORMAT_RGBA32. Converting...", + SDL_GetPixelFormatName(surf->format->format)); SDL_Surface *convertedSurf = SDL_ConvertSurfaceFormat(surf, SDL_PIXELFORMAT_RGBA32, 0); SDL_FreeSurface(surf); if (!convertedSurf) { - logger->log("Error, image convert failed: %s", SDL_GetError()); + Log::info("Error, image convert failed: %s", SDL_GetError()); return nullptr; } surf = convertedSurf; @@ -219,8 +217,8 @@ Image *Image::_GLload(SDL_Surface *image) if (realWidth < width || realHeight < height) { - logger->log("Warning: image too large, cropping to %dx%d texture!", - realWidth, realHeight); + Log::warn("Image too large, cropping to %dx%d texture!", + realWidth, realHeight); } // Determine 32-bit masks based on byte order @@ -253,7 +251,7 @@ Image *Image::_GLload(SDL_Surface *image) if (!image) { - logger->log("Error, image convert failed: out of memory"); + Log::info("Error, image convert failed: out of memory"); return nullptr; } @@ -308,7 +306,7 @@ Image *Image::_GLload(SDL_Surface *image) errmsg = "GL_OUT_OF_MEMORY"; break; } - logger->log("Error: Image GL import failed: %s", errmsg); + Log::error("Image GL import failed: %s", errmsg); return nullptr; } diff --git a/src/resources/imageset.cpp b/src/resources/imageset.cpp index 34cf1fd8..1f194b4f 100644 --- a/src/resources/imageset.cpp +++ b/src/resources/imageset.cpp @@ -50,7 +50,7 @@ Image *ImageSet::get(size_t i) const { if (i >= mImages.size()) { - logger->log("Warning: No sprite %d in this image set", (int) i); + Log::warn("No sprite %d in this image set", (int) i); return nullptr; } diff --git a/src/resources/imagewriter.cpp b/src/resources/imagewriter.cpp index ddf1fbee..cf4c6803 100644 --- a/src/resources/imagewriter.cpp +++ b/src/resources/imagewriter.cpp @@ -42,7 +42,7 @@ bool ImageWriter::writePNG(SDL_Surface *surface, const std::string &filename) png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!png_ptr) { - logger->log("Had trouble creating png_structp"); + Log::info("Had trouble creating png_structp"); return false; } @@ -50,21 +50,21 @@ bool ImageWriter::writePNG(SDL_Surface *surface, const std::string &filename) if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); - logger->log("Could not create png_info"); + Log::info("Could not create png_info"); return false; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, (png_infopp)nullptr); - logger->log("problem writing to %s", filename.c_str()); + Log::info("problem writing to %s", filename.c_str()); return false; } FILE *fp = fopen(filename.c_str(), "wb"); if (!fp) { - logger->log("could not open file %s for writing", filename.c_str()); + Log::info("could not open file %s for writing", filename.c_str()); return false; } @@ -83,7 +83,7 @@ bool ImageWriter::writePNG(SDL_Surface *surface, const std::string &filename) row_pointers = new png_bytep[surface->h]; if (!row_pointers) { - logger->log("Had trouble converting surface to row pointers"); + Log::info("Had trouble converting surface to row pointers"); fclose(fp); return false; } diff --git a/src/resources/itemdb.cpp b/src/resources/itemdb.cpp index 05f6ad0b..1d217fc2 100644 --- a/src/resources/itemdb.cpp +++ b/src/resources/itemdb.cpp @@ -148,7 +148,7 @@ const ItemInfo &ItemDB::get(int id) const auto i = mItemInfos.find(id); if (i == mItemInfos.end()) { - logger->log("ItemDB: Warning, unknown item ID# %d", id); + Log::info("ItemDB: Warning, unknown item ID# %d", id); return *mUnknown; } @@ -164,8 +164,8 @@ const ItemInfo &ItemDB::get(const std::string &name) const { if (!name.empty()) { - logger->log("ItemDB: Warning, unknown item name \"%s\"", - name.c_str()); + Log::info("ItemDB: Warning, unknown item name \"%s\"", + name.c_str()); } return *mUnknown; } @@ -202,8 +202,8 @@ void ItemDB::loadSoundRef(ItemInfo &itemInfo, XML::Node node) } else { - logger->log("ItemDB: Ignoring unknown sound event '%s'", - event.c_str()); + Log::info("ItemDB: Ignoring unknown sound event '%s'", + event.c_str()); } } @@ -237,15 +237,15 @@ void ItemDB::loadReplacement(ItemInfo &info, XML::Node replaceNode) if (sprite == SPRITE_UNKNOWN) { - logger->log("ItemDB: Invalid sprite name '%s' in replace tag", - spriteString.data()); + Log::info("ItemDB: Invalid sprite name '%s' in replace tag", + spriteString.data()); return; } if (direction == DIRECTION_UNKNOWN) { - logger->log("ItemDB: Invalid direction name '%s' in replace tag", - directionString.data()); + Log::info("ItemDB: Invalid direction name '%s' in replace tag", + directionString.data()); return; } @@ -266,7 +266,7 @@ void ItemDB::loadReplacement(ItemInfo &info, XML::Node replaceNode) void ItemDB::unload() { - logger->log("Unloading item database..."); + Log::info("Unloading item database..."); delete mUnknown; mUnknown = nullptr; @@ -283,12 +283,12 @@ void ItemDB::loadCommonRef(ItemInfo &itemInfo, XML::Node node, const std::string if (!itemInfo.id) { - logger->log("ItemDB: Invalid or missing item Id in %s!", filename.c_str()); + Log::info("ItemDB: Invalid or missing item Id in %s!", filename.c_str()); return; } else if (mItemInfos.find(itemInfo.id) != mItemInfos.end()) { - logger->log("ItemDB: Redefinition of item Id %d in %s", itemInfo.id, filename.c_str()); + Log::info("ItemDB: Redefinition of item Id %d in %s", itemInfo.id, filename.c_str()); } itemInfo.mView = node.getProperty("view", 0); @@ -346,8 +346,8 @@ void ItemDB::addItem(ItemInfo *itemInfo) if (itr == mNamedItemInfos.end()) mNamedItemInfos[temp] = itemInfo; else - logger->log("ItemDB: Duplicate name (%s) for item id %d found.", - temp.c_str(), itemInfo->id); + Log::info("ItemDB: Duplicate name (%s) for item id %d found.", + temp.c_str(), itemInfo->id); } } @@ -359,7 +359,7 @@ static void checkParameter(int id, const T param, const T errorValue) std::stringstream errMsg; errMsg << "ItemDB: Missing " << param << " attribute for item id " << id << "!"; - logger->log("%s", errMsg.str().c_str()); + Log::info("%s", errMsg.str().c_str()); } } @@ -368,7 +368,7 @@ void ItemDB::checkItemInfo(ItemInfo &itemInfo) int id = itemInfo.id; if (!itemInfo.attackAction.empty()) if (itemInfo.attackRange == 0) - logger->log("ItemDB: Missing attack range from weapon %i!", id); + Log::info("ItemDB: Missing attack range from weapon %i!", id); if (id >= 0) { @@ -504,7 +504,7 @@ void ManaServItemDB::readItemNode(XML::Node node, const std::string &filename) std::string trigger = itemChild.getProperty("trigger", std::string()); if (trigger.empty()) { - logger->log("Found empty trigger effect label in %s, skipping.", filename.c_str()); + Log::info("Found empty trigger effect label in %s, skipping.", filename.c_str()); continue; } @@ -514,8 +514,8 @@ void ManaServItemDB::readItemNode(XML::Node node, const std::string &filename) auto triggerLabel = triggerTable.find(trigger); if (triggerLabel == triggerTable.end()) { - logger->log("Warning: unknown trigger %s in item %d!", - trigger.c_str(), itemInfo->id); + Log::warn("Unknown trigger %s in item %d!", + trigger.c_str(), itemInfo->id); continue; } @@ -528,7 +528,7 @@ void ManaServItemDB::readItemNode(XML::Node node, const std::string &filename) int duration = effectChild.getProperty("duration", 0); if (attribute.empty() || !value) { - logger->log("Warning: incomplete modifier definition in %s, skipping.", filename.c_str()); + Log::warn("Incomplete modifier definition in %s, skipping.", filename.c_str()); continue; } auto it = extraStats.cbegin(); @@ -537,7 +537,7 @@ void ManaServItemDB::readItemNode(XML::Node node, const std::string &filename) ++it; if (it == extraStats.end()) { - logger->log("Warning: unknown modifier tag %s in %s, skipping.", attribute.c_str(), filename.c_str()); + Log::warn("Unknown modifier tag %s in %s, skipping.", attribute.c_str(), filename.c_str()); continue; } effect.push_back( diff --git a/src/resources/mapreader.cpp b/src/resources/mapreader.cpp index b952cdcc..3ca5763c 100644 --- a/src/resources/mapreader.cpp +++ b/src/resources/mapreader.cpp @@ -77,7 +77,7 @@ static std::string resolveRelativePath(std::string base, std::string relative) Map *MapReader::readMap(const std::string &filename) { - logger->log("Attempting to read map %s", filename.c_str()); + Log::info("Attempting to read map %s", filename.c_str()); Map *map = nullptr; XML::Document doc(filename); @@ -89,7 +89,7 @@ Map *MapReader::readMap(const std::string &filename) { if (node.name() != "map") { - logger->log("Error: Not a map file (%s)!", filename.c_str()); + Log::error("Not a map file (%s)!", filename.c_str()); } else { @@ -98,7 +98,7 @@ Map *MapReader::readMap(const std::string &filename) } else { - logger->log("Error while parsing map file (%s)!", filename.c_str()); + Log::info("Error while parsing map file (%s)!", filename.c_str()); } if (map) @@ -119,9 +119,9 @@ Map *MapReader::readMap(XML::Node node, const std::string &path) if (tilew < 0 || tileh < 0) { - logger->log("MapReader: Warning: " - "Unitialized tile width or height value for map: %s", - path.c_str()); + Log::info("MapReader: Warning: " + "Unitialized tile width or height value for map: %s", + path.c_str()); return nullptr; } @@ -174,15 +174,15 @@ Map *MapReader::readMap(XML::Node node, const std::string &path) const int objW = objectNode.getProperty("width", 0); const int objH = objectNode.getProperty("height", 0); - logger->log("- Loading object name: %s type: %s at %d:%d", - objName.c_str(), objType.c_str(), - objX, objY); + Log::info("- Loading object name: %s type: %s at %d:%d", + objName.c_str(), objType.c_str(), + objX, objY); if (objType == "PARTICLE_EFFECT") { if (objName.empty()) { - logger->log(" Warning: No particle file given"); + Log::info(" Warning: No particle file given"); continue; } @@ -203,7 +203,7 @@ Map *MapReader::readMap(XML::Node node, const std::string &path) } else { - logger->log(" Warning: Unknown object type"); + Log::info(" Warning: Unknown object type"); } } } @@ -293,7 +293,7 @@ static void readLayer(XML::Node node, Map *map) map->addLayer(layer); } - logger->log("- Loading layer \"%s\"", name.c_str()); + Log::info("- Loading layer \"%s\"", name.c_str()); int x = 0; int y = 0; @@ -333,8 +333,8 @@ static void readLayer(XML::Node node, Map *map) if (!compression.empty() && compression != "gzip" && compression != "zlib") { - logger->log("Warning: only gzip or zlib layer " - "compression supported!"); + Log::warn("Only gzip or zlib layer " + "compression supported!"); return; } @@ -383,7 +383,7 @@ static void readLayer(XML::Node node, Map *map) if (!inflated) { - logger->log("Error: Could not decompress layer!"); + Log::error("Could not decompress layer!"); return; } } @@ -415,7 +415,7 @@ static void readLayer(XML::Node node, Map *map) const auto data = childNode.textContent(); if (data.empty()) { - logger->log("Error: CSV layer data is empty!"); + Log::error("CSV layer data is empty!"); continue; } @@ -432,7 +432,7 @@ static void readLayer(XML::Node node, Map *map) if (errno == ERANGE) { - logger->log("Error: Range error in tile layer data!"); + Log::error("Range error in tile layer data!"); break; } @@ -452,7 +452,7 @@ static void readLayer(XML::Node node, Map *map) pos = strchr(end, ','); if (!pos) { - logger->log("Error: CSV layer data too short!"); + Log::error("CSV layer data too short!"); break; } ++pos; @@ -536,8 +536,7 @@ static Tileset *readTileset(XML::Node node, const std::string &path, } else { - logger->log("Warning: Failed to load tileset (%s)", - source.c_str()); + Log::warn("Failed to load tileset (%s)", source.c_str()); } } } diff --git a/src/resources/monsterdb.cpp b/src/resources/monsterdb.cpp index 4963f93f..7f092a0e 100644 --- a/src/resources/monsterdb.cpp +++ b/src/resources/monsterdb.cpp @@ -111,11 +111,11 @@ void MonsterDB::readMonsterNode(XML::Node node, const std::string &filename) } else { - logger->log("MonsterDB: Warning, sound effect %s for " - "unknown event %s of monster %s in %s", - soundFile.c_str(), event.c_str(), - currentInfo->name.c_str(), - filename.c_str()); + Log::info("MonsterDB: Warning, sound effect %s for " + "unknown event %s of monster %s in %s", + soundFile.c_str(), event.c_str(), + currentInfo->name.c_str(), + filename.c_str()); } } else if (spriteNode.name() == "attack") @@ -168,7 +168,7 @@ BeingInfo *MonsterDB::get(int id) if (i == mMonsterInfos.end()) { - logger->log("MonsterDB: Warning, unknown monster ID %d requested", id); + Log::info("MonsterDB: Warning, unknown monster ID %d requested", id); return BeingInfo::Unknown; } diff --git a/src/resources/music.cpp b/src/resources/music.cpp index 069af588..b73d89ce 100644 --- a/src/resources/music.cpp +++ b/src/resources/music.cpp @@ -40,7 +40,7 @@ Music *Music::load(SDL_RWops *rw) return new Music(music); } - logger->log("Error, failed to load music: %s", Mix_GetError()); + Log::info("Error, failed to load music: %s", Mix_GetError()); return nullptr; } diff --git a/src/resources/npcdb.cpp b/src/resources/npcdb.cpp index 6b1c3150..44292525 100644 --- a/src/resources/npcdb.cpp +++ b/src/resources/npcdb.cpp @@ -46,7 +46,7 @@ void NPCDB::readNPCNode(XML::Node node, const std::string &filename) int id = node.getProperty("id", 0); if (id == 0) { - logger->log("NPC Database: NPC with missing ID in %s", filename.c_str()); + Log::info("NPC Database: NPC with missing ID in %s", filename.c_str()); return; } @@ -94,7 +94,7 @@ BeingInfo *NPCDB::get(int id) if (i == mNPCInfos.end()) { - logger->log("NPCDB: Warning, unknown NPC ID %d requested", id); + Log::info("NPCDB: Warning, unknown NPC ID %d requested", id); return BeingInfo::Unknown; } diff --git a/src/resources/questdb.cpp b/src/resources/questdb.cpp new file mode 100644 index 00000000..1424c20e --- /dev/null +++ b/src/resources/questdb.cpp @@ -0,0 +1,232 @@ +/* + * The Mana Client + * Copyright (C) 2025 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "resources/questdb.h" +#include "log.h" + +#include <algorithm> +#include <unordered_map> +#include <utility> + +namespace QuestDB { + +// The quests are stored in a map using their variable ID as the key +static std::unordered_map<int, Quest> quests; + +// Helper function to check if a container contains a value +template<typename Container, typename Value> +static bool contains(const Container &container, const Value &value) +{ + return std::find(container.begin(), container.end(), value) != container.end(); +} + +void init() +{ + unload(); +} + +void readQuestVarNode(XML::Node node, const std::string &filename) +{ + int varId = 0; + if (!node.attribute("id", varId)) + return; + + Quest &quest = quests[varId]; + + for (auto child : node.children()) + { + if (child.name() == "effect") + { + QuestEffect &effect = quest.effects.emplace_back(); + child.attribute("map", effect.map); + child.attribute("npc", effect.npcId); + child.attribute("effect", effect.statusEffectId); + child.attribute("value", effect.values); + + if (effect.map.empty() || effect.npcId == 0 || effect.statusEffectId == 0 || effect.values.empty()) + { + Log::warn("effect node for var %d is missing required attributes", varId); + } + } + else if (child.name() == "quest") + { + QuestState &state = quest.states.emplace_back(); + child.attribute("name", state.name); + child.attribute("group", state.group); + child.attribute("incomplete", state.incomplete); + child.attribute("complete", state.complete); + + if (state.incomplete.empty() && state.complete.empty()) + { + Log::warn("quest node for var %d ('%s') has neither 'complete' nor 'incomplete' values", + varId, state.name.c_str()); + continue; + } + + for (auto questChild : child.children()) + { + QuestRowType rowType; + std::string_view tag = questChild.name(); + if (tag == "text") + rowType = QuestRowType::Text; + else if (tag == "name") + rowType = QuestRowType::Name; + else if (tag == "reward") + rowType = QuestRowType::Reward; + else if (tag == "questgiver" || tag == "giver") + rowType = QuestRowType::Giver; + else if (tag == "coordinates") + rowType = QuestRowType::Coordinates; + else if (tag == "npc") + rowType = QuestRowType::NPC; + else + { + Log::warn("unknown quest row type '%s' for var %d ('%s')", + tag.data(), varId, state.name.c_str()); + continue; + } + + QuestRow &row = state.rows.emplace_back(rowType); + row.text = questChild.textContent(); + + if (rowType == QuestRowType::Coordinates) + { + questChild.attribute("x", row.x); + questChild.attribute("y", row.y); + } + } + } + } +} + +void unload() +{ + quests.clear(); +} + +bool hasQuests() +{ + return !quests.empty(); +} + +// In quests, the map name may include the file extension. This is discouraged +// but supported for compatibility. +static std::string_view baseName(const std::string &fileName) +{ + auto pos = fileName.find_last_of('.'); + return pos == std::string::npos ? fileName : std::string_view(fileName.data(), pos); +} + +QuestEffectMap getActiveEffects(const QuestVars &questVars, + const std::string &mapName) +{ + QuestEffectMap activeEffects; + + for (auto &[var, quest] : std::as_const(quests)) + { + auto value = questVars.get(var); + + for (auto &effect : quest.effects) + { + if (baseName(effect.map) != mapName) + continue; + if (!contains(effect.values, value)) + continue; + + activeEffects.set(effect.npcId, effect.statusEffectId); + } + } + + return activeEffects; +} + +std::vector<QuestEntry> getQuestsEntries(const QuestVars &questVars, + bool skipCompleted) +{ + std::vector<QuestEntry> activeQuests; + + for (auto &[varId, quest] : std::as_const(quests)) + { + auto value = questVars.get(varId); + + for (auto &state : quest.states) + { + bool matchesIncomplete = contains(state.incomplete, value); + bool matchesComplete = contains(state.complete, value); + + if (skipCompleted && matchesComplete) + continue; + + if (matchesIncomplete || matchesComplete) + { + QuestEntry &entry = activeQuests.emplace_back(); + entry.varId = varId; + entry.completed = matchesComplete; + entry.state = &state; + } + } + } + + return activeQuests; +} + +static std::pair<int, int> countQuestEntries(const Quest &quest, int value) +{ + int totalEntries = 0; + int completedEntries = 0; + + for (const auto &state : quest.states) + { + bool matchesIncomplete = contains(state.incomplete, value); + bool matchesComplete = contains(state.complete, value); + + if (matchesIncomplete || matchesComplete) + { + totalEntries++; + if (matchesComplete) + completedEntries++; + } + } + + return { totalEntries, completedEntries }; +} + +QuestChange questChange(int varId, int oldValue, int newValue) +{ + if (newValue == oldValue) + return QuestChange::None; + + auto questIt = quests.find(varId); + if (questIt == quests.end()) + return QuestChange::None; + + const Quest &quest = questIt->second; + + auto [oldQuestEntries, oldCompletedEntries] = countQuestEntries(quest, oldValue); + auto [newQuestEntries, newCompletedEntries] = countQuestEntries(quest, newValue); + + if (newCompletedEntries > oldCompletedEntries) + return QuestChange::Completed; + if (newQuestEntries > oldQuestEntries) + return QuestChange::New; + return QuestChange::None; +} + +} // namespace QuestDB diff --git a/src/resources/questdb.h b/src/resources/questdb.h new file mode 100644 index 00000000..43996b0b --- /dev/null +++ b/src/resources/questdb.h @@ -0,0 +1,139 @@ +/* + * The Mana Client + * Copyright (C) 2025 The Mana Developers + * + * This file is part of The Mana Client. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "utils/xml.h" + +#include <map> +#include <string> +#include <vector> + +/** + * A map that returns a default value for non-existent keys. + */ +template<typename Key, typename Value, Value def = Value()> +class MapWithDefault +{ +public: + void set(Key key, Value value) + { + mVars[key] = value; + } + + Value get(Key key) const + { + auto it = mVars.find(key); + return it != mVars.end() ? it->second : def; + } + + void clear() + { + mVars.clear(); + } + +private: + std::map<Key, Value> mVars; +}; + +struct QuestEffect +{ + std::vector<int> values; // Quest variable values to which the effect applies + std::string map; // Map name the NPC is located on + int npcId = 0; + int statusEffectId = 0; +}; + +// Map of quest variables, from variable ID to value +using QuestVars = MapWithDefault<int, int>; + +// Map of quest effects, from NPC ID to status effect ID +using QuestEffectMap = MapWithDefault<int, int>; + +enum class QuestRowType +{ + Text, + Name, + Reward, + Giver, + Coordinates, + NPC +}; + +struct QuestRow +{ + QuestRow(QuestRowType type) + : type(type) + {} + + QuestRowType type; + std::string text; + int x = 0; + int y = 0; +}; + +struct QuestState +{ + std::string name; // Name of the quest in this state + std::string group; // Group name of the quest in this state + std::vector<int> incomplete; // Quest variable values for this state (quest incomplete) + std::vector<int> complete; // Quest variable values for this state (quest complete) + std::vector<QuestRow> rows; // Rows of text in the Quests window for this state +}; + +struct Quest +{ + std::vector<QuestEffect> effects; + std::vector<QuestState> states; +}; + +struct QuestEntry +{ + int varId; + bool completed; + const QuestState *state; + + const std::string &name() const { return state->name; } + const std::vector<QuestRow> &rows() const { return state->rows; } +}; + +enum class QuestChange +{ + None, + New, + Completed +}; + +namespace QuestDB +{ + void init(); + void readQuestVarNode(XML::Node node, const std::string &filename); + void unload(); + + bool hasQuests(); + + QuestEffectMap getActiveEffects(const QuestVars &questVars, + const std::string &mapName); + + std::vector<QuestEntry> getQuestsEntries(const QuestVars &questVars, + bool skipCompleted = false); + + QuestChange questChange(int varId, int oldValue, int newValue); +}; diff --git a/src/resources/resource.cpp b/src/resources/resource.cpp index cdff8060..17864cf5 100644 --- a/src/resources/resource.cpp +++ b/src/resources/resource.cpp @@ -31,7 +31,7 @@ void Resource::decRef(OrphanPolicy orphanPolicy) { // Reference may not already have reached zero if (mRefCount == 0) { - logger->log("Warning: mRefCount already zero for %s", mIdPath.c_str()); + Log::warn("mRefCount already zero for %s", mIdPath.c_str()); assert(false); } diff --git a/src/resources/resourcemanager.cpp b/src/resources/resourcemanager.cpp index e62407e3..2857c0df 100644 --- a/src/resources/resourcemanager.cpp +++ b/src/resources/resourcemanager.cpp @@ -45,7 +45,7 @@ ResourceManager *ResourceManager::instance = nullptr; ResourceManager::ResourceManager() { - logger->log("Initializing resource manager..."); + Log::info("Initializing resource manager..."); } ResourceManager::~ResourceManager() @@ -86,11 +86,11 @@ void ResourceManager::cleanUp(Resource *res) { if (res->mRefCount > 0) { - logger->log("ResourceManager::~ResourceManager() cleaning up %d " - "reference%s to %s", - res->mRefCount, - (res->mRefCount == 1) ? "" : "s", - res->mIdPath.c_str()); + Log::info("ResourceManager::~ResourceManager() cleaning up %d " + "reference%s to %s", + res->mRefCount, + (res->mRefCount == 1) ? "" : "s", + res->mIdPath.c_str()); } delete res; @@ -118,7 +118,7 @@ void ResourceManager::cleanOrphans() } else { - logger->log("ResourceManager::release(%s)", res->mIdPath.c_str()); + Log::info("ResourceManager::release(%s)", res->mIdPath.c_str()); iter = mOrphanedResources.erase(iter); delete res; // delete only after removal from list, to avoid issues in recursion } @@ -129,12 +129,12 @@ void ResourceManager::cleanOrphans() bool ResourceManager::addToSearchPath(const std::string &path, bool append) { - logger->log("Adding to PhysicsFS: %s", path.c_str()); if (!FS::addToSearchPath(path, append)) { - logger->log("Error: %s", FS::getLastError()); + Log::error("Couldn't add search path: %s (%s)", path.c_str(), FS::getLastError()); return false; } + Log::info("Added search path: %s", path.c_str()); return true; } diff --git a/src/resources/settingsmanager.cpp b/src/resources/settingsmanager.cpp index 9323d4d1..3dfb5eb4 100644 --- a/src/resources/settingsmanager.cpp +++ b/src/resources/settingsmanager.cpp @@ -27,6 +27,7 @@ #include "resources/monsterdb.h" #include "resources/npcdb.h" #include "resources/abilitydb.h" +#include "resources/questdb.h" #include "resources/statuseffectdb.h" #include "net/net.h" @@ -38,6 +39,9 @@ #include "log.h" #include "units.h" +#include <string> +#include <set> + namespace SettingsManager { static std::string mSettingsFile; @@ -53,6 +57,7 @@ namespace SettingsManager hairDB.init(); itemDb->init(); MonsterDB::init(); + QuestDB::init(); AbilityDB::init(); NPCDB::init(); EmoteDB::init(); @@ -96,6 +101,7 @@ namespace SettingsManager NPCDB::unload(); AbilityDB::unload(); MonsterDB::unload(); + QuestDB::unload(); if (itemDb) itemDb->unload(); hairDB.unload(); @@ -107,7 +113,7 @@ namespace SettingsManager */ static bool loadFile(const std::string &filename) { - logger->log("Loading game settings from %s", filename.c_str()); + Log::info("Loading game settings from %s", filename.c_str()); XML::Document doc(filename); XML::Node node = doc.rootNode(); @@ -118,7 +124,7 @@ namespace SettingsManager // FIXME: check root node's name when bjorn decides it's time if (!node /*|| node.name() != "settings" */) { - logger->log("Settings Manager: %s is not a valid settings file!", filename.c_str()); + Log::info("Settings Manager: %s is not a valid settings file!", filename.c_str()); return false; } @@ -156,7 +162,7 @@ namespace SettingsManager // check if we're not entering a loop if (mIncludedFiles.find(includeFile) != mIncludedFiles.end()) { - logger->log("Warning: Circular include loop detecting while including %s from %s", includeFile.c_str(), filename.c_str()); + Log::warn("Circular include loop detecting while including %s from %s", includeFile.c_str(), filename.c_str()); } else { @@ -165,7 +171,7 @@ namespace SettingsManager } else { - logger->log("Warning: <include> element without 'file' or 'name' attribute in %s", filename.c_str()); + Log::warn("<include> element without 'file' or 'name' attribute in %s", filename.c_str()); } } else if (childNode.name() == "option") @@ -177,7 +183,7 @@ namespace SettingsManager if (!name.empty()) paths.setValue(name, value); else - logger->log("Warning: option without a name found in %s", filename.c_str()); + Log::warn("option without a name found in %s", filename.c_str()); } else if (childNode.name() == "attribute") { @@ -221,6 +227,10 @@ namespace SettingsManager { NPCDB::readNPCNode(childNode, filename); } + else if (childNode.name() == "var") + { + QuestDB::readQuestVarNode(childNode, filename); + } else if (childNode.name() == "emote") { EmoteDB::readEmoteNode(childNode, filename); diff --git a/src/resources/settingsmanager.h b/src/resources/settingsmanager.h index 5b70f865..d9a6994c 100644 --- a/src/resources/settingsmanager.h +++ b/src/resources/settingsmanager.h @@ -20,10 +20,6 @@ #pragma once -#include <string> -#include <list> -#include <set> - namespace SettingsManager { void load(); diff --git a/src/resources/soundeffect.cpp b/src/resources/soundeffect.cpp index 19d7a820..1b0492d7 100644 --- a/src/resources/soundeffect.cpp +++ b/src/resources/soundeffect.cpp @@ -36,7 +36,7 @@ SoundEffect *SoundEffect::load(SDL_RWops *rw) return new SoundEffect(soundEffect); } - logger->log("Error, failed to load sound effect: %s", Mix_GetError()); + Log::info("Error, failed to load sound effect: %s", Mix_GetError()); return nullptr; } diff --git a/src/resources/spritedef.cpp b/src/resources/spritedef.cpp index 85e5e566..0e7f12dd 100644 --- a/src/resources/spritedef.cpp +++ b/src/resources/spritedef.cpp @@ -44,7 +44,7 @@ Action *SpriteDef::getAction(const std::string &action) const if (i == mActions.end()) { - logger->log("Warning: no action \"%s\" defined!", action.c_str()); + Log::warn("No action \"%s\" defined!", action.c_str()); return nullptr; } @@ -66,7 +66,7 @@ SpriteDef *SpriteDef::load(const std::string &animationFile, int variant) if (!rootNode || rootNode.name() != "sprite") { - logger->log("Error, failed to parse %s", animationFile.c_str()); + Log::info("Error, failed to parse %s", animationFile.c_str()); std::string errorFile = paths.getStringValue("sprites") + paths.getStringValue("spriteErrorFile"); @@ -157,7 +157,7 @@ void SpriteDef::loadImageSet(XML::Node node, const std::string &palettes) auto imageSet = resman->getImageSet(imageSrc, width, height); if (!imageSet) { - logger->error(strprintf("Couldn't load imageset (%s)!", + Log::critical(strprintf("Couldn't load imageset (%s)!", imageSrc.c_str())); } @@ -174,16 +174,16 @@ void SpriteDef::loadAction(XML::Node node, int variant_offset) auto si = mImageSets.find(imageSetName); if (si == mImageSets.end()) { - logger->log("Warning: imageset \"%s\" not defined in %s", - imageSetName.c_str(), getIdPath().c_str()); + Log::warn("imageset \"%s\" not defined in %s", + imageSetName.c_str(), getIdPath().c_str()); return; } ImageSet *imageSet = si->second; if (actionName == SpriteAction::INVALID) { - logger->log("Warning: Unknown action \"%s\" defined in %s", - actionName.c_str(), getIdPath().c_str()); + Log::warn("Unknown action \"%s\" defined in %s", + actionName.c_str(), getIdPath().c_str()); return; } auto *action = new Action; @@ -215,8 +215,8 @@ void SpriteDef::loadAnimation(XML::Node animationNode, if (directionType == DIRECTION_INVALID) { - logger->log("Warning: Unknown direction \"%s\" used in %s", - directionName.c_str(), getIdPath().c_str()); + Log::warn("Unknown direction \"%s\" used in %s", + directionName.c_str(), getIdPath().c_str()); return; } @@ -239,7 +239,7 @@ void SpriteDef::loadAnimation(XML::Node animationNode, if (index < 0) { - logger->log("No valid value for 'index'"); + Log::info("No valid value for 'index'"); continue; } @@ -247,7 +247,7 @@ void SpriteDef::loadAnimation(XML::Node animationNode, if (!img) { - logger->log("No image at index %d", index + variant_offset); + Log::info("No image at index %d", index + variant_offset); continue; } @@ -260,7 +260,7 @@ void SpriteDef::loadAnimation(XML::Node animationNode, if (start < 0 || end < 0) { - logger->log("No valid value for 'start' or 'end'"); + Log::info("No valid value for 'start' or 'end'"); continue; } @@ -270,7 +270,7 @@ void SpriteDef::loadAnimation(XML::Node animationNode, if (!img) { - logger->log("No image at index %d", start + variant_offset); + Log::info("No image at index %d", start + variant_offset); break; } @@ -295,8 +295,8 @@ void SpriteDef::includeSprite(XML::Node includeNode) if (processedFiles.find(filename) != processedFiles.end()) { - logger->log("Error, Tried to include %s which already is included.", - filename.c_str()); + Log::info("Error, Tried to include %s which already is included.", + filename.c_str()); return; } processedFiles.insert(filename); @@ -306,7 +306,7 @@ void SpriteDef::includeSprite(XML::Node includeNode) if (!rootNode || rootNode.name() != "sprite") { - logger->log("Error, no sprite root node in %s", filename.c_str()); + Log::info("Error, no sprite root node in %s", filename.c_str()); return; } diff --git a/src/resources/theme.cpp b/src/resources/theme.cpp index 8e4a07be..0c332902 100644 --- a/src/resources/theme.cpp +++ b/src/resources/theme.cpp @@ -25,7 +25,6 @@ #include "configuration.h" #include "log.h" -#include "textrenderer.h" #include "resources/dye.h" #include "resources/image.h" @@ -75,7 +74,7 @@ ThemeInfo::ThemeInfo(const std::string &path) if (rootNode.attribute("name", name) && !name.empty()) this->doc = std::move(doc); else - logger->log("Error: Theme '%s' has no name!", path.c_str()); + Log::error("Theme '%s' has no name!", path.c_str()); } std::string ThemeInfo::getFullPath() const @@ -228,15 +227,14 @@ void Skin::updateAlpha(float alpha) Theme::Theme(const ThemeInfo &themeInfo) : mThemePath(themeInfo.getFullPath()) - , mProgressColors(THEME_PROG_END) { listen(Event::ConfigChannel); readTheme(themeInfo); if (mPalettes.empty()) { - logger->log("Error, theme did not define any palettes: %s", - themeInfo.getPath().c_str()); + Log::info("Error, theme did not define any palettes: %s", + themeInfo.getPath().c_str()); // Avoid crashing mPalettes.emplace_back(THEME_COLORS_END); @@ -377,9 +375,12 @@ void Theme::drawSkin(Graphics *graphics, SkinType type, const WidgetState &state getSkin(type).draw(graphics, state); } -void Theme::drawProgressBar(Graphics *graphics, const gcn::Rectangle &area, - const gcn::Color &color, float progress, - const std::string &text) const +void Theme::drawProgressBar(Graphics *graphics, + const gcn::Rectangle &area, + const gcn::Color &color, + float progress, + const std::string &text, + ProgressPalette progressType) const { gcn::Font *oldFont = graphics->getFont(); gcn::Color oldColor = graphics->getColor(); @@ -408,17 +409,21 @@ void Theme::drawProgressBar(Graphics *graphics, const gcn::Rectangle &area, { if (auto skinState = skin.getState(widgetState.flags)) { - auto font = skinState->textFormat.bold ? boldFont : gui->getFont(); + const TextFormat *textFormat = &skinState->textFormat; + + if (progressType < THEME_PROG_END && mProgressTextFormats[progressType]) + textFormat = &(*mProgressTextFormats[progressType]); + + auto font = textFormat->bold ? boldFont : gui->getFont(); const int textX = area.x + area.width / 2; const int textY = area.y + (area.height - font->getHeight()) / 2; - TextRenderer::renderText(graphics, - text, - textX, - textY, - gcn::Graphics::CENTER, - font, - skinState->textFormat); + graphics->drawText(text, + textX, + textY, + gcn::Graphics::CENTER, + font, + *textFormat); } } @@ -479,7 +484,7 @@ static bool check(bool value, const char *msg, ...) { va_list ap; va_start(ap, msg); - logger->vlog(msg, ap); + Log::vinfo(msg, ap); va_end(ap); } return !value; @@ -487,9 +492,9 @@ static bool check(bool value, const char *msg, ...) bool Theme::readTheme(const ThemeInfo &themeInfo) { - logger->log("Loading %s theme from '%s'...", - themeInfo.getName().c_str(), - themeInfo.getPath().c_str()); + Log::info("Loading %s theme from '%s'...", + themeInfo.getName().c_str(), + themeInfo.getPath().c_str()); XML::Node rootNode = themeInfo.getDocument().rootNode(); @@ -507,10 +512,10 @@ bool Theme::readTheme(const ThemeInfo &themeInfo) else if (childNode.name() == "icon") readIconNode(childNode); else - logger->log("Theme: Unknown node '%s'!", childNode.name().data()); + Log::info("Theme: Unknown node '%s'!", childNode.name().data()); } - logger->log("Finished loading theme."); + Log::info("Finished loading theme."); for (auto &[_, skin] : mSkins) skin.updateAlpha(mAlpha); @@ -588,9 +593,8 @@ static void readSkinStateRectNode(XML::Node node, SkinState &state) node.attribute("fill", rect.filled); } -static void readSkinStateTextNode(XML::Node node, SkinState &state) +static void readTextNode(XML::Node node, TextFormat &textFormat) { - auto &textFormat = state.textFormat; node.attribute("bold", textFormat.bold); node.attribute("color", textFormat.color); node.attribute("outlineColor", textFormat.outlineColor); @@ -625,7 +629,7 @@ void Theme::readSkinStateNode(XML::Node node, Skin &skin) const else if (childNode.name() == "rect") readSkinStateRectNode(childNode, state); else if (childNode.name() == "text") - readSkinStateTextNode(childNode, state); + readTextNode(childNode, state.textFormat); } skin.addState(std::move(state)); @@ -689,7 +693,7 @@ void Theme::readSkinStateImgNode(XML::Node node, SkinState &state) const border.right = right; border.top = top; border.bottom = bottom; - border.image = image->getSubImage(x, y, width, height); + border.image.reset(image->getSubImage(x, y, width, height)); node.attribute("fill", border.fillMode); } @@ -705,7 +709,7 @@ inline void fromString(const char *str, gcn::Color &value) if (strlen(str) < 7 || str[0] != '#') { error: - logger->log("Error, invalid theme color palette: %s", str); + Log::info("Error, invalid theme color palette: %s", str); value = gcn::Color(0, 0, 0); return; } @@ -788,6 +792,7 @@ static int readColorId(const std::string &id) "WHISPER_TAB", "BACKGROUND", "HIGHLIGHT", + "HIGHLIGHT_TEXT", "TAB_FLASH", "SHOP_WARNING", "ITEM_EQUIPPED", @@ -873,7 +878,7 @@ void Theme::readPaletteNode(XML::Node node) { int paletteId; if (node.attribute("id", paletteId) && static_cast<size_t>(paletteId) != mPalettes.size()) - logger->log("Theme: Non-consecutive palette 'id' attribute with value %d!", paletteId); + Log::info("Theme: Non-consecutive palette 'id' attribute with value %d!", paletteId); Palette &palette = mPalettes.emplace_back(THEME_COLORS_END); @@ -882,7 +887,7 @@ void Theme::readPaletteNode(XML::Node node) if (childNode.name() == "color") readColorNode(childNode, palette); else - logger->log("Theme: Unknown node '%s'!", childNode.name().data()); + Log::info("Theme: Unknown node '%s'!", childNode.name().data()); } } @@ -916,5 +921,15 @@ void Theme::readProgressBarNode(XML::Node node) if (check(id >= 0, "Theme: 'progress' element has unknown 'id' attribute: '%s'!", idStr.c_str())) return; - mProgressColors[id] = std::make_unique<DyePalette>(node.getProperty("color", std::string())); + std::string color; + if (node.attribute("color", color)) + mProgressColors[id] = std::make_unique<DyePalette>(color); + + for (auto childNode : node.children()) + { + if (childNode.name() == "text") + readTextNode(childNode, mProgressTextFormats[id].emplace()); + else + Log::info("Theme: Unknown node '%s' in progressbar!", childNode.name().data()); + } } diff --git a/src/resources/theme.h b/src/resources/theme.h index bfd3b33a..fbcb263b 100644 --- a/src/resources/theme.h +++ b/src/resources/theme.h @@ -30,6 +30,7 @@ #include "resources/image.h" #include "utils/xml.h" +#include <array> #include <map> #include <memory> #include <optional> @@ -227,6 +228,7 @@ class Theme : public EventListener WHISPER_TAB, BACKGROUND, HIGHLIGHT, + HIGHLIGHT_TEXT, TAB_FLASH, SHOP_WARNING, ITEM_EQUIPPED, @@ -283,7 +285,7 @@ class Theme : public EventListener static const gcn::Color &getThemeColor(int type); static gcn::Color getProgressColor(int type, float progress); - + const Palette &getPalette(size_t index) const; /** @@ -305,7 +307,8 @@ class Theme : public EventListener const gcn::Rectangle &area, const gcn::Color &color, float progress, - const std::string &text = std::string()) const; + const std::string &text = std::string(), + ProgressPalette progressType = ProgressPalette::THEME_PROG_END) const; const Skin &getSkin(SkinType skinType) const; @@ -357,5 +360,6 @@ class Theme : public EventListener float mAlpha = 1.0; std::vector<Palette> mPalettes; - std::vector<std::unique_ptr<DyePalette>> mProgressColors; + std::array<std::unique_ptr<DyePalette>, THEME_PROG_END> mProgressColors; + std::array<std::optional<TextFormat>, THEME_PROG_END> mProgressTextFormats; }; |