diff --git a/.gitignore b/.gitignore index 287c07412..b8ec6881f 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,19 @@ perl/ submodules/* cmake-build-debug/ -.nfs.* \ No newline at end of file +.nfs.* + +# Visual Studio and CMAKE Generated Files +/.vs/ +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*.cmake +*.ilk +*.pdb +*.sln +*.dir/ +libs/ +bin/ +/Win32 +/client_files/**/CMakeFiles/ diff --git a/README.md b/README.md index f3b829732..31f88be38 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # EQEmulator Core Server -|Travis CI (Linux)|Appveyor w/ Bots (Windows) |Appveyor w/o Bots (Windows) | +|Travis CI (Linux)|Appveyor (Windows x86) |Appveyor (Windows x64) | |:---:|:---:|:---:| -|[![Linux CI](https://travis-ci.org/EQEmu/Server.svg?branch=master)](https://travis-ci.org/EQEmu/Server) |[![Build status](https://ci.appveyor.com/api/projects/status/scr25kmntx36c1ub/branch/master?svg=true)](https://ci.appveyor.com/project/KimLS/server-87crp/branch/master) |[![Build status](https://ci.appveyor.com/api/projects/status/mdwbr4o9l6mxqofj/branch/master?svg=true)](https://ci.appveyor.com/project/KimLS/server-w0pq2/branch/master) | +|[![Linux CI](https://travis-ci.org/EQEmu/Server.svg?branch=master)](https://travis-ci.org/EQEmu/Server) |[![Build status](https://ci.appveyor.com/api/projects/status/v3utuu0dttm2cqd0?svg=true)](https://ci.appveyor.com/project/KimLS/server) |[![Build status](https://ci.appveyor.com/api/projects/status/scr25kmntx36c1ub?svg=true)](https://ci.appveyor.com/project/KimLS/server-87crp) | *** diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 4cd537c79..34be1121a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -78,6 +78,7 @@ SET(common_sources unix.cpp platform.cpp json/jsoncpp.cpp + namegenerator/namegen.cpp net/console_server.cpp net/console_server_connection.cpp net/crc32.cpp @@ -562,6 +563,7 @@ SET(common_headers event/timer.h json/json.h json/json-forwards.h + namegenerator/namegen.h net/console_server.h net/console_server_connection.h net/crc32.h diff --git a/common/database.cpp b/common/database.cpp index b3a4c0bf4..4a554c2ac 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -870,6 +870,38 @@ void Database::GetCharName(uint32 char_id, char* name) { } } +const char* Database::GetCharNameByID(uint32 char_id) { + std::string query = fmt::format("SELECT `name` FROM `character_data` WHERE id = {}", char_id); + auto results = QueryDatabase(query); + + if (!results.Success()) { + return ""; + } + + if (results.RowCount() == 0) { + return ""; + } + + auto row = results.begin(); + return row[0]; +} + +const char* Database::GetNPCNameByID(uint32 npc_id) { + std::string query = fmt::format("SELECT `name` FROM `npc_types` WHERE id = {}", npc_id); + auto results = QueryDatabase(query); + + if (!results.Success()) { + return ""; + } + + if (results.RowCount() == 0) { + return ""; + } + + auto row = results.begin(); + return row[0]; +} + bool Database::LoadVariables() { auto results = QueryDatabase(StringFormat("SELECT varname, value, unix_timestamp() FROM variables where unix_timestamp(ts) >= %d", varcache.last_update)); @@ -2318,3 +2350,4 @@ int Database::GetInstanceID(uint32 char_id, uint32 zone_id) { return 0; } + diff --git a/common/database.h b/common/database.h index 2239282ba..d94401299 100644 --- a/common/database.h +++ b/common/database.h @@ -137,6 +137,8 @@ public: void GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID = 0); void GetCharName(uint32 char_id, char* name); + const char *GetCharNameByID(uint32 char_id); + const char *GetNPCNameByID(uint32 npc_id); void LoginIP(uint32 AccountID, const char* LoginIP); /* Instancing */ diff --git a/common/database/database_dump_service.cpp b/common/database/database_dump_service.cpp index c4aa971d6..d505a1a24 100644 --- a/common/database/database_dump_service.cpp +++ b/common/database/database_dump_service.cpp @@ -50,7 +50,11 @@ std::string DatabaseDumpService::execute(const std::string &cmd, bool return_res const char *file_name = "db-exec-result.txt"; if (return_result) { +#ifdef _WINDOWS + std::system((cmd + " > " + file_name + " 2>&1").c_str()); +#else std::system((cmd + " > " + file_name).c_str()); +#endif } else { std::system((cmd).c_str()); @@ -95,7 +99,7 @@ bool DatabaseDumpService::IsTarAvailable() */ bool DatabaseDumpService::Is7ZipAvailable() { - std::string version_output = execute("7z -help"); + std::string version_output = execute("7z --help"); return version_output.find("7-Zip") != std::string::npos; } @@ -396,9 +400,8 @@ void DatabaseDumpService::Dump() else if (Is7ZipAvailable()) { execute( fmt::format( - "7z a -t7z {}.zip -C {} {}.sql", + "7z a -t7z {}.zip {}.sql", GetDumpFileNameWithPath(), - GetSetDumpPath(), GetDumpFileNameWithPath() ) ); diff --git a/common/event/event_loop.h b/common/event/event_loop.h index 295c0532d..537c3280a 100644 --- a/common/event/event_loop.h +++ b/common/event/event_loop.h @@ -25,6 +25,10 @@ namespace EQ uv_run(&m_loop, UV_RUN_DEFAULT); } + void Shutdown() { + uv_stop(&m_loop); + } + uv_loop_t* Handle() { return &m_loop; } private: diff --git a/common/namegenerator/namegen.cpp b/common/namegenerator/namegen.cpp new file mode 100644 index 000000000..3ef880ea4 --- /dev/null +++ b/common/namegenerator/namegen.cpp @@ -0,0 +1,522 @@ +/** + * + * @file A fantasy name generator library. + * @version 1.0.1 + * @license Public Domain + * @author German M. Bravo (Kronuz) + * + */ + +#include "namegen.h" + +#include // for move, reverse +#include // for rng seed +#include // for size_t, mbsrtowcs, wcsrtombs +#include // for towupper +#include // for make_unique +#include // for mt19937, uniform_real_distribution +#include // for invalid_argument, out_of_range + + +using namespace NameGen; + + +static std::mt19937 rng(std::chrono::high_resolution_clock::now().time_since_epoch().count()); + + +// https://isocpp.org/wiki/faq/ctors#static-init-order +// Avoid the "static initialization order fiasco" +const std::unordered_map>& Generator::SymbolMap() +{ + static auto* const symbols = new std::unordered_map>({ + { + "s", { + "ach", "ack", "ad", "age", "ald", "ale", "an", "ang", "ar", "ard", + "as", "ash", "at", "ath", "augh", "aw", "ban", "bel", "bur", "cer", + "cha", "che", "dan", "dar", "del", "den", "dra", "dyn", "ech", "eld", + "elm", "em", "en", "end", "eng", "enth", "er", "ess", "est", "et", + "gar", "gha", "hat", "hin", "hon", "ia", "ight", "ild", "im", "ina", + "ine", "ing", "ir", "is", "iss", "it", "kal", "kel", "kim", "kin", + "ler", "lor", "lye", "mor", "mos", "nal", "ny", "nys", "old", "om", + "on", "or", "orm", "os", "ough", "per", "pol", "qua", "que", "rad", + "rak", "ran", "ray", "ril", "ris", "rod", "roth", "ryn", "sam", + "say", "ser", "shy", "skel", "sul", "tai", "tan", "tas", "ther", + "tia", "tin", "ton", "tor", "tur", "um", "und", "unt", "urn", "usk", + "ust", "ver", "ves", "vor", "war", "wor", "yer" + } + }, + { + "v", { + "a", "e", "i", "o", "u", "y" + } + }, + { + "V", { + "a", "e", "i", "o", "u", "y", "ae", "ai", "au", "ay", "ea", "ee", + "ei", "eu", "ey", "ia", "ie", "oe", "oi", "oo", "ou", "ui" + } + }, + { + "c", { + "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", + "s", "t", "v", "w", "x", "y", "z" + } + }, + { + "B", { + "b", "bl", "br", "c", "ch", "chr", "cl", "cr", "d", "dr", "f", "g", + "h", "j", "k", "l", "ll", "m", "n", "p", "ph", "qu", "r", "rh", "s", + "sch", "sh", "sl", "sm", "sn", "st", "str", "sw", "t", "th", "thr", + "tr", "v", "w", "wh", "y", "z", "zh" + } + }, + { + "C", { + "b", "c", "ch", "ck", "d", "f", "g", "gh", "h", "k", "l", "ld", "ll", + "lt", "m", "n", "nd", "nn", "nt", "p", "ph", "q", "r", "rd", "rr", + "rt", "s", "sh", "ss", "st", "t", "th", "v", "w", "y", "z" + } + }, + { + "i", { + "air", "ankle", "ball", "beef", "bone", "bum", "bumble", "bump", + "cheese", "clod", "clot", "clown", "corn", "dip", "dolt", "doof", + "dork", "dumb", "face", "finger", "foot", "fumble", "goof", + "grumble", "head", "knock", "knocker", "knuckle", "loaf", "lump", + "lunk", "meat", "muck", "munch", "nit", "numb", "pin", "puff", + "skull", "snark", "sneeze", "thimble", "twerp", "twit", "wad", + "wimp", "wipe" + } + }, + { + "m", { + "baby", "booble", "bunker", "cuddle", "cuddly", "cutie", "doodle", + "foofie", "gooble", "honey", "kissie", "lover", "lovey", "moofie", + "mooglie", "moopie", "moopsie", "nookum", "poochie", "poof", + "poofie", "pookie", "schmoopie", "schnoogle", "schnookie", + "schnookum", "smooch", "smoochie", "smoosh", "snoogle", "snoogy", + "snookie", "snookum", "snuggy", "sweetie", "woogle", "woogy", + "wookie", "wookum", "wuddle", "wuddly", "wuggy", "wunny" + } + }, + { + "M", { + "boo", "bunch", "bunny", "cake", "cakes", "cute", "darling", + "dumpling", "dumplings", "face", "foof", "goo", "head", "kin", + "kins", "lips", "love", "mush", "pie", "poo", "pooh", "pook", "pums" + } + }, + { + "D", { + "b", "bl", "br", "cl", "d", "f", "fl", "fr", "g", "gh", "gl", "gr", + "h", "j", "k", "kl", "m", "n", "p", "th", "w" + } + }, + { + "d", { + "elch", "idiot", "ob", "og", "ok", "olph", "olt", "omph", "ong", + "onk", "oo", "oob", "oof", "oog", "ook", "ooz", "org", "ork", "orm", + "oron", "ub", "uck", "ug", "ulf", "ult", "um", "umb", "ump", "umph", + "un", "unb", "ung", "unk", "unph", "unt", "uzz" + } + } + }); + + return *symbols; +} + +Generator::Generator() +{ +} + + +Generator::Generator(std::vector>&& generators_) : + generators(std::move(generators_)) +{ +} + + +size_t Generator::combinations() +{ + size_t total = 1; + for (auto& g : generators) { + total *= g->combinations(); + } + return total; +} + + +size_t Generator::min() +{ + size_t final = 0; + for (auto& g : generators) { + final += g->min(); + } + return final; +} + + +size_t Generator::max() +{ + size_t final = 0; + for (auto& g : generators) { + final += g->max(); + } + return final; +} + + +std::string Generator::toString() { + std::string str; + for (auto& g : generators) { + str.append(g->toString()); + } + return str; +} + + +void Generator::add(std::unique_ptr&& g) +{ + generators.push_back(std::move(g)); +} + + +Random::Random() +{ +} + +Random::Random(std::vector>&& generators_) : + Generator(std::move(generators_)) +{ +} + +size_t Random::combinations() +{ + size_t total = 0; + for (auto& g : generators) { + total += g->combinations(); + } + return total ? total : 1; +} + +size_t Random::min() +{ + size_t final = -1; + for (auto& g : generators) { + size_t current = g->min(); + if (current < final) { + final = current; + } + } + return final; +} + +size_t Random::max() +{ + size_t final = 0; + for (auto& g : generators) { + size_t current = g->max(); + if (current > final) { + final = current; + } + } + return final; +} + + +std::string Random::toString() +{ + if (!generators.size()) { + return ""; + } + std::uniform_real_distribution distribution(0, generators.size() - 1); + int rnd = distribution(rng) + 0.5; + return generators[rnd]->toString(); +} + + +Sequence::Sequence() +{ +} + +Sequence::Sequence(std::vector>&& generators_) : + Generator(std::move(generators_)) +{ +} + +Literal::Literal(const std::string &value_) : + value(value_) +{ +} + +size_t Literal::combinations() +{ + return 1; +} + +size_t Literal::min() +{ + return value.size(); +} + +size_t Literal::max() +{ + return value.size(); +} + +std::string Literal::toString() +{ + return value; +} + +Reverser::Reverser(std::unique_ptr&& g) +{ + add(std::move(g)); +} + + +std::string Reverser::toString() +{ + std::wstring str = towstring(Generator::toString()); + std::reverse(str.begin(), str.end()); + return tostring(str); +} + +Capitalizer::Capitalizer(std::unique_ptr&& g) +{ + add(std::move(g)); +} + +std::string Capitalizer::toString() +{ + std::wstring str = towstring(Generator::toString()); + str[0] = std::towupper(str[0]); + return tostring(str); +} + + +Collapser::Collapser(std::unique_ptr&& g) +{ + add(std::move(g)); +} + +std::string Collapser::toString() +{ + std::wstring str = towstring(Generator::toString()); + std::wstring out; + int cnt = 0; + wchar_t pch = L'\0'; + for (auto ch : str) { + if (ch == pch) { + cnt++; + } else { + cnt = 0; + } + int mch = 2; + switch(ch) { + case 'a': + case 'h': + case 'i': + case 'j': + case 'q': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + mch = 1; + } + if (cnt < mch) { + out.push_back(ch); + } + pch = ch; + } + return tostring(out); +} + + +Generator::Generator(const std::string &pattern, bool collapse_triples) { + std::unique_ptr last; + + std::stack> stack; + std::unique_ptr top = std::unique_ptr(); + + for (auto c : pattern) { + switch (c) { + case '<': + stack.push(std::move(top)); + top = std::unique_ptr(); + break; + case '(': + stack.push(std::move(top)); + top = std::unique_ptr(); + break; + case '>': + case ')': + if (stack.size() == 0) { + throw std::invalid_argument("Unbalanced brackets"); + } else if (c == '>' && top->type != group_types::symbol) { + throw std::invalid_argument("Unexpected '>' in pattern"); + } else if (c == ')' && top->type != group_types::literal) { + throw std::invalid_argument("Unexpected ')' in pattern"); + } + last = top->produce(); + top = std::move(stack.top()); + stack.pop(); + top->add(std::move(last)); + break; + case '|': + top->split(); + break; + case '!': + if (top->type == group_types::symbol) { + top->wrap(wrappers::capitalizer); + } else { + top->add(c); + } + break; + case '~': + if (top->type == group_types::symbol) { + top->wrap(wrappers::reverser); + } else { + top->add(c); + } + break; + default: + top->add(c); + break; + } + } + + if (stack.size() != 0) { + throw std::invalid_argument("Missing closing bracket"); + } + + std::unique_ptr g = top->produce(); + if (collapse_triples) { + g = std::unique_ptr(new Collapser(std::move(g))); + } + add(std::move(g)); +} + + +Generator::Group::Group(group_types_t type_) : + type(type_) +{ +} + +void Generator::Group::add(std::unique_ptr&& g) +{ + while (!wrappers.empty()) { + switch (wrappers.top()) { + case reverser: + g = std::unique_ptr(new Reverser(std::move(g))); + break; + case capitalizer: + g = std::unique_ptr(new Capitalizer(std::move(g))); + break; + } + wrappers.pop(); + } + if (set.size() == 0) { + set.push_back(std::unique_ptr()); + } + set.back()->add(std::move(g)); +} + +void Generator::Group::add(char c) +{ + std::string value(1, c); + std::unique_ptr g = std::unique_ptr(); + g->add(std::unique_ptr(new Literal(value))); + Group::add(std::move(g)); +} + +std::unique_ptr Generator::Group::produce() +{ + switch (set.size()) { + case 0: + return std::unique_ptr(new Literal("")); + case 1: + return std::move(*set.begin()); + default: + return std::unique_ptr(new Random(std::move(set))); + } +} + +void Generator::Group::split() +{ + if (set.size() == 0) { + set.push_back(std::unique_ptr()); + } + set.push_back(std::unique_ptr()); +} + +void Generator::Group::wrap(wrappers_t type) +{ + wrappers.push(type); +} + +Generator::GroupSymbol::GroupSymbol() : + Group(group_types::symbol) +{ +} + +void Generator::GroupSymbol::add(char c) +{ + std::string value(1, c); + std::unique_ptr g = std::unique_ptr(); + try { + static const auto& symbols = SymbolMap(); + for (const auto& s : symbols.at(value)) { + g->add(std::unique_ptr(new Literal(s))); + } + } catch (const std::out_of_range&) { + g->add(std::unique_ptr(new Literal(value))); + } + Group::add(std::move(g)); +} + +Generator::GroupLiteral::GroupLiteral() : + Group(group_types::literal) +{ +} + +std::wstring towstring(const std::string & s) +{ + const char *cs = s.c_str(); + const size_t wn = std::mbsrtowcs(nullptr, &cs, 0, nullptr); + + if (wn == static_cast(-1)) { + return L""; + } + + std::vector buf(wn); + cs = s.c_str(); + const size_t wn_again = std::mbsrtowcs(buf.data(), &cs, wn, nullptr); + + if (wn_again == static_cast(-1)) { + return L""; + } + + return std::wstring(buf.data(), wn); +} + +std::string tostring(const std::wstring & s) +{ + const wchar_t *cs = s.c_str(); + const size_t wn = std::wcsrtombs(nullptr, &cs, 0, nullptr); + + if (wn == static_cast(-1)) { + return ""; + } + + std::vector buf(wn); + const size_t wn_again = std::wcsrtombs(buf.data(), &cs, wn, nullptr); + + if (wn_again == static_cast(-1)) { + return ""; + } + + return std::string(buf.data(), wn); +} diff --git a/common/namegenerator/namegen.h b/common/namegenerator/namegen.h new file mode 100644 index 000000000..4ddcf1bf5 --- /dev/null +++ b/common/namegenerator/namegen.h @@ -0,0 +1,269 @@ +/** + * + * @file A fantasy name generator library. + * @version 1.0.1 + * @license Public Domain + * @author German M. Bravo (Kronuz) + * + * This library is designed after the RinkWorks Fantasy Name Generator. + * @see http://www.rinkworks.com/namegen/ + * + * @example + * NameGen::Generator generator("sV'i"); + * generator.toString(); // Returns a new name each call with produce() + * // => "entheu'loaf" + * + * ## Pattern Syntax + * + * The compile() function creates a name generator based on an input + * pattern. The letters s, v, V, c, B, C, i, m, M, D, and d represent + * different types of random replacements. Everything else is produced + * literally. + * + * s - generic syllable + * v - vowel + * V - vowel or vowel combination + * c - consonant + * B - consonant or consonant combination suitable for beginning a word + * C - consonant or consonant combination suitable anywhere in a word + * i - insult + * m - mushy name + * M - mushy name ending + * D - consonant suited for a stupid person's name + * d - syllable suited for a stupid person's name (begins with a vowel) + * + * All characters between parenthesis () are produced literally. For + * example, the pattern "s(dim)", produces a random generic syllable + * followed by "dim". + * + * Characters between angle brackets <> produce patterns from the table + * above. Imagine the entire pattern is wrapped in one of these. + * + * In both types of groupings, a vertical bar | denotes a random + * choice. Empty groups are allowed. For example, "(foo|bar)" produces + * either "foo" or "bar". The pattern "" produces a constant, + * vowel, or nothing at all. + * + * An exclamation point ! means to capitalize the component that + * follows it. For example, "!(foo)" will produce "Foo" and "v!s" will + * produce a lowercase vowel followed by a capitalized syllable, like + * "eRod". + * + * A tilde ~ means to reverse the letters of the component that + * follows it. For example, "~(foo)" will produce "oof". To reverse an + * entire template, wrap it in brackets. For example, to reverse + * "sV'i" as a whole use "~". The template "~sV'i" will only + * reverse the initial syllable. + * + * ## Internals + * + * A name generator is anything with a toString() method, including, + * importantly, strings themselves. The generator constructors + * (Random, Sequence) perform additional optimizations when *not* used + * with the `new` keyword: they may pass through a provided generator, + * combine provided generators, or even return a simple string. + * + * New pattern symbols added to Generator.symbols will automatically + * be used by the compiler. + */ + +#pragma once + +#include // for size_t +#include // for wstring +#include // for unique_ptr +#include // for stack +#include // for string +#include // for unordered_map +#include // for vector + + +namespace NameGen { + +// Middle Earth +#define MIDDLE_EARTH "(bil|bal|ban|hil|ham|hal|hol|hob|wil|me|or|ol|od|gor|for|fos|tol|ar|fin|ere|leo|vi|bi|bren|thor)(|go|orbis|apol|adur|mos|ri|i|na|ole|n)(|tur|axia|and|bo|gil|bin|bras|las|mac|grim|wise|l|lo|fo|co|ra|via|da|ne|ta|y|wen|thiel|phin|dir|dor|tor|rod|on|rdo|dis)" + +// Japanese Names (Constrained) +#define JAPANESE_NAMES_CONSTRAINED "(aka|aki|bashi|gawa|kawa|furu|fuku|fuji|hana|hara|haru|hashi|hira|hon|hoshi|ichi|iwa|kami|kawa|ki|kita|kuchi|kuro|marui|matsu|miya|mori|moto|mura|nabe|naka|nishi|no|da|ta|o|oo|oka|saka|saki|sawa|shita|shima|i|suzu|taka|take|to|toku|toyo|ue|wa|wara|wata|yama|yoshi|kei|ko|zawa|zen|sen|ao|gin|kin|ken|shiro|zaki|yuki|asa)(||||||||||bashi|gawa|kawa|furu|fuku|fuji|hana|hara|haru|hashi|hira|hon|hoshi|chi|wa|ka|kami|kawa|ki|kita|kuchi|kuro|marui|matsu|miya|mori|moto|mura|nabe|naka|nishi|no|da|ta|o|oo|oka|saka|saki|sawa|shita|shima|suzu|taka|take|to|toku|toyo|ue|wa|wara|wata|yama|yoshi|kei|ko|zawa|zen|sen|ao|gin|kin|ken|shiro|zaki|yuki|sa)" + +// Japanese Names (Diverse) +#define JAPANESE_NAMES_DIVERSE "(a|i|u|e|o|||||)(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)(|(ka|ki|ki|ku|ku|ke|ke|ko|ko|sa|sa|sa|shi|shi|shi|su|su|se|so|ta|ta|chi|chi|tsu|te|to|na|ni|ni|nu|nu|ne|no|no|ha|hi|fu|fu|he|ho|ma|ma|ma|mi|mi|mi|mu|mu|mu|mu|me|mo|mo|mo|ya|yu|yu|yu|yo|ra|ra|ra|ri|ru|ru|ru|re|ro|ro|ro|wa|wa|wa|wa|wo|wo)))(|||n)" + +// Chinese Names +#define CHINESE_NAMES "(zh|x|q|sh|h)(ao|ian|uo|ou|ia)(|(l|w|c|p|b|m)(ao|ian|uo|ou|ia)(|n)|-(l|w|c|p|b|m)(ao|ian|uo|ou|ia)(|(d|j|q|l)(a|ai|iu|ao|i)))" + +// Greek Names +#define GREEK_NAMES "(tia)|s(os)|Bc(ios)|Bv(ios|os)>" + +// Hawaiian Names (1) +#define HAWAIIAN_NAMES_1 "((h|k|l|m|n|p|w|')|)(a|e|i|o|u)((h|k|l|m|n|p|w|')|)(a|e|i|o|u)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)(((h|k|l|m|n|p|w|')|)(a|e|i|o|u)|)" + +// Hawaiian Names (2) +#define HAWAIIAN_NAMES_2 "((h|k|l|m|n|p|w|)(a|e|i|o|u|a'|e'|i'|o'|u'|ae|ai|ao|au|oi|ou|eu|ei)(k|l|m|n|p|)|)(h|k|l|m|n|p|w|)(a|e|i|o|u|a'|e'|i'|o'|u'|ae|ai|ao|au|oi|ou|eu|ei)(k|l|m|n|p|)" + +// Old Latin Place Names +#define OLD_LATIN_PLACE_NAMES "sv(nia|lia|cia|sia)" + +// Dragons (Pern) +#define DRAGONS_PERN "<|>>(th)" + +// Dragon Riders +#define DRAGON_RIDERS "c'" + +// Pokemon +#define POKEMON "v(mon|chu|zard|rtle)" + +// Fantasy (Vowels, R, etc.) +#define FANTASY_VOWELS_R "(|(|s|h|ty|ph|r))(i|ae|ya|ae|eu|ia|i|eo|ai|a)(lo|la|sri|da|dai|the|sty|lae|due|li|lly|ri|na|ral|sur|rith)(|(su|nu|sti|llo|ria|))(|(n|ra|p|m|lis|cal|deu|dil|suir|phos|ru|dru|rin|raap|rgue))" + +// Fantasy (S, A, etc.) +#define FANTASY_S_A "(cham|chan|jisk|lis|frich|isk|lass|mind|sond|sund|ass|chad|lirt|und|mar|lis|il|)(jask|ast|ista|adar|irra|im|ossa|assa|osia|ilsa|)(|(an|ya|la|sta|sda|sya|st|nya))" + +// Fantasy (H, L, etc.) +#define FANTASY_H_L "(ch|ch't|sh|cal|val|ell|har|shar|shal|rel|laen|ral|jh't|alr|ch|ch't|av)(|(is|al|ow|ish|ul|el|ar|iel))(aren|aeish|aith|even|adur|ulash|alith|atar|aia|erin|aera|ael|ira|iel|ahur|ishul)" + +// Fantasy (N, L, etc.) +#define FANTASY_N_L "(ethr|qil|mal|er|eal|far|fil|fir|ing|ind|il|lam|quel|quar|quan|qar|pal|mal|yar|um|ard|enn|ey)(|(|on|us|un|ar|as|en|ir|ur|at|ol|al|an))(uard|wen|arn|on|il|ie|on|iel|rion|rian|an|ista|rion|rian|cil|mol|yon)" + +// Fantasy (K, N, etc.) +#define FANTASY_K_N "(taith|kach|chak|kank|kjar|rak|kan|kaj|tach|rskal|kjol|jok|jor|jad|kot|kon|knir|kror|kol|tul|rhaok|rhak|krol|jan|kag|ryr)(|in|or|an|ar|och|un|mar|yk|ja|arn|ir|ros|ror)(|(mund|ard|arn|karr|chim|kos|rir|arl|kni|var|an|in|ir|a|i|as))" + +// Fantasy (J, G, Z, etc.) +#define FANTASY_J_G_Z "(aj|ch|etz|etzl|tz|kal|gahn|kab|aj|izl|ts|jaj|lan|kach|chaj|qaq|jol|ix|az|biq|nam)(|(|aw|al|yes|il|ay|en|tom||oj|im|ol|aj|an|as))(aj|am|al|aqa|ende|elja|ich|ak|ix|in|ak|al|il|ek|ij|os|al|im)" + +// Fantasy (K, J, Y, etc.) +#define FANTASY_K_J_Y "(yi|shu|a|be|na|chi|cha|cho|ksa|yi|shu)(th|dd|jj|sh|rr|mk|n|rk|y|jj|th)(us|ash|eni|akra|nai|ral|ect|are|el|urru|aja|al|uz|ict|arja|ichi|ural|iru|aki|esh)" + +// Fantasy (S, E, etc.) +#define FANTASY_S_E "(syth|sith|srr|sen|yth|ssen|then|fen|ssth|kel|syn|est|bess|inth|nen|tin|cor|sv|iss|ith|sen|slar|ssil|sthen|svis|s|ss|s|ss)(|(tys|eus|yn|of|es|en|ath|elth|al|ell|ka|ith|yrrl|is|isl|yr|ast|iy))(us|yn|en|ens|ra|rg|le|en|ith|ast|zon|in|yn|ys)" + + + class Generator + { + typedef enum wrappers { + capitalizer, + reverser + } wrappers_t; + + typedef enum group_types { + symbol, + literal + } group_types_t; + + + class Group { + std::stack wrappers; + std::vector> set; + + public: + group_types_t type; + + Group(group_types_t type_); + virtual ~Group() { } + + std::unique_ptr produce(); + void split(); + void wrap(wrappers_t type); + void add(std::unique_ptr&& g); + + virtual void add(char c); + }; + + + class GroupSymbol : public Group { + public: + GroupSymbol(); + void add(char c); + }; + + + class GroupLiteral : public Group { + public: + GroupLiteral(); + }; + + protected: + std::vector> generators; + + public: + static const std::unordered_map>& SymbolMap(); + + Generator(); + Generator(const std::string& pattern, bool collapse_triples=true); + Generator(std::vector>&& generators_); + + virtual ~Generator() = default; + + virtual size_t combinations(); + virtual size_t min(); + virtual size_t max(); + virtual std::string toString(); + + void add(std::unique_ptr&& g); + }; + + + class Random : public Generator + { + public: + Random(); + Random(std::vector>&& generators_); + + size_t combinations(); + size_t min(); + size_t max(); + std::string toString(); + }; + + + class Sequence : public Generator + { + public: + Sequence(); + Sequence(std::vector>&& generators_); + }; + + + class Literal : public Generator + { + std::string value; + + public: + Literal(const std::string& value_); + + size_t combinations(); + size_t min(); + size_t max(); + std::string toString(); + }; + + + class Reverser : public Generator { + public: + Reverser(std::unique_ptr&& g); + + std::string toString(); + }; + + + class Capitalizer : public Generator + { + public: + Capitalizer(std::unique_ptr&& g); + + std::string toString(); + }; + + + class Collapser : public Generator + { + public: + Collapser(std::unique_ptr&& g); + + std::string toString(); + }; + +} + +std::wstring towstring(const std::string& s); +std::string tostring(const std::wstring& s); \ No newline at end of file diff --git a/common/ruletypes.h b/common/ruletypes.h index 85d670071..af70bf3c1 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -161,6 +161,7 @@ RULE_BOOL(Character, DismountWater, true, "Dismount horses when entering water") RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing") RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted") RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated") +RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared") RULE_CATEGORY_END() RULE_CATEGORY(Mercs) diff --git a/common/version.h b/common/version.h index 99261fe83..7c378e763 100644 --- a/common/version.h +++ b/common/version.h @@ -37,7 +37,7 @@ #define CURRENT_BINARY_DATABASE_VERSION 9152 #ifdef BOTS - #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9026 + #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027 #else #define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0 #endif diff --git a/utils/scripts/eqemu_server.pl b/utils/scripts/eqemu_server.pl index a5ae9e7b8..6618b021e 100755 --- a/utils/scripts/eqemu_server.pl +++ b/utils/scripts/eqemu_server.pl @@ -52,6 +52,10 @@ if (-e "eqemu_server_skip_update.txt") { $skip_self_update_check = 1; } +if (-e "eqemu_server_skip_maps_update.txt") { + $skip_self_maps_update_check = 1; +} + #::: Check for script self update check_xml_to_json_conversion() if $ARGV[0] eq "convert_xml"; do_self_update_check_routine() if !$skip_self_update_check; @@ -460,7 +464,7 @@ sub do_installer_routines { get_remote_file($install_repository_request_url . "libmysql.dll", "libmysql.dll", 1); } - map_files_fetch_bulk(); + map_files_fetch_bulk() if !$skip_self_maps_update_check; opcodes_fetch(); plugins_fetch(); quest_files_fetch(); @@ -533,7 +537,10 @@ sub check_for_world_bootup_database_update { if ($binary_database_version == $local_database_version && $ARGV[0] eq "ran_from_world") { print "[Update] Database up to date...\n"; - exit; + if (trim($db_version[2]) == 0) { + print "[Update] Continuing bootup\n"; + exit; + } } else { #::: We ran world - Database needs to update, lets backup and run updates and continue world bootup @@ -1705,26 +1712,22 @@ sub fetch_server_dlls { sub fetch_peq_db_full { print "[Install] Downloading latest PEQ Database... Please wait...\n"; - get_remote_file("http://edit.projecteq.net/weekly/peq_beta.zip", "updates_staged/peq_beta.zip", 1); + get_remote_file("http://db.projecteq.net/api/v1/dump/latest", "updates_staged/peq-latest.zip", 1); print "[Install] Downloaded latest PEQ Database... Extracting...\n"; - unzip('updates_staged/peq_beta.zip', 'updates_staged/peq_db/'); - my $start_dir = "updates_staged/peq_db"; + unzip('updates_staged/peq-latest.zip', 'updates_staged/peq_db/'); + my $start_dir = "updates_staged/peq_db/peq-dump"; find( sub { push @files, $File::Find::name unless -d; }, $start_dir ); for my $file (@files) { $destination_file = $file; - $destination_file =~ s/updates_staged\/peq_db\///g; - if ($file =~ /peqbeta|player_tables/i) { + $destination_file =~ s/updates_staged\/peq_db\/peq-dump\///g; + if ($file =~ /create_tables_content|create_tables_login|create_tables_player|create_tables_queryserv|create_tables_state|create_tables_system/i) { print "[Install] DB :: Installing :: " . $destination_file . "\n"; get_mysql_result_from_file($file); } } - - #::: PEQ DB baseline version - print get_mysql_result("DELETE FROM db_version"); - print get_mysql_result("INSERT INTO `db_version` (`version`) VALUES (9130);"); } sub map_files_fetch_bulk { diff --git a/utils/sql/git/bots/bots_db_update_manifest.txt b/utils/sql/git/bots/bots_db_update_manifest.txt index 1e18516b7..86e7c49dd 100644 --- a/utils/sql/git/bots/bots_db_update_manifest.txt +++ b/utils/sql/git/bots/bots_db_update_manifest.txt @@ -25,6 +25,7 @@ 9024|2019_06_27_bots_pet_get_lost.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'petgetlost'|empty| 9025|2019_08_26_bots_owner_option_spawn_message.sql|SELECT * FROM db_version WHERE bots_version >= 9025|empty| 9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty| +9027|2020_03_30_bots_view_update.sql|SELECT * FROM db_version WHERE bots_version >= 9027|empty| # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/bots/required/2020_03_30_bots_view_update.sql b/utils/sql/git/bots/required/2020_03_30_bots_view_update.sql new file mode 100644 index 000000000..d4948efed --- /dev/null +++ b/utils/sql/git/bots/required/2020_03_30_bots_view_update.sql @@ -0,0 +1,24 @@ +DROP VIEW IF EXISTS `vw_bot_character_mobs`; + +-- Views +CREATE VIEW `vw_bot_character_mobs` AS +SELECT +_utf8'C' AS mob_type, +c.`id`, +c.`name`, +c.`class`, +c.`level`, +c.`last_login`, +c.`zone_id`, +c.`deleted_at` +FROM `character_data` AS c +UNION ALL +SELECT _utf8'B' AS mob_type, +b.`bot_id` AS id, +b.`name`, +b.`class`, +b.`level`, +b.`last_spawn` AS last_login, +b.`zone_id`, +NULL AS `deleted_at` +FROM `bot_data` AS b; diff --git a/utils/sql/git/required/2020_03_09_convert_myisam_to_innodb.sql b/utils/sql/git/required/2020_03_09_convert_myisam_to_innodb.sql index ec9f6607b..31ccf1094 100644 --- a/utils/sql/git/required/2020_03_09_convert_myisam_to_innodb.sql +++ b/utils/sql/git/required/2020_03_09_convert_myisam_to_innodb.sql @@ -1,7 +1,3 @@ -ALTER TABLE `aa_actions` ENGINE=InnoDB; -ALTER TABLE `aa_effects` ENGINE=InnoDB; -ALTER TABLE `aa_required_level_cost` ENGINE=InnoDB; -ALTER TABLE `aa_timers` ENGINE=InnoDB; ALTER TABLE `account_flags` ENGINE=InnoDB; ALTER TABLE `account_ip` ENGINE=InnoDB; ALTER TABLE `account` ENGINE=InnoDB; diff --git a/world/client.cpp b/world/client.cpp index ebc01e03e..424265935 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -46,6 +46,7 @@ #include "clientlist.h" #include "wguild_mgr.h" #include "sof_char_create_data.h" +#include "../common/namegenerator/namegen.h" #include #include @@ -533,80 +534,14 @@ bool Client::HandleNameApprovalPacket(const EQApplicationPacket *app) return true; } -bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) { - // creates up to a 10 char name - char vowels[18]="aeiouyaeiouaeioe"; - char cons[48]="bcdfghjklmnpqrstvwxzybcdgklmnprstvwbcdgkpstrkd"; - char rndname[17]="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - char paircons[33]="ngrkndstshthphsktrdrbrgrfrclcr"; - int rndnum=emu_random.Int(0, 75),n=1; - bool dlc=false; - bool vwl=false; - bool dbl=false; - if (rndnum>63) - { // rndnum is 0 - 75 where 64-75 is cons pair, 17-63 is cons, 0-16 is vowel - rndnum=(rndnum-61)*2; // name can't start with "ng" "nd" or "rk" - rndname[0]=paircons[rndnum]; - rndname[1]=paircons[rndnum+1]; - n=2; - } - else if (rndnum>16) - { - rndnum-=17; - rndname[0]=cons[rndnum]; - } - else - { - rndname[0]=vowels[rndnum]; - vwl=true; - } - int namlen=emu_random.Int(5, 10); - for (int i=n;i46) - { // pick a cons pair - if (i>namlen-3) // last 2 chars in name? - { // name can only end in cons pair "rk" "st" "sh" "th" "ph" "sk" "nd" or "ng" - rndnum=emu_random.Int(0, 7)*2; - } - else - { // pick any from the set - rndnum=(rndnum-47)*2; - } - rndname[i]=paircons[rndnum]; - rndname[i+1]=paircons[rndnum+1]; - dlc=true; // flag keeps second letter from being doubled below - i+=1; - } - else - { // select a single cons - rndname[i]=cons[rndnum]; - } - } - else - { // select a vowel - rndname[i]=vowels[emu_random.Int(0, 16)]; - } - vwl=!vwl; - if (!dbl && !dlc) - { // one chance at double letters in name - if (!emu_random.Int(0, i+9)) // chances decrease towards end of name - { - rndname[i+1]=rndname[i]; - dbl=true; - i+=1; - } - } - } +bool Client::HandleGenerateRandomNamePacket(const EQApplicationPacket *app) +{ + NameGen::Generator generator("!Bvss"); + std::string random_name = generator.toString(); - rndname[0]=toupper(rndname[0]); - NameGeneration_Struct* ngs = (NameGeneration_Struct*)app->pBuffer; - memset(ngs->name,0,64); - strcpy(ngs->name,rndname); + auto *ngs = (NameGeneration_Struct *) app->pBuffer; + memset(ngs->name, 0, 64); + strcpy(ngs->name, random_name.c_str()); QueuePacket(app); return true; diff --git a/world/main.cpp b/world/main.cpp index dbdad0079..c8800187c 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -432,6 +432,7 @@ int main(int argc, char** argv) { RegisterConsoleFunctions(console); } + zoneserver_list.Init(); std::unique_ptr server_connection; server_connection.reset(new EQ::Net::ServertalkServer()); diff --git a/world/zonelist.cpp b/world/zonelist.cpp index 53ac1f2ae..a046c6a90 100644 --- a/world/zonelist.cpp +++ b/world/zonelist.cpp @@ -39,7 +39,6 @@ ZSList::ZSList() { NextID = 1; CurGroupID = 1; - LastAllocatedPort = 0; memset(pLockedZones, 0, sizeof(pLockedZones)); m_tick.reset(new EQ::Timer(5000, true, std::bind(&ZSList::OnTick, this, std::placeholders::_1))); @@ -76,7 +75,12 @@ void ZSList::Remove(const std::string &uuid) auto iter = zone_server_list.begin(); while (iter != zone_server_list.end()) { if ((*iter)->GetUUID().compare(uuid) == 0) { + auto port = (*iter)->GetCPort(); zone_server_list.erase(iter); + + if (port != 0) { + m_ports_free.push_back(port); + } return; } iter++; @@ -239,6 +243,14 @@ bool ZSList::SetLockedZone(uint16 iZoneID, bool iLock) { return false; } +void ZSList::Init() +{ + const WorldConfig* Config = WorldConfig::get(); + for (uint16 i = Config->ZonePortLow; i <= Config->ZonePortHigh; ++i) { + m_ports_free.push_back(i); + } +} + bool ZSList::IsZoneLocked(uint16 iZoneID) { for (auto &zone : pLockedZones) { if (zone == iZoneID) @@ -577,30 +589,15 @@ void ZSList::RebootZone(const char* ip1, uint16 port, const char* ip2, uint32 sk safe_delete_array(tmp); } -uint16 ZSList::GetAvailableZonePort() +uint16 ZSList::GetAvailableZonePort() { - const WorldConfig *Config = WorldConfig::get(); - int i; - uint16 port = 0; - - if (LastAllocatedPort == 0) - i = Config->ZonePortLow; - else - i = LastAllocatedPort + 1; - - while (i != LastAllocatedPort && port == 0) { - if (i>Config->ZonePortHigh) - i = Config->ZonePortLow; - - if (!FindByPort(i)) { - port = i; - break; - } - i++; + if (m_ports_free.empty()) { + return 0; } - LastAllocatedPort = port; - return port; + auto first = m_ports_free.front(); + m_ports_free.pop_front(); + return first; } uint32 ZSList::TriggerBootup(uint32 iZoneID, uint32 iInstanceID) { diff --git a/world/zonelist.h b/world/zonelist.h index 8b8525f57..5066d4e6d 100644 --- a/world/zonelist.h +++ b/world/zonelist.h @@ -7,6 +7,7 @@ #include "../common/event/timer.h" #include #include +#include class WorldTCPConnection; class ServerPacket; @@ -22,6 +23,7 @@ public: ZSList(); ~ZSList(); + void Init(); bool IsZoneLocked(uint16 iZoneID); bool SendPacket(ServerPacket *pack); bool SendPacket(uint32 zoneid, ServerPacket *pack); @@ -73,8 +75,7 @@ private: uint32 NextID; uint16 pLockedZones[MaxLockedZones]; uint32 CurGroupID; - uint16 LastAllocatedPort; - + std::deque m_ports_free; std::unique_ptr m_tick; std::unique_ptr m_keepalive; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 8005e5dec..aa5c11c72 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4018,6 +4018,14 @@ void Client::Handle_OP_CastSpell(const EQApplicationPacket *app) //Message(Chat::Red, "You cant cast right now, you arent in control of yourself!"); return; } + + // Hack for broken RoF2 which allows casting after a zoned IVU/IVA + if (invisible_undead || invisible_animals) { + BuffFadeByEffect(SE_InvisVsAnimals); + BuffFadeByEffect(SE_InvisVsUndead); + BuffFadeByEffect(SE_InvisVsUndead2); + BuffFadeByEffect(SE_Invisibility); // Included per JJ for completeness - client handles this one atm + } CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer; diff --git a/zone/command.cpp b/zone/command.cpp index 66be91b64..dcf60de0d 100755 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -196,6 +196,7 @@ int command_init(void) command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", 80, command_disarmtrap) || command_add("distance", "- Reports the distance between you and your target.", 80, command_distance) || command_add("doanim", "[animnum] [type] - Send an EmoteAnim for you or your target", 50, command_doanim) || + command_add("editmassrespawn", "[name-search] [second-value] - Mass (Zone wide) NPC respawn timer editing command", 100, command_editmassrespawn) || command_add("emote", "['name'/'world'/'zone'] [type] [message] - Send an emote message", 80, command_emote) || command_add("emotesearch", "Searches NPC Emotes", 80, command_emotesearch) || command_add("emoteview", "Lists all NPC Emotes", 80, command_emoteview) || @@ -6497,6 +6498,144 @@ void command_doanim(Client *c, const Seperator *sep) c->DoAnim(atoi(sep->arg[1]),atoi(sep->arg[2])); } +void command_editmassrespawn(Client* c, const Seperator* sep) +{ + if (strcasecmp(sep->arg[1], "usage") == 0) { + c->Message(Chat::White, "#editmassrespawn [exact_match: =]npc_type_name new_respawn_seconds (apply)"); + return; + } + + std::string search_npc_type; + if (sep->arg[1]) { + search_npc_type = sep->arg[1]; + } + + int change_respawn_seconds = 0; + if (sep->arg[2] && sep->IsNumber(2)) { + change_respawn_seconds = atoi(sep->arg[2]); + } + + bool change_apply = false; + if (sep->arg[3] && strcasecmp(sep->arg[3], "apply") == 0) { + change_apply = true; + } + + std::string search_encapsulator = "%"; + if (search_npc_type[0] == '=') { + + search_npc_type = search_npc_type.substr(1); + search_encapsulator = ""; + } + + std::string query = fmt::format( + SQL( + SELECT npc_types.id, spawn2.spawngroupID, spawn2.id, npc_types.name, spawn2.respawntime + FROM spawn2 + INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID + INNER JOIN npc_types ON spawnentry.npcID = npc_types.id + WHERE spawn2.zone LIKE '{}' + AND spawn2.version = '{}' + AND npc_types.name LIKE '{}{}{}' + ORDER BY npc_types.id, spawn2.spawngroupID, spawn2.id + ), + zone->GetShortName(), + zone->GetInstanceVersion(), + search_encapsulator, + search_npc_type, + search_encapsulator + ); + + std::string status = "(Searching)"; + if (change_apply) { + status = "(Applying)"; + } + + int results_count = 0; + + auto results = database.QueryDatabase(query); + if (results.Success() && results.RowCount()) { + + results_count = results.RowCount(); + + for (auto row : results) { + c->Message( + Chat::Yellow, + fmt::format( + "NPC (npcid:{}) (sgid:{}) (s2id:{}) [{}] Respawn: Current [{}] New [{}] {}", + row[0], + row[1], + row[2], + row[3], + row[4], + change_respawn_seconds, + status + ).c_str() + ); + } + + c->Message(Chat::Yellow, "Found (%i) NPC's that match this search...", results_count); + + if (change_respawn_seconds > 0) { + + if (change_apply) { + + results = database.QueryDatabase( + fmt::format( + SQL( + UPDATE spawn2 + SET respawntime = '{}' + WHERE id IN ( + SELECT spawn2.id + FROM spawn2 + INNER JOIN spawnentry ON spawn2.spawngroupID = spawnentry.spawngroupID + INNER JOIN npc_types ON spawnentry.npcID = npc_types.id + WHERE spawn2.zone LIKE '{}' + AND spawn2.version = '{}' + AND npc_types.name LIKE '{}{}{}' + ) + ), + change_respawn_seconds, + zone->GetShortName(), + zone->GetInstanceVersion(), + search_encapsulator, + search_npc_type, + search_encapsulator + ) + ); + + if (results.Success()) { + + c->Message(Chat::Yellow, "Changes applied to (%i) NPC 'Spawn2' entries", results_count); + zone->Repop(); + } + else { + + c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); + } + } + else { + + std::string saylink = fmt::format( + "#editmassrespawn {}{} {} apply", + (search_encapsulator.empty() ? "=" : ""), + search_npc_type, + change_respawn_seconds + ); + + c->Message( + Chat::Yellow, "To apply these changes, click <%s> or type [%s]", + EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Apply").c_str(), + saylink.c_str() + ); + } + } + } + else { + + c->Message(Chat::Yellow, "Found (0) NPC's that match this search..."); + } +} + void command_randomfeatures(Client *c, const Seperator *sep) { Mob *target=c->GetTarget(); @@ -7807,7 +7946,7 @@ void command_npcemote(Client *c, const Seperator *sep) void command_npceditmass(Client *c, const Seperator *sep) { if (strcasecmp(sep->arg[1], "usage") == 0) { - c->Message(Chat::White, "#npceditmass search_column [exact_match: =]search_value change_column change_value"); + c->Message(Chat::White, "#npceditmass search_column [exact_match: =]search_value change_column change_value (apply)"); return; } @@ -7955,7 +8094,7 @@ void command_npceditmass(Client *c, const Seperator *sep) std::string saylink = fmt::format( "#npceditmass {} {}{} {} {} apply", search_column, - (exact_match ? '=' : '\0'), + (exact_match ? "=" : ""), search_value, change_column, change_value diff --git a/zone/command.h b/zone/command.h index e1b028bb1..d15372416 100644 --- a/zone/command.h +++ b/zone/command.h @@ -91,6 +91,7 @@ void command_disablerecipe(Client *c, const Seperator *sep); void command_disarmtrap(Client *c, const Seperator *sep); void command_distance(Client *c, const Seperator *sep); void command_doanim(Client *c, const Seperator *sep); +void command_editmassrespawn(Client* c, const Seperator* sep); void command_emote(Client *c, const Seperator *sep); void command_emotesearch(Client* c, const Seperator *sep); void command_emoteview(Client* c, const Seperator *sep); diff --git a/zone/embparser_api.cpp b/zone/embparser_api.cpp index efdc09803..b95e80ae7 100644 --- a/zone/embparser_api.cpp +++ b/zone/embparser_api.cpp @@ -822,6 +822,54 @@ XS(XS__isdisctome) { XSRETURN(1); } +XS(XS__getracename); +XS(XS__getracename) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getracename(uint16 race_id)"); + + dXSTARG; + uint16 race_id = (int) SvIV(ST(0)); + std::string race_name = quest_manager.getracename(race_id); + + sv_setpv(TARG, race_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + +XS(XS__getspellname); +XS(XS__getspellname) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getspellname(uint32 spell_id)"); + + dXSTARG; + uint32 spell_id = (int) SvIV(ST(0)); + std::string spell_name = quest_manager.getspellname(spell_id); + + sv_setpv(TARG, spell_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + +XS(XS__getskillname); +XS(XS__getskillname) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getskillname(int skill_id)"); + + dXSTARG; + int skill_id = (int) SvIV(ST(0)); + std::string skill_name = quest_manager.getskillname(skill_id); + + sv_setpv(TARG, skill_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + XS(XS__safemove); XS(XS__safemove) { dXSARGS; @@ -2342,7 +2390,6 @@ XS(XS__updatetaskactivity) { XS(XS__resettaskactivity); XS(XS__resettaskactivity) { dXSARGS; - unsigned int task, activity; if (items == 2) { int task_id = (int) SvIV(ST(0)); int activity_id = (int) SvIV(ST(1)); @@ -2613,6 +2660,23 @@ XS(XS__istaskappropriate) { XSRETURN(1); } +XS(XS__gettaskname); +XS(XS__gettaskname) { + dXSARGS; + if (items != 1) { + Perl_croak(aTHX_ "Usage: quest::gettaskname(uint32 task_id)"); + } + + dXSTARG; + uint32 task_id = (int) SvIV(ST(0)); + std::string task_name = quest_manager.gettaskname(task_id); + + sv_setpv(TARG, task_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + XS(XS__popup); // prototype to pass -Wmissing-prototypes XS(XS__popup) { dXSARGS; @@ -2797,6 +2861,51 @@ XS(XS__collectitems) { XSRETURN_IV(quantity); } +XS(XS__countitem); +XS(XS__countitem) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::countitem(int item_id)"); + + uint32 item_id = (int) SvIV(ST(0)); + + int quantity = quest_manager.countitem(item_id); + + XSRETURN_IV(quantity); +} + +XS(XS__getitemname); +XS(XS__getitemname) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getitemname(uint32 item_id)"); + + dXSTARG; + uint32 item_id = (int) SvIV(ST(0)); + std::string item_name = quest_manager.getitemname(item_id); + + sv_setpv(TARG, item_name.c_str()); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + +XS(XS__getnpcnamebyid); +XS(XS__getnpcnamebyid) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getnpcnamebyid(uint32 npc_id)"); + + dXSTARG; + uint32 npc_id = (int) SvIV(ST(0)); + const char *npc_name = quest_manager.getnpcnamebyid(npc_id); + + sv_setpv(TARG, npc_name); + XSprePUSH; + PUSHTARG; + XSRETURN(1); +} + XS(XS__UpdateSpawnTimer); XS(XS__UpdateSpawnTimer) { dXSARGS; @@ -3063,6 +3172,25 @@ XS(XS__RemoveFromInstanceByCharID) { XSRETURN_EMPTY; } +XS(XS__CheckInstanceByCharID); +XS(XS__CheckInstanceByCharID) { + dXSARGS; + if (items != 2) { + Perl_croak(aTHX_ "Usage: quest::CheckInstanceByCharID(uint16 instance_id, uint32 char_id)"); + } + + bool RETVAL; + dXSTARG; + + uint16 instance_id = (int) SvUV(ST(0)); + uint32 char_id = (int) SvUV(ST(1)); + RETVAL = quest_manager.CheckInstanceByCharID(instance_id, char_id); + XSprePUSH; + PUSHu((IV) RETVAL); + + XSRETURN(1); +} + XS(XS__RemoveAllFromInstance); XS(XS__RemoveAllFromInstance) { dXSARGS; @@ -3151,6 +3279,57 @@ XS(XS__saylink) { XSRETURN(1); } +XS(XS__getcharnamebyid); +XS(XS__getcharnamebyid) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getcharnamebyid(uint32 char_id)"); + dXSTARG; + + Const_char *RETVAL; + uint32 char_id = (int) SvUV(ST(0)); + + RETVAL = quest_manager.getcharnamebyid(char_id); + + sv_setpv(TARG, RETVAL); + XSprePUSH; + PUSHTARG; +} + +XS(XS__getcharidbyname); +XS(XS__getcharidbyname) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getcharidbyname(string name)"); + dXSTARG; + + uint32 RETVAL; + const char *name = (const char *) SvPV_nolen(ST(0)); + + RETVAL = quest_manager.getcharidbyname(name); + XSprePUSH; + PUSHu((UV)RETVAL); + + XSRETURN(1); +} + +XS(XS__getcurrencyid); +XS(XS__getcurrencyid) { + dXSARGS; + if (items != 1) + Perl_croak(aTHX_ "Usage: quest::getcurrencyid(uint32 item_id)"); + dXSTARG; + + int RETVAL; + uint32 item_id = (int) SvUV(ST(0));; + + RETVAL = quest_manager.getcurrencyid(item_id); + XSprePUSH; + PUSHi((IV)RETVAL); + + XSRETURN(1); +} + XS(XS__getguildnamebyid); XS(XS__getguildnamebyid) { dXSARGS; @@ -3927,6 +4106,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "RemoveAllFromInstance"), XS__RemoveAllFromInstance, file); newXS(strcpy(buf, "RemoveFromInstance"), XS__RemoveFromInstance, file); newXS(strcpy(buf, "RemoveFromInstanceByCharID"), XS__RemoveFromInstanceByCharID, file); + newXS(strcpy(buf, "CheckInstanceByCharID"), XS__CheckInstanceByCharID, file); newXS(strcpy(buf, "SendMail"), XS__SendMail, file); newXS(strcpy(buf, "SetRunning"), XS__SetRunning, file); newXS(strcpy(buf, "activespeakactivity"), XS__activespeakactivity, file); @@ -3951,6 +4131,7 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "clearspawntimers"), XS__clearspawntimers, file); newXS(strcpy(buf, "collectitems"), XS__collectitems, file); newXS(strcpy(buf, "completedtasksinset"), XS__completedtasksinset, file); + newXS(strcpy(buf, "countitem"), XS__countitem, file); newXS(strcpy(buf, "createdoor"), XS__CreateDoor, file); newXS(strcpy(buf, "creategroundobject"), XS__CreateGroundObject, file); newXS(strcpy(buf, "creategroundobjectfrommodel"), XS__CreateGroundObjectFromModel, file); @@ -3990,18 +4171,27 @@ EXTERN_C XS(boot_quest) { newXS(strcpy(buf, "follow"), XS__follow, file); newXS(strcpy(buf, "forcedoorclose"), XS__forcedoorclose, file); newXS(strcpy(buf, "forcedooropen"), XS__forcedooropen, file); + newXS(strcpy(buf, "getcharidbyname"), XS__getcharidbyname, file); + newXS(strcpy(buf, "getcurrencyid"), XS__getcurrencyid, file); newXS(strcpy(buf, "getinventoryslotid"), XS__getinventoryslotid, file); + newXS(strcpy(buf, "getitemname"), XS__getitemname, file); newXS(strcpy(buf, "getItemName"), XS_qc_getItemName, file); + newXS(strcpy(buf, "getnpcnamebyid"), XS__getnpcnamebyid, file); newXS(strcpy(buf, "get_spawn_condition"), XS__get_spawn_condition, file); + newXS(strcpy(buf, "getcharnamebyid"), XS__getcharnamebyid, file); newXS(strcpy(buf, "getguildnamebyid"), XS__getguildnamebyid, file); newXS(strcpy(buf, "getguildidbycharid"), XS__getguildidbycharid, file); newXS(strcpy(buf, "getgroupidbycharid"), XS__getgroupidbycharid, file); newXS(strcpy(buf, "getraididbycharid"), XS__getraididbycharid, file); + newXS(strcpy(buf, "getracename"), XS__getracename, file); + newXS(strcpy(buf, "getspellname"), XS__getspellname, file); + newXS(strcpy(buf, "getskillname"), XS__getskillname, file); newXS(strcpy(buf, "getlevel"), XS__getlevel, file); newXS(strcpy(buf, "getplayerburiedcorpsecount"), XS__getplayerburiedcorpsecount, file); newXS(strcpy(buf, "getplayercorpsecount"), XS__getplayercorpsecount, file); newXS(strcpy(buf, "getplayercorpsecountbyzoneid"), XS__getplayercorpsecountbyzoneid, file); newXS(strcpy(buf, "gettaskactivitydonecount"), XS__gettaskactivitydonecount, file); + newXS(strcpy(buf, "gettaskname"), XS__gettaskname, file); newXS(strcpy(buf, "givecash"), XS__givecash, file); newXS(strcpy(buf, "gmmove"), XS__gmmove, file); newXS(strcpy(buf, "gmsay"), XS__gmsay, file); diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index 69e262199..d680460c7 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -393,6 +393,18 @@ bool lua_is_disc_tome(int item_id) { return quest_manager.isdisctome(item_id); } +std::string lua_get_race_name(uint32 race_id) { + return quest_manager.getracename(race_id); +} + +std::string lua_get_spell_name(uint32 spell_id) { + return quest_manager.getspellname(spell_id); +} + +std::string lua_get_skill_name(int skill_id) { + return quest_manager.getskillname(skill_id); +} + void lua_safe_move() { quest_manager.safemove(); } @@ -729,6 +741,10 @@ bool lua_is_task_appropriate(int task) { return quest_manager.istaskappropriate(task); } +std::string lua_get_task_name(uint32 task_id) { + return quest_manager.gettaskname(task_id); +} + void lua_popup(const char *title, const char *text, uint32 id, uint32 buttons, uint32 duration) { quest_manager.popup(title, text, id, buttons, duration); } @@ -781,6 +797,10 @@ int lua_collect_items(uint32 item_id, bool remove) { return quest_manager.collectitems(item_id, remove); } +int lua_count_item(uint32 item_id) { + return quest_manager.countitem(item_id); +} + void lua_update_spawn_timer(uint32 id, uint32 new_time) { quest_manager.UpdateSpawnTimer(id, new_time); } @@ -803,6 +823,10 @@ std::string lua_item_link(int item_id) { return quest_manager.varlink(text, item_id); } +std::string lua_get_item_name(uint32 item_id) { + return quest_manager.getitemname(item_id); +} + std::string lua_say_link(const char *phrase, bool silent, const char *link_name) { char text[256] = { 0 }; strncpy(text, phrase, 255); @@ -854,6 +878,18 @@ bool lua_delete_data(std::string bucket_key) { return DataBucket::DeleteData(bucket_key); } +const char *lua_get_char_name_by_id(uint32 char_id) { + return database.GetCharNameByID(char_id); +} + +uint32 lua_get_char_id_by_name(const char* name) { + return quest_manager.getcharidbyname(name); +} + +int lua_get_currency_id(uint32 item_id) { + return quest_manager.getcurrencyid(item_id); +} + const char *lua_get_guild_name_by_id(uint32 guild_id) { return quest_manager.getguildnamebyid(guild_id); } @@ -866,6 +902,10 @@ int lua_get_group_id_by_char_id(uint32 char_id) { return database.GetGroupIDByCharID(char_id); } +const char *lua_get_npc_name_by_id(uint32 npc_id) { + return quest_manager.getnpcnamebyid(npc_id); +} + int lua_get_raid_id_by_char_id(uint32 char_id) { return database.GetRaidIDByCharID(char_id); } @@ -922,6 +962,10 @@ void lua_remove_from_instance_by_char_id(uint32 instance_id, uint32 char_id) { quest_manager.RemoveFromInstanceByCharID(instance_id, char_id); } +bool lua_check_instance_by_char_id(uint32 instance_id, uint32 char_id) { + return quest_manager.CheckInstanceByCharID(instance_id, char_id); +} + void lua_remove_all_from_instance(uint32 instance_id) { quest_manager.RemoveAllFromInstance(instance_id); } @@ -1644,6 +1688,9 @@ luabind::scope lua_register_general() { luabind::def("depop_zone", &lua_depop_zone), luabind::def("repop_zone", &lua_repop_zone), luabind::def("is_disc_tome", &lua_is_disc_tome), + luabind::def("get_race_name", (std::string(*)(uint16))&lua_get_race_name), + luabind::def("get_spell_name", (std::string(*)(uint32))&lua_get_spell_name), + luabind::def("get_skill_name", (std::string(*)(int))&lua_get_skill_name), luabind::def("safe_move", &lua_safe_move), luabind::def("rain", &lua_rain), luabind::def("snow", &lua_snow), @@ -1711,6 +1758,7 @@ luabind::scope lua_register_general() { luabind::def("active_tasks_in_set", &lua_active_tasks_in_set), luabind::def("completed_tasks_in_set", &lua_completed_tasks_in_set), luabind::def("is_task_appropriate", &lua_is_task_appropriate), + luabind::def("get_task_name", (std::string(*)(uint32))&lua_get_task_name), luabind::def("popup", &lua_popup), luabind::def("clear_spawn_timers", &lua_clear_spawn_timers), luabind::def("zone_emote", &lua_zone_emote), @@ -1724,11 +1772,13 @@ luabind::scope lua_register_general() { luabind::def("create_door", &lua_create_door), luabind::def("modify_npc_stat", &lua_modify_npc_stat), luabind::def("collect_items", &lua_collect_items), + luabind::def("count_item", &lua_count_item), luabind::def("update_spawn_timer", &lua_update_spawn_timer), luabind::def("merchant_set_item", (void(*)(uint32,uint32))&lua_merchant_set_item), luabind::def("merchant_set_item", (void(*)(uint32,uint32,uint32))&lua_merchant_set_item), luabind::def("merchant_count_item", &lua_merchant_count_item), luabind::def("item_link", &lua_item_link), + luabind::def("get_item_name", (std::string(*)(uint32))&lua_get_item_name), luabind::def("say_link", (std::string(*)(const char*,bool,const char*))&lua_say_link), luabind::def("say_link", (std::string(*)(const char*,bool))&lua_say_link), luabind::def("say_link", (std::string(*)(const char*))&lua_say_link), @@ -1739,9 +1789,13 @@ luabind::scope lua_register_general() { luabind::def("set_data", (void(*)(std::string, std::string))&lua_set_data), luabind::def("set_data", (void(*)(std::string, std::string, std::string))&lua_set_data), luabind::def("delete_data", (bool(*)(std::string))&lua_delete_data), + luabind::def("get_char_name_by_id", &lua_get_char_name_by_id), + luabind::def("get_char_id_by_name", (uint32(*)(const char*))&lua_get_char_id_by_name), + luabind::def("get_currency_id", &lua_get_currency_id), luabind::def("get_guild_name_by_id", &lua_get_guild_name_by_id), luabind::def("get_guild_id_by_char_id", &lua_get_guild_id_by_char_id), luabind::def("get_group_id_by_char_id", &lua_get_group_id_by_char_id), + luabind::def("get_npc_name_by_id", &lua_get_npc_name_by_id), luabind::def("get_raid_id_by_char_id", &lua_get_raid_id_by_char_id), luabind::def("create_instance", &lua_create_instance), luabind::def("destroy_instance", &lua_destroy_instance), @@ -1757,6 +1811,7 @@ luabind::scope lua_register_general() { luabind::def("assign_raid_to_instance", &lua_assign_raid_to_instance), luabind::def("remove_from_instance", &lua_remove_from_instance), luabind::def("remove_from_instance_by_char_id", &lua_remove_from_instance_by_char_id), + luabind::def("check_instance_by_char_id", (bool(*)(uint16, uint32))&lua_check_instance_by_char_id), luabind::def("remove_all_from_instance", &lua_remove_all_from_instance), luabind::def("flag_instance_by_group_leader", &lua_flag_instance_by_group_leader), luabind::def("flag_instance_by_raid_leader", &lua_flag_instance_by_raid_leader), diff --git a/zone/main.cpp b/zone/main.cpp index b82f37484..a04884286 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -67,6 +67,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #include "../common/event/timer.h" #include "../common/net/eqstream.h" #include "../common/net/servertalk_server.h" +#include "../common/content/world_content_service.h" #include #include @@ -93,9 +94,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA #endif -#include "../common/content/world_content_service.h" - - volatile bool RunLoops = true; extern volatile bool is_zone_loaded; @@ -609,19 +607,19 @@ int main(int argc, char** argv) { return 0; } +void Shutdown() +{ + Zone::Shutdown(true); + LogInfo("Shutting down..."); + LogSys.CloseFileLogs(); + EQ::EventLoop::Get().Shutdown(); +} + void CatchSignal(int sig_num) { #ifdef _WINDOWS LogInfo("Recieved signal: [{}]", sig_num); #endif - RunLoops = false; -} - -void Shutdown() -{ - Zone::Shutdown(true); - RunLoops = false; - LogInfo("Shutting down..."); - LogSys.CloseFileLogs(); + Shutdown(); } /* Update Window Title with relevant information */ diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 708c03fa6..c4fe03bf6 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -747,6 +747,13 @@ void Client::AI_Process() RunTo(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z); } } + if (RuleB(Character, ProcessFearedProximity) && proximity_timer.Check()) { + entity_list.ProcessMove(this, glm::vec3(GetX(), GetY(), GetZ())); + if (RuleB(TaskSystem, EnableTaskSystem) && RuleB(TaskSystem, EnableTaskProximity)) + ProcessTaskProximities(GetX(), GetY(), GetZ()); + + m_Proximity = glm::vec3(GetX(), GetY(), GetZ()); + } return; } diff --git a/zone/npc.cpp b/zone/npc.cpp index 44cdd1344..e65da4cfb 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -962,7 +962,7 @@ void NPC::Depop(bool StartSpawnTimer) { } bool NPC::DatabaseCastAccepted(int spell_id) { - for (int i=0; i < 12; i++) { + for (int i=0; i < EFFECT_COUNT; i++) { switch(spells[spell_id].effectid[i]) { case SE_Stamina: { if(IsEngaged() && GetHPRatio() < 100) diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index 853623e3b..de09b8193 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -906,6 +906,31 @@ bool QuestManager::isdisctome(int item_id) { return(true); } +std::string QuestManager::getracename(uint16 race_id) { + return GetRaceIDName(race_id); +} + +std::string QuestManager::getspellname(uint32 spell_id) { + if (!IsValidSpell(spell_id)) { + return "INVALID SPELL ID IN GETSPELLNAME"; + } + + std::string spell_name = GetSpellName(spell_id); + return spell_name; +} + +std::string QuestManager::getskillname(int skill_id) { + if (skill_id >= 0 && skill_id < EQEmu::skills::SkillCount) { + std::map Skills = EQEmu::skills::GetSkillTypeMap(); + for (auto skills_iter : Skills) { + if (skill_id == skills_iter.first) { + return skills_iter.second; + } + } + } + return std::string(); +} + void QuestManager::safemove() { QuestManagerCurrentQuestVars(); if (initiator && initiator->IsClient()) @@ -2432,6 +2457,16 @@ bool QuestManager::istaskappropriate(int task) { return false; } +std::string QuestManager::gettaskname(uint32 task_id) { + QuestManagerCurrentQuestVars(); + + if (RuleB(TaskSystem, EnableTaskSystem)) { + return taskmanager->GetTaskName(task_id); + } + + return std::string(); +} + void QuestManager::clearspawntimers() { if(!zone) return; @@ -2580,6 +2615,32 @@ int QuestManager::collectitems(uint32 item_id, bool remove) return quantity; } +int QuestManager::countitem(uint32 item_id) { + QuestManagerCurrentQuestVars(); + int quantity = 0; + EQEmu::ItemInstance *item = nullptr; + static const int16 slots[][2] = { + { EQEmu::invslot::POSSESSIONS_BEGIN, EQEmu::invslot::POSSESSIONS_END }, + { EQEmu::invbag::GENERAL_BAGS_BEGIN, EQEmu::invbag::GENERAL_BAGS_END }, + { EQEmu::invbag::CURSOR_BAG_BEGIN, EQEmu::invbag::CURSOR_BAG_END}, + { EQEmu::invslot::BANK_BEGIN, EQEmu::invslot::BANK_END }, + { EQEmu::invbag::BANK_BAGS_BEGIN, EQEmu::invbag::BANK_BAGS_END }, + { EQEmu::invslot::SHARED_BANK_BEGIN, EQEmu::invslot::SHARED_BANK_END }, + { EQEmu::invbag::SHARED_BANK_BAGS_BEGIN, EQEmu::invbag::SHARED_BANK_BAGS_END }, + }; + const size_t size = sizeof(slots) / sizeof(slots[0]); + for (int slot_index = 0; slot_index < size; ++slot_index) { + for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) { + item = initiator->GetInv().GetItem(slot_id); + if (item && item->GetID() == item_id) { + quantity += item->IsStackable() ? item->GetCharges() : 1; + } + } + } + + return quantity; +} + void QuestManager::UpdateSpawnTimer(uint32 id, uint32 newTime) { bool found = false; @@ -2670,6 +2731,23 @@ const char* QuestManager::varlink(char* perltext, int item_id) { return perltext; } +std::string QuestManager::getitemname(uint32 item_id) { + const EQEmu::ItemData* item_data = database.GetItem(item_id); + if (!item_data) { + return "INVALID ITEM ID IN GETITEMNAME"; + } + + std::string item_name = item_data->Name; + return item_name; +} + +const char *QuestManager::getnpcnamebyid(uint32 npc_id) { + if (npc_id > 0) { + return database.GetNPCNameByID(npc_id); + } + return ""; +} + uint16 QuestManager::CreateInstance(const char *zone, int16 version, uint32 duration) { QuestManagerCurrentQuestVars(); @@ -2811,6 +2889,10 @@ void QuestManager::RemoveFromInstanceByCharID(uint16 instance_id, uint32 char_id database.RemoveClientFromInstance(instance_id, char_id); } +bool QuestManager::CheckInstanceByCharID(uint16 instance_id, uint32 char_id) { + return database.CharacterInInstanceGroup(instance_id, char_id); +} + void QuestManager::RemoveAllFromInstance(uint16 instance_id) { QuestManagerCurrentQuestVars(); @@ -2869,6 +2951,28 @@ std::string QuestManager::saylink(char *saylink_text, bool silent, const char *l return EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink_text, silent, link_name); } +const char* QuestManager::getcharnamebyid(uint32 char_id) { + if (char_id > 0) { + return database.GetCharNameByID(char_id); + } + return ""; +} + +uint32 QuestManager::getcharidbyname(const char* name) { + return database.GetCharacterID(name); +} + +int QuestManager::getcurrencyid(uint32 item_id) { + auto iter = zone->AlternateCurrencies.begin(); + while (iter != zone->AlternateCurrencies.end()) { + if (item_id == (*iter).item_id) { + return (*iter).id; + } + ++iter; + } + return 0; +} + const char* QuestManager::getguildnamebyid(int guild_id) { if (guild_id > 0) return guild_mgr.GetGuildName(guild_id); diff --git a/zone/questmgr.h b/zone/questmgr.h index 2aa1df109..caed775cc 100644 --- a/zone/questmgr.h +++ b/zone/questmgr.h @@ -107,6 +107,9 @@ public: void level(int newlevel); void traindisc(int discipline_tome_item_id); bool isdisctome(int item_id); + std::string getracename(uint16 race_id); + std::string getspellname(uint32 spell_id); + std::string getskillname(int skill_id); void safemove(); void rain(int weather); void snow(int weather); @@ -213,12 +216,15 @@ public: int activetasksinset(int taskset); int completedtasksinset(int taskset); bool istaskappropriate(int task); + std::string gettaskname(uint32 task_id); void clearspawntimers(); void ze(int type, const char *str); void we(int type, const char *str); int getlevel(uint8 type); int collectitems(uint32 item_id, bool remove); int collectitems_processSlot(int16 slot_id, uint32 item_id, bool remove); + int countitem(uint32 item_id); + std::string getitemname(uint32 item_id); void enabletitle(int titleset); bool checktitle(int titlecheck); void removetitle(int titlecheck); @@ -242,6 +248,7 @@ public: void AssignRaidToInstance(uint16 instance_id); void RemoveFromInstance(uint16 instance_id); void RemoveFromInstanceByCharID(uint16 instance_id, uint32 char_id); + bool CheckInstanceByCharID(uint16 instance_id, uint32 char_id); //void RemoveGroupFromInstance(uint16 instance_id); //potentially useful but not implmented at this time. //void RemoveRaidFromInstance(uint16 instance_id); //potentially useful but not implmented at this time. void RemoveAllFromInstance(uint16 instance_id); @@ -250,9 +257,13 @@ public: void FlagInstanceByRaidLeader(uint32 zone, int16 version); const char* varlink(char* perltext, int item_id); std::string saylink(char *saylink_text, bool silent, const char *link_name); + const char* getcharnamebyid(uint32 char_id); + uint32 getcharidbyname(const char* name); + int getcurrencyid(uint32 item_id); const char* getguildnamebyid(int guild_id); int getguildidbycharid(uint32 char_id); int getgroupidbycharid(uint32 char_id); + const char* getnpcnamebyid(uint32 npc_id); int getraididbycharid(uint32 char_id); void SetRunning(bool val); bool IsRunning(); diff --git a/zone/spells.cpp b/zone/spells.cpp index 5b8ee33f8..be1419caa 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3426,6 +3426,8 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r bool isproc, int level_override) { + bool is_damage_or_lifetap_spell = IsDamageSpell(spell_id) || IsLifetapSpell(spell_id); + // well we can't cast a spell on target without a target if(!spelltar) { @@ -3967,9 +3969,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r // send to people in the area, ignoring caster and target //live dosent send this to anybody but the caster //entity_list.QueueCloseClients(spelltar, action_packet, true, 200, this, true, spelltar->IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS); - - // TEMPORARY - this is the message for the spell. - // double message on effects that use ChangeHP - working on this message_packet = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); CombatDamage_Struct *cd = (CombatDamage_Struct *)message_packet->pBuffer; cd->target = action->target; @@ -3980,7 +3979,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r cd->hit_heading = action->hit_heading; cd->hit_pitch = action->hit_pitch; cd->damage = 0; - if(!IsEffectInSpell(spell_id, SE_BindAffinity)){ + if(!IsEffectInSpell(spell_id, SE_BindAffinity) && !is_damage_or_lifetap_spell){ entity_list.QueueCloseClients( spelltar, /* Sender */ message_packet, /* Packet */ @@ -3990,6 +3989,19 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r true, /* Packet ACK */ (spelltar->IsClient() ? FilterPCSpells : FilterNPCSpells) /* Message Filter Type: (8 or 9) */ ); + } else if (is_damage_or_lifetap_spell && + (IsClient() || + (HasOwner() && + GetOwner()->IsClient() + ) + ) + ) { + (HasOwner() ? GetOwner() : this)->CastToClient()->QueuePacket( + message_packet, + true, + Mob::CLIENT_CONNECTINGALL, + (spelltar->IsClient() ? FilterPCSpells : FilterNPCSpells) + ); } safe_delete(action_packet); safe_delete(message_packet); diff --git a/zone/tasks.cpp b/zone/tasks.cpp index bbee65c1e..882b9ea3e 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -955,6 +955,17 @@ bool TaskManager::AppropriateLevel(int TaskID, int PlayerLevel) { } +std::string TaskManager::GetTaskName(uint32 task_id) +{ + if (task_id > 0 && task_id < MAXTASKS) { + if (Tasks[task_id] != nullptr) { + return Tasks[task_id]->Title; + } + } + + return std::string(); +} + int TaskManager::GetTaskMinLevel(int TaskID) { if (Tasks[TaskID]->MinLevel) diff --git a/zone/tasks.h b/zone/tasks.h index 0bc45c146..48fc8e2cc 100644 --- a/zone/tasks.h +++ b/zone/tasks.h @@ -299,6 +299,7 @@ public: bool AppropriateLevel(int TaskID, int PlayerLevel); int GetTaskMinLevel(int TaskID); int GetTaskMaxLevel(int TaskID); + std::string GetTaskName(uint32 task_id); void TaskSetSelector(Client *c, ClientTaskState *state, Mob *mob, int TaskSetID); void TaskQuestSetSelector(Client *c, ClientTaskState *state, Mob *mob, int count, int *tasks); // task list provided by QuestManager (perl/lua) void SendActiveTasksToClient(Client *c, bool TaskComplete=false); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 52338b6c8..71041924f 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -57,7 +57,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA extern EntityList entity_list; extern Zone* zone; extern volatile bool is_zone_loaded; -extern void CatchSignal(int); +extern void Shutdown(); extern WorldServer worldserver; extern PetitionList petition_list; extern uint32 numclients; @@ -192,8 +192,15 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) if (pack->size != sizeof(ServerConnectInfo)) break; ServerConnectInfo* sci = (ServerConnectInfo*)pack->pBuffer; - LogInfo("World assigned Port: [{}] for this zone", sci->port); - ZoneConfig::SetZonePort(sci->port); + + if (sci->port == 0) { + LogCritical("World did not have a port to assign from this server, the port range was not large enough."); + Shutdown(); + } + else { + LogInfo("World assigned Port: [{}] for this zone", sci->port); + ZoneConfig::SetZonePort(sci->port); + } break; } case ServerOP_ChannelMessage: { @@ -482,7 +489,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) } case ServerOP_ShutdownAll: { entity_list.Save(); - CatchSignal(2); + Shutdown(); break; } case ServerOP_ZoneShutdown: { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index d290ff7fc..e27ff5636 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -3708,7 +3708,7 @@ void ZoneDatabase::LoadBuffs(Client *client) if (!IsValidSpell(buffs[index].spellid)) continue; - for (int effectIndex = 0; effectIndex < 12; ++effectIndex) { + for (int effectIndex = 0; effectIndex < EFFECT_COUNT; ++effectIndex) { if (spells[buffs[index].spellid].effectid[effectIndex] == SE_Charm) { buffs[index].spellid = SPELL_UNKNOWN;