diff --git a/.gitignore b/.gitignore index 7796bda9f..4b691209f 100644 --- a/.gitignore +++ b/.gitignore @@ -127,6 +127,9 @@ aes_state.txt # intellij project folder .idea/ +# VSCode project folder +.vscode/ + # Python cache *.pyd *.pyc @@ -242,6 +245,9 @@ external_stlport nel_tools* ryzom_tools* +#personal projects +personal/ + # Dumps *.dmp diff --git a/nel/include/nel/gui/css_parser.h b/nel/include/nel/gui/css_parser.h index c3a426ee7..360e70a8d 100644 --- a/nel/include/nel/gui/css_parser.h +++ b/nel/include/nel/gui/css_parser.h @@ -73,7 +73,7 @@ namespace NLGUI void preprocess(); // parse selectors + combinators - std::vector parse_selector(const std::string &sel, std::string &pseudoElement) const; + std::vector parse_selector(const std::string &sel, std::string &pseudoElement, std::string::size_type &pos) const; // parse selector and style void parseRule(const std::string &selectorString, const std::string &styleString); diff --git a/nel/include/nel/gui/css_selector.h b/nel/include/nel/gui/css_selector.h index ed04ba86d..3228869f3 100644 --- a/nel/include/nel/gui/css_selector.h +++ b/nel/include/nel/gui/css_selector.h @@ -96,6 +96,8 @@ namespace NLGUI // match An+B rule to child index (1 based) bool matchNth(sint childNr, sint a, sint b) const; + // match :lang(xx) + bool matchLang(const CHtmlElement &elm, const std::string &pseudo) const; // parse nth-child string to 'a' and 'b' components // :nth-child(odd) diff --git a/nel/include/nel/gui/css_style.h b/nel/include/nel/gui/css_style.h index 5521818a4..9e8ad4b0e 100644 --- a/nel/include/nel/gui/css_style.h +++ b/nel/include/nel/gui/css_style.h @@ -188,6 +188,12 @@ namespace NLGUI void applyLineStyle(const std::string &value, CSSLineStyle *dest, const CSSLineStyle ¤tStyle) const; void applyPaddingWidth(const std::string &value, uint32 *dest, const uint32 currentPadding, uint32 fontSize) const; + // parse and replace var(--name, fallback) function + // return false if property should be ignored + bool cssFuncVar(std::string &func, const TStyle &styleRules, const std::set &seenProperties) const; + // return false if property was not defined + bool lookupPropertyValue(const std::string &name, std::string &value, const TStyle &styleRules) const; + public: void reset(); diff --git a/nel/include/nel/gui/group_html.h b/nel/include/nel/gui/group_html.h index 172c8b434..600c9b248 100644 --- a/nel/include/nel/gui/group_html.h +++ b/nel/include/nel/gui/group_html.h @@ -858,6 +858,7 @@ namespace NLGUI CCurlWWWData *data; std::string url; std::string dest; + std::string tmpdest; std::string luaScript; std::string md5sum; TDataType type; diff --git a/nel/include/nel/gui/html_element.h b/nel/include/nel/gui/html_element.h index b7bceb4ab..22f037cd4 100644 --- a/nel/include/nel/gui/html_element.h +++ b/nel/include/nel/gui/html_element.h @@ -84,6 +84,9 @@ namespace NLGUI TStyle getPseudo(const std::string &key) const; void setPseudo(const std::string &key, const TStyle &style); + // return lang property for css :lang() pseudo class + std::string getInheritedLanguage() const; + private: // pseudo elements like ":before" and ":after" std::map _Pseudo; diff --git a/nel/include/nel/gui/html_parser.h b/nel/include/nel/gui/html_parser.h index 0c36014da..cf1392c0b 100644 --- a/nel/include/nel/gui/html_parser.h +++ b/nel/include/nel/gui/html_parser.h @@ -19,6 +19,9 @@ #include "nel/misc/types_nl.h" +// Forward declarations for libxml2 +typedef struct _xmlNode xmlNode; + namespace NLGUI { class CHtmlElement; diff --git a/nel/include/nel/misc/xml_macros.h b/nel/include/nel/misc/xml_macros.h new file mode 100644 index 000000000..d25cd4c30 --- /dev/null +++ b/nel/include/nel/misc/xml_macros.h @@ -0,0 +1,74 @@ +// NeL - MMORPG Framework +// Copyright (C) 2010 Winch Gate Property Limited +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + + + +#ifndef XML_MACROS_H +#define XML_MACROS_H + +// +// xmlNodePtr cur; +// CXMLAutoPtr prop; +// +// sint i; +// XML_READ_SINT(cur, "prop_name", i, -1); +// + +#define XML_READ_UINT(node, name, var, def) { \ + uint tmp; \ + prop = (char *) xmlGetProp(node, (xmlChar*)name); \ + if (prop && fromString((const char*)prop, tmp)) \ + var = tmp; \ + else \ + var = def; \ +} + +#define XML_READ_SINT(node, name, var, def) { \ + sint tmp; \ + prop = (char *) xmlGetProp(node, (xmlChar*)name); \ + if (prop && fromString((const char*)prop, tmp)) \ + var = tmp; \ + else \ + var = def; \ +} + +#define XML_READ_BOOL(node, name, var, def) { \ + prop = (char *) xmlGetProp(node, (xmlChar*)name); \ + if (prop) \ + var = NLMISC::toBool((const char*)prop); \ + else \ + var = def; \ +} + +#define XML_READ_COLOR(node, name, var, def) { \ + NLMISC::CRGBA tmp; \ + prop = (char *) xmlGetProp(node, (xmlChar*)name); \ + if (prop && fromString((const char*)prop, tmp)) \ + var = tmp; \ + else \ + var = def; \ +} + +#define XML_READ_STRING(node, name, var, def) { \ + prop = (char *) xmlGetProp(node, (xmlChar*)name); \ + if (prop) \ + var = (const char*)prop; \ + else \ + var = def; \ +} + +#endif // XML_MACROS_H + diff --git a/nel/src/3d/font_manager.cpp b/nel/src/3d/font_manager.cpp index 3d468f987..a2f0f6cb2 100644 --- a/nel/src/3d/font_manager.cpp +++ b/nel/src/3d/font_manager.cpp @@ -146,6 +146,9 @@ void CFontManager::computeString (NLMISC::CUtfStringView sv, { // Creating font k.Char = *it; + // draw tab as space + if (k.Char == '\t') + k.Char = ' '; if (k.Char < 0x20) // Control Characters k.Char += 0x2400; if (k.Char == 0x7F) // DEL @@ -304,6 +307,9 @@ void CFontManager::computeStringInfo ( NLMISC::CUtfStringView sv, { // Creating font k.Char = *it; + // draw tab as space + if (k.Char == '\t') + k.Char = ' '; if (k.Char < 0x20) k.Char += 0x2400; k.FontGenerator = fontGen; diff --git a/nel/src/gui/css_parser.cpp b/nel/src/gui/css_parser.cpp index ab9f6edb7..b3e3861bb 100644 --- a/nel/src/gui/css_parser.cpp +++ b/nel/src/gui/css_parser.cpp @@ -50,7 +50,11 @@ namespace NLGUI pos = elements[i].find_first_of(':'); if (pos != std::string::npos) { - std::string key = trim(toLowerAscii(elements[i].substr(0, pos))); + // css properties are case-insensitive, but + // custom properties (--name; ...;) are case sensitive + std::string key = trim(elements[i].substr(0, pos)); + if (key.size() < 2 || (key[0] != '-' && key[1] != '-')) + key = toLowerAscii(key); std::string value = trim(elements[i].substr(pos+1)); styles.push_back(TStylePair(key, value)); } @@ -94,25 +98,22 @@ namespace NLGUI // @internal void CCssParser::parseRule(const std::string &selectorString, const std::string &styleString) { - std::vector selectors; - NLMISC::explode(selectorString, std::string(","), selectors); - TStyleVec props; props = parseDecls(styleString); // duplicate props to each selector in selector list, // example 'div > p, h1' creates 'div>p' and 'h1' - for(uint i=0; i CCssParser::parse_selector(const std::string &sel, std::string &pseudoElement) const + std::vector CCssParser::parse_selector(const std::string &sel, std::string &pseudoElement, std::string::size_type &pos) const { std::vector result; CCssSelector current; @@ -349,8 +350,8 @@ namespace NLGUI pseudoElement.clear(); bool failed = false; - std::string::size_type start = 0, pos = 0; - while(pos < sel.size()) + std::string::size_type start = pos; + while(pos < sel.size() && sel[pos] != ',') { std::string uc; uc = sel[pos]; @@ -366,6 +367,14 @@ namespace NLGUI continue; } + if (sel[pos] == '*' && current.empty()) + { + pos++; + current.Element = "*"; + start = pos; + continue; + } + if(sel[pos] == '#') { pos++; @@ -612,7 +621,8 @@ namespace NLGUI } else if (isSpace) { - current.Combinator = ' '; + if (sel[pos] != ',' && sel[pos] != '\0') + current.Combinator = ' '; } else { diff --git a/nel/src/gui/css_selector.cpp b/nel/src/gui/css_selector.cpp index 18d0375b0..dfc01b855 100644 --- a/nel/src/gui/css_selector.cpp +++ b/nel/src/gui/css_selector.cpp @@ -235,6 +235,11 @@ namespace NLGUI // 1st child should be '1' and not '0' if (!matchNth(elm.childIndex+1, a, b)) return false; } + else if (startsWith(PseudoClass[i], "lang(")) + { + std::string lang = PseudoClass[i].substr(5, PseudoClass[i].size() - 6); + if (lang.empty() || !matchLang(elm, lang)) return false; + } else { return false; @@ -324,6 +329,29 @@ namespace NLGUI } } + bool CCssSelector::matchLang(const CHtmlElement &elm, const std::string &pseudo) const + { + // TODO: does not support comma separated, or escaped/quoted/wildcard tags + std::string lang = toLowerAscii(elm.getInheritedLanguage()); + if (lang.empty() || pseudo.empty()) + return false; + + // lang = 'en', pseudo = 'en-US' + if (lang.size() < pseudo.size()) + return false; + + std::string selector = toLowerAscii(pseudo); + bool selectorHasRegion = selector.find("-") != std::string::npos; + bool langHasRegion = lang.find("-") != std::string::npos; + + // both are 'en', or 'en-US' type + if (langHasRegion == selectorHasRegion) + return lang == selector; + + // lang = 'en-US', selector = 'en' + return lang[selector.size()] == '-' && startsWith(lang, selector); + } + std::string CCssSelector::toString() const { std::string ret; diff --git a/nel/src/gui/css_style.cpp b/nel/src/gui/css_style.cpp index ac36da726..3ff9744bc 100644 --- a/nel/src/gui/css_style.cpp +++ b/nel/src/gui/css_style.cpp @@ -386,27 +386,35 @@ namespace NLGUI // - normalize values void CCssStyle::normalize(const TStyle &styleRules, CStyleParams &style, const CStyleParams ¤t) const { + std::set seenProperties; + TStyle::const_iterator it; for (it=styleRules.begin(); it != styleRules.end(); ++it) { + std::string value = it->second; + + // replace possible custom properties, ignore property if var() fails + if (!cssFuncVar(value, styleRules, seenProperties)) + continue; + // update local copy of applied style - style.StyleRules[it->first] = it->second; + style.StyleRules[it->first] = value; if (it->first == "color") { - if (it->second == "inherit") + if (value == "inherit") { style.TextColor = current.TextColor; } else { - scanHTMLColor(it->second.c_str(), style.TextColor); + scanHTMLColor(value.c_str(), style.TextColor); } } else if (it->first == "font") { - if (it->second == "inherit") + if (value == "inherit") { style.FontSize = current.FontSize; style.FontFamily = current.FontFamily; @@ -417,42 +425,42 @@ namespace NLGUI else if (it->first == "font-size") { - if (it->second == "inherit") + if (value == "inherit") { style.FontSize = current.FontSize; } - else if (it->second == "x-small") + else if (value == "x-small") { style.FontSize = 10; // 62.5% } - else if (it->second == "small") + else if (value == "small") { style.FontSize = 13; // 80%; } - else if (it->second == "medium") + else if (value == "medium") { style.FontSize = 16; // 100%; } - else if (it->second == "large") + else if (value == "large") { style.FontSize = 18; // 112.5% } - else if (it->second == "x-large") + else if (value == "x-large") { style.FontSize = 24; // 150% } - else if (it->second == "xx-large") + else if (value == "xx-large") { style.FontSize = 32; // 200%; } - else if (it->second == "smaller") + else if (value == "smaller") { if (style.FontSize < 5) style.FontSize = 3; else style.FontSize -= 2; } - else if (it->second == "larger") + else if (value == "larger") { style.FontSize += 2; } @@ -460,7 +468,7 @@ namespace NLGUI { float tmpf; std::string unit; - if (getCssLength(tmpf, unit, it->second.c_str())) + if (getCssLength(tmpf, unit, value.c_str())) { if (unit == "rem") style.FontSize = Root.FontSize * tmpf; @@ -479,16 +487,16 @@ namespace NLGUI if (it->first == "background-repeat") { // old ryzom specific value - if (it->second == "1") + if (value == "1") style.StyleRules[it->first] = "repeat"; } else if (it->first == "display") { - if (it->second == "inherit") + if (value == "inherit") style.DisplayBlock = current.DisplayBlock; else - style.DisplayBlock = (it->second == "block" || it->second == "table"); + style.DisplayBlock = (value == "block" || value == "table"); } } } @@ -1668,5 +1676,181 @@ namespace NLGUI } } + // *************************************************************************** + static void skipString(const std::string &value, std::string::size_type &pos) + { + char quote = value[pos]; + while(pos < value.size() && value[pos] != quote) + { + if (value[pos] == '\\') + pos++; + + pos++; + } + } + static void skipBlock(const std::string &value, std::string::size_type &pos, bool isString) + { + char openChar = value[pos]; + char closeChar = value[pos]; + if (openChar == '(') closeChar = ')'; + else if (openChar == '[') closeChar = ']'; + else if (openChar == '{') closeChar = '}'; + pos++; + + while(pos < value.size()) + { + char c = value[pos]; + if (c == '\\') + pos++; + else if (!isString && (c == '(' || c == '[' || c == '{')) + skipBlock(value, pos, false); + else if (c == closeChar) + break; + else if (c == '"' || c == '\'') + { + if (isString) + break; + + skipBlock(value, pos, true); + } + + pos++; + } + } + + static void skipWhitespace(const std::string &value, std::string::size_type &pos) + { + while(pos < value.size() && (value[pos] == ' ' || value[pos] == '\t' || value[pos] == '\r')) + pos++; + } + + // *************************************************************************** + bool CCssStyle::cssFuncVar(std::string &func, const TStyle &styleRules, const std::set &seenProperties) const + { + // TODO: fails if var() is inside string, ie '--text: ".. var(...) .."'; + + // start of 'var(' + std::string::size_type pos = func.find("var("); + if (pos == std::string::npos) + return true; + + // simple test to make sure 'var' is not substring + if (pos > 0 && (func[pos-1] != '_') && ((func[pos-1] >= 'a' && func[pos-1] <= 'z') || (func[pos-1] >= 'A' && func[pos-1] <='Z'))) + return true; + + // find closing ')' + std::string::size_type funcStart = pos; + std::string::size_type funcEnd = funcStart + 3; + skipBlock(func, funcEnd, false); + + pos += 4; + + // ',' separator + std::string::size_type sep = func.find_first_of(",)", pos); + if (sep > funcEnd) + { + // unlikely + sep = funcEnd; + } + else if (sep + 1 == funcEnd) + { + // no whitespace between ',' and ')', ie 'var(--name,)' + return false; + } + + // extract name + std::string name = func.substr(funcStart + 4, sep - pos); + if (seenProperties.count(name) > 0) + return false; + + std::string value; + // if name is not defined or resolves to 'initial', use fallback + bool found = lookupPropertyValue(name, value, styleRules); + if (found) { + // check if substituted value has 'var()' + std::set newSeen = seenProperties; + newSeen.insert(name); + found = cssFuncVar(value, styleRules, newSeen); + if (value == "initial") + found = false; + } + + // --name failed and we have fallback + if (!found && func[sep] == ',') + { + sep++; + skipWhitespace(func, sep); + + value = func.substr(sep, funcEnd - sep); + if (value.empty()) + { + found = true; + } + else + { + // check if substituted fallback has 'var()' + std::set newSeen = seenProperties; + newSeen.insert(name); + found = cssFuncVar(value, styleRules, newSeen); + if (value == "initial") + found = false; + } + } + + // invalidate property as both name and fallback failed + if (!found) + return false; + + // everything before 'var(' and after ')' + std::string result; + if (funcStart > 0) + result = trim(func.substr(0, funcStart)) + " "; + + result += trim(value); + if ((funcEnd+1) < func.size()) + result += " " + trim(func.substr(funcEnd+1)); + + // check replaced string for var() + std::set newSeen = seenProperties; + newSeen.insert(name); + bool success = cssFuncVar(result, styleRules, newSeen); + if (result == "initial") + success = false; + + func = result; + return success; + } + + // *************************************************************************** + bool CCssStyle::lookupPropertyValue(const std::string &name, std::string &value, const TStyle &styleRules) const + { + bool success = true; + TStyle::const_iterator it = styleRules.find(name); + if (it != styleRules.end()) + value = it->second; + else if (Current.hasStyle(name)) + value = Current.getStyle(name); + else + success = false; + + if (success && value != "inherit") + return true; + + std::vector::const_reverse_iterator rit = _StyleStack.rbegin(); + for(; rit != _StyleStack.rend(); ++rit) + { + if (rit->hasStyle(name)) + { + value = rit->getStyle(name); + if (value != "inherit") + { + return true; + } + } + } + + return false; + } + } // namespace diff --git a/nel/src/gui/group_html.cpp b/nel/src/gui/group_html.cpp index 2ac2e7f05..2836b1c1f 100644 --- a/nel/src/gui/group_html.cpp +++ b/nel/src/gui/group_html.cpp @@ -525,18 +525,19 @@ namespace NLGUI return false; } - string tmpdest = download.dest + ".tmp"; + // use browser Id so that two browsers would not use same temp file + download.tmpdest = localImageName(_Id + download.dest) + ".tmp"; // erase the tmp file if exists - if (CFile::fileExists(tmpdest)) + if (CFile::fileExists(download.tmpdest)) { - CFile::deleteFile(tmpdest); + CFile::deleteFile(download.tmpdest); } - FILE *fp = nlfopen (tmpdest, "wb"); + FILE *fp = nlfopen (download.tmpdest, "wb"); if (fp == NULL) { - nlwarning("Can't open file '%s' for writing: code=%d '%s'", tmpdest.c_str (), errno, strerror(errno)); + nlwarning("Can't open file '%s' for writing: code=%d '%s'", download.tmpdest.c_str (), errno, strerror(errno)); return false; } @@ -544,7 +545,7 @@ namespace NLGUI if (!curl) { fclose(fp); - CFile::deleteFile(tmpdest); + CFile::deleteFile(download.tmpdest); nlwarning("Creating cURL handle failed, unable to download '%s'", download.url.c_str()); return false; @@ -608,54 +609,57 @@ namespace NLGUI void CGroupHTML::finishCurlDownload(const CDataDownload &download) { - std::string tmpfile = download.dest + ".tmp"; - if (download.type == ImgType) { - // there is race condition if two browser instances are downloading same file - // second instance deletes first tmpfile and creates new file for itself. - if (CFile::getFileSize(tmpfile) > 0) + if (CFile::fileExists(download.tmpdest) && CFile::getFileSize(download.tmpdest) > 0) { try { // verify that image is not corrupted uint32 w, h; - CBitmap::loadSize(tmpfile, w, h); + CBitmap::loadSize(download.tmpdest, w, h); if (w != 0 && h != 0) { - // if not tmpfile, then img is already in cache - if (CFile::fileExists(tmpfile)) - { - if (CFile::fileExists(download.dest)) - { - CFile::deleteFile(download.dest); - } - - // to reload image on page, the easiest seems to be changing texture - // to temp file temporarily. that forces driver to reload texture from disk - // ITexture::touch() seem not to do this. - // cache was updated, first set texture as temp file - for(uint i = 0; i < download.imgs.size(); i++) - { - setImage(download.imgs[i].Image, tmpfile, download.imgs[i].Type); - setImageSize(download.imgs[i].Image, download.imgs[i].Style); - } - - CFile::moveFile(download.dest, tmpfile); - } + if (CFile::fileExists(download.dest)) + CFile::deleteFile(download.dest); + // to reload image on page, the easiest seems to be changing texture + // to temp file temporarily. that forces driver to reload texture from disk + // ITexture::touch() seem not to do this. + // cache was updated, first set texture as temp file for(uint i = 0; i < download.imgs.size(); i++) { - setImage(download.imgs[i].Image, download.dest, download.imgs[i].Type); + setImage(download.imgs[i].Image, download.tmpdest, download.imgs[i].Type); setImageSize(download.imgs[i].Image, download.imgs[i].Style); } + CFile::moveFile(download.dest, download.tmpdest); } } catch(const NLMISC::Exception &e) { // exception message has .tmp file name, so keep it for further analysis - nlwarning("Invalid image (%s): %s", download.url.c_str(), e.what()); + nlwarning("Invalid image (%s) from url (%s): %s", download.tmpdest.c_str(), download.url.c_str(), e.what()); + } + } + + if (CFile::fileExists(download.dest) && CFile::getFileSize(download.dest) > 0) + { + try + { + // verify that image is not corrupted + uint32 w, h; + CBitmap::loadSize(download.dest, w, h); + if (w != 0 && h != 0) + for(uint i = 0; i < download.imgs.size(); i++) + { + setImage(download.imgs[i].Image, download.dest, download.imgs[i].Type); + setImageSize(download.imgs[i].Image, download.imgs[i].Style); + } + } + catch(const NLMISC::Exception &e) + { + nlwarning("Invalid image (%s) from url (%s): %s", download.dest.c_str(), download.url.c_str(), e.what()); } } @@ -664,13 +668,13 @@ namespace NLGUI if (download.type == StylesheetType) { - if (CFile::fileExists(tmpfile)) + if (CFile::fileExists(download.tmpdest)) { if (CFile::fileExists(download.dest)) { CFile::deleteFile(download.dest); } - CFile::moveFile(download.dest, tmpfile); + CFile::moveFile(download.dest, download.tmpdest); } cssDownloadFinished(download.url, download.dest); @@ -681,20 +685,20 @@ namespace NLGUI { bool verified = false; // no tmpfile if file was already in cache - if (CFile::fileExists(tmpfile)) + if (CFile::fileExists(download.tmpdest)) { - verified = download.md5sum.empty() || (download.md5sum != getMD5(tmpfile).toString()); + verified = download.md5sum.empty() || (download.md5sum != getMD5(download.tmpdest).toString()); if (verified) { if (CFile::fileExists(download.dest)) { CFile::deleteFile(download.dest); } - CFile::moveFile(download.dest, tmpfile); + CFile::moveFile(download.dest, download.tmpdest); } else { - CFile::deleteFile(tmpfile); + CFile::deleteFile(download.tmpdest); } } else if (CFile::fileExists(download.dest)) @@ -746,7 +750,7 @@ namespace NLGUI if (type != OverImage) { std::string temp = dest; - if (!CFile::fileExists(temp)) + if (!CFile::fileExists(temp) || CFile::getFileSize(temp) == 0) { temp = placeholder; } @@ -967,9 +971,9 @@ namespace NLGUI { fclose(it->fp); - if (CFile::fileExists(it->dest + ".tmp")) + if (CFile::fileExists(it->tmpdest)) { - CFile::deleteFile(it->dest + ".tmp"); + CFile::deleteFile(it->tmpdest); } } } @@ -4071,10 +4075,9 @@ namespace NLGUI obj.LastModified = data.data->getLastModified(); CHttpCache::getInstance()->store(data.dest, obj); - std::string tmpfile = data.dest + ".tmp"; - if (code == 304 && CFile::fileExists(tmpfile)) + if (code == 304 && CFile::fileExists(data.tmpdest)) { - CFile::deleteFile(tmpfile); + CFile::deleteFile(data.tmpdest); } } else if ((code >= 301 && code <= 303) || code == 307 || code == 308) @@ -4102,10 +4105,9 @@ namespace NLGUI LOG_DL("Redirect '%s'", location.c_str()); // no finished callback called, so cleanup old temp - std::string tmpfile = data.dest + ".tmp"; - if (CFile::fileExists(tmpfile)) + if (CFile::fileExists(data.tmpdest)) { - CFile::deleteFile(tmpfile); + CFile::deleteFile(data.tmpdest); } return; } diff --git a/nel/src/gui/html_element.cpp b/nel/src/gui/html_element.cpp index f0b764640..8735cc56e 100644 --- a/nel/src/gui/html_element.cpp +++ b/nel/src/gui/html_element.cpp @@ -122,6 +122,21 @@ namespace NLGUI } } + // *************************************************************************** + std::string CHtmlElement::getInheritedLanguage() const + { + const CHtmlElement *node = this; + while(node) + { + if (node->hasAttribute("lang")) + return node->getAttribute("lang"); + + node = node->parent; + } + + return ""; + } + // *************************************************************************** std::string CHtmlElement::toString(bool tree, uint depth) const { diff --git a/nel/src/gui/html_parser.cpp b/nel/src/gui/html_parser.cpp index bcb83bf8e..b8eb0176b 100644 --- a/nel/src/gui/html_parser.cpp +++ b/nel/src/gui/html_parser.cpp @@ -66,7 +66,15 @@ namespace NLGUI { if (node->type == XML_TEXT_NODE) { - parent.Children.push_back(CHtmlElement(CHtmlElement::TEXT_NODE, (const char*)(node->content))); + // linebreak right after pre,textare open tag should be removed + if (parent.Children.empty() && (*node->content == '\n') && (parent.ID == HTML_PRE || parent.ID == HTML_TEXTAREA)) + { + parent.Children.push_back(CHtmlElement(CHtmlElement::TEXT_NODE, (const char*)(node->content) + 1)); + } + else + { + parent.Children.push_back(CHtmlElement(CHtmlElement::TEXT_NODE, (const char*)(node->content))); + } } else if (node->type == XML_ELEMENT_NODE) @@ -174,6 +182,16 @@ namespace NLGUI { parseNode(node->children, elm, styles, links); + if (!elm.Children.empty() && elm.ID == HTML_PRE && elm.Children.back().Type == CHtmlElement::TEXT_NODE) + { + std::string::size_type size = elm.Children.back().Value.size(); + // strip last '\n' from non-empty line + if (size > 1 && elm.Children.back().Value[size-1] == '\n') + { + elm.Children.back().Value = elm.Children.back().Value.substr(0, size - 1); + } + } + // must cleanup nested tags that libxml2 does not fix // dt without end tag:
// dd without end tag:
diff --git a/nel/src/misc/bitmap.cpp b/nel/src/misc/bitmap.cpp index a40d7257d..f10b41587 100644 --- a/nel/src/misc/bitmap.cpp +++ b/nel/src/misc/bitmap.cpp @@ -3570,7 +3570,7 @@ void CBitmap::loadSize(NLMISC::IStream &f, uint32 &retWidth, uint32 &retHeight) } while(!eof); } - else if(fileType == JPG_HEADER) + else if(memcmp(&fileType, &JPG_HEADER, 2) == 0) { uint8 blockMarker1 = 0; uint8 blockMarker2 = 0; diff --git a/ryzom/client/src/interface_v3/dbctrl_sheet.cpp b/ryzom/client/src/interface_v3/dbctrl_sheet.cpp index e5a2853a6..7b38b9aeb 100644 --- a/ryzom/client/src/interface_v3/dbctrl_sheet.cpp +++ b/ryzom/client/src/interface_v3/dbctrl_sheet.cpp @@ -56,6 +56,7 @@ #include "../r2/editor.h" #include "nel/gui/lua_manager.h" +#include "nel/misc/xml_macros.h" extern CSheetManager SheetMngr; @@ -565,6 +566,15 @@ CCtrlDraggable(param) _RegenText = NULL; _RegenTextValue = 0; + _RegenTextEnabled = true; + _RegenTextShadow = true; + _RegenTextOutline = false; + _RegenTextFctLua = false; + _RegenTextY = 2; + _RegenTextFontSize = 8; + _RegenTextColor = NLMISC::CRGBA::White; + _RegenTextShadowColor = NLMISC::CRGBA::Black; + _RegenTextOutlineColor = NLMISC::CRGBA::Black; } // ---------------------------------------------------------------------------- @@ -677,6 +687,17 @@ bool CDBCtrlSheet::parse(xmlNodePtr cur, CInterfaceGroup * parentGroup) prop = (char*) xmlGetProp( cur, (xmlChar*)"focus_buff_icon" ); if (prop) _FocusBuffIcon = string((const char *)prop); + XML_READ_BOOL(cur, "regen_text", _RegenTextEnabled, true); + XML_READ_BOOL(cur, "regen_text_shadow", _RegenTextShadow, true); + XML_READ_BOOL(cur, "regen_text_outline", _RegenTextOutline, false); + XML_READ_SINT(cur, "regen_text_y", _RegenTextY, 2); + XML_READ_UINT(cur, "regen_text_fontsize", _RegenTextFontSize, 8); + XML_READ_COLOR(cur, "regen_text_color", _RegenTextColor, NLMISC::CRGBA::White); + XML_READ_COLOR(cur, "regen_text_shadow_color", _RegenTextShadowColor, NLMISC::CRGBA::Black); + XML_READ_COLOR(cur, "regen_text_outline_color", _RegenTextOutlineColor, NLMISC::CRGBA::Black); + XML_READ_STRING(cur, "regen_text_fct", _RegenTextFct, ""); + _RegenTextFctLua = startsWith(_RegenTextFct, "lua:"); + updateActualType(); // Init size for Type initSheetSize(); @@ -2119,35 +2140,8 @@ void CDBCtrlSheet::draw() rVR.drawQuad(_RenderLayer + 1, regenTris[tri], backTex, CRGBA::White, false); } - if (!_RegenText) { - _RegenText = new CViewText(CViewBase::TCtorParam()); - _RegenText->setId(getId() + ":regen"); - _RegenText->setParent(_Parent); - _RegenText->setOverflowText(std::string()); - _RegenText->setModulateGlobalColor(false); - _RegenText->setMultiLine(false); - _RegenText->setTextMode(CViewText::ClipWord); - _RegenText->setFontSizing("0", "0"); - // TODO: font size / color hardcoded. - _RegenText->setFontSize(8); - _RegenText->setColor(CRGBA::White); - _RegenText->setShadow(true); - _RegenText->setActive(true); - _RegenText->updateTextContext(); - } - - // TODO: ticks in second hardcoded - uint32 nextValue = _RegenTickRange.EndTick > LastGameCycle ? (_RegenTickRange.EndTick - LastGameCycle) / 10 : 0; - if (_RegenTextValue != nextValue) - { - _RegenTextValue = nextValue; - _RegenText->setText(toString("%d", _RegenTextValue)); - _RegenText->updateTextContext(); - } - _RegenText->setXReal(_XReal+1); - _RegenText->setYReal(_YReal+2); - _RegenText->setRenderLayer(_RenderLayer+2); - _RegenText->draw(); + if (_RegenTextEnabled) + drawRegenText(); } } @@ -2177,6 +2171,118 @@ void CDBCtrlSheet::draw() } } +// ---------------------------------------------------------------------------- +void CDBCtrlSheet::drawRegenText() +{ + if (!_RegenText) { + _RegenText = new CViewText(CViewBase::TCtorParam()); + _RegenText->setId(getId() + ":regen"); + _RegenText->setParent(_Parent); + _RegenText->setOverflowText(std::string()); + _RegenText->setModulateGlobalColor(false); + _RegenText->setMultiLine(true); + _RegenText->setTextMode(CViewText::ClipWord); + _RegenText->setFontSize(_RegenTextFontSize); + _RegenText->setColor(_RegenTextColor); + // do not set shadow if outline is set to avoid clearing it on draw (would call invalidate) + _RegenText->setShadow(_RegenTextShadow && !_RegenTextOutline); + _RegenText->setShadowOutline(false); + _RegenText->setShadowColor(_RegenTextShadowColor); + + _RegenText->setActive(true); + _RegenText->updateTextContext(); + } + + // TODO: 10 hardcoded (ticks in second) + sint32 nextValue; + if (_RegenTickRange.EndTick > LastGameCycle) + nextValue = (_RegenTickRange.EndTick - LastGameCycle) / 10; + else if (_RegenTextFctLua) + nextValue = ((sint64)_RegenTickRange.EndTick - (sint64)LastGameCycle) / 10; + else + nextValue = 0; + + if (_RegenTextValue != nextValue) + { + _RegenTextValue = nextValue; + if (_RegenTextFct.empty()) + { + // format as "10m", "9'59", "59" + if (_RegenTextValue > 600) + { + _RegenText->setText(toString("%dm", _RegenTextValue / 60)); + } + else if (_RegenTextValue > 0) + { + if (_RegenTextValue < 60) + _RegenText->setText(toString("%d", _RegenTextValue)); + else + _RegenText->setText(toString("%d'%02d", _RegenTextValue / 60, _RegenTextValue % 60)); + } + else + { + _RegenText->setText(""); + } + } + else + { + std::string fct; + if (_RegenTextFctLua) + { + CCDBNodeBranch *root = getRootBranch(); + if (root) + fct = toString("%s(%d, '%s')", _RegenTextFct.c_str(), _RegenTextValue, root->getFullName().c_str()); + else + fct = toString("%s(%d, nil)", _RegenTextFct.c_str(), _RegenTextValue); + } + else + { + fct = toString("%s(%d)", _RegenTextFct.c_str(), _RegenTextValue); + } + + // if using color tags in format, then RegenText color should be set to CRGBA::White + // as tag color is modulated with main color + std::string result; + if (CInterfaceExpr::evalAsString(fct, result)) + _RegenText->setTextFormatTaged(result); + } + + _RegenText->updateTextContext(); + // todo: posref + // note: if x,y is moved outside icon area it might get cliped and not be visible (wreal/hreal == 0) + _RegenText->setX(_WReal / 2 -_RegenText->getMaxUsedW() / 2); + // move RegenTextY=0 to baseline + _RegenText->setY(_RegenTextY - _RegenText->getFontLegHeight()); + } + + _RegenText->setXReal(_XReal + _RegenText->getX()); + _RegenText->setYReal(_YReal + _RegenText->getY()); + _RegenText->setRenderLayer(_RenderLayer+2); + + // TODO: create shader for this + if (_RegenTextOutline) + { + // player.xml t_bonus_text template way of drawing + sint x = _RegenText->getXReal(); + sint y = _RegenText->getYReal(); + + _RegenText->setColor(_RegenTextShadowColor); + _RegenText->setXReal(x-1); _RegenText->setYReal(y+0); _RegenText->draw(); + _RegenText->setXReal(x+1); _RegenText->setYReal(y+0); _RegenText->draw(); + _RegenText->setXReal(x+0); _RegenText->setYReal(y-1); _RegenText->draw(); + _RegenText->setXReal(x+0); _RegenText->setYReal(y+1); _RegenText->draw(); + + _RegenText->setColor(_RegenTextColor); + _RegenText->setXReal(x); _RegenText->setYReal(y); + _RegenText->draw(); + _RegenText->draw(); + } + else + { + _RegenText->draw(); + _RegenText->draw(); + } +} // ---------------------------------------------------------------------------- void CDBCtrlSheet::drawRotatedQuad(CViewRenderer &vr, float angle, float scale, uint renderLayer, uint32 texId, sint32 texWidth, sint32 texHeight) @@ -4771,6 +4877,12 @@ std::string CDBCtrlSheet::getContextHelpWindowName() const return CCtrlBase::getContextHelpWindowName(); } +// *************************************************************************** +void CDBCtrlSheet::setRegenTextFct(const std::string &s) +{ + _RegenTextFct = s; + _RegenTextFctLua = startsWith(s, "lua:"); +} // *************************************************************************** void CDBCtrlSheet::setRegenTickRange(const CTickRange &tickRange) diff --git a/ryzom/client/src/interface_v3/dbctrl_sheet.h b/ryzom/client/src/interface_v3/dbctrl_sheet.h index b2b82aff9..da2233b66 100644 --- a/ryzom/client/src/interface_v3/dbctrl_sheet.h +++ b/ryzom/client/src/interface_v3/dbctrl_sheet.h @@ -602,6 +602,25 @@ public: void setRegenTickRange(const CTickRange &tickRange); const CTickRange &getRegenTickRange() const { return _RegenTickRange; } + // Default regen text is displayed on bottom of icon. + void setRegenText(bool b) { _RegenTextEnabled = b; } + // Allow to override default formatter. + // First parameter will be replaced with current timer value (always >= 0) + // If its a lua function, then parameters are + // 1: current timer value; can be negative + // 2: DB path for ctrl root (ie UI:VARIABLES:BONUSES:0), or nil + // + // ie: "secondsToTimeStringShort" -> CInterfaceExpr::evalAsString("secondsToTimeStringShort(123)", ret) + // ie: "lua:secondsToTimeStringShort" -> CInterfaceExpr::evalAsString("lua:secondsToTimeStringShort(123, 'UI:VARIABLES:BONUSES:0')", ret) + void setRegenTextFct(const std::string &s); + void setRegenTextY(sint32 y) { _RegenTextY = y; } + void setRegenTextShadow(bool b) { _RegenTextShadow = b; } + void setRegenTextShadowColor(NLMISC::CRGBA c) { _RegenTextShadowColor = c; } + void setRegenTextOutline(bool b) { _RegenTextOutline = b; } + void setRegenTextOutlineColor(NLMISC::CRGBA c) { _RegenTextOutlineColor = c; } + void setRegenTextFontSize(uint32 s) { _RegenTextFontSize = s; } + void setRegenTextColor(NLMISC::CRGBA c) { _RegenTextColor = c; } + // start notify anim (at the end of regen usually) void startNotifyAnim(); @@ -738,7 +757,19 @@ protected: CTickRange _RegenTickRange; NLGUI::CViewText *_RegenText; - uint32 _RegenTextValue; + sint32 _RegenTextValue; + // + std::string _RegenTextFct; + bool _RegenTextFctLua; + bool _RegenTextEnabled; + bool _RegenTextShadow; + bool _RegenTextOutline; + sint32 _RegenTextY; + uint32 _RegenTextFontSize; + NLMISC::CRGBA _RegenTextShadowColor; + NLMISC::CRGBA _RegenTextOutlineColor; + NLMISC::CRGBA _RegenTextColor; + /// D'n'd sint32 _DragX, _DragY; @@ -852,6 +883,9 @@ private: // gelper to draw the notify animation void drawRotatedQuad(CViewRenderer &vr, float angle, float scale, uint renderLayer, uint32 textureId, sint32 texWidth, sint32 texHeight); + // create and draw regen text over icon + void drawRegenText(); + }; /** User type (used with expression system of the interface, see interface_expr.h, that contains a pointer to a CDBCtrlSheet diff --git a/ryzom/client/src/interface_v3/dbgroup_list_sheet_bonus_malus.cpp b/ryzom/client/src/interface_v3/dbgroup_list_sheet_bonus_malus.cpp index 175d7339b..f41dd3085 100644 --- a/ryzom/client/src/interface_v3/dbgroup_list_sheet_bonus_malus.cpp +++ b/ryzom/client/src/interface_v3/dbgroup_list_sheet_bonus_malus.cpp @@ -23,7 +23,7 @@ #include "stdpch.h" #include "dbgroup_list_sheet_bonus_malus.h" #include "interface_manager.h" - +#include "nel/misc/xml_macros.h" using namespace std; using namespace NLMISC; @@ -35,87 +35,106 @@ NLMISC_REGISTER_OBJECT(CViewBase, CDBGroupListSheetBonusMalus, std::string, "lis // *************************************************************************** CDBGroupListSheetBonusMalus::CDBGroupListSheetBonusMalus(const TCtorParam ¶m) -: CDBGroupListSheet(param) +: CDBGroupListSheet(param), + _RegenTextEnabled(true), + _RegenTextY(-14), _RegenTextFontSize(8), + _RegenTextColor(NLMISC::CRGBA::White), + _RegenTextDisabledColor(NLMISC::CRGBA(127,127,127)) { - _TextId= -1; - // want leave space between controls in the list // Yoyo: I think it's better like this, + this is important for space consideration and because of XPCat/PVPOutpost //_ListLeaveSpace= false; } - // *************************************************************************** -bool CDBGroupListSheetBonusMalus::parse (xmlNodePtr cur, CInterfaceGroup *parentGroup) +CDBGroupListSheetBonusMalus::CSheetChildTimer::CSheetChildTimer() +: TimerDB(NULL), DisabledDB(NULL), TimerCache(0), + _RegenTextColor(NLMISC::CRGBA::White), + _RegenTextDisabledColor(NLMISC::CRGBA(127,127,127)) { - CInterfaceManager *pIM= CInterfaceManager::getInstance(); +} - if(!CDBGroupListSheet::parse(cur, parentGroup)) - return false; +// *************************************************************************** +void CDBGroupListSheetBonusMalus::CSheetChildTimer::init(CDBGroupListSheet *pFather, uint index) +{ + // init my parent + CSheetChild::init(pFather, index); - // read the texture - CXMLAutoPtr prop; - prop = (char*) xmlGetProp( cur, (xmlChar*)"disable_texture" ); - if (prop) + CCDBNodeBranch *root = Ctrl->getRootBranch(); + if (root) { - CInterfaceManager *pIM = CInterfaceManager::getInstance(); - CViewRenderer &rVR = *CViewRenderer::getInstance(); - _TextId= rVR.getTextureIdFromName ((const char *)prop); + TimerDB = dynamic_cast(root->getNode(ICDBNode::CTextId("DISABLED_TIME"), false)); + DisabledDB = dynamic_cast(root->getNode(ICDBNode::CTextId("DISABLED"), false)); } - // get the Node leaves to be tested each frame - uint i= 0; - for(;;) + if (Ctrl) { - string db= toString("%s:%d:" DISABLE_LEAF, _DbBranchName.c_str(), i); - CCDBNodeLeaf *node= NLGUI::CDBManager::getInstance()->getDbProp(db, false); - if(!node) + CDBGroupListSheetBonusMalus *owner = dynamic_cast(pFather); + if (owner) { - break; + _RegenTextColor = owner->_RegenTextColor; + _RegenTextDisabledColor = owner->_RegenTextDisabledColor; + Ctrl->setRegenText(owner->_RegenTextEnabled); + Ctrl->setRegenTextY(owner->_RegenTextY); + Ctrl->setRegenTextColor(owner->_RegenTextColor); + Ctrl->setRegenTextFontSize(owner->_RegenTextFontSize); + if (!owner->_RegenTextFct.empty()) + Ctrl->setRegenTextFct(owner->_RegenTextFct); } - else - { - _DisableStates.push_back(node); - i++; - } - } - return true; + Ctrl->setRegenTextOutline(true); + } } // *************************************************************************** -void CDBGroupListSheetBonusMalus::draw () +void CDBGroupListSheetBonusMalus::CSheetChildTimer::update(CDBGroupListSheet * /* pFather */) { - CDBGroupListSheet::draw(); - -// CInterfaceManager *pIM= CInterfaceManager::getInstance(); -// CViewRenderer &rVR= *CViewRenderer::getInstance(); - -// sint32 drl= getRenderLayer()+1; + if(!TimerDB) + return; - // May draw disable bitmaps on the ctrl sheets if disabled. - uint numCtrls= (uint)min(_SheetChildren.size(), _DisableStates.size()); - for(uint i=0;igetValue32(); + if (TimerCache != tick) { - CDBCtrlSheet *ctrl= _SheetChildren[i]->Ctrl; - // if the ctrl is displayed, and if the state is disabled - if(ctrl->getActive()) + TimerCache = TimerDB->getValue32(); + Ctrl->setRegenTickRange(CTickRange(LastGameCycle, TimerCache)); + if (DisabledDB) { - if(_DisableStates[i]->getValue32()!=0) + if (DisabledDB->getValue32() == 0) { - ctrl->setGrayed(true); - /* - // YOYO: for now, don't display the gray bitmap. cross not cool. - CRGBA crossColor= ctrl->getSheetColor(); - crossColor.A>>= 2; - // Draw the disable bitmap on this control. +1 for the slot (ugly) - rVR.drawRotFlipBitmap(drl, ctrl->getXReal()+1, ctrl->getYReal()+1, - CCtrlSheetInfo::BrickSheetWidth, CCtrlSheetInfo::BrickSheetHeight, 0, 0, _TextId, crossColor); - */ + // active timer + Ctrl->setGrayed(false); + Ctrl->setRegenTextColor(_RegenTextColor); } else - ctrl->setGrayed(false); + { + // skill disabled timer + Ctrl->setGrayed(true); + Ctrl->setRegenTextColor(_RegenTextDisabledColor); + } + } + else + { + Ctrl->setGrayed(true); } } } +// *************************************************************************** +bool CDBGroupListSheetBonusMalus::parse (xmlNodePtr cur, CInterfaceGroup *parentGroup) +{ + CInterfaceManager *pIM= CInterfaceManager::getInstance(); + + if(!CDBGroupListSheet::parse(cur, parentGroup)) + return false; + + CXMLAutoPtr prop; + XML_READ_BOOL(cur, "regen_text", _RegenTextEnabled, true); + XML_READ_SINT(cur, "regen_text_y", _RegenTextY, -14); + XML_READ_UINT(cur, "regen_text_fontsize", _RegenTextFontSize, 8); + XML_READ_COLOR(cur, "regen_text_color", _RegenTextColor, NLMISC::CRGBA::White); + XML_READ_COLOR(cur, "regen_text_disabled_color", _RegenTextDisabledColor, NLMISC::CRGBA(127, 127, 127)); + XML_READ_STRING(cur, "regen_text_fct", _RegenTextFct, ""); + + return true; +} + diff --git a/ryzom/client/src/interface_v3/dbgroup_list_sheet_bonus_malus.h b/ryzom/client/src/interface_v3/dbgroup_list_sheet_bonus_malus.h index 05774ec8d..d63e664ed 100644 --- a/ryzom/client/src/interface_v3/dbgroup_list_sheet_bonus_malus.h +++ b/ryzom/client/src/interface_v3/dbgroup_list_sheet_bonus_malus.h @@ -25,7 +25,6 @@ #include "nel/misc/types_nl.h" #include "dbgroup_list_sheet.h" - // *************************************************************************** /** * Special list_sheet that display some disalbe bitmap if needed according to DB @@ -40,14 +39,34 @@ public: /// Constructor CDBGroupListSheetBonusMalus(const TCtorParam ¶m); - virtual bool parse (xmlNodePtr cur, CInterfaceGroup *parentGroup); + // A child node + struct CSheetChildTimer : public CDBGroupListSheet::CSheetChild + { + CSheetChildTimer(); + virtual void init(CDBGroupListSheet *pFather, uint index) NL_OVERRIDE; + virtual void update(CDBGroupListSheet *pFather) NL_OVERRIDE; + + NLMISC::CCDBNodeLeaf *TimerDB; + NLMISC::CCDBNodeLeaf *DisabledDB; + uint TimerCache; + + NLMISC::CRGBA _RegenTextColor; + NLMISC::CRGBA _RegenTextDisabledColor; + }; + + virtual bool parse(xmlNodePtr cur, CInterfaceGroup *parentGroup) NL_OVERRIDE; - virtual void draw (); + virtual CSheetChild *createSheetChild() NL_OVERRIDE { return new CSheetChildTimer; } private: - sint32 _TextId; + friend CSheetChildTimer; - std::vector _DisableStates; + bool _RegenTextEnabled; + std::string _RegenTextFct; + sint32 _RegenTextY; + uint32 _RegenTextFontSize; + NLMISC::CRGBA _RegenTextColor; + NLMISC::CRGBA _RegenTextDisabledColor; }; diff --git a/ryzom/common/src/game_share/effect_families.cpp b/ryzom/common/src/game_share/effect_families.cpp index 391626393..8d00cf200 100644 --- a/ryzom/common/src/game_share/effect_families.cpp +++ b/ryzom/common/src/game_share/effect_families.cpp @@ -323,19 +323,6 @@ namespace EFFECT_FAMILIES { "thorn_wall_aura.sbrick", PowerThornWall }, { "water_wall_aura.sbrick", PowerWaterWall }, { "lightning_wall_aura.sbrick", PowerLightningWall }, - - { "life_aura.sbrick", PowerRootLifeAura }, - { "stamina_aura.sbrick", PowerRootStaminaAura }, - { "sap_aura.sbrick", PowerRootSapAura }, - { "umbrella_aura.sbrick", PowerRootUmbrella }, - { "melee_protection_aura.sbrick", PowerRootProtection }, - { "anti_magic_shield_aura.sbrick", PowerRootAntiMagicShield }, - { "war_cry_aura.sbrick", PowerRootWarCry }, - { "fire_wall_aura.sbrick", PowerRootFireWall }, - { "thorn_wall_aura.sbrick", PowerRootThornWall }, - { "water_wall_aura.sbrick", PowerRootWaterWall }, - { "lightning_wall_aura.sbrick", PowerRootLightningWall }, - { "chg_charac.sbrick", PowerChgCharac }, { "mod_defense.sbrick", PowerModDefenseSkill }, { "mod_dodge.sbrick", PowerModDodgeSkill },