mirror of
https://github.com/ErikasKontenis/SabrehavenServer.git
synced 2025-05-02 18:49:21 +02:00
1212 lines
30 KiB
C++
1212 lines
30 KiB
C++
/**
|
|
* Tibia GIMUD Server - a free and open-source MMORPG server emulator
|
|
* Copyright (C) 2019 Sabrehaven and Mark Samman <mark.samman@gmail.com>
|
|
*
|
|
* 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<size_t>(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<char>(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<char>(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<uint32_t>(std::strtoul(message.substr(39, 1).c_str(), nullptr, 16) & 0xF);
|
|
|
|
// get truncated hash
|
|
uint32_t truncHash = static_cast<uint32_t>(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;
|
|
}
|
|
|
|
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<int32_t> uniformRand;
|
|
if (minNumber == maxNumber) {
|
|
return minNumber;
|
|
} else if (minNumber > maxNumber) {
|
|
std::swap(minNumber, maxNumber);
|
|
}
|
|
return uniformRand(getRandomGenerator(), std::uniform_int_distribution<int32_t>::param_type(minNumber, maxNumber));
|
|
}
|
|
|
|
int32_t normal_random(int32_t minNumber, int32_t maxNumber)
|
|
{
|
|
static std::normal_distribution<float> 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},
|
|
};
|
|
|
|
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},
|
|
};
|
|
|
|
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<CombatType_t>(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<boost::filesystem::path>& 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;
|
|
}
|
|
}
|
|
}
|