/** * Tibia GIMUD Server - a free and open-source MMORPG server emulator * Copyright (C) 2019 Sabrehaven and Mark Samman * * 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 * (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 General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "otpch.h" #include "tools.h" #include "configmanager.h" extern ConfigManager g_config; void printXMLError(const std::string& where, const std::string& fileName, const pugi::xml_parse_result& result) { std::cout << '[' << where << "] Failed to load " << fileName << ": " << result.description() << std::endl; FILE* file = fopen(fileName.c_str(), "rb"); if (!file) { return; } char buffer[32768]; uint32_t currentLine = 1; std::string line; size_t offset = static_cast(result.offset); size_t lineOffsetPosition = 0; size_t index = 0; size_t bytes; do { bytes = fread(buffer, 1, 32768, file); for (size_t i = 0; i < bytes; ++i) { char ch = buffer[i]; if (ch == '\n') { if ((index + i) >= offset) { lineOffsetPosition = line.length() - ((index + i) - offset); bytes = 0; break; } ++currentLine; line.clear(); } else { line.push_back(ch); } } index += bytes; } while (bytes == 32768); fclose(file); std::cout << "Line " << currentLine << ':' << std::endl; std::cout << line << std::endl; for (size_t i = 0; i < lineOffsetPosition; i++) { if (line[i] == '\t') { std::cout << '\t'; } else { std::cout << ' '; } } std::cout << '^' << std::endl; } inline static uint32_t circularShift(int bits, uint32_t value) { return (value << bits) | (value >> (32 - bits)); } static void processSHA1MessageBlock(const uint8_t* messageBlock, uint32_t* H) { uint32_t W[80]; for (int i = 0; i < 16; ++i) { const size_t offset = i << 2; W[i] = messageBlock[offset] << 24 | messageBlock[offset + 1] << 16 | messageBlock[offset + 2] << 8 | messageBlock[offset + 3]; } for (int i = 16; i < 80; ++i) { W[i] = circularShift(1, W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16]); } uint32_t A = H[0], B = H[1], C = H[2], D = H[3], E = H[4]; for (int i = 0; i < 20; ++i) { const uint32_t tmp = circularShift(5, A) + ((B & C) | ((~B) & D)) + E + W[i] + 0x5A827999; E = D; D = C; C = circularShift(30, B); B = A; A = tmp; } for (int i = 20; i < 40; ++i) { const uint32_t tmp = circularShift(5, A) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1; E = D; D = C; C = circularShift(30, B); B = A; A = tmp; } for (int i = 40; i < 60; ++i) { const uint32_t tmp = circularShift(5, A) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC; E = D; D = C; C = circularShift(30, B); B = A; A = tmp; } for (int i = 60; i < 80; ++i) { const uint32_t tmp = circularShift(5, A) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6; E = D; D = C; C = circularShift(30, B); B = A; A = tmp; } H[0] += A; H[1] += B; H[2] += C; H[3] += D; H[4] += E; } std::string transformToSHA1(const std::string& input) { uint32_t H[] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; uint8_t messageBlock[64]; size_t index = 0; uint32_t length_low = 0; uint32_t length_high = 0; for (char ch : input) { messageBlock[index++] = ch; length_low += 8; if (length_low == 0) { length_high++; } if (index == 64) { processSHA1MessageBlock(messageBlock, H); index = 0; } } messageBlock[index++] = 0x80; if (index > 56) { while (index < 64) { messageBlock[index++] = 0; } processSHA1MessageBlock(messageBlock, H); index = 0; } while (index < 56) { messageBlock[index++] = 0; } messageBlock[56] = length_high >> 24; messageBlock[57] = length_high >> 16; messageBlock[58] = length_high >> 8; messageBlock[59] = length_high; messageBlock[60] = length_low >> 24; messageBlock[61] = length_low >> 16; messageBlock[62] = length_low >> 8; messageBlock[63] = length_low; processSHA1MessageBlock(messageBlock, H); char hexstring[41]; static const char hexDigits[] = {"0123456789abcdef"}; for (int hashByte = 20; --hashByte >= 0;) { const uint8_t byte = H[hashByte >> 2] >> (((3 - hashByte) & 3) << 3); index = hashByte << 1; hexstring[index] = hexDigits[byte >> 4]; hexstring[index + 1] = hexDigits[byte & 15]; } return std::string(hexstring, 40); } uint8_t getLiquidColor(uint8_t type) { uint8_t result = FLUID_COLOR_NONE; switch (type) { case FLUID_WATER: result = FLUID_COLOR_BLUE; break; case FLUID_NONE: result = FLUID_COLOR_NONE; break; case FLUID_SLIME: result = FLUID_COLOR_GREEN; break; case FLUID_BEER: case FLUID_MUD: case FLUID_OIL: case FLUID_RUM: result = FLUID_COLOR_BROWN; break; case FLUID_MILK: case FLUID_COCONUTMILK: result = FLUID_COLOR_WHITE; break; case FLUID_WINE: case FLUID_MANAFLUID: result = FLUID_COLOR_PURPLE; break; case FLUID_BLOOD: case FLUID_LIFEFLUID: result = FLUID_COLOR_RED; break; case FLUID_URINE: case FLUID_LEMONADE: case FLUID_FRUITJUICE: result = FLUID_COLOR_YELLOW; break; default: result = FLUID_COLOR_NONE; break; } return result; } void extractArticleAndName(std::string& data, std::string& article, std::string& name) { std::string xarticle = data.substr(0, 3); if (xarticle == "an ") { name = data.substr(3, data.size()); article = "an"; } else { xarticle = data.substr(0, 2); if (xarticle == "a ") { name = data.substr(2, data.size()); article = "a"; } else { name = data; article = ""; } } } std::string pluralizeString(std::string str) { if (str == "meat") return "meat"; int n = str.length(); char ch = str[n - 1]; char ch2 = str[n - 2]; std::string str2; if (ch == 'y') str2 = str.substr(0, n - 1) + "ies"; else if (ch == 'o' || ch == 's' || ch == 'x') str2 = str + "es"; else if (ch == 'h'&& ch2 == 'c') str2 = str + "es"; else if (ch == 'f') str2 = str.substr(0, n - 1) + "ves"; else if (ch == 'e'&&ch2 == 'f') str2 = str.substr(0, n - 2) + "ves"; else str2 = str + "s"; return str2; } std::string generateToken(const std::string& key, uint32_t ticks) { // generate message from ticks std::string message(8, 0); for (uint8_t i = 8; --i; ticks >>= 8) { message[i] = static_cast(ticks & 0xFF); } // hmac key pad generation std::string iKeyPad(64, 0x36), oKeyPad(64, 0x5C); for (uint8_t i = 0; i < key.length(); ++i) { iKeyPad[i] ^= key[i]; oKeyPad[i] ^= key[i]; } oKeyPad.reserve(84); // hmac concat inner pad with message iKeyPad.append(message); // hmac first pass message.assign(transformToSHA1(iKeyPad)); // hmac concat outer pad with message, conversion from hex to int needed for (uint8_t i = 0; i < message.length(); i += 2) { oKeyPad.push_back(static_cast(std::strtoul(message.substr(i, 2).c_str(), nullptr, 16))); } // hmac second pass message.assign(transformToSHA1(oKeyPad)); // calculate hmac offset uint32_t offset = static_cast(std::strtoul(message.substr(39, 1).c_str(), nullptr, 16) & 0xF); // get truncated hash uint32_t truncHash = static_cast(std::strtoul(message.substr(2 * offset, 8).c_str(), nullptr, 16)) & 0x7FFFFFFF; message.assign(std::to_string(truncHash)); // return only last AUTHENTICATOR_DIGITS (default 6) digits, also asserts exactly 6 digits uint32_t hashLen = message.length(); message.assign(message.substr(hashLen - std::min(hashLen, AUTHENTICATOR_DIGITS))); message.insert(0, AUTHENTICATOR_DIGITS - std::min(hashLen, AUTHENTICATOR_DIGITS), '0'); return message; } void replaceString(std::string& str, const std::string& sought, const std::string& replacement) { size_t pos = 0; size_t start = 0; size_t soughtLen = sought.length(); size_t replaceLen = replacement.length(); while ((pos = str.find(sought, start)) != std::string::npos) { str = str.substr(0, pos) + replacement + str.substr(pos + soughtLen); start = pos + replaceLen; } } void trim_right(std::string& source, char t) { source.erase(source.find_last_not_of(t) + 1); } void trim_left(std::string& source, char t) { source.erase(0, source.find_first_not_of(t)); } void toLowerCaseString(std::string& source) { std::transform(source.begin(), source.end(), source.begin(), tolower); } std::string asLowerCaseString(std::string source) { toLowerCaseString(source); return source; } std::string asUpperCaseString(std::string source) { std::transform(source.begin(), source.end(), source.begin(), toupper); return source; } std::string asCamelCaseString(std::string source) { bool active = true; for (int i = 0; source[i] != '\0'; i++) { if (std::isalpha(source[i])) { if (active) { source[i] = std::toupper(source[i]); active = false; } else { source[i] = std::tolower(source[i]); } } else if (source[i] == ' ') { active = true; } } return source; } StringVec explodeString(const std::string& inString, const std::string& separator, int32_t limit/* = -1*/) { StringVec returnVector; std::string::size_type start = 0, end = 0; while (--limit != -1 && (end = inString.find(separator, start)) != std::string::npos) { returnVector.push_back(inString.substr(start, end - start)); start = end + separator.size(); } returnVector.push_back(inString.substr(start)); return returnVector; } IntegerVec vectorAtoi(const StringVec& stringVector) { IntegerVec returnVector; for (const auto& string : stringVector) { returnVector.push_back(std::stoi(string)); } return returnVector; } std::mt19937& getRandomGenerator() { static std::random_device rd; static std::mt19937 generator(rd()); return generator; } int32_t uniform_random(int32_t minNumber, int32_t maxNumber) { static std::uniform_int_distribution uniformRand; if (minNumber == maxNumber) { return minNumber; } else if (minNumber > maxNumber) { std::swap(minNumber, maxNumber); } return uniformRand(getRandomGenerator(), std::uniform_int_distribution::param_type(minNumber, maxNumber)); } int32_t normal_random(int32_t minNumber, int32_t maxNumber) { static std::normal_distribution normalRand(0.5f, 0.25f); if (minNumber == maxNumber) { return minNumber; } else if (minNumber > maxNumber) { std::swap(minNumber, maxNumber); } int32_t increment; const int32_t diff = maxNumber - minNumber; const float v = normalRand(getRandomGenerator()); if (v < 0.0) { increment = diff / 2; } else if (v > 1.0) { increment = (diff + 1) / 2; } else { increment = round(v * diff); } return minNumber + increment; } bool boolean_random(double probability/* = 0.5*/) { static std::bernoulli_distribution booleanRand; return booleanRand(getRandomGenerator(), std::bernoulli_distribution::param_type(probability)); } void trimString(std::string& str) { str.erase(str.find_last_not_of(' ') + 1); str.erase(0, str.find_first_not_of(' ')); } std::string convertIPToString(uint32_t ip) { char buffer[17]; int res = sprintf(buffer, "%u.%u.%u.%u", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24)); if (res < 0) { return {}; } return buffer; } std::string formatDate(time_t time) { const tm* tms = localtime(&time); if (!tms) { return {}; } char buffer[20]; int res = sprintf(buffer, "%02d/%02d/%04d %02d:%02d:%02d", tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour, tms->tm_min, tms->tm_sec); if (res < 0) { return {}; } return {buffer, 19}; } std::string formatDateShort(time_t time) { const tm* tms = localtime(&time); if (!tms) { return {}; } char buffer[12]; size_t res = strftime(buffer, 12, "%d %b %Y", tms); if (res == 0) { return {}; } return {buffer, 11}; } Direction getDirection(const std::string& string) { Direction direction = DIRECTION_NORTH; if (string == "north" || string == "n" || string == "0") { direction = DIRECTION_NORTH; } else if (string == "east" || string == "e" || string == "1") { direction = DIRECTION_EAST; } else if (string == "south" || string == "s" || string == "2") { direction = DIRECTION_SOUTH; } else if (string == "west" || string == "w" || string == "3") { direction = DIRECTION_WEST; } else if (string == "southwest" || string == "south west" || string == "south-west" || string == "sw" || string == "4") { direction = DIRECTION_SOUTHWEST; } else if (string == "southeast" || string == "south east" || string == "south-east" || string == "se" || string == "5") { direction = DIRECTION_SOUTHEAST; } else if (string == "northwest" || string == "north west" || string == "north-west" || string == "nw" || string == "6") { direction = DIRECTION_NORTHWEST; } else if (string == "northeast" || string == "north east" || string == "north-east" || string == "ne" || string == "7") { direction = DIRECTION_NORTHEAST; } return direction; } Position getNextPosition(Direction direction, Position pos) { switch (direction) { case DIRECTION_NORTH: pos.y--; break; case DIRECTION_SOUTH: pos.y++; break; case DIRECTION_WEST: pos.x--; break; case DIRECTION_EAST: pos.x++; break; case DIRECTION_SOUTHWEST: pos.x--; pos.y++; break; case DIRECTION_NORTHWEST: pos.x--; pos.y--; break; case DIRECTION_NORTHEAST: pos.x++; pos.y--; break; case DIRECTION_SOUTHEAST: pos.x++; pos.y++; break; default: break; } return pos; } Direction getDirectionTo(const Position& from, const Position& to) { Direction dir; int32_t x_offset = Position::getOffsetX(from, to); if (x_offset < 0) { dir = DIRECTION_EAST; x_offset = std::abs(x_offset); } else { dir = DIRECTION_WEST; } int32_t y_offset = Position::getOffsetY(from, to); if (y_offset >= 0) { if (y_offset > x_offset) { dir = DIRECTION_NORTH; } else if (y_offset == x_offset) { if (dir == DIRECTION_EAST) { dir = DIRECTION_NORTHEAST; } else { dir = DIRECTION_NORTHWEST; } } } else { y_offset = std::abs(y_offset); if (y_offset > x_offset) { dir = DIRECTION_SOUTH; } else if (y_offset == x_offset) { if (dir == DIRECTION_EAST) { dir = DIRECTION_SOUTHEAST; } else { dir = DIRECTION_SOUTHWEST; } } } return dir; } struct MagicEffectNames { const char* name; MagicEffectClasses effect; }; struct ShootTypeNames { const char* name; ShootType_t shoot; }; struct CombatTypeNames { const char* name; CombatType_t combat; }; struct AmmoTypeNames { const char* name; Ammo_t ammoType; }; struct WeaponActionNames { const char* name; WeaponAction_t weaponAction; }; struct SkullNames { const char* name; Skulls_t skull; }; struct FluidNames { const char* name; FluidTypes_t fluidType; }; MagicEffectNames magicEffectNames[] = { {"redspark", CONST_ME_DRAWBLOOD}, {"bluebubble", CONST_ME_LOSEENERGY}, {"poff", CONST_ME_POFF}, {"yellowspark", CONST_ME_BLOCKHIT}, {"explosionarea", CONST_ME_EXPLOSIONAREA}, {"explosion", CONST_ME_EXPLOSIONHIT}, {"firearea", CONST_ME_FIREAREA}, {"yellowbubble", CONST_ME_YELLOW_RINGS}, {"greenbubble", CONST_ME_GREEN_RINGS}, {"blackspark", CONST_ME_HITAREA}, {"teleport", CONST_ME_TELEPORT}, {"energy", CONST_ME_ENERGYHIT}, {"blueshimmer", CONST_ME_MAGIC_BLUE}, {"redshimmer", CONST_ME_MAGIC_RED}, {"greenshimmer", CONST_ME_MAGIC_GREEN}, {"fire", CONST_ME_HITBYFIRE}, {"greenspark", CONST_ME_HITBYPOISON}, {"mortarea", CONST_ME_MORTAREA}, {"greennote", CONST_ME_SOUND_GREEN}, {"rednote", CONST_ME_SOUND_RED}, {"poison", CONST_ME_POISONAREA}, {"yellownote", CONST_ME_SOUND_YELLOW}, {"purplenote", CONST_ME_SOUND_PURPLE}, {"bluenote", CONST_ME_SOUND_BLUE}, {"whitenote", CONST_ME_SOUND_WHITE}, {"bubbles", CONST_ME_BUBBLES}, {"dice", CONST_ME_CRAPS}, {"giftwraps", CONST_ME_GIFT_WRAPS}, {"yellowfirework", CONST_ME_FIREWORK_YELLOW}, {"redfirework", CONST_ME_FIREWORK_RED}, {"bluefirework", CONST_ME_FIREWORK_BLUE}, }; ShootTypeNames shootTypeNames[] = { {"spear", CONST_ANI_SPEAR}, {"bolt", CONST_ANI_BOLT}, {"arrow", CONST_ANI_ARROW}, {"fire", CONST_ANI_FIRE}, {"energy", CONST_ANI_ENERGY}, {"poisonarrow", CONST_ANI_POISONARROW}, {"burstarrow", CONST_ANI_BURSTARROW}, {"throwingstar", CONST_ANI_THROWINGSTAR}, {"throwingknife", CONST_ANI_THROWINGKNIFE}, {"smallstone", CONST_ANI_SMALLSTONE}, {"death", CONST_ANI_DEATH}, {"largerock", CONST_ANI_LARGEROCK}, {"snowball", CONST_ANI_SNOWBALL}, {"powerbolt", CONST_ANI_POWERBOLT}, {"poison", CONST_ANI_POISON}, {"infernalbolt", CONST_ANI_INFERNALBOLT}, }; CombatTypeNames combatTypeNames[] = { {"physical", COMBAT_PHYSICALDAMAGE}, {"energy", COMBAT_ENERGYDAMAGE}, {"drown", COMBAT_DROWNDAMAGE}, {"earth", COMBAT_EARTHDAMAGE}, {"poison", COMBAT_EARTHDAMAGE}, {"fire", COMBAT_FIREDAMAGE}, {"undefined", COMBAT_UNDEFINEDDAMAGE}, {"lifedrain", COMBAT_LIFEDRAIN}, {"manadrain", COMBAT_MANADRAIN}, {"healing", COMBAT_HEALING}, }; AmmoTypeNames ammoTypeNames[] = { {"spear", AMMO_SPEAR}, {"bolt", AMMO_BOLT}, {"arrow", AMMO_ARROW}, {"poisonarrow", AMMO_ARROW}, {"burstarrow", AMMO_ARROW}, {"throwingstar", AMMO_THROWINGSTAR}, {"throwingknife", AMMO_THROWINGKNIFE}, {"smallstone", AMMO_STONE}, {"largerock", AMMO_STONE}, {"snowball", AMMO_SNOWBALL}, {"powerbolt", AMMO_BOLT}, }; WeaponActionNames weaponActionNames[] = { {"move", WEAPONACTION_MOVE}, {"removecharge", WEAPONACTION_REMOVECHARGE}, {"removecount", WEAPONACTION_REMOVECOUNT}, }; SkullNames skullNames[] = { {"none", SKULL_NONE}, {"yellow", SKULL_YELLOW}, {"green", SKULL_GREEN}, {"white", SKULL_WHITE}, {"red", SKULL_RED}, }; FluidNames fluidNames[] = { {"none", FLUID_NONE}, {"water", FLUID_WATER}, {"wine", FLUID_WINE}, {"beer", FLUID_BEER}, {"mud", FLUID_MUD}, {"blood", FLUID_BLOOD}, {"slime", FLUID_SLIME}, {"oil", FLUID_OIL}, {"urine", FLUID_URINE}, {"milk", FLUID_MILK}, {"manafluid", FLUID_MANAFLUID}, {"lifefluid", FLUID_LIFEFLUID}, {"lemonade", FLUID_LEMONADE}, {"rum", FLUID_RUM}, {"coconutmilk", FLUID_COCONUTMILK}, {"fruitjuice", FLUID_FRUITJUICE} }; MagicEffectClasses getMagicEffect(const std::string& strValue) { for (auto& magicEffectName : magicEffectNames) { if (strcasecmp(strValue.c_str(), magicEffectName.name) == 0) { return magicEffectName.effect; } } return CONST_ME_NONE; } ShootType_t getShootType(const std::string& strValue) { for (size_t i = 0, size = sizeof(shootTypeNames) / sizeof(ShootTypeNames); i < size; ++i) { if (strcasecmp(strValue.c_str(), shootTypeNames[i].name) == 0) { return shootTypeNames[i].shoot; } } return CONST_ANI_NONE; } CombatType_t getCombatType(const std::string& strValue) { for (size_t i = 0, size = sizeof(combatTypeNames) / sizeof(CombatTypeNames); i < size; ++i) { if (strcasecmp(strValue.c_str(), combatTypeNames[i].name) == 0) { return combatTypeNames[i].combat; } } return COMBAT_NONE; } std::string getCombatName(CombatType_t combatType) { for (size_t i = 0, size = sizeof(combatTypeNames) / sizeof(CombatTypeNames); i < size; ++i) { if (combatTypeNames[i].combat == combatType) { return combatTypeNames[i].name; } } return "unknown"; } Ammo_t getAmmoType(const std::string& strValue) { for (size_t i = 0, size = sizeof(ammoTypeNames) / sizeof(AmmoTypeNames); i < size; ++i) { if (strcasecmp(strValue.c_str(), ammoTypeNames[i].name) == 0) { return ammoTypeNames[i].ammoType; } } return AMMO_NONE; } WeaponAction_t getWeaponAction(const std::string& strValue) { for (size_t i = 0, size = sizeof(weaponActionNames) / sizeof(WeaponActionNames); i < size; ++i) { if (strcasecmp(strValue.c_str(), weaponActionNames[i].name) == 0) { return weaponActionNames[i].weaponAction; } } return WEAPONACTION_NONE; } Skulls_t getSkullType(const std::string& strValue) { for (size_t i = 0, size = sizeof(skullNames) / sizeof(SkullNames); i < size; ++i) { if (strcasecmp(strValue.c_str(), skullNames[i].name) == 0) { return skullNames[i].skull; } } return SKULL_NONE; } FluidTypes_t getFluidType(const std::string& strValue) { for (size_t i = 0, size = sizeof(fluidNames) / sizeof(FluidNames); i < size; ++i) { if (strcasecmp(strValue.c_str(), fluidNames[i].name) == 0) { return fluidNames[i].fluidType; } } return FLUID_NONE; } std::string getSkillName(uint8_t skillid) { switch (skillid) { case SKILL_FIST: return "fist fighting"; case SKILL_CLUB: return "club fighting"; case SKILL_SWORD: return "sword fighting"; case SKILL_AXE: return "axe fighting"; case SKILL_DISTANCE: return "distance fighting"; case SKILL_SHIELD: return "shielding"; case SKILL_FISHING: return "fishing"; case SKILL_MAGLEVEL: return "magic level"; case SKILL_LEVEL: return "level"; default: return "unknown"; } } std::string ucfirst(std::string str) { for (char& i : str) { if (i != ' ') { i = toupper(i); break; } } return str; } std::string ucwords(std::string str) { size_t strLength = str.length(); if (strLength == 0) { return str; } str[0] = toupper(str.front()); for (size_t i = 1; i < strLength; ++i) { if (str[i - 1] == ' ') { str[i] = toupper(str[i]); } } return str; } bool booleanString(const std::string& str) { if (str.empty()) { return false; } char ch = tolower(str.front()); return ch != 'f' && ch != 'n' && ch != '0'; } std::string getWeaponName(WeaponType_t weaponType) { switch (weaponType) { case WEAPON_SWORD: return "sword"; case WEAPON_CLUB: return "club"; case WEAPON_AXE: return "axe"; case WEAPON_DISTANCE: return "distance"; case WEAPON_WAND: return "wand"; case WEAPON_AMMO: return "ammunition"; default: return std::string(); } } size_t combatTypeToIndex(CombatType_t combatType) { switch (combatType) { case COMBAT_PHYSICALDAMAGE: return 0; case COMBAT_ENERGYDAMAGE: return 1; case COMBAT_EARTHDAMAGE: return 2; case COMBAT_FIREDAMAGE: return 3; case COMBAT_UNDEFINEDDAMAGE: return 4; case COMBAT_LIFEDRAIN: return 5; case COMBAT_MANADRAIN: return 6; case COMBAT_HEALING: return 7; case COMBAT_DROWNDAMAGE: return 8; default: return 0; } } CombatType_t indexToCombatType(size_t v) { return static_cast(1 << v); } itemAttrTypes stringToItemAttribute(const std::string& str) { if (str == "aid") { return ITEM_ATTRIBUTE_ACTIONID; } else if (str == "mid") { return ITEM_ATTRIBUTE_MOVEMENTID; } else if (str == "description") { return ITEM_ATTRIBUTE_DESCRIPTION; } else if (str == "text") { return ITEM_ATTRIBUTE_TEXT; } else if (str == "date") { return ITEM_ATTRIBUTE_DATE; } else if (str == "writer") { return ITEM_ATTRIBUTE_WRITER; } else if (str == "name") { return ITEM_ATTRIBUTE_NAME; } else if (str == "article") { return ITEM_ATTRIBUTE_ARTICLE; } else if (str == "pluralname") { return ITEM_ATTRIBUTE_PLURALNAME; } else if (str == "weight") { return ITEM_ATTRIBUTE_WEIGHT; } else if (str == "attack") { return ITEM_ATTRIBUTE_ATTACK; } else if (str == "defense") { return ITEM_ATTRIBUTE_DEFENSE; } else if (str == "armor") { return ITEM_ATTRIBUTE_ARMOR; } else if (str == "shootrange") { return ITEM_ATTRIBUTE_SHOOTRANGE; } else if (str == "owner") { return ITEM_ATTRIBUTE_OWNER; } else if (str == "duration") { return ITEM_ATTRIBUTE_DURATION; } else if (str == "decaystate") { return ITEM_ATTRIBUTE_DECAYSTATE; } else if (str == "corpseowner") { return ITEM_ATTRIBUTE_CORPSEOWNER; } else if (str == "charges") { return ITEM_ATTRIBUTE_CHARGES; } else if (str == "fluidtype") { return ITEM_ATTRIBUTE_FLUIDTYPE; } else if (str == "doorid") { return ITEM_ATTRIBUTE_DOORID; } return ITEM_ATTRIBUTE_NONE; } std::string getFirstLine(const std::string& str) { std::string firstLine; firstLine.reserve(str.length()); for (const char c : str) { if (c == '\n') { break; } firstLine.push_back(c); } return firstLine; } const char* getReturnMessage(ReturnValue value) { switch (value) { case RETURNVALUE_DESTINATIONOUTOFREACH: return "Destination is out of reach."; case RETURNVALUE_NOTMOVEABLE: return "You cannot move this object."; case RETURNVALUE_DROPTWOHANDEDITEM: return "Drop the double-handed object first."; case RETURNVALUE_BOTHHANDSNEEDTOBEFREE: return "Both hands need to be free."; case RETURNVALUE_CANNOTBEDRESSED: return "You cannot dress this object there."; case RETURNVALUE_PUTTHISOBJECTINYOURHAND: return "Put this object in your hand."; case RETURNVALUE_PUTTHISOBJECTINBOTHHANDS: return "Put this object in both hands."; case RETURNVALUE_CANONLYUSEONEWEAPON: return "You may only use one weapon."; case RETURNVALUE_TOOFARAWAY: return "Too far away."; case RETURNVALUE_FIRSTGODOWNSTAIRS: return "First go downstairs."; case RETURNVALUE_FIRSTGOUPSTAIRS: return "First go upstairs."; case RETURNVALUE_NOTENOUGHCAPACITY: return "This object is too heavy for you to carry."; case RETURNVALUE_CONTAINERNOTENOUGHROOM: return "You cannot put more objects in this container."; case RETURNVALUE_NEEDEXCHANGE: case RETURNVALUE_NOTENOUGHROOM: return "There is not enough room."; case RETURNVALUE_CANNOTPICKUP: return "You cannot take this object."; case RETURNVALUE_CANNOTTHROW: return "You cannot throw there."; case RETURNVALUE_THEREISNOWAY: return "There is no way."; case RETURNVALUE_THISISIMPOSSIBLE: return "This is impossible."; case RETURNVALUE_PLAYERISPZLOCKED: return "You can not enter a protection zone after attacking another player."; case RETURNVALUE_PLAYERISNOTINVITED: return "You are not invited."; case RETURNVALUE_CREATUREDOESNOTEXIST: return "Creature does not exist."; case RETURNVALUE_DEPOTISFULL: return "You cannot put more items in this depot."; case RETURNVALUE_CANNOTUSETHISOBJECT: return "You cannot use this object."; case RETURNVALUE_PLAYERWITHTHISNAMEISNOTONLINE: return "A player with this name is not online."; case RETURNVALUE_NOTREQUIREDLEVELTOUSERUNE: return "You do not have the required magic level to use this rune."; case RETURNVALUE_YOUAREALREADYTRADING: return "You are already trading."; case RETURNVALUE_THISPLAYERISALREADYTRADING: return "This player is already trading."; case RETURNVALUE_YOUMAYNOTLOGOUTDURINGAFIGHT: return "You may not logout during or immediately after a fight!"; case RETURNVALUE_DIRECTPLAYERSHOOT: return "You are not allowed to shoot directly on players."; case RETURNVALUE_NOTENOUGHLEVEL: return "You do not have enough level."; case RETURNVALUE_NOTENOUGHMAGICLEVEL: return "You do not have enough magic level."; case RETURNVALUE_NOTENOUGHMANA: return "You do not have enough mana."; case RETURNVALUE_NOTENOUGHSOUL: return "You do not have enough soul."; case RETURNVALUE_YOUAREEXHAUSTED: return "You are exhausted."; case RETURNVALUE_CANONLYUSETHISRUNEONCREATURES: return "You can only use this rune on creatures."; case RETURNVALUE_PLAYERISNOTREACHABLE: return "Player is not reachable."; case RETURNVALUE_CREATUREISNOTREACHABLE: return "Creature is not reachable."; case RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE: return "This action is not permitted in a protection zone."; case RETURNVALUE_YOUMAYNOTATTACKTHISPLAYER: return "You may not attack this player."; case RETURNVALUE_YOUMAYNOTATTACKTHISCREATURE: return "You may not attack this creature."; case RETURNVALUE_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE: return "You may not attack a person in a protection zone."; case RETURNVALUE_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE: return "You may not attack a person while you are in a protection zone."; case RETURNVALUE_YOUCANONLYUSEITONCREATURES: return "You can only use it on creatures."; case RETURNVALUE_TURNSECUREMODETOATTACKUNMARKEDPLAYERS: return "Turn secure mode off if you really want to attack unmarked players."; case RETURNVALUE_YOUNEEDPREMIUMACCOUNT: return "You need a premium account."; case RETURNVALUE_YOUNEEDTOLEARNTHISSPELL: return "You need to learn this spell first."; case RETURNVALUE_YOURVOCATIONCANNOTUSETHISSPELL: return "Your vocation cannot use this spell."; case RETURNVALUE_YOUNEEDAWEAPONTOUSETHISSPELL: return "You need to equip a weapon to use this spell."; case RETURNVALUE_PLAYERISPZLOCKEDLEAVEPVPZONE: return "You can not leave a pvp zone after attacking another player."; case RETURNVALUE_PLAYERISPZLOCKEDENTERPVPZONE: return "You can not enter a pvp zone after attacking another player."; case RETURNVALUE_ACTIONNOTPERMITTEDINANOPVPZONE: return "This action is not permitted in a non pvp zone."; case RETURNVALUE_YOUCANNOTLOGOUTHERE: return "You can not logout here."; case RETURNVALUE_YOUNEEDAMAGICITEMTOCASTSPELL: return "You need a magic item to cast this spell."; case RETURNVALUE_CANNOTCONJUREITEMHERE: return "You cannot conjure items here."; case RETURNVALUE_YOUNEEDTOSPLITYOURSPEARS: return "You need to split your spears first."; case RETURNVALUE_NAMEISTOOAMBIGIOUS: return "Name is too ambigious."; case RETURNVALUE_CANONLYUSEONESHIELD: return "You may use only one shield."; case RETURNVALUE_NOPARTYMEMBERSINRANGE: return "No party members in range."; case RETURNVALUE_YOUARENOTTHEOWNER: return "You are not the owner."; case RETURNVALUE_TRADEPLAYERFARAWAY: return "Trade player is too far away."; case RETURNVALUE_YOUDONTOWNTHISHOUSE: return "You don't own this house."; case RETURNVALUE_TRADEPLAYERALREADYOWNSAHOUSE: return "Trade player already owns a house."; case RETURNVALUE_TRADEPLAYERHIGHESTBIDDER: return "Trade player is currently the highest bidder of an auctioned house."; case RETURNVALUE_YOUCANNOTTRADETHISHOUSE: return "You can not trade this house."; default: // RETURNVALUE_NOTPOSSIBLE, etc return "Sorry, not possible."; } } void getFilesInDirectory(const boost::filesystem::path& root, const std::string& ext, std::vector& ret) { if (!boost::filesystem::exists(root)) { return; } if (boost::filesystem::is_directory(root)) { boost::filesystem::recursive_directory_iterator it(root); boost::filesystem::recursive_directory_iterator endit; while (it != endit) { if (boost::filesystem::is_regular_file(*it) && it->path().extension() == ext) { ret.push_back(it->path().filename()); } ++it; } } } std::string getClientVersionString(uint32_t version) { return getClientVersionString(static_cast(version)); } std::string getClientVersionString(ClientVersion_t version) { std::string result; switch (version) { case CLIENT_VERSION_780: result = "7.80"; break; case CLIENT_VERSION_781: result = "7.81"; break; case CLIENT_VERSION_790: result = "7.90"; break; case CLIENT_VERSION_792: result = "7.92"; break; case CLIENT_VERSION_800: result = "8.00"; break; default: result = "Unknown"; break; } return result; }