mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-31 17:26:30 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f68e4a41a | |||
| e103422ca5 | |||
| 0cbfad975d | |||
| 2a20c69c69 | |||
| de2dfc1a7e | |||
| bad631df59 | |||
| e8f1aa253a | |||
| 889e57a5af | |||
| 5cfdeb928e | |||
| 7519b0225e | |||
| 04fdc54522 | |||
| f39155952f | |||
| 5acc181d64 | |||
| 2ae0b7dd3e | |||
| b0d4f095ef | |||
| 7c7a88650b | |||
| afaa8f4100 | |||
| 0d72295cc9 | |||
| 9d4f231619 | |||
| fcb0a47280 | |||
| 1e50f19f7e | |||
| 33bb5aa8e5 | |||
| 6a668f8aa5 | |||
| df499b22ab | |||
| 7bc00cb466 | |||
| 51f6108aab | |||
| c13f9f80d9 | |||
| 443abf9199 | |||
| 1556e05b2f | |||
| 1d645aa5f6 | |||
| 9f42da5bad | |||
| 4a8222f243 | |||
| db4c515853 | |||
| 462656a201 | |||
| ddd98be383 |
@@ -1,3 +1,97 @@
|
|||||||
|
## [22.4.4] - 02/24/2023
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Add Caster Range Command, and IsValidSpellRange Checks ([#2942](https://github.com/EQEmu/Server/pull/2942)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-23
|
||||||
|
* Cleanup BotDatabase::LoadBuffs ([#2981](https://github.com/EQEmu/Server/pull/2981)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-24
|
||||||
|
* Verify Bots Group Integrity on join ([#2980](https://github.com/EQEmu/Server/pull/2980)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-23
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
* Cleanup #peekinv Command ([#2969](https://github.com/EQEmu/Server/pull/2969)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-23
|
||||||
|
|
||||||
|
### Doors
|
||||||
|
|
||||||
|
* Fix doors triggering invalid zone fetches of dest_zone of "none" ([#2985](https://github.com/EQEmu/Server/pull/2985)) ([Akkadius](https://github.com/Akkadius)) 2023-02-24
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Adjust database manifest to include .sql extension ([Akkadius](https://github.com/Akkadius)) 2023-02-25
|
||||||
|
* Correct Mend reuse time and add reduction support. ([#2972](https://github.com/EQEmu/Server/pull/2972)) ([nytmyr](https://github.com/nytmyr)) 2023-02-23
|
||||||
|
* Fix Beneficial Target of Target procs ([#2987](https://github.com/EQEmu/Server/pull/2987)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-24
|
||||||
|
* Fix for undefined MySQL library behavior. ([#2834](https://github.com/EQEmu/Server/pull/2834)) ([KimLS](https://github.com/KimLS)) 2023-02-25
|
||||||
|
|
||||||
|
### Pathing
|
||||||
|
|
||||||
|
* Improve roambox logic ([#2983](https://github.com/EQEmu/Server/pull/2983)) ([Akkadius](https://github.com/Akkadius)) 2023-02-24
|
||||||
|
* More z-clip improvements, Wurm and Spectral Iksar race adjustments ([#2988](https://github.com/EQEmu/Server/pull/2988)) ([Akkadius](https://github.com/Akkadius)) 2023-02-25
|
||||||
|
* Smoother pathing z-correction ([#2982](https://github.com/EQEmu/Server/pull/2982)) ([Akkadius](https://github.com/Akkadius)) 2023-02-24
|
||||||
|
|
||||||
|
### Player Events
|
||||||
|
|
||||||
|
* Add QS processing, mutex tweaks ([#2984](https://github.com/EQEmu/Server/pull/2984)) ([Akkadius](https://github.com/Akkadius)) 2023-02-25
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add IsAutoAttackEnabled() to Perl/Lua ([#2979](https://github.com/EQEmu/Server/pull/2979)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-23
|
||||||
|
* Add IsAutoFireEnabled() to Perl/Lua ([#2978](https://github.com/EQEmu/Server/pull/2978)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-23
|
||||||
|
* Fix EVENT_TIMER crash when entity is no longer available ([#2986](https://github.com/EQEmu/Server/pull/2986)) ([Akkadius](https://github.com/Akkadius)) 2023-02-24
|
||||||
|
|
||||||
|
### Scaling
|
||||||
|
|
||||||
|
* Add support for zone ID and instance version to NPC Scaling ([#2968](https://github.com/EQEmu/Server/pull/2968)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-25
|
||||||
|
|
||||||
|
### Tradeskills
|
||||||
|
|
||||||
|
* Fix for Lore Conflict ([#2977](https://github.com/EQEmu/Server/pull/2977)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-24
|
||||||
|
|
||||||
|
## [22.4.3] - 02/21/2023
|
||||||
|
|
||||||
|
### Bots
|
||||||
|
|
||||||
|
* Change HasBotItem(item_id) to return slot_id instead of bool. ([#2966](https://github.com/EQEmu/Server/pull/2966)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-19
|
||||||
|
* Change SaveTimers to Replace instead of Insert. ([#2951](https://github.com/EQEmu/Server/pull/2951)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-18
|
||||||
|
* Fix output of ^spells while ^Enforcespellsettings is enabled ([#2959](https://github.com/EQEmu/Server/pull/2959)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-18
|
||||||
|
|
||||||
|
### Crash
|
||||||
|
|
||||||
|
* Fix crash with EVENT_UNEQUIP_ITEM_BOT ([#2973](https://github.com/EQEmu/Server/pull/2973)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-20
|
||||||
|
* Fix world crash in player event processing ([#2960](https://github.com/EQEmu/Server/pull/2960)) ([Akkadius](https://github.com/Akkadius)) 2023-02-18
|
||||||
|
|
||||||
|
### Database
|
||||||
|
|
||||||
|
* Address deadlock in player events ([#2974](https://github.com/EQEmu/Server/pull/2974)) ([Akkadius](https://github.com/Akkadius)) 2023-02-21
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix MIR LDoN Theme Items on LDoN Merchants ([#2971](https://github.com/EQEmu/Server/pull/2971)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-20
|
||||||
|
* Fix OOCMute not functioning ([#2970](https://github.com/EQEmu/Server/pull/2970)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-20
|
||||||
|
|
||||||
|
### Pathing
|
||||||
|
|
||||||
|
* Improvements to z-clipping, z-recovery and z-calculations ([#2975](https://github.com/EQEmu/Server/pull/2975)) ([Akkadius](https://github.com/Akkadius)) 2023-02-21
|
||||||
|
|
||||||
|
### Pets
|
||||||
|
|
||||||
|
* Client Pet summoned by NPC should not change guard location. ([#2967](https://github.com/EQEmu/Server/pull/2967)) ([noudess](https://github.com/noudess)) 2023-02-19
|
||||||
|
|
||||||
|
### Player Events
|
||||||
|
|
||||||
|
* Create new event ITEM_CREATION ([#2944](https://github.com/EQEmu/Server/pull/2944)) ([Akkadius](https://github.com/Akkadius)) 2023-02-18
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add client->SignalClient() overload to Perl ([#2963](https://github.com/EQEmu/Server/pull/2963)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-19
|
||||||
|
* Fix Perl SetSimpleRoamBox Overloads ([#2961](https://github.com/EQEmu/Server/pull/2961)) ([Kinglykrab](https://github.com/Kinglykrab)) 2023-02-19
|
||||||
|
|
||||||
|
### Reload API
|
||||||
|
|
||||||
|
* Add world handlers for certain opcodes ([#2958](https://github.com/EQEmu/Server/pull/2958)) ([Akkadius](https://github.com/Akkadius)) 2023-02-18
|
||||||
|
|
||||||
|
### SQL
|
||||||
|
|
||||||
|
* Add date to optional Drakkin Guktan Faction Update ([#2965](https://github.com/EQEmu/Server/pull/2965)) ([joligario](https://github.com/joligario)) 2023-02-19
|
||||||
|
|
||||||
## [22.4.2] - 02/18/2023
|
## [22.4.2] - 02/18/2023
|
||||||
|
|
||||||
### Content
|
### Content
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
content_db.SetMysql(database.getMySQL());
|
content_db.SetMySQL(database);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogSys.SetDatabase(&database)
|
LogSys.SetDatabase(&database)
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ int main(int argc, char **argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
content_db.SetMysql(database.getMySQL());
|
content_db.SetMySQL(database);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogSys.SetDatabase(&database)
|
LogSys.SetDatabase(&database)
|
||||||
|
|||||||
+47
-41
@@ -34,7 +34,8 @@
|
|||||||
|
|
||||||
DBcore::DBcore()
|
DBcore::DBcore()
|
||||||
{
|
{
|
||||||
mysql_init(&mysql);
|
mysql = mysql_init(nullptr);
|
||||||
|
mysqlOwner = true;
|
||||||
pHost = nullptr;
|
pHost = nullptr;
|
||||||
pUser = nullptr;
|
pUser = nullptr;
|
||||||
pPassword = nullptr;
|
pPassword = nullptr;
|
||||||
@@ -42,6 +43,7 @@ DBcore::DBcore()
|
|||||||
pCompress = false;
|
pCompress = false;
|
||||||
pSSL = false;
|
pSSL = false;
|
||||||
pStatus = Closed;
|
pStatus = Closed;
|
||||||
|
m_mutex = new Mutex;
|
||||||
}
|
}
|
||||||
|
|
||||||
DBcore::~DBcore()
|
DBcore::~DBcore()
|
||||||
@@ -51,16 +53,10 @@ DBcore::~DBcore()
|
|||||||
* are re-using the default database connection pointer when we dont have an
|
* are re-using the default database connection pointer when we dont have an
|
||||||
* external configuration setup ex: (content_database)
|
* external configuration setup ex: (content_database)
|
||||||
*/
|
*/
|
||||||
std::string mysql_connection_host;
|
if (mysqlOwner) {
|
||||||
if (mysql.host) {
|
mysql_close(mysql);
|
||||||
mysql_connection_host = mysql.host;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (GetOriginHost() != mysql_connection_host) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mysql_close(&mysql);
|
|
||||||
safe_delete_array(pHost);
|
safe_delete_array(pHost);
|
||||||
safe_delete_array(pUser);
|
safe_delete_array(pUser);
|
||||||
safe_delete_array(pPassword);
|
safe_delete_array(pPassword);
|
||||||
@@ -70,17 +66,18 @@ DBcore::~DBcore()
|
|||||||
// Sends the MySQL server a keepalive
|
// Sends the MySQL server a keepalive
|
||||||
void DBcore::ping()
|
void DBcore::ping()
|
||||||
{
|
{
|
||||||
if (!MDatabase.trylock()) {
|
if (!m_mutex->trylock()) {
|
||||||
// well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive
|
// well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
mysql_ping(&mysql);
|
mysql_ping(mysql);
|
||||||
MDatabase.unlock();
|
m_mutex->unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
MySQLRequestResult DBcore::QueryDatabase(std::string query, bool retryOnFailureOnce)
|
MySQLRequestResult DBcore::QueryDatabase(std::string query, bool retryOnFailureOnce)
|
||||||
{
|
{
|
||||||
return QueryDatabase(query.c_str(), query.length(), retryOnFailureOnce);
|
auto r = QueryDatabase(query.c_str(), query.length(), retryOnFailureOnce);
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DBcore::DoesTableExist(std::string table_name)
|
bool DBcore::DoesTableExist(std::string table_name)
|
||||||
@@ -95,18 +92,16 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
|||||||
BenchTimer timer;
|
BenchTimer timer;
|
||||||
timer.reset();
|
timer.reset();
|
||||||
|
|
||||||
LockMutex lock(&MDatabase);
|
LockMutex lock(m_mutex);
|
||||||
|
|
||||||
// Reconnect if we are not connected before hand.
|
// Reconnect if we are not connected before hand.
|
||||||
if (pStatus != Connected) {
|
if (pStatus != Connected) {
|
||||||
Open();
|
Open();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// request query. != 0 indicates some kind of error.
|
// request query. != 0 indicates some kind of error.
|
||||||
if (mysql_real_query(&mysql, query, querylen) != 0) {
|
if (mysql_real_query(mysql, query, querylen) != 0) {
|
||||||
unsigned int errorNumber = mysql_errno(&mysql);
|
unsigned int errorNumber = mysql_errno(mysql);
|
||||||
|
|
||||||
if (errorNumber == CR_SERVER_GONE_ERROR) {
|
if (errorNumber == CR_SERVER_GONE_ERROR) {
|
||||||
pStatus = Error;
|
pStatus = Error;
|
||||||
@@ -130,26 +125,26 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
|||||||
|
|
||||||
auto errorBuffer = new char[MYSQL_ERRMSG_SIZE];
|
auto errorBuffer = new char[MYSQL_ERRMSG_SIZE];
|
||||||
|
|
||||||
snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
|
snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(mysql), mysql_error(mysql));
|
||||||
|
|
||||||
return MySQLRequestResult(nullptr, 0, 0, 0, 0, (uint32) mysql_errno(&mysql), errorBuffer);
|
return MySQLRequestResult(nullptr, 0, 0, 0, 0, (uint32) mysql_errno(mysql), errorBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto errorBuffer = new char[MYSQL_ERRMSG_SIZE];
|
auto errorBuffer = new char[MYSQL_ERRMSG_SIZE];
|
||||||
snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
|
snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(mysql), mysql_error(mysql));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Error logging
|
* Error logging
|
||||||
*/
|
*/
|
||||||
if (mysql_errno(&mysql) > 0 && strlen(query) > 0) {
|
if (mysql_errno(mysql) > 0 && strlen(query) > 0) {
|
||||||
LogMySQLError("[{}] [{}]\n[{}]", mysql_errno(&mysql), mysql_error(&mysql), query);
|
LogMySQLError("[{}] [{}]\n[{}]", mysql_errno(mysql), mysql_error(mysql), query);
|
||||||
}
|
}
|
||||||
|
|
||||||
return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql), errorBuffer);
|
return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(mysql), errorBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
// successful query. get results.
|
// successful query. get results.
|
||||||
MYSQL_RES *res = mysql_store_result(&mysql);
|
MYSQL_RES *res = mysql_store_result(mysql);
|
||||||
uint32 rowCount = 0;
|
uint32 rowCount = 0;
|
||||||
|
|
||||||
if (res != nullptr) {
|
if (res != nullptr) {
|
||||||
@@ -158,10 +153,10 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
|||||||
|
|
||||||
MySQLRequestResult requestResult(
|
MySQLRequestResult requestResult(
|
||||||
res,
|
res,
|
||||||
(uint32) mysql_affected_rows(&mysql),
|
(uint32) mysql_affected_rows(mysql),
|
||||||
rowCount,
|
rowCount,
|
||||||
(uint32) mysql_field_count(&mysql),
|
(uint32) mysql_field_count(mysql),
|
||||||
(uint32) mysql_insert_id(&mysql)
|
(uint32) mysql_insert_id(mysql)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
|
if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
|
||||||
@@ -207,7 +202,7 @@ uint32 DBcore::DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen)
|
|||||||
{
|
{
|
||||||
// No good reason to lock the DB, we only need it in the first place to check char encoding.
|
// No good reason to lock the DB, we only need it in the first place to check char encoding.
|
||||||
// LockMutex lock(&MDatabase);
|
// LockMutex lock(&MDatabase);
|
||||||
return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen);
|
return mysql_real_escape_string(mysql, tobuf, frombuf, fromlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DBcore::Open(
|
bool DBcore::Open(
|
||||||
@@ -222,7 +217,7 @@ bool DBcore::Open(
|
|||||||
bool iSSL
|
bool iSSL
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LockMutex lock(&MDatabase);
|
LockMutex lock(m_mutex);
|
||||||
safe_delete_array(pHost);
|
safe_delete_array(pHost);
|
||||||
safe_delete_array(pUser);
|
safe_delete_array(pUser);
|
||||||
safe_delete_array(pPassword);
|
safe_delete_array(pPassword);
|
||||||
@@ -242,13 +237,13 @@ bool DBcore::Open(uint32 *errnum, char *errbuf)
|
|||||||
if (errbuf) {
|
if (errbuf) {
|
||||||
errbuf[0] = 0;
|
errbuf[0] = 0;
|
||||||
}
|
}
|
||||||
LockMutex lock(&MDatabase);
|
LockMutex lock(m_mutex);
|
||||||
if (GetStatus() == Connected) {
|
if (GetStatus() == Connected) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (GetStatus() == Error) {
|
if (GetStatus() == Error) {
|
||||||
mysql_close(&mysql);
|
mysql_close(mysql);
|
||||||
mysql_init(&mysql); // Initialize structure again
|
mysql_init(mysql); // Initialize structure again
|
||||||
}
|
}
|
||||||
if (!pHost) {
|
if (!pHost) {
|
||||||
return false;
|
return false;
|
||||||
@@ -265,7 +260,7 @@ bool DBcore::Open(uint32 *errnum, char *errbuf)
|
|||||||
if (pSSL) {
|
if (pSSL) {
|
||||||
flags |= CLIENT_SSL;
|
flags |= CLIENT_SSL;
|
||||||
}
|
}
|
||||||
if (mysql_real_connect(&mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) {
|
if (mysql_real_connect(mysql, pHost, pUser, pPassword, pDatabase, pPort, 0, flags)) {
|
||||||
pStatus = Connected;
|
pStatus = Connected;
|
||||||
|
|
||||||
std::string connected_origin_host = pHost;
|
std::string connected_origin_host = pHost;
|
||||||
@@ -275,21 +270,16 @@ bool DBcore::Open(uint32 *errnum, char *errbuf)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (errnum) {
|
if (errnum) {
|
||||||
*errnum = mysql_errno(&mysql);
|
*errnum = mysql_errno(mysql);
|
||||||
}
|
}
|
||||||
if (errbuf) {
|
if (errbuf) {
|
||||||
snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
|
snprintf(errbuf, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(mysql), mysql_error(mysql));
|
||||||
}
|
}
|
||||||
pStatus = Error;
|
pStatus = Error;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DBcore::SetMysql(MYSQL *mysql)
|
|
||||||
{
|
|
||||||
DBcore::mysql = *mysql;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string &DBcore::GetOriginHost() const
|
const std::string &DBcore::GetOriginHost() const
|
||||||
{
|
{
|
||||||
return origin_host;
|
return origin_host;
|
||||||
@@ -299,3 +289,19 @@ void DBcore::SetOriginHost(const std::string &origin_host)
|
|||||||
{
|
{
|
||||||
DBcore::origin_host = origin_host;
|
DBcore::origin_host = origin_host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string DBcore::Escape(const std::string& s)
|
||||||
|
{
|
||||||
|
const std::size_t s_len = s.length();
|
||||||
|
std::vector<char> temp((s_len * 2) + 1, '\0');
|
||||||
|
mysql_real_escape_string(mysql, temp.data(), s.c_str(), s_len);
|
||||||
|
|
||||||
|
return temp.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DBcore::SetMutex(Mutex *mutex)
|
||||||
|
{
|
||||||
|
safe_delete(m_mutex);
|
||||||
|
|
||||||
|
DBcore::m_mutex = mutex;
|
||||||
|
}
|
||||||
|
|||||||
+14
-4
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
#include <mysql.h>
|
#include <mysql.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
class DBcore {
|
class DBcore {
|
||||||
public:
|
public:
|
||||||
@@ -27,16 +28,22 @@ public:
|
|||||||
void TransactionBegin();
|
void TransactionBegin();
|
||||||
void TransactionCommit();
|
void TransactionCommit();
|
||||||
void TransactionRollback();
|
void TransactionRollback();
|
||||||
|
std::string Escape(const std::string& s);
|
||||||
uint32 DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen);
|
uint32 DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen);
|
||||||
void ping();
|
void ping();
|
||||||
MYSQL *getMySQL() { return &mysql; }
|
|
||||||
void SetMysql(MYSQL *mysql);
|
|
||||||
|
|
||||||
const std::string &GetOriginHost() const;
|
const std::string &GetOriginHost() const;
|
||||||
void SetOriginHost(const std::string &origin_host);
|
void SetOriginHost(const std::string &origin_host);
|
||||||
|
|
||||||
bool DoesTableExist(std::string table_name);
|
bool DoesTableExist(std::string table_name);
|
||||||
|
|
||||||
|
void SetMySQL(const DBcore &o)
|
||||||
|
{
|
||||||
|
mysql = o.mysql;
|
||||||
|
mysqlOwner = false;
|
||||||
|
}
|
||||||
|
void SetMutex(Mutex *mutex);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool Open(
|
bool Open(
|
||||||
const char *iHost,
|
const char *iHost,
|
||||||
@@ -53,10 +60,13 @@ protected:
|
|||||||
private:
|
private:
|
||||||
bool Open(uint32 *errnum = nullptr, char *errbuf = nullptr);
|
bool Open(uint32 *errnum = nullptr, char *errbuf = nullptr);
|
||||||
|
|
||||||
MYSQL mysql;
|
MYSQL* mysql;
|
||||||
Mutex MDatabase;
|
bool mysqlOwner;
|
||||||
|
Mutex *m_mutex;
|
||||||
eStatus pStatus;
|
eStatus pStatus;
|
||||||
|
|
||||||
|
std::mutex m_query_lock{};
|
||||||
|
|
||||||
std::string origin_host;
|
std::string origin_host;
|
||||||
|
|
||||||
char *pHost;
|
char *pHost;
|
||||||
|
|||||||
@@ -113,7 +113,9 @@ bool PlayerEventLogs::IsEventEnabled(PlayerEvent::EventType event)
|
|||||||
// this processes any current player events on the queue
|
// this processes any current player events on the queue
|
||||||
void PlayerEventLogs::ProcessBatchQueue()
|
void PlayerEventLogs::ProcessBatchQueue()
|
||||||
{
|
{
|
||||||
|
m_batch_queue_lock.lock();
|
||||||
if (m_record_batch_queue.empty()) {
|
if (m_record_batch_queue.empty()) {
|
||||||
|
m_batch_queue_lock.unlock();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,7 +130,6 @@ void PlayerEventLogs::ProcessBatchQueue()
|
|||||||
);
|
);
|
||||||
|
|
||||||
// empty
|
// empty
|
||||||
m_batch_queue_lock.lock();
|
|
||||||
m_record_batch_queue = {};
|
m_record_batch_queue = {};
|
||||||
m_batch_queue_lock.unlock();
|
m_batch_queue_lock.unlock();
|
||||||
}
|
}
|
||||||
@@ -602,10 +603,10 @@ std::string PlayerEventLogs::GetDiscordPayloadFromEvent(const PlayerEvent::Playe
|
|||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
// general process function, used in world or UCS depending on rule Logging:PlayerEventsQSProcess
|
// general process function, used in world or QS depending on rule Logging:PlayerEventsQSProcess
|
||||||
void PlayerEventLogs::Process()
|
void PlayerEventLogs::Process()
|
||||||
{
|
{
|
||||||
if (m_process_batch_events_timer.Check()) {
|
if (m_process_batch_events_timer.Check() || m_record_batch_queue.size() >= RuleI(Logging, BatchPlayerEventProcessChunkSize)) {
|
||||||
ProcessBatchQueue();
|
ProcessBatchQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -699,6 +700,7 @@ void PlayerEventLogs::SetSettingsDefaults()
|
|||||||
m_settings[PlayerEvent::KILLED_NPC].event_enabled = 0;
|
m_settings[PlayerEvent::KILLED_NPC].event_enabled = 0;
|
||||||
m_settings[PlayerEvent::KILLED_NAMED_NPC].event_enabled = 1;
|
m_settings[PlayerEvent::KILLED_NAMED_NPC].event_enabled = 1;
|
||||||
m_settings[PlayerEvent::KILLED_RAID_NPC].event_enabled = 1;
|
m_settings[PlayerEvent::KILLED_RAID_NPC].event_enabled = 1;
|
||||||
|
m_settings[PlayerEvent::ITEM_CREATION].event_enabled = 1;
|
||||||
|
|
||||||
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
||||||
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
|
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ namespace PlayerEvent {
|
|||||||
KILLED_NPC,
|
KILLED_NPC,
|
||||||
KILLED_NAMED_NPC,
|
KILLED_NAMED_NPC,
|
||||||
KILLED_RAID_NPC,
|
KILLED_RAID_NPC,
|
||||||
|
ITEM_CREATION,
|
||||||
MAX // dont remove
|
MAX // dont remove
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -110,7 +111,8 @@ namespace PlayerEvent {
|
|||||||
"Possible Hack",
|
"Possible Hack",
|
||||||
"Killed NPC",
|
"Killed NPC",
|
||||||
"Killed Named NPC",
|
"Killed Named NPC",
|
||||||
"Killed Raid NPC"
|
"Killed Raid NPC",
|
||||||
|
"Item Creation"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic struct used by all events
|
// Generic struct used by all events
|
||||||
@@ -184,6 +186,40 @@ namespace PlayerEvent {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// used in Trade event
|
||||||
|
struct ItemCreationEvent {
|
||||||
|
int64 item_id;
|
||||||
|
std::string item_name;
|
||||||
|
uint16 to_slot;
|
||||||
|
int16 charges;
|
||||||
|
uint32 aug1;
|
||||||
|
uint32 aug2;
|
||||||
|
uint32 aug3;
|
||||||
|
uint32 aug4;
|
||||||
|
uint32 aug5;
|
||||||
|
uint32 aug6;
|
||||||
|
bool attuned;
|
||||||
|
|
||||||
|
// cereal
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &ar)
|
||||||
|
{
|
||||||
|
ar(
|
||||||
|
CEREAL_NVP(item_id),
|
||||||
|
CEREAL_NVP(item_name),
|
||||||
|
CEREAL_NVP(to_slot),
|
||||||
|
CEREAL_NVP(charges),
|
||||||
|
CEREAL_NVP(aug1),
|
||||||
|
CEREAL_NVP(aug2),
|
||||||
|
CEREAL_NVP(aug3),
|
||||||
|
CEREAL_NVP(aug4),
|
||||||
|
CEREAL_NVP(aug5),
|
||||||
|
CEREAL_NVP(aug6),
|
||||||
|
CEREAL_NVP(attuned)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// used in Trade event
|
// used in Trade event
|
||||||
struct TradeItem {
|
struct TradeItem {
|
||||||
int64 item_id;
|
int64 item_id;
|
||||||
|
|||||||
+1
-1
@@ -132,7 +132,7 @@ enum { //reuse times
|
|||||||
InstillDoubtReuseTime = 9,
|
InstillDoubtReuseTime = 9,
|
||||||
FishingReuseTime = 11,
|
FishingReuseTime = 11,
|
||||||
ForagingReuseTime = 50,
|
ForagingReuseTime = 50,
|
||||||
MendReuseTime = 290,
|
MendReuseTime = 360,
|
||||||
BashReuseTime = 5,
|
BashReuseTime = 5,
|
||||||
BackstabReuseTime = 9,
|
BackstabReuseTime = 9,
|
||||||
KickReuseTime = 5,
|
KickReuseTime = 5,
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ public:
|
|||||||
int32_t expansion_bitmask;
|
int32_t expansion_bitmask;
|
||||||
uint8_t enforce_spell_settings;
|
uint8_t enforce_spell_settings;
|
||||||
uint8_t archery_setting;
|
uint8_t archery_setting;
|
||||||
|
uint32_t caster_range;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string PrimaryKey()
|
static std::string PrimaryKey()
|
||||||
@@ -129,6 +130,7 @@ public:
|
|||||||
"expansion_bitmask",
|
"expansion_bitmask",
|
||||||
"enforce_spell_settings",
|
"enforce_spell_settings",
|
||||||
"archery_setting",
|
"archery_setting",
|
||||||
|
"caster_range",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,6 +187,7 @@ public:
|
|||||||
"expansion_bitmask",
|
"expansion_bitmask",
|
||||||
"enforce_spell_settings",
|
"enforce_spell_settings",
|
||||||
"archery_setting",
|
"archery_setting",
|
||||||
|
"caster_range",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -275,6 +278,7 @@ public:
|
|||||||
e.expansion_bitmask = -1;
|
e.expansion_bitmask = -1;
|
||||||
e.enforce_spell_settings = 0;
|
e.enforce_spell_settings = 0;
|
||||||
e.archery_setting = 0;
|
e.archery_setting = 0;
|
||||||
|
e.caster_range = 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -361,6 +365,7 @@ public:
|
|||||||
e.expansion_bitmask = static_cast<int32_t>(atoi(row[47]));
|
e.expansion_bitmask = static_cast<int32_t>(atoi(row[47]));
|
||||||
e.enforce_spell_settings = static_cast<uint8_t>(strtoul(row[48], nullptr, 10));
|
e.enforce_spell_settings = static_cast<uint8_t>(strtoul(row[48], nullptr, 10));
|
||||||
e.archery_setting = static_cast<uint8_t>(strtoul(row[49], nullptr, 10));
|
e.archery_setting = static_cast<uint8_t>(strtoul(row[49], nullptr, 10));
|
||||||
|
e.caster_range = static_cast<uint32_t>(strtoul(row[50], nullptr, 10));
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -443,6 +448,7 @@ public:
|
|||||||
v.push_back(columns[47] + " = " + std::to_string(e.expansion_bitmask));
|
v.push_back(columns[47] + " = " + std::to_string(e.expansion_bitmask));
|
||||||
v.push_back(columns[48] + " = " + std::to_string(e.enforce_spell_settings));
|
v.push_back(columns[48] + " = " + std::to_string(e.enforce_spell_settings));
|
||||||
v.push_back(columns[49] + " = " + std::to_string(e.archery_setting));
|
v.push_back(columns[49] + " = " + std::to_string(e.archery_setting));
|
||||||
|
v.push_back(columns[50] + " = " + std::to_string(e.caster_range));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -514,6 +520,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.expansion_bitmask));
|
v.push_back(std::to_string(e.expansion_bitmask));
|
||||||
v.push_back(std::to_string(e.enforce_spell_settings));
|
v.push_back(std::to_string(e.enforce_spell_settings));
|
||||||
v.push_back(std::to_string(e.archery_setting));
|
v.push_back(std::to_string(e.archery_setting));
|
||||||
|
v.push_back(std::to_string(e.caster_range));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -593,6 +600,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.expansion_bitmask));
|
v.push_back(std::to_string(e.expansion_bitmask));
|
||||||
v.push_back(std::to_string(e.enforce_spell_settings));
|
v.push_back(std::to_string(e.enforce_spell_settings));
|
||||||
v.push_back(std::to_string(e.archery_setting));
|
v.push_back(std::to_string(e.archery_setting));
|
||||||
|
v.push_back(std::to_string(e.caster_range));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
}
|
}
|
||||||
@@ -676,6 +684,7 @@ public:
|
|||||||
e.expansion_bitmask = static_cast<int32_t>(atoi(row[47]));
|
e.expansion_bitmask = static_cast<int32_t>(atoi(row[47]));
|
||||||
e.enforce_spell_settings = static_cast<uint8_t>(strtoul(row[48], nullptr, 10));
|
e.enforce_spell_settings = static_cast<uint8_t>(strtoul(row[48], nullptr, 10));
|
||||||
e.archery_setting = static_cast<uint8_t>(strtoul(row[49], nullptr, 10));
|
e.archery_setting = static_cast<uint8_t>(strtoul(row[49], nullptr, 10));
|
||||||
|
e.caster_range = static_cast<uint32_t>(strtoul(row[50], nullptr, 10));
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -750,6 +759,7 @@ public:
|
|||||||
e.expansion_bitmask = static_cast<int32_t>(atoi(row[47]));
|
e.expansion_bitmask = static_cast<int32_t>(atoi(row[47]));
|
||||||
e.enforce_spell_settings = static_cast<uint8_t>(strtoul(row[48], nullptr, 10));
|
e.enforce_spell_settings = static_cast<uint8_t>(strtoul(row[48], nullptr, 10));
|
||||||
e.archery_setting = static_cast<uint8_t>(strtoul(row[49], nullptr, 10));
|
e.archery_setting = static_cast<uint8_t>(strtoul(row[49], nullptr, 10));
|
||||||
|
e.caster_range = static_cast<uint32_t>(strtoul(row[50], nullptr, 10));
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,11 +16,14 @@
|
|||||||
#include "../../strings.h"
|
#include "../../strings.h"
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
|
|
||||||
|
|
||||||
class BaseNpcScaleGlobalBaseRepository {
|
class BaseNpcScaleGlobalBaseRepository {
|
||||||
public:
|
public:
|
||||||
struct NpcScaleGlobalBase {
|
struct NpcScaleGlobalBase {
|
||||||
int32_t type;
|
int32_t type;
|
||||||
int32_t level;
|
int32_t level;
|
||||||
|
uint32_t zone_id;
|
||||||
|
int32_t instance_version;
|
||||||
int32_t ac;
|
int32_t ac;
|
||||||
int32_t hp;
|
int32_t hp;
|
||||||
int32_t accuracy;
|
int32_t accuracy;
|
||||||
@@ -59,6 +62,8 @@ public:
|
|||||||
return {
|
return {
|
||||||
"type",
|
"type",
|
||||||
"level",
|
"level",
|
||||||
|
"zone_id",
|
||||||
|
"instance_version",
|
||||||
"ac",
|
"ac",
|
||||||
"hp",
|
"hp",
|
||||||
"accuracy",
|
"accuracy",
|
||||||
@@ -93,6 +98,8 @@ public:
|
|||||||
return {
|
return {
|
||||||
"type",
|
"type",
|
||||||
"level",
|
"level",
|
||||||
|
"zone_id",
|
||||||
|
"instance_version",
|
||||||
"ac",
|
"ac",
|
||||||
"hp",
|
"hp",
|
||||||
"accuracy",
|
"accuracy",
|
||||||
@@ -161,6 +168,8 @@ public:
|
|||||||
|
|
||||||
e.type = 0;
|
e.type = 0;
|
||||||
e.level = 0;
|
e.level = 0;
|
||||||
|
e.zone_id = 0;
|
||||||
|
e.instance_version = -1;
|
||||||
e.ac = 0;
|
e.ac = 0;
|
||||||
e.hp = 0;
|
e.hp = 0;
|
||||||
e.accuracy = 0;
|
e.accuracy = 0;
|
||||||
@@ -212,8 +221,9 @@ public:
|
|||||||
{
|
{
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"{} WHERE id = {} LIMIT 1",
|
"{} WHERE {} = {} LIMIT 1",
|
||||||
BaseSelect(),
|
BaseSelect(),
|
||||||
|
PrimaryKey(),
|
||||||
npc_scale_global_base_id
|
npc_scale_global_base_id
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -224,32 +234,34 @@ public:
|
|||||||
|
|
||||||
e.type = static_cast<int32_t>(atoi(row[0]));
|
e.type = static_cast<int32_t>(atoi(row[0]));
|
||||||
e.level = static_cast<int32_t>(atoi(row[1]));
|
e.level = static_cast<int32_t>(atoi(row[1]));
|
||||||
e.ac = static_cast<int32_t>(atoi(row[2]));
|
e.zone_id = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||||
e.hp = static_cast<int32_t>(atoi(row[3]));
|
e.instance_version = static_cast<int32_t>(atoi(row[3]));
|
||||||
e.accuracy = static_cast<int32_t>(atoi(row[4]));
|
e.ac = static_cast<int32_t>(atoi(row[4]));
|
||||||
e.slow_mitigation = static_cast<int32_t>(atoi(row[5]));
|
e.hp = static_cast<int32_t>(atoi(row[5]));
|
||||||
e.attack = static_cast<int32_t>(atoi(row[6]));
|
e.accuracy = static_cast<int32_t>(atoi(row[6]));
|
||||||
e.strength = static_cast<int32_t>(atoi(row[7]));
|
e.slow_mitigation = static_cast<int32_t>(atoi(row[7]));
|
||||||
e.stamina = static_cast<int32_t>(atoi(row[8]));
|
e.attack = static_cast<int32_t>(atoi(row[8]));
|
||||||
e.dexterity = static_cast<int32_t>(atoi(row[9]));
|
e.strength = static_cast<int32_t>(atoi(row[9]));
|
||||||
e.agility = static_cast<int32_t>(atoi(row[10]));
|
e.stamina = static_cast<int32_t>(atoi(row[10]));
|
||||||
e.intelligence = static_cast<int32_t>(atoi(row[11]));
|
e.dexterity = static_cast<int32_t>(atoi(row[11]));
|
||||||
e.wisdom = static_cast<int32_t>(atoi(row[12]));
|
e.agility = static_cast<int32_t>(atoi(row[12]));
|
||||||
e.charisma = static_cast<int32_t>(atoi(row[13]));
|
e.intelligence = static_cast<int32_t>(atoi(row[13]));
|
||||||
e.magic_resist = static_cast<int32_t>(atoi(row[14]));
|
e.wisdom = static_cast<int32_t>(atoi(row[14]));
|
||||||
e.cold_resist = static_cast<int32_t>(atoi(row[15]));
|
e.charisma = static_cast<int32_t>(atoi(row[15]));
|
||||||
e.fire_resist = static_cast<int32_t>(atoi(row[16]));
|
e.magic_resist = static_cast<int32_t>(atoi(row[16]));
|
||||||
e.poison_resist = static_cast<int32_t>(atoi(row[17]));
|
e.cold_resist = static_cast<int32_t>(atoi(row[17]));
|
||||||
e.disease_resist = static_cast<int32_t>(atoi(row[18]));
|
e.fire_resist = static_cast<int32_t>(atoi(row[18]));
|
||||||
e.corruption_resist = static_cast<int32_t>(atoi(row[19]));
|
e.poison_resist = static_cast<int32_t>(atoi(row[19]));
|
||||||
e.physical_resist = static_cast<int32_t>(atoi(row[20]));
|
e.disease_resist = static_cast<int32_t>(atoi(row[20]));
|
||||||
e.min_dmg = static_cast<int32_t>(atoi(row[21]));
|
e.corruption_resist = static_cast<int32_t>(atoi(row[21]));
|
||||||
e.max_dmg = static_cast<int32_t>(atoi(row[22]));
|
e.physical_resist = static_cast<int32_t>(atoi(row[22]));
|
||||||
e.hp_regen_rate = static_cast<int32_t>(atoi(row[23]));
|
e.min_dmg = static_cast<int32_t>(atoi(row[23]));
|
||||||
e.attack_delay = static_cast<int32_t>(atoi(row[24]));
|
e.max_dmg = static_cast<int32_t>(atoi(row[24]));
|
||||||
e.spell_scale = static_cast<int32_t>(atoi(row[25]));
|
e.hp_regen_rate = static_cast<int32_t>(atoi(row[25]));
|
||||||
e.heal_scale = static_cast<int32_t>(atoi(row[26]));
|
e.attack_delay = static_cast<int32_t>(atoi(row[26]));
|
||||||
e.special_abilities = row[27] ? row[27] : "";
|
e.spell_scale = static_cast<int32_t>(atoi(row[27]));
|
||||||
|
e.heal_scale = static_cast<int32_t>(atoi(row[28]));
|
||||||
|
e.special_abilities = row[29] ? row[29] : "";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -285,32 +297,34 @@ public:
|
|||||||
|
|
||||||
v.push_back(columns[0] + " = " + std::to_string(e.type));
|
v.push_back(columns[0] + " = " + std::to_string(e.type));
|
||||||
v.push_back(columns[1] + " = " + std::to_string(e.level));
|
v.push_back(columns[1] + " = " + std::to_string(e.level));
|
||||||
v.push_back(columns[2] + " = " + std::to_string(e.ac));
|
v.push_back(columns[2] + " = " + std::to_string(e.zone_id));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.hp));
|
v.push_back(columns[3] + " = " + std::to_string(e.instance_version));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.accuracy));
|
v.push_back(columns[4] + " = " + std::to_string(e.ac));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.slow_mitigation));
|
v.push_back(columns[5] + " = " + std::to_string(e.hp));
|
||||||
v.push_back(columns[6] + " = " + std::to_string(e.attack));
|
v.push_back(columns[6] + " = " + std::to_string(e.accuracy));
|
||||||
v.push_back(columns[7] + " = " + std::to_string(e.strength));
|
v.push_back(columns[7] + " = " + std::to_string(e.slow_mitigation));
|
||||||
v.push_back(columns[8] + " = " + std::to_string(e.stamina));
|
v.push_back(columns[8] + " = " + std::to_string(e.attack));
|
||||||
v.push_back(columns[9] + " = " + std::to_string(e.dexterity));
|
v.push_back(columns[9] + " = " + std::to_string(e.strength));
|
||||||
v.push_back(columns[10] + " = " + std::to_string(e.agility));
|
v.push_back(columns[10] + " = " + std::to_string(e.stamina));
|
||||||
v.push_back(columns[11] + " = " + std::to_string(e.intelligence));
|
v.push_back(columns[11] + " = " + std::to_string(e.dexterity));
|
||||||
v.push_back(columns[12] + " = " + std::to_string(e.wisdom));
|
v.push_back(columns[12] + " = " + std::to_string(e.agility));
|
||||||
v.push_back(columns[13] + " = " + std::to_string(e.charisma));
|
v.push_back(columns[13] + " = " + std::to_string(e.intelligence));
|
||||||
v.push_back(columns[14] + " = " + std::to_string(e.magic_resist));
|
v.push_back(columns[14] + " = " + std::to_string(e.wisdom));
|
||||||
v.push_back(columns[15] + " = " + std::to_string(e.cold_resist));
|
v.push_back(columns[15] + " = " + std::to_string(e.charisma));
|
||||||
v.push_back(columns[16] + " = " + std::to_string(e.fire_resist));
|
v.push_back(columns[16] + " = " + std::to_string(e.magic_resist));
|
||||||
v.push_back(columns[17] + " = " + std::to_string(e.poison_resist));
|
v.push_back(columns[17] + " = " + std::to_string(e.cold_resist));
|
||||||
v.push_back(columns[18] + " = " + std::to_string(e.disease_resist));
|
v.push_back(columns[18] + " = " + std::to_string(e.fire_resist));
|
||||||
v.push_back(columns[19] + " = " + std::to_string(e.corruption_resist));
|
v.push_back(columns[19] + " = " + std::to_string(e.poison_resist));
|
||||||
v.push_back(columns[20] + " = " + std::to_string(e.physical_resist));
|
v.push_back(columns[20] + " = " + std::to_string(e.disease_resist));
|
||||||
v.push_back(columns[21] + " = " + std::to_string(e.min_dmg));
|
v.push_back(columns[21] + " = " + std::to_string(e.corruption_resist));
|
||||||
v.push_back(columns[22] + " = " + std::to_string(e.max_dmg));
|
v.push_back(columns[22] + " = " + std::to_string(e.physical_resist));
|
||||||
v.push_back(columns[23] + " = " + std::to_string(e.hp_regen_rate));
|
v.push_back(columns[23] + " = " + std::to_string(e.min_dmg));
|
||||||
v.push_back(columns[24] + " = " + std::to_string(e.attack_delay));
|
v.push_back(columns[24] + " = " + std::to_string(e.max_dmg));
|
||||||
v.push_back(columns[25] + " = " + std::to_string(e.spell_scale));
|
v.push_back(columns[25] + " = " + std::to_string(e.hp_regen_rate));
|
||||||
v.push_back(columns[26] + " = " + std::to_string(e.heal_scale));
|
v.push_back(columns[26] + " = " + std::to_string(e.attack_delay));
|
||||||
v.push_back(columns[27] + " = '" + Strings::Escape(e.special_abilities) + "'");
|
v.push_back(columns[27] + " = " + std::to_string(e.spell_scale));
|
||||||
|
v.push_back(columns[28] + " = " + std::to_string(e.heal_scale));
|
||||||
|
v.push_back(columns[29] + " = '" + Strings::Escape(e.special_abilities) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -334,6 +348,8 @@ public:
|
|||||||
|
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.level));
|
v.push_back(std::to_string(e.level));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_version));
|
||||||
v.push_back(std::to_string(e.ac));
|
v.push_back(std::to_string(e.ac));
|
||||||
v.push_back(std::to_string(e.hp));
|
v.push_back(std::to_string(e.hp));
|
||||||
v.push_back(std::to_string(e.accuracy));
|
v.push_back(std::to_string(e.accuracy));
|
||||||
@@ -391,6 +407,8 @@ public:
|
|||||||
|
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.level));
|
v.push_back(std::to_string(e.level));
|
||||||
|
v.push_back(std::to_string(e.zone_id));
|
||||||
|
v.push_back(std::to_string(e.instance_version));
|
||||||
v.push_back(std::to_string(e.ac));
|
v.push_back(std::to_string(e.ac));
|
||||||
v.push_back(std::to_string(e.hp));
|
v.push_back(std::to_string(e.hp));
|
||||||
v.push_back(std::to_string(e.accuracy));
|
v.push_back(std::to_string(e.accuracy));
|
||||||
@@ -452,32 +470,34 @@ public:
|
|||||||
|
|
||||||
e.type = static_cast<int32_t>(atoi(row[0]));
|
e.type = static_cast<int32_t>(atoi(row[0]));
|
||||||
e.level = static_cast<int32_t>(atoi(row[1]));
|
e.level = static_cast<int32_t>(atoi(row[1]));
|
||||||
e.ac = static_cast<int32_t>(atoi(row[2]));
|
e.zone_id = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||||
e.hp = static_cast<int32_t>(atoi(row[3]));
|
e.instance_version = static_cast<int32_t>(atoi(row[3]));
|
||||||
e.accuracy = static_cast<int32_t>(atoi(row[4]));
|
e.ac = static_cast<int32_t>(atoi(row[4]));
|
||||||
e.slow_mitigation = static_cast<int32_t>(atoi(row[5]));
|
e.hp = static_cast<int32_t>(atoi(row[5]));
|
||||||
e.attack = static_cast<int32_t>(atoi(row[6]));
|
e.accuracy = static_cast<int32_t>(atoi(row[6]));
|
||||||
e.strength = static_cast<int32_t>(atoi(row[7]));
|
e.slow_mitigation = static_cast<int32_t>(atoi(row[7]));
|
||||||
e.stamina = static_cast<int32_t>(atoi(row[8]));
|
e.attack = static_cast<int32_t>(atoi(row[8]));
|
||||||
e.dexterity = static_cast<int32_t>(atoi(row[9]));
|
e.strength = static_cast<int32_t>(atoi(row[9]));
|
||||||
e.agility = static_cast<int32_t>(atoi(row[10]));
|
e.stamina = static_cast<int32_t>(atoi(row[10]));
|
||||||
e.intelligence = static_cast<int32_t>(atoi(row[11]));
|
e.dexterity = static_cast<int32_t>(atoi(row[11]));
|
||||||
e.wisdom = static_cast<int32_t>(atoi(row[12]));
|
e.agility = static_cast<int32_t>(atoi(row[12]));
|
||||||
e.charisma = static_cast<int32_t>(atoi(row[13]));
|
e.intelligence = static_cast<int32_t>(atoi(row[13]));
|
||||||
e.magic_resist = static_cast<int32_t>(atoi(row[14]));
|
e.wisdom = static_cast<int32_t>(atoi(row[14]));
|
||||||
e.cold_resist = static_cast<int32_t>(atoi(row[15]));
|
e.charisma = static_cast<int32_t>(atoi(row[15]));
|
||||||
e.fire_resist = static_cast<int32_t>(atoi(row[16]));
|
e.magic_resist = static_cast<int32_t>(atoi(row[16]));
|
||||||
e.poison_resist = static_cast<int32_t>(atoi(row[17]));
|
e.cold_resist = static_cast<int32_t>(atoi(row[17]));
|
||||||
e.disease_resist = static_cast<int32_t>(atoi(row[18]));
|
e.fire_resist = static_cast<int32_t>(atoi(row[18]));
|
||||||
e.corruption_resist = static_cast<int32_t>(atoi(row[19]));
|
e.poison_resist = static_cast<int32_t>(atoi(row[19]));
|
||||||
e.physical_resist = static_cast<int32_t>(atoi(row[20]));
|
e.disease_resist = static_cast<int32_t>(atoi(row[20]));
|
||||||
e.min_dmg = static_cast<int32_t>(atoi(row[21]));
|
e.corruption_resist = static_cast<int32_t>(atoi(row[21]));
|
||||||
e.max_dmg = static_cast<int32_t>(atoi(row[22]));
|
e.physical_resist = static_cast<int32_t>(atoi(row[22]));
|
||||||
e.hp_regen_rate = static_cast<int32_t>(atoi(row[23]));
|
e.min_dmg = static_cast<int32_t>(atoi(row[23]));
|
||||||
e.attack_delay = static_cast<int32_t>(atoi(row[24]));
|
e.max_dmg = static_cast<int32_t>(atoi(row[24]));
|
||||||
e.spell_scale = static_cast<int32_t>(atoi(row[25]));
|
e.hp_regen_rate = static_cast<int32_t>(atoi(row[25]));
|
||||||
e.heal_scale = static_cast<int32_t>(atoi(row[26]));
|
e.attack_delay = static_cast<int32_t>(atoi(row[26]));
|
||||||
e.special_abilities = row[27] ? row[27] : "";
|
e.spell_scale = static_cast<int32_t>(atoi(row[27]));
|
||||||
|
e.heal_scale = static_cast<int32_t>(atoi(row[28]));
|
||||||
|
e.special_abilities = row[29] ? row[29] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -504,32 +524,34 @@ public:
|
|||||||
|
|
||||||
e.type = static_cast<int32_t>(atoi(row[0]));
|
e.type = static_cast<int32_t>(atoi(row[0]));
|
||||||
e.level = static_cast<int32_t>(atoi(row[1]));
|
e.level = static_cast<int32_t>(atoi(row[1]));
|
||||||
e.ac = static_cast<int32_t>(atoi(row[2]));
|
e.zone_id = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||||
e.hp = static_cast<int32_t>(atoi(row[3]));
|
e.instance_version = static_cast<int32_t>(atoi(row[3]));
|
||||||
e.accuracy = static_cast<int32_t>(atoi(row[4]));
|
e.ac = static_cast<int32_t>(atoi(row[4]));
|
||||||
e.slow_mitigation = static_cast<int32_t>(atoi(row[5]));
|
e.hp = static_cast<int32_t>(atoi(row[5]));
|
||||||
e.attack = static_cast<int32_t>(atoi(row[6]));
|
e.accuracy = static_cast<int32_t>(atoi(row[6]));
|
||||||
e.strength = static_cast<int32_t>(atoi(row[7]));
|
e.slow_mitigation = static_cast<int32_t>(atoi(row[7]));
|
||||||
e.stamina = static_cast<int32_t>(atoi(row[8]));
|
e.attack = static_cast<int32_t>(atoi(row[8]));
|
||||||
e.dexterity = static_cast<int32_t>(atoi(row[9]));
|
e.strength = static_cast<int32_t>(atoi(row[9]));
|
||||||
e.agility = static_cast<int32_t>(atoi(row[10]));
|
e.stamina = static_cast<int32_t>(atoi(row[10]));
|
||||||
e.intelligence = static_cast<int32_t>(atoi(row[11]));
|
e.dexterity = static_cast<int32_t>(atoi(row[11]));
|
||||||
e.wisdom = static_cast<int32_t>(atoi(row[12]));
|
e.agility = static_cast<int32_t>(atoi(row[12]));
|
||||||
e.charisma = static_cast<int32_t>(atoi(row[13]));
|
e.intelligence = static_cast<int32_t>(atoi(row[13]));
|
||||||
e.magic_resist = static_cast<int32_t>(atoi(row[14]));
|
e.wisdom = static_cast<int32_t>(atoi(row[14]));
|
||||||
e.cold_resist = static_cast<int32_t>(atoi(row[15]));
|
e.charisma = static_cast<int32_t>(atoi(row[15]));
|
||||||
e.fire_resist = static_cast<int32_t>(atoi(row[16]));
|
e.magic_resist = static_cast<int32_t>(atoi(row[16]));
|
||||||
e.poison_resist = static_cast<int32_t>(atoi(row[17]));
|
e.cold_resist = static_cast<int32_t>(atoi(row[17]));
|
||||||
e.disease_resist = static_cast<int32_t>(atoi(row[18]));
|
e.fire_resist = static_cast<int32_t>(atoi(row[18]));
|
||||||
e.corruption_resist = static_cast<int32_t>(atoi(row[19]));
|
e.poison_resist = static_cast<int32_t>(atoi(row[19]));
|
||||||
e.physical_resist = static_cast<int32_t>(atoi(row[20]));
|
e.disease_resist = static_cast<int32_t>(atoi(row[20]));
|
||||||
e.min_dmg = static_cast<int32_t>(atoi(row[21]));
|
e.corruption_resist = static_cast<int32_t>(atoi(row[21]));
|
||||||
e.max_dmg = static_cast<int32_t>(atoi(row[22]));
|
e.physical_resist = static_cast<int32_t>(atoi(row[22]));
|
||||||
e.hp_regen_rate = static_cast<int32_t>(atoi(row[23]));
|
e.min_dmg = static_cast<int32_t>(atoi(row[23]));
|
||||||
e.attack_delay = static_cast<int32_t>(atoi(row[24]));
|
e.max_dmg = static_cast<int32_t>(atoi(row[24]));
|
||||||
e.spell_scale = static_cast<int32_t>(atoi(row[25]));
|
e.hp_regen_rate = static_cast<int32_t>(atoi(row[25]));
|
||||||
e.heal_scale = static_cast<int32_t>(atoi(row[26]));
|
e.attack_delay = static_cast<int32_t>(atoi(row[26]));
|
||||||
e.special_abilities = row[27] ? row[27] : "";
|
e.spell_scale = static_cast<int32_t>(atoi(row[27]));
|
||||||
|
e.heal_scale = static_cast<int32_t>(atoi(row[28]));
|
||||||
|
e.special_abilities = row[29] ? row[29] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -240,8 +240,8 @@ public:
|
|||||||
v.push_back(columns[7] + " = " + std::to_string(e.z));
|
v.push_back(columns[7] + " = " + std::to_string(e.z));
|
||||||
v.push_back(columns[8] + " = " + std::to_string(e.heading));
|
v.push_back(columns[8] + " = " + std::to_string(e.heading));
|
||||||
v.push_back(columns[9] + " = " + std::to_string(e.event_type_id));
|
v.push_back(columns[9] + " = " + std::to_string(e.event_type_id));
|
||||||
v.push_back(columns[10] + " = '" + Strings::Escape(e.event_type_name) + "'");
|
v.push_back(columns[10] + " = '" + db.Escape(e.event_type_name) + "'");
|
||||||
v.push_back(columns[11] + " = '" + Strings::Escape(e.event_data) + "'");
|
v.push_back(columns[11] + " = '" + db.Escape(e.event_data) + "'");
|
||||||
v.push_back(columns[12] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
v.push_back(columns[12] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -274,8 +274,8 @@ public:
|
|||||||
v.push_back(std::to_string(e.z));
|
v.push_back(std::to_string(e.z));
|
||||||
v.push_back(std::to_string(e.heading));
|
v.push_back(std::to_string(e.heading));
|
||||||
v.push_back(std::to_string(e.event_type_id));
|
v.push_back(std::to_string(e.event_type_id));
|
||||||
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
|
v.push_back("'" + db.Escape(e.event_type_name) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.event_data) + "'");
|
v.push_back("'" + db.Escape(e.event_data) + "'");
|
||||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
@@ -316,8 +316,8 @@ public:
|
|||||||
v.push_back(std::to_string(e.z));
|
v.push_back(std::to_string(e.z));
|
||||||
v.push_back(std::to_string(e.heading));
|
v.push_back(std::to_string(e.heading));
|
||||||
v.push_back(std::to_string(e.event_type_id));
|
v.push_back(std::to_string(e.event_type_id));
|
||||||
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
|
v.push_back("'" + db.Escape(e.event_type_name) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.event_data) + "'");
|
v.push_back("'" + db.Escape(e.event_data) + "'");
|
||||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
|||||||
+1
-1
@@ -319,7 +319,6 @@ RULE_CATEGORY_END()
|
|||||||
RULE_CATEGORY(Map)
|
RULE_CATEGORY(Map)
|
||||||
RULE_BOOL(Map, FixPathingZOnSendTo, false, "Try to repair Z coordinates in the SendTo routine as well")
|
RULE_BOOL(Map, FixPathingZOnSendTo, false, "Try to repair Z coordinates in the SendTo routine as well")
|
||||||
RULE_BOOL(Map, FixZWhenPathing, true, "Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor)")
|
RULE_BOOL(Map, FixZWhenPathing, true, "Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor)")
|
||||||
RULE_REAL(Map, DistanceCanTravelBeforeAdjustment, 10.0, "Distance a mob can path before FixZ is called, depends on FixZWhenPathing")
|
|
||||||
RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining whether or not NPC is hitting Best Z calcs (blue for hit, red for miss)")
|
RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining whether or not NPC is hitting Best Z calcs (blue for hit, red for miss)")
|
||||||
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging")
|
||||||
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply")
|
||||||
@@ -783,6 +782,7 @@ RULE_BOOL(Logging, PrintFileFunctionAndLine, false, "Ex: [World Server] [net.cpp
|
|||||||
RULE_BOOL(Logging, WorldGMSayLogging, true, "Relay worldserver logging to zone processes via GM say output")
|
RULE_BOOL(Logging, WorldGMSayLogging, true, "Relay worldserver logging to zone processes via GM say output")
|
||||||
RULE_BOOL(Logging, PlayerEventsQSProcess, false, "Have query server process player events instead of world. Useful when wanting to use a dedicated server and database for processing player events on separate disk")
|
RULE_BOOL(Logging, PlayerEventsQSProcess, false, "Have query server process player events instead of world. Useful when wanting to use a dedicated server and database for processing player events on separate disk")
|
||||||
RULE_INT(Logging, BatchPlayerEventProcessIntervalSeconds, 5, "This is the interval in which player events are processed in world or qs")
|
RULE_INT(Logging, BatchPlayerEventProcessIntervalSeconds, 5, "This is the interval in which player events are processed in world or qs")
|
||||||
|
RULE_INT(Logging, BatchPlayerEventProcessChunkSize, 10000, "This is the cap of events that can be inserted into the queue before a force flush. This is to keep from hitting MySQL max_allowed_packet and killing the connection")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(HotReload)
|
RULE_CATEGORY(HotReload)
|
||||||
|
|||||||
+3
-3
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
// Build variables
|
// Build variables
|
||||||
// these get injected during the build pipeline
|
// these get injected during the build pipeline
|
||||||
#define CURRENT_VERSION "22.4.2-dev" // always append -dev to the current version for custom-builds
|
#define CURRENT_VERSION "22.4.4-dev" // always append -dev to the current version for custom-builds
|
||||||
#define LOGIN_VERSION "0.8.0"
|
#define LOGIN_VERSION "0.8.0"
|
||||||
#define COMPILE_DATE __DATE__
|
#define COMPILE_DATE __DATE__
|
||||||
#define COMPILE_TIME __TIME__
|
#define COMPILE_TIME __TIME__
|
||||||
@@ -42,8 +42,8 @@
|
|||||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CURRENT_BINARY_DATABASE_VERSION 9220
|
#define CURRENT_BINARY_DATABASE_VERSION 9221
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9038
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "eqemu-server",
|
"name": "eqemu-server",
|
||||||
"version": "22.4.2",
|
"version": "22.4.4",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EQEmu/Server.git"
|
"url": "https://github.com/EQEmu/Server.git"
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
#include "worldserver.h"
|
#include "worldserver.h"
|
||||||
#include "../common/path_manager.h"
|
#include "../common/path_manager.h"
|
||||||
#include "../common/zone_store.h"
|
#include "../common/zone_store.h"
|
||||||
|
#include "../common/events/player_event_logs.h"
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
@@ -47,6 +48,7 @@ WorldServer *worldserver = 0;
|
|||||||
EQEmuLogSys LogSys;
|
EQEmuLogSys LogSys;
|
||||||
PathManager path;
|
PathManager path;
|
||||||
ZoneStore zone_store;
|
ZoneStore zone_store;
|
||||||
|
PlayerEventLogs player_event_logs;
|
||||||
|
|
||||||
void CatchSignal(int sig_num)
|
void CatchSignal(int sig_num)
|
||||||
{
|
{
|
||||||
@@ -106,6 +108,9 @@ int main()
|
|||||||
/* Load Looking For Guild Manager */
|
/* Load Looking For Guild Manager */
|
||||||
lfguildmanager.LoadDatabase();
|
lfguildmanager.LoadDatabase();
|
||||||
|
|
||||||
|
Timer player_event_process_timer(1000);
|
||||||
|
player_event_logs.SetDatabase(&database)->Init();
|
||||||
|
|
||||||
auto loop_fn = [&](EQ::Timer* t) {
|
auto loop_fn = [&](EQ::Timer* t) {
|
||||||
Timer::SetCurrentTime();
|
Timer::SetCurrentTime();
|
||||||
|
|
||||||
@@ -117,6 +122,10 @@ int main()
|
|||||||
if (LFGuildExpireTimer.Check()) {
|
if (LFGuildExpireTimer.Check()) {
|
||||||
lfguildmanager.ExpireEntries();
|
lfguildmanager.ExpireEntries();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (player_event_process_timer.Check()) {
|
||||||
|
player_event_logs.Process();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
EQ::Timer process_timer(loop_fn);
|
EQ::Timer process_timer(loop_fn);
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "lfguild.h"
|
#include "lfguild.h"
|
||||||
#include "queryservconfig.h"
|
#include "queryservconfig.h"
|
||||||
#include "worldserver.h"
|
#include "worldserver.h"
|
||||||
|
#include "../common/events/player_events.h"
|
||||||
|
#include "../common/events/player_event_logs.h"
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
@@ -89,6 +91,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
|||||||
case 0: {
|
case 0: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case ServerOP_PlayerEvent: {
|
||||||
|
auto n = PlayerEvent::PlayerEventContainer{};
|
||||||
|
auto s = (ServerSendPlayerEvent_Struct *) p.Data();
|
||||||
|
EQ::Util::MemoryStreamReader ss(s->cereal_data, s->cereal_size);
|
||||||
|
cereal::BinaryInputArchive archive(ss);
|
||||||
|
archive(n);
|
||||||
|
|
||||||
|
player_event_logs.AddToQueue(n.player_event_log);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
case ServerOP_KeepAlive: {
|
case ServerOP_KeepAlive: {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
content_db.SetMysql(database.getMySQL());
|
content_db.SetMySQL(database);
|
||||||
}
|
}
|
||||||
|
|
||||||
LogSys.SetDatabase(&database)
|
LogSys.SetDatabase(&database)
|
||||||
|
|||||||
@@ -474,6 +474,7 @@
|
|||||||
9218|2023_01_24_item_recast.sql|show columns from character_item_recast like '%recast_type%'|contains|smallint
|
9218|2023_01_24_item_recast.sql|show columns from character_item_recast like '%recast_type%'|contains|smallint
|
||||||
9219|2023_01_29_merchant_status_requirements.sql|SHOW COLUMNS FROM merchantlist LIKE 'min_status'|empty|
|
9219|2023_01_29_merchant_status_requirements.sql|SHOW COLUMNS FROM merchantlist LIKE 'min_status'|empty|
|
||||||
9220|2022_12_19_player_events_tables.sql|SHOW TABLES LIKE 'player_event_logs'|empty|
|
9220|2022_12_19_player_events_tables.sql|SHOW TABLES LIKE 'player_event_logs'|empty|
|
||||||
|
9221|2023_02_24_npc_scaling_zone_id_instance_version.sql|SHOW COLUMNS FROM `npc_scale_global_base` LIKE 'zone_id'|empty|
|
||||||
|
|
||||||
# Upgrade conditions:
|
# Upgrade conditions:
|
||||||
# This won't be needed after this system is implemented, but it is used database that are not
|
# This won't be needed after this system is implemented, but it is used database that are not
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
9035|2022_12_04_bot_archery.sql|SHOW COLUMNS FROM `bot_data` LIKE 'archery_setting'|empty|
|
9035|2022_12_04_bot_archery.sql|SHOW COLUMNS FROM `bot_data` LIKE 'archery_setting'|empty|
|
||||||
9036|2023_01_19_drop_bot_views.sql|SHOW TABLES LIKE 'vw_groups'|not_empty|
|
9036|2023_01_19_drop_bot_views.sql|SHOW TABLES LIKE 'vw_groups'|not_empty|
|
||||||
9037|2023_01_22_add_name_index.sql||show index from bot_data WHERE key_name = 'name`|empty|
|
9037|2023_01_22_add_name_index.sql||show index from bot_data WHERE key_name = 'name`|empty|
|
||||||
|
9038|2023_02_16_add_caster_range.sql|SHOW COLUMNS FROM `bot_data` LIKE 'caster_range'|empty|
|
||||||
|
|
||||||
# Upgrade conditions:
|
# Upgrade conditions:
|
||||||
# This won't be needed after this system is implemented, but it is used database that are not
|
# This won't be needed after this system is implemented, but it is used database that are not
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `bot_data`
|
||||||
|
ADD COLUMN `caster_range` INT(11) UNSIGNED NOT NULL DEFAULT '300' AFTER `archery_setting`;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
ALTER TABLE `npc_scale_global_base`
|
||||||
|
ADD COLUMN `zone_id` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `level`,
|
||||||
|
ADD COLUMN `instance_version` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `zone_id`,
|
||||||
|
DROP PRIMARY KEY,
|
||||||
|
ADD PRIMARY KEY (`type`, `level`, `zone_id`, `instance_version`) USING BTREE;
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#include <thread>
|
||||||
|
#include "../../common/repositories/zone_repository.h"
|
||||||
|
#include "../../common/eqemu_config.h"
|
||||||
|
#include <signal.h>
|
||||||
|
|
||||||
|
Database db;
|
||||||
|
Database db2;
|
||||||
|
|
||||||
|
volatile sig_atomic_t stop;
|
||||||
|
void inthand(int signum) {
|
||||||
|
stop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void DatabaseTest()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
LogInfo("DatabaseTest Query");
|
||||||
|
db.QueryDatabase("SELECT 1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[noreturn]] void DatabaseTestSecondConnection()
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
LogInfo("DatabaseTest Query");
|
||||||
|
db2.QueryDatabase("SELECT 1");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WorldserverCLI::TestDatabaseConcurrency(int argc, char **argv, argh::parser &cmd, std::string &description)
|
||||||
|
{
|
||||||
|
description = "Test command to test database concurrency";
|
||||||
|
|
||||||
|
if (cmd[{"-h", "--help"}]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
signal(SIGINT, inthand);
|
||||||
|
|
||||||
|
LogInfo("Database test");
|
||||||
|
|
||||||
|
auto mutex = new Mutex;
|
||||||
|
|
||||||
|
auto c = EQEmuConfig::get();
|
||||||
|
LogInfo("Connecting to MySQL");
|
||||||
|
if (!db.Connect(
|
||||||
|
c->DatabaseHost.c_str(),
|
||||||
|
c->DatabaseUsername.c_str(),
|
||||||
|
c->DatabasePassword.c_str(),
|
||||||
|
c->DatabaseDB.c_str(),
|
||||||
|
c->DatabasePort
|
||||||
|
)) {
|
||||||
|
LogError("Cannot continue without a database connection");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
db.SetMutex(mutex);
|
||||||
|
|
||||||
|
db2.SetMySQL(db);
|
||||||
|
|
||||||
|
db2.SetMutex(mutex);
|
||||||
|
|
||||||
|
std::thread(DatabaseTest).detach();
|
||||||
|
std::thread(DatabaseTest).detach();
|
||||||
|
std::thread(DatabaseTestSecondConnection).detach();
|
||||||
|
|
||||||
|
while (!stop) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
safe_delete(mutex);
|
||||||
|
}
|
||||||
@@ -224,6 +224,13 @@ void EQEmuApiWorldDataService::reload(Json::Value &r, const std::vector<std::str
|
|||||||
else {
|
else {
|
||||||
pack = new ServerPacket(c.opcode, 0);
|
pack = new ServerPacket(c.opcode, 0);
|
||||||
message(r, fmt::format("Reloading [{}] globally", c.desc));
|
message(r, fmt::format("Reloading [{}] globally", c.desc));
|
||||||
|
|
||||||
|
if (c.opcode == ServerOP_ReloadLogs) {
|
||||||
|
LogSys.LoadLogDatabaseSettings();
|
||||||
|
}
|
||||||
|
else if (c.opcode == ServerOP_ReloadRules) {
|
||||||
|
RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
found_command = true;
|
found_command = true;
|
||||||
|
|||||||
+7
-11
@@ -131,12 +131,6 @@ inline void UpdateWindowTitle(std::string new_title)
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void PlayerEventQueueListener() {
|
|
||||||
while (RunLoops) {
|
|
||||||
player_event_logs.Process();
|
|
||||||
Sleep(1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* World process entrypoint
|
* World process entrypoint
|
||||||
@@ -381,13 +375,9 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Timer player_event_process_timer(1000);
|
||||||
player_event_logs.SetDatabase(&database)->Init();
|
player_event_logs.SetDatabase(&database)->Init();
|
||||||
|
|
||||||
if (!RuleB(Logging, PlayerEventsQSProcess)) {
|
|
||||||
LogInfo("[PlayerEventQueueListener] Booting queue processor");
|
|
||||||
std::thread(PlayerEventQueueListener).detach();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto loop_fn = [&](EQ::Timer* t) {
|
auto loop_fn = [&](EQ::Timer* t) {
|
||||||
Timer::SetCurrentTime();
|
Timer::SetCurrentTime();
|
||||||
|
|
||||||
@@ -435,6 +425,10 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
client_list.Process();
|
client_list.Process();
|
||||||
|
|
||||||
|
if (player_event_process_timer.Check()) {
|
||||||
|
player_event_logs.Process();
|
||||||
|
}
|
||||||
|
|
||||||
if (PurgeInstanceTimer.Check()) {
|
if (PurgeInstanceTimer.Check()) {
|
||||||
database.PurgeExpiredInstances();
|
database.PurgeExpiredInstances();
|
||||||
database.PurgeAllDeletedDataBuckets();
|
database.PurgeAllDeletedDataBuckets();
|
||||||
@@ -484,6 +478,8 @@ int main(int argc, char **argv)
|
|||||||
LogInfo("Signaling HTTP service to stop");
|
LogInfo("Signaling HTTP service to stop");
|
||||||
LogSys.CloseFileLogs();
|
LogSys.CloseFileLogs();
|
||||||
|
|
||||||
|
WorldBoot::Shutdown();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-4
@@ -30,6 +30,8 @@
|
|||||||
extern ZSList zoneserver_list;
|
extern ZSList zoneserver_list;
|
||||||
extern WorldConfig Config;
|
extern WorldConfig Config;
|
||||||
|
|
||||||
|
auto mutex = new Mutex;
|
||||||
|
|
||||||
void WorldBoot::GMSayHookCallBackProcessWorld(uint16 log_category, const char *func, std::string message)
|
void WorldBoot::GMSayHookCallBackProcessWorld(uint16 log_category, const char *func, std::string message)
|
||||||
{
|
{
|
||||||
// we don't want to loop up with chat messages
|
// we don't want to loop up with chat messages
|
||||||
@@ -136,9 +138,7 @@ bool WorldBoot::LoadDatabaseConnections()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Multi-tenancy - content database
|
||||||
* Multi-tenancy: Content database
|
|
||||||
*/
|
|
||||||
if (!c->ContentDbHost.empty()) {
|
if (!c->ContentDbHost.empty()) {
|
||||||
if (!content_db.Connect(
|
if (!content_db.Connect(
|
||||||
c->ContentDbHost.c_str(),
|
c->ContentDbHost.c_str(),
|
||||||
@@ -153,7 +153,12 @@ bool WorldBoot::LoadDatabaseConnections()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
content_db.SetMysql(database.getMySQL());
|
content_db.SetMySQL(database);
|
||||||
|
// when database and content_db share the same underlying mysql connection
|
||||||
|
// it needs to be protected by a shared mutex otherwise we produce concurrency issues
|
||||||
|
// when database actions are occurring in different threads
|
||||||
|
database.SetMutex(mutex);
|
||||||
|
content_db.SetMutex(mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -652,3 +657,8 @@ void WorldBoot::CheckForPossibleConfigurationIssues()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WorldBoot::Shutdown()
|
||||||
|
{
|
||||||
|
safe_delete(mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public:
|
|||||||
static void RegisterLoginservers();
|
static void RegisterLoginservers();
|
||||||
static bool DatabaseLoadRoutines(int argc, char **argv);
|
static bool DatabaseLoadRoutines(int argc, char **argv);
|
||||||
static void CheckForPossibleConfigurationIssues();
|
static void CheckForPossibleConfigurationIssues();
|
||||||
|
static void Shutdown();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,10 +31,12 @@ void WorldserverCLI::CommandHandler(int argc, char **argv)
|
|||||||
function_map["test:expansion"] = &WorldserverCLI::ExpansionTestCommand;
|
function_map["test:expansion"] = &WorldserverCLI::ExpansionTestCommand;
|
||||||
function_map["test:repository"] = &WorldserverCLI::TestRepository;
|
function_map["test:repository"] = &WorldserverCLI::TestRepository;
|
||||||
function_map["test:repository2"] = &WorldserverCLI::TestRepository2;
|
function_map["test:repository2"] = &WorldserverCLI::TestRepository2;
|
||||||
|
function_map["test:db-concurrency"] = &WorldserverCLI::TestDatabaseConcurrency;
|
||||||
|
|
||||||
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
|
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include "cli/database_concurrency.cpp"
|
||||||
#include "cli/copy_character.cpp"
|
#include "cli/copy_character.cpp"
|
||||||
#include "cli/database_dump.cpp"
|
#include "cli/database_dump.cpp"
|
||||||
#include "cli/database_get_schema.cpp"
|
#include "cli/database_get_schema.cpp"
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public:
|
|||||||
static void ExpansionTestCommand(int argc, char **argv, argh::parser &cmd, std::string &description);
|
static void ExpansionTestCommand(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
static void TestRepository(int argc, char **argv, argh::parser &cmd, std::string &description);
|
static void TestRepository(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
static void TestRepository2(int argc, char **argv, argh::parser &cmd, std::string &description);
|
static void TestRepository2(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
|
static void TestDatabaseConcurrency(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1332,6 +1332,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
case ServerOP_ItemStatus:
|
case ServerOP_ItemStatus:
|
||||||
case ServerOP_KickPlayer:
|
case ServerOP_KickPlayer:
|
||||||
case ServerOP_KillPlayer:
|
case ServerOP_KillPlayer:
|
||||||
|
case ServerOP_OOCMute:
|
||||||
case ServerOP_OOZGroupMessage:
|
case ServerOP_OOZGroupMessage:
|
||||||
case ServerOP_Petition:
|
case ServerOP_Petition:
|
||||||
case ServerOP_RaidGroupSay:
|
case ServerOP_RaidGroupSay:
|
||||||
|
|||||||
+47
-51
@@ -71,6 +71,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
|||||||
m_enforce_spell_settings = 0;
|
m_enforce_spell_settings = 0;
|
||||||
m_bot_archery_setting = 0;
|
m_bot_archery_setting = 0;
|
||||||
m_expansion_bitmask = -1;
|
m_expansion_bitmask = -1;
|
||||||
|
m_bot_caster_range = 0;
|
||||||
SetBotID(0);
|
SetBotID(0);
|
||||||
SetBotSpellID(0);
|
SetBotSpellID(0);
|
||||||
SetSpawnStatus(false);
|
SetSpawnStatus(false);
|
||||||
@@ -2240,16 +2241,16 @@ void Bot::AI_Bot_Init()
|
|||||||
AIautocastspell_timer.reset(nullptr);
|
AIautocastspell_timer.reset(nullptr);
|
||||||
casting_spell_AIindex = static_cast<uint8>(AIBot_spells.size());
|
casting_spell_AIindex = static_cast<uint8>(AIBot_spells.size());
|
||||||
|
|
||||||
roambox_max_x = 0;
|
m_roambox.max_x = 0;
|
||||||
roambox_max_y = 0;
|
m_roambox.max_y = 0;
|
||||||
roambox_min_x = 0;
|
m_roambox.min_x = 0;
|
||||||
roambox_min_y = 0;
|
m_roambox.min_y = 0;
|
||||||
roambox_distance = 0;
|
m_roambox.distance = 0;
|
||||||
roambox_destination_x = 0;
|
m_roambox.dest_x = 0;
|
||||||
roambox_destination_y = 0;
|
m_roambox.dest_y = 0;
|
||||||
roambox_destination_z = 0;
|
m_roambox.dest_z = 0;
|
||||||
roambox_min_delay = 2500;
|
m_roambox.delay = 2500;
|
||||||
roambox_delay = 2500;
|
m_roambox.min_delay = 2500;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::SpellProcess() {
|
void Bot::SpellProcess() {
|
||||||
@@ -3115,26 +3116,7 @@ void Bot::AI_Process()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
float melee_distance_min = melee_distance / 2.0f;
|
float melee_distance_min = melee_distance / 2.0f;
|
||||||
|
float caster_distance_max = GetBotCasterMaxRange(melee_distance_max);
|
||||||
// Calculate caster distances
|
|
||||||
float caster_distance_max = 0.0f;
|
|
||||||
float caster_distance_min = 0.0f;
|
|
||||||
float caster_distance = 0.0f;
|
|
||||||
{
|
|
||||||
if (GetLevel() >= GetStopMeleeLevel() && GetClass() >= WARRIOR && GetClass() <= BERSERKER) {
|
|
||||||
caster_distance_max = MAX_CASTER_DISTANCE[(GetClass() - 1)];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (caster_distance_max) {
|
|
||||||
|
|
||||||
caster_distance_min = melee_distance_max;
|
|
||||||
if (caster_distance_max <= caster_distance_min) {
|
|
||||||
caster_distance_max = caster_distance_min * 1.25f;
|
|
||||||
}
|
|
||||||
|
|
||||||
caster_distance = ((caster_distance_max + caster_distance_min) / 2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool atArcheryRange = IsArcheryRange(tar);
|
bool atArcheryRange = IsArcheryRange(tar);
|
||||||
|
|
||||||
@@ -3157,11 +3139,11 @@ void Bot::AI_Process()
|
|||||||
ChangeBotArcherWeapons(IsBotArcher());
|
ChangeBotArcherWeapons(IsBotArcher());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
bool stop_melee_level = GetLevel() >= GetStopMeleeLevel();
|
||||||
if (IsBotArcher() && atArcheryRange) {
|
if (IsBotArcher() && atArcheryRange) {
|
||||||
atCombatRange = true;
|
atCombatRange = true;
|
||||||
}
|
}
|
||||||
else if (caster_distance_max && tar_distance <= caster_distance_max) {
|
else if (caster_distance_max && tar_distance <= caster_distance_max && stop_melee_level) {
|
||||||
atCombatRange = true;
|
atCombatRange = true;
|
||||||
}
|
}
|
||||||
else if (tar_distance <= melee_distance) {
|
else if (tar_distance <= melee_distance) {
|
||||||
@@ -4478,23 +4460,19 @@ uint32 Bot::CountBotItem(uint32 item_id) {
|
|||||||
return item_count;
|
return item_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::HasBotItem(uint32 item_id) {
|
int16 Bot::HasBotItem(uint32 item_id) {
|
||||||
bool has_item = false;
|
EQ::ItemInstance const *inst = nullptr;
|
||||||
EQ::ItemInstance *inst = nullptr;
|
|
||||||
|
|
||||||
for (uint16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) {
|
for (uint16 slot_id = EQ::invslot::EQUIPMENT_BEGIN; slot_id <= EQ::invslot::EQUIPMENT_END; ++slot_id) {
|
||||||
inst = GetBotItem(slot_id);
|
inst = GetBotItem(slot_id);
|
||||||
if (!inst || !inst->GetItem()) {
|
if (!inst || !inst->GetItem()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (inst->GetID() == item_id) {
|
if (inst->GetID() == item_id) {
|
||||||
has_item = true;
|
return slot_id;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return INVALID_INDEX;
|
||||||
return has_item;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::RemoveBotItem(uint32 item_id) {
|
void Bot::RemoveBotItem(uint32 item_id) {
|
||||||
@@ -4569,6 +4547,7 @@ bool Bot::AddBotToGroup(Bot* bot, Group* group) {
|
|||||||
group->SendUpdate(groupActUpdate, TempLeader);
|
group->SendUpdate(groupActUpdate, TempLeader);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
group->VerifyGroup();
|
||||||
Result = true;
|
Result = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4616,17 +4595,17 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
|||||||
struct ClientTrade {
|
struct ClientTrade {
|
||||||
ItemInstance* trade_item_instance;
|
ItemInstance* trade_item_instance;
|
||||||
int16 from_client_slot;
|
int16 from_client_slot;
|
||||||
int16 to_bot_slot;
|
int16 to_bot_slot = invslot::SLOT_INVALID;
|
||||||
|
|
||||||
ClientTrade(ItemInstance* item, int16 from) : trade_item_instance(item), from_client_slot(from), to_bot_slot(invslot::SLOT_INVALID) { }
|
ClientTrade(ItemInstance* item, int16 from) : trade_item_instance(item), from_client_slot(from) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientReturn {
|
struct ClientReturn {
|
||||||
const ItemInstance* return_item_instance;
|
ItemInstance* return_item_instance;
|
||||||
int16 from_bot_slot;
|
int16 from_bot_slot;
|
||||||
int16 to_client_slot;
|
int16 to_client_slot = invslot::SLOT_INVALID;
|
||||||
|
|
||||||
ClientReturn(const ItemInstance* item, int16 from) : return_item_instance(item), from_bot_slot(from), to_client_slot(invslot::SLOT_INVALID) { }
|
ClientReturn(ItemInstance* item, int16 from) : return_item_instance(item), from_bot_slot(from) { }
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int16 bot_equip_order[invslot::EQUIPMENT_COUNT] = {
|
static const int16 bot_equip_order[invslot::EQUIPMENT_COUNT] = {
|
||||||
@@ -4906,7 +4885,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
|||||||
if (trade_instance->GetItem()->IsType2HWeapon()) {
|
if (trade_instance->GetItem()->IsType2HWeapon()) {
|
||||||
if (!melee_secondary) {
|
if (!melee_secondary) {
|
||||||
melee_2h_weapon = true;
|
melee_2h_weapon = true;
|
||||||
auto equipped_secondary_weapon = m_inv[invslot::slotSecondary];
|
auto equipped_secondary_weapon = GetBotItem(invslot::slotSecondary);
|
||||||
if (equipped_secondary_weapon) {
|
if (equipped_secondary_weapon) {
|
||||||
client_return.push_back(ClientReturn(equipped_secondary_weapon, invslot::slotSecondary));
|
client_return.push_back(ClientReturn(equipped_secondary_weapon, invslot::slotSecondary));
|
||||||
}
|
}
|
||||||
@@ -4922,7 +4901,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
|||||||
!trade_instance->IsWeapon()
|
!trade_instance->IsWeapon()
|
||||||
) {
|
) {
|
||||||
melee_secondary = true;
|
melee_secondary = true;
|
||||||
auto equipped_primary_weapon = m_inv[invslot::slotPrimary];
|
auto equipped_primary_weapon = GetBotItem(invslot::slotPrimary);
|
||||||
if (equipped_primary_weapon && equipped_primary_weapon->GetItem()->IsType2HWeapon()) {
|
if (equipped_primary_weapon && equipped_primary_weapon->GetItem()->IsType2HWeapon()) {
|
||||||
client_return.push_back(ClientReturn(equipped_primary_weapon, invslot::slotPrimary));
|
client_return.push_back(ClientReturn(equipped_primary_weapon, invslot::slotPrimary));
|
||||||
}
|
}
|
||||||
@@ -4937,7 +4916,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
|||||||
trade_iterator.to_bot_slot = index;
|
trade_iterator.to_bot_slot = index;
|
||||||
|
|
||||||
if (m_inv[index]) {
|
if (m_inv[index]) {
|
||||||
client_return.push_back(ClientReturn(m_inv[index], index));
|
client_return.push_back(ClientReturn(GetBotItem(index), index));
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@@ -5071,9 +5050,8 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client*
|
|||||||
client->DeleteItemInInventory(return_iterator.from_bot_slot);
|
client->DeleteItemInInventory(return_iterator.from_bot_slot);
|
||||||
} else { // successful trade returns
|
} else { // successful trade returns
|
||||||
auto return_instance = m_inv.PopItem(return_iterator.from_bot_slot);
|
auto return_instance = m_inv.PopItem(return_iterator.from_bot_slot);
|
||||||
//if (*return_instance != *return_iterator.return_item_instance) {
|
|
||||||
// // TODO: add logging
|
// // TODO: add logging
|
||||||
//}
|
|
||||||
|
|
||||||
if (!database.botdb.DeleteItemBySlot(GetBotID(), return_iterator.from_bot_slot)) {
|
if (!database.botdb.DeleteItemBySlot(GetBotID(), return_iterator.from_bot_slot)) {
|
||||||
OwnerMessage(
|
OwnerMessage(
|
||||||
@@ -9490,7 +9468,7 @@ void Bot::ListBotSpells(uint8 min_level)
|
|||||||
auto spell_count = 0;
|
auto spell_count = 0;
|
||||||
auto spell_number = 1;
|
auto spell_number = 1;
|
||||||
|
|
||||||
for (const auto& s : (AIBot_spells.size() > AIBot_spells_enforced.size()) ? AIBot_spells : AIBot_spells_enforced) {
|
for (const auto& s : (GetBotEnforceSpellSetting()) ? AIBot_spells_enforced : AIBot_spells) {
|
||||||
auto b = bot_spell_settings.find(s.spellid);
|
auto b = bot_spell_settings.find(s.spellid);
|
||||||
if (b == bot_spell_settings.end() && s.minlevel >= min_level) {
|
if (b == bot_spell_settings.end() && s.minlevel >= min_level) {
|
||||||
bot_owner->Message(
|
bot_owner->Message(
|
||||||
@@ -9830,4 +9808,22 @@ void Bot::SendSpellAnim(uint16 target_id, uint16 spell_id)
|
|||||||
entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles));
|
entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Bot::GetBotCasterMaxRange(float melee_distance_max) {// Calculate caster distances
|
||||||
|
float caster_distance_max = 0.0f;
|
||||||
|
float caster_distance_min = 0.0f;
|
||||||
|
float caster_distance = 0.0f;
|
||||||
|
|
||||||
|
caster_distance_max = GetBotCasterRange() * GetBotCasterRange();
|
||||||
|
if (!GetBotCasterRange() && GetLevel() >= GetStopMeleeLevel() && GetClass() >= WARRIOR && GetClass() <= BERSERKER) {
|
||||||
|
caster_distance_max = MAX_CASTER_DISTANCE[GetClass() - 1];
|
||||||
|
}
|
||||||
|
if (caster_distance_max) {
|
||||||
|
caster_distance_min = melee_distance_max;
|
||||||
|
if (caster_distance_max <= caster_distance_min) {
|
||||||
|
caster_distance_max = caster_distance_min * 1.25f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return caster_distance_max;
|
||||||
|
}
|
||||||
|
|
||||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
||||||
|
|||||||
+6
-3
@@ -348,6 +348,8 @@ public:
|
|||||||
void SetStopMeleeLevel(uint8 level);
|
void SetStopMeleeLevel(uint8 level);
|
||||||
void SetGuardMode();
|
void SetGuardMode();
|
||||||
void SetHoldMode();
|
void SetHoldMode();
|
||||||
|
uint32 GetBotCasterRange() { return m_bot_caster_range; }
|
||||||
|
bool IsValidSpellRange(uint16 spell_id, Mob const* tar);
|
||||||
|
|
||||||
// Bot AI Methods
|
// Bot AI Methods
|
||||||
void AI_Bot_Init();
|
void AI_Bot_Init();
|
||||||
@@ -490,7 +492,7 @@ public:
|
|||||||
EQ::constants::StanceType GetBotStance() { return _botStance; }
|
EQ::constants::StanceType GetBotStance() { return _botStance; }
|
||||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||||
bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; }
|
bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; }
|
||||||
|
float GetBotCasterMaxRange(float melee_distance_max);
|
||||||
bool IsGroupHealer() { return m_CastingRoles.GroupHealer; }
|
bool IsGroupHealer() { return m_CastingRoles.GroupHealer; }
|
||||||
bool IsGroupSlower() { return m_CastingRoles.GroupSlower; }
|
bool IsGroupSlower() { return m_CastingRoles.GroupSlower; }
|
||||||
bool IsGroupNuker() { return m_CastingRoles.GroupNuker; }
|
bool IsGroupNuker() { return m_CastingRoles.GroupNuker; }
|
||||||
@@ -623,6 +625,7 @@ public:
|
|||||||
else
|
else
|
||||||
_botStance = EQ::constants::stancePassive;
|
_botStance = EQ::constants::stancePassive;
|
||||||
}
|
}
|
||||||
|
void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; }
|
||||||
void SetSpellRecastTimer(int timer_index, int32 recast_delay);
|
void SetSpellRecastTimer(int timer_index, int32 recast_delay);
|
||||||
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
|
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
|
||||||
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
|
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
|
||||||
@@ -720,7 +723,7 @@ public:
|
|||||||
uint32 CountBotItem(uint32 item_id);
|
uint32 CountBotItem(uint32 item_id);
|
||||||
std::map<uint16, uint32> GetBotItemSlots();
|
std::map<uint16, uint32> GetBotItemSlots();
|
||||||
uint32 GetBotItemBySlot(uint16 slot_id);
|
uint32 GetBotItemBySlot(uint16 slot_id);
|
||||||
bool HasBotItem(uint32 item_id);
|
int16 HasBotItem(uint32 item_id);
|
||||||
void RemoveBotItem(uint32 item_id);
|
void RemoveBotItem(uint32 item_id);
|
||||||
uint32 GetTotalPlayTime();
|
uint32 GetTotalPlayTime();
|
||||||
|
|
||||||
@@ -820,7 +823,7 @@ private:
|
|||||||
bool m_pulling_flag;
|
bool m_pulling_flag;
|
||||||
bool m_returning_flag;
|
bool m_returning_flag;
|
||||||
eStandingPetOrder m_previous_pet_order;
|
eStandingPetOrder m_previous_pet_order;
|
||||||
|
uint32 m_bot_caster_range;
|
||||||
BotCastingRoles m_CastingRoles;
|
BotCastingRoles m_CastingRoles;
|
||||||
std::map<std::string,std::string> bot_data_buckets;
|
std::map<std::string,std::string> bot_data_buckets;
|
||||||
std::map<std::string,std::string> bot_owner_data_buckets;
|
std::map<std::string,std::string> bot_owner_data_buckets;
|
||||||
|
|||||||
+52
-1
@@ -1366,6 +1366,7 @@ int bot_command_init(void)
|
|||||||
bot_command_add("bottitle", "Sets a bots title", AccountStatus::Player, bot_subcommand_bot_title) ||
|
bot_command_add("bottitle", "Sets a bots title", AccountStatus::Player, bot_subcommand_bot_title) ||
|
||||||
bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", AccountStatus::Player, bot_subcommand_bot_update) ||
|
bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", AccountStatus::Player, bot_subcommand_bot_update) ||
|
||||||
bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_subcommand_bot_woad) ||
|
bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_subcommand_bot_woad) ||
|
||||||
|
bot_command_add("casterrange", "Controls the range casters will try to stay away from a mob (if too far, they will skip spells that are out-of-range)", AccountStatus::Player, bot_command_caster_range) ||
|
||||||
bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) ||
|
bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) ||
|
||||||
bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_subcommand_circle) ||
|
bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_subcommand_circle) ||
|
||||||
bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) ||
|
bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) ||
|
||||||
@@ -9320,7 +9321,7 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto* inst = my_bot->GetBotItem(slot_id);
|
auto* inst = my_bot->GetBotItem(slot_id);
|
||||||
if (!inst) {
|
if (!inst) {
|
||||||
std::string slot_message = "is";
|
std::string slot_message = "is";
|
||||||
switch (slot_id) {
|
switch (slot_id) {
|
||||||
@@ -10848,3 +10849,53 @@ void bot_command_enforce_spell_list(Client* c, const Seperator *sep)
|
|||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bot_command_caster_range(Client* c, const Seperator* sep)
|
||||||
|
{
|
||||||
|
if (helper_command_alias_fail(c, "bot_command_caster_range", sep->arg[0], "casterrange")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||||
|
c->Message(Chat::White, "usage: <target_bot> %s [current | value: 0 - 300].", sep->arg[0]);
|
||||||
|
c->Message(Chat::White, "note: Can only be used for Casters or Hybrids.");
|
||||||
|
c->Message(Chat::White, "note: Use [current] to check the current setting.");
|
||||||
|
c->Message(Chat::White, "note: Set the value to the minimum distance you want your bot to try to remain from its target.");
|
||||||
|
c->Message(Chat::White, "note: If they are too far for a spell, it will be skipped.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto my_bot = ActionableBots::AsTarget_ByBot(c);
|
||||||
|
if (!my_bot) {
|
||||||
|
c->Message(Chat::White, "You must <target> a bot that you own to use this command.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) {
|
||||||
|
c->Message(Chat::White, "You must <target> a caster or hybrid class to use this command.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 crange = 0;
|
||||||
|
if (sep->IsNumber(1)) {
|
||||||
|
crange = atoi(sep->arg[1]);
|
||||||
|
if (crange >= 0 && crange <= 300) {
|
||||||
|
my_bot->SetBotCasterRange(crange);
|
||||||
|
if (!database.botdb.SaveBotCasterRange(c->CharacterID(), my_bot->GetBotID(), crange)) {
|
||||||
|
c->Message(Chat::White, "%s for '%s'", BotDatabase::fail::SaveBotCasterRange(), my_bot->GetCleanName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c->Message(Chat::White, "Successfully set Caster Range for %s to %u.", my_bot->GetCleanName(), crange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c->Message(Chat::White, "You must enter a value within the range of 0 - 300.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!strcasecmp(sep->arg[1], "current")) {
|
||||||
|
c->Message(Chat::White, "My current range is %u.", my_bot->GetBotCasterRange());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
c->Message(Chat::White, "Incorrect argument, use help for a list of options.");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -553,6 +553,7 @@ void bot_command_attack(Client *c, const Seperator *sep);
|
|||||||
void bot_command_bind_affinity(Client *c, const Seperator *sep);
|
void bot_command_bind_affinity(Client *c, const Seperator *sep);
|
||||||
void bot_command_bot(Client *c, const Seperator *sep);
|
void bot_command_bot(Client *c, const Seperator *sep);
|
||||||
void bot_command_botgroup(Client *c, const Seperator *sep);
|
void bot_command_botgroup(Client *c, const Seperator *sep);
|
||||||
|
void bot_command_caster_range(Client* c, const Seperator* sep);
|
||||||
void bot_command_charm(Client *c, const Seperator *sep);
|
void bot_command_charm(Client *c, const Seperator *sep);
|
||||||
void bot_command_cure(Client *c, const Seperator *sep);
|
void bot_command_cure(Client *c, const Seperator *sep);
|
||||||
void bot_command_defensive(Client *c, const Seperator *sep);
|
void bot_command_defensive(Client *c, const Seperator *sep);
|
||||||
|
|||||||
+73
-28
@@ -485,6 +485,8 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
|
|||||||
loaded_bot->SetBotEnforceSpellSetting((l.enforce_spell_settings ? true : false));
|
loaded_bot->SetBotEnforceSpellSetting((l.enforce_spell_settings ? true : false));
|
||||||
|
|
||||||
loaded_bot->SetBotArcherySetting((l.archery_setting ? true : false));
|
loaded_bot->SetBotArcherySetting((l.archery_setting ? true : false));
|
||||||
|
|
||||||
|
loaded_bot->SetBotCasterRange(l.caster_range);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -545,6 +547,7 @@ bool BotDatabase::SaveNewBot(Bot* bot_inst, uint32& bot_id)
|
|||||||
e.expansion_bitmask = bot_inst->GetExpansionBitmask();
|
e.expansion_bitmask = bot_inst->GetExpansionBitmask();
|
||||||
e.enforce_spell_settings = bot_inst->GetBotEnforceSpellSetting();
|
e.enforce_spell_settings = bot_inst->GetBotEnforceSpellSetting();
|
||||||
e.archery_setting = bot_inst->IsBotArcher() ? 1 : 0;
|
e.archery_setting = bot_inst->IsBotArcher() ? 1 : 0;
|
||||||
|
e.caster_range = bot_inst->GetBotCasterRange();
|
||||||
|
|
||||||
auto b = BotDataRepository::InsertOne(database, e);
|
auto b = BotDataRepository::InsertOne(database, e);
|
||||||
if (!b.bot_id) {
|
if (!b.bot_id) {
|
||||||
@@ -639,10 +642,11 @@ bool BotDatabase::DeleteBot(const uint32 bot_id)
|
|||||||
|
|
||||||
bool BotDatabase::LoadBuffs(Bot* bot_inst)
|
bool BotDatabase::LoadBuffs(Bot* bot_inst)
|
||||||
{
|
{
|
||||||
if (!bot_inst)
|
if (!bot_inst) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
query = StringFormat(
|
query = fmt::format(
|
||||||
"SELECT"
|
"SELECT"
|
||||||
" `spell_id`,"
|
" `spell_id`,"
|
||||||
" `caster_level`,"
|
" `caster_level`,"
|
||||||
@@ -663,45 +667,58 @@ bool BotDatabase::LoadBuffs(Bot* bot_inst)
|
|||||||
" `extra_di_chance`,"
|
" `extra_di_chance`,"
|
||||||
" `instrument_mod`"
|
" `instrument_mod`"
|
||||||
" FROM `bot_buffs`"
|
" FROM `bot_buffs`"
|
||||||
" WHERE `bot_id` = '%u'",
|
" WHERE `bot_id` = {}",
|
||||||
bot_inst->GetBotID()
|
bot_inst->GetBotID()
|
||||||
);
|
);
|
||||||
auto results = database.QueryDatabase(query);
|
auto results = database.QueryDatabase(query);
|
||||||
if (!results.Success())
|
|
||||||
|
if (!results.Success()) {
|
||||||
return false;
|
return false;
|
||||||
if (!results.RowCount())
|
}
|
||||||
|
|
||||||
|
if (!results.RowCount()) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
Buffs_Struct* bot_buffs = bot_inst->GetBuffs();
|
Buffs_Struct* bot_buffs = bot_inst->GetBuffs();
|
||||||
if (!bot_buffs)
|
|
||||||
|
if (!bot_buffs) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 max_slots = bot_inst->GetMaxBuffSlots();
|
||||||
|
for (int index = 0; index < max_slots; index++) {
|
||||||
|
bot_buffs[index].spellid = SPELL_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
int buff_count = 0;
|
int buff_count = 0;
|
||||||
for (auto row = results.begin(); row != results.end() && buff_count < BUFF_COUNT; ++row) {
|
for (auto row = results.begin(); row != results.end() && buff_count < BUFF_COUNT; ++row) {
|
||||||
bot_buffs[buff_count].spellid = atoi(row[0]);
|
bot_buffs[buff_count].spellid = atoul(row[0]);
|
||||||
bot_buffs[buff_count].casterlevel = atoi(row[1]);
|
bot_buffs[buff_count].casterlevel = atoul(row[1]);
|
||||||
//row[2] (duration_formula) can probably be removed
|
//row[2] (duration_formula) can probably be removed
|
||||||
bot_buffs[buff_count].ticsremaining = atoi(row[3]);
|
bot_buffs[buff_count].ticsremaining = Strings::ToInt(row[3]);
|
||||||
|
|
||||||
if (CalculatePoisonCounters(bot_buffs[buff_count].spellid) > 0)
|
bot_buffs[buff_count].counters = 0;
|
||||||
bot_buffs[buff_count].counters = atoi(row[4]);
|
if (CalculatePoisonCounters(bot_buffs[buff_count].spellid) > 0) {
|
||||||
else if (CalculateDiseaseCounters(bot_buffs[buff_count].spellid) > 0)
|
bot_buffs[buff_count].counters = atoul(row[4]);
|
||||||
bot_buffs[buff_count].counters = atoi(row[5]);
|
} else if (CalculateDiseaseCounters(bot_buffs[buff_count].spellid) > 0) {
|
||||||
else if (CalculateCurseCounters(bot_buffs[buff_count].spellid) > 0)
|
bot_buffs[buff_count].counters = atoul(row[5]);
|
||||||
bot_buffs[buff_count].counters = atoi(row[6]);
|
} else if (CalculateCurseCounters(bot_buffs[buff_count].spellid) > 0) {
|
||||||
else if (CalculateCorruptionCounters(bot_buffs[buff_count].spellid) > 0)
|
bot_buffs[buff_count].counters = atoul(row[6]);
|
||||||
bot_buffs[buff_count].counters = atoi(row[7]);
|
} else if (CalculateCorruptionCounters(bot_buffs[buff_count].spellid) > 0) {
|
||||||
|
bot_buffs[buff_count].counters = atoul(row[7]);
|
||||||
|
}
|
||||||
|
|
||||||
bot_buffs[buff_count].hit_number = atoi(row[8]);
|
bot_buffs[buff_count].hit_number = atoul(row[8]);
|
||||||
bot_buffs[buff_count].melee_rune = atoi(row[9]);
|
bot_buffs[buff_count].melee_rune = atoul(row[9]);
|
||||||
bot_buffs[buff_count].magic_rune = atoi(row[10]);
|
bot_buffs[buff_count].magic_rune = atoul(row[10]);
|
||||||
bot_buffs[buff_count].dot_rune = atoi(row[11]);
|
bot_buffs[buff_count].dot_rune = atoul(row[11]);
|
||||||
bot_buffs[buff_count].persistant_buff = ((atoi(row[12])) ? (true) : (false));
|
bot_buffs[buff_count].persistant_buff = (Strings::ToBool(row[12])) != 0;
|
||||||
bot_buffs[buff_count].caston_x = atoi(row[13]);
|
bot_buffs[buff_count].caston_x = Strings::ToInt(row[13]);
|
||||||
bot_buffs[buff_count].caston_y = atoi(row[14]);
|
bot_buffs[buff_count].caston_y = Strings::ToInt(row[14]);
|
||||||
bot_buffs[buff_count].caston_z = atoi(row[15]);
|
bot_buffs[buff_count].caston_z = Strings::ToInt(row[15]);
|
||||||
bot_buffs[buff_count].ExtraDIChance = atoi(row[16]);
|
bot_buffs[buff_count].ExtraDIChance = Strings::ToInt(row[16]);
|
||||||
bot_buffs[buff_count].instrument_mod = atoi(row[17]);
|
bot_buffs[buff_count].instrument_mod = atoul(row[17]);
|
||||||
bot_buffs[buff_count].casterid = 0;
|
bot_buffs[buff_count].casterid = 0;
|
||||||
++buff_count;
|
++buff_count;
|
||||||
}
|
}
|
||||||
@@ -962,7 +979,10 @@ bool BotDatabase::SaveTimers(Bot* bot_inst)
|
|||||||
if (bot_timers[timer_index] <= Timer::GetCurrentTime())
|
if (bot_timers[timer_index] <= Timer::GetCurrentTime())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
query = StringFormat("INSERT INTO `bot_timers` (`bot_id`, `timer_id`, `timer_value`) VALUES ('%u', '%u', '%u')", bot_inst->GetBotID(), (timer_index + 1), bot_timers[timer_index]);
|
query = fmt::format(
|
||||||
|
"REPLACE INTO `bot_timers` (`bot_id`, `timer_id`, `timer_value`) VALUES ('{}', '{}', '{}')",
|
||||||
|
bot_inst->GetBotID(), (timer_index + 1), bot_timers[timer_index]
|
||||||
|
);
|
||||||
auto results = database.QueryDatabase(query);
|
auto results = database.QueryDatabase(query);
|
||||||
if (!results.Success()) {
|
if (!results.Success()) {
|
||||||
DeleteTimers(bot_inst->GetBotID());
|
DeleteTimers(bot_inst->GetBotID());
|
||||||
@@ -3148,6 +3168,30 @@ std::string BotDatabase::GetBotNameByID(const uint32 bot_id)
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BotDatabase::SaveBotCasterRange(const uint32 owner_id, const uint32 bot_id, const uint32 bot_caster_range_value)
|
||||||
|
{
|
||||||
|
if (!owner_id || !bot_id) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
query = fmt::format(
|
||||||
|
"UPDATE `bot_data`"
|
||||||
|
" SET `caster_range` = '{}'"
|
||||||
|
" WHERE `owner_id` = '{}'"
|
||||||
|
" AND `bot_id` = '{}'",
|
||||||
|
bot_caster_range_value,
|
||||||
|
owner_id,
|
||||||
|
bot_id
|
||||||
|
);
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
|
||||||
|
if (!results.Success()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/* fail::Bot functions */
|
/* fail::Bot functions */
|
||||||
const char* BotDatabase::fail::LoadBotsList() { return "Failed to bots list"; }
|
const char* BotDatabase::fail::LoadBotsList() { return "Failed to bots list"; }
|
||||||
const char* BotDatabase::fail::LoadOwnerID() { return "Failed to load owner ID"; }
|
const char* BotDatabase::fail::LoadOwnerID() { return "Failed to load owner ID"; }
|
||||||
@@ -3202,6 +3246,7 @@ const char* BotDatabase::fail::ToggleAllHelmAppearances() { return "Failed to sa
|
|||||||
const char* BotDatabase::fail::SaveFollowDistance() { return "Failed to save follow distance"; }
|
const char* BotDatabase::fail::SaveFollowDistance() { return "Failed to save follow distance"; }
|
||||||
const char* BotDatabase::fail::SaveAllFollowDistances() { return "Failed to save all follow distances"; }
|
const char* BotDatabase::fail::SaveAllFollowDistances() { return "Failed to save all follow distances"; }
|
||||||
const char* BotDatabase::fail::SaveStopMeleeLevel() { return "Failed to save stop melee level"; }
|
const char* BotDatabase::fail::SaveStopMeleeLevel() { return "Failed to save stop melee level"; }
|
||||||
|
const char* BotDatabase::fail::SaveBotCasterRange() { return "Failed to save caster range"; }
|
||||||
|
|
||||||
/* fail::Bot heal rotation functions */
|
/* fail::Bot heal rotation functions */
|
||||||
const char* BotDatabase::fail::LoadHealRotationIDByBotID() { return "Failed to load heal rotation ID by bot ID"; }
|
const char* BotDatabase::fail::LoadHealRotationIDByBotID() { return "Failed to load heal rotation ID by bot ID"; }
|
||||||
|
|||||||
@@ -146,6 +146,8 @@ public:
|
|||||||
bool SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag);
|
bool SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag);
|
||||||
bool SaveOwnerOption(const uint32 owner_id, const std::pair<size_t, size_t> type, const std::pair<bool, bool> flag);
|
bool SaveOwnerOption(const uint32 owner_id, const std::pair<size_t, size_t> type, const std::pair<bool, bool> flag);
|
||||||
|
|
||||||
|
bool SaveBotCasterRange(const uint32 owner_id, const uint32 bot_id, const uint32 bot_caster_range_value);
|
||||||
|
|
||||||
/* Bot bot-group functions */
|
/* Bot bot-group functions */
|
||||||
bool QueryBotGroupExistence(const std::string& botgroup_name);
|
bool QueryBotGroupExistence(const std::string& botgroup_name);
|
||||||
|
|
||||||
@@ -250,6 +252,7 @@ public:
|
|||||||
static const char* SaveFollowDistance();
|
static const char* SaveFollowDistance();
|
||||||
static const char* SaveAllFollowDistances();
|
static const char* SaveAllFollowDistances();
|
||||||
static const char* SaveStopMeleeLevel();
|
static const char* SaveStopMeleeLevel();
|
||||||
|
static const char* SaveBotCasterRange();
|
||||||
|
|
||||||
/* fail::Bot bot-group functions */
|
/* fail::Bot bot-group functions */
|
||||||
static const char* QueryBotGroupExistence();
|
static const char* QueryBotGroupExistence();
|
||||||
|
|||||||
+44
-3
@@ -98,8 +98,10 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
if (!(!addMob->IsImmuneToSpell(botSpell.SpellId, this) && addMob->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) {
|
if (!(!addMob->IsImmuneToSpell(botSpell.SpellId, this) && addMob->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost);
|
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, addMob)) {
|
||||||
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost);
|
||||||
|
}
|
||||||
if (castedSpell) {
|
if (castedSpell) {
|
||||||
BotGroupSay(
|
BotGroupSay(
|
||||||
this,
|
this,
|
||||||
@@ -260,7 +262,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
|
|
||||||
uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore();
|
uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore();
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == BARD) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
|
||||||
|
}
|
||||||
|
|
||||||
if (castedSpell) {
|
if (castedSpell) {
|
||||||
/*if (TempDontHealMeBeforeTime != tar->DontHealMeBefore())
|
/*if (TempDontHealMeBeforeTime != tar->DontHealMeBefore())
|
||||||
@@ -340,7 +344,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
}
|
}
|
||||||
uint32 TempDontRootMeBefore = tar->DontRootMeBefore();
|
uint32 TempDontRootMeBefore = tar->DontRootMeBefore();
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontRootMeBefore);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontRootMeBefore);
|
||||||
|
}
|
||||||
|
|
||||||
if (TempDontRootMeBefore != tar->DontRootMeBefore()) {
|
if (TempDontRootMeBefore != tar->DontRootMeBefore()) {
|
||||||
tar->SetDontRootMeBefore(TempDontRootMeBefore);
|
tar->SetDontRootMeBefore(TempDontRootMeBefore);
|
||||||
@@ -488,8 +494,11 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
if (IsInvulnerabilitySpell(botSpell.SpellId)) {
|
if (IsInvulnerabilitySpell(botSpell.SpellId)) {
|
||||||
tar = this; //target self for invul type spells
|
tar = this; //target self for invul type spells
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == BARD) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SpellType_Nuke: {
|
case SpellType_Nuke: {
|
||||||
@@ -573,8 +582,10 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SpellType_Dispel: {
|
case SpellType_Dispel: {
|
||||||
@@ -594,9 +605,11 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
// TODO: Check target to see if there is anything to dispel
|
// TODO: Check target to see if there is anything to dispel
|
||||||
|
|
||||||
if (tar->CountDispellableBuffs() > 0) {
|
if (tar->CountDispellableBuffs() > 0) {
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SpellType_Pet: {
|
case SpellType_Pet: {
|
||||||
@@ -768,7 +781,6 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
if (CheckSpellRecastTimers(this, itr->SpellIndex)) {
|
if (CheckSpellRecastTimers(this, itr->SpellIndex)) {
|
||||||
uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore();
|
uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore();
|
||||||
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontBuffMeBefore);
|
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontBuffMeBefore);
|
||||||
|
|
||||||
if (TempDontBuffMeBefore != tar->DontBuffMeBefore())
|
if (TempDontBuffMeBefore != tar->DontBuffMeBefore())
|
||||||
tar->SetDontBuffMeBefore(TempDontBuffMeBefore);
|
tar->SetDontBuffMeBefore(TempDontBuffMeBefore);
|
||||||
}
|
}
|
||||||
@@ -797,8 +809,10 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)))
|
if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SpellType_Snare: {
|
case SpellType_Snare: {
|
||||||
@@ -820,7 +834,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
|
|
||||||
uint32 TempDontSnareMeBefore = tar->DontSnareMeBefore();
|
uint32 TempDontSnareMeBefore = tar->DontSnareMeBefore();
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontSnareMeBefore);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontSnareMeBefore);
|
||||||
|
}
|
||||||
|
|
||||||
if (TempDontSnareMeBefore != tar->DontSnareMeBefore())
|
if (TempDontSnareMeBefore != tar->DontSnareMeBefore())
|
||||||
tar->SetDontSnareMeBefore(TempDontSnareMeBefore);
|
tar->SetDontSnareMeBefore(TempDontSnareMeBefore);
|
||||||
@@ -888,7 +904,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
|
|
||||||
uint32 TempDontDotMeBefore = tar->DontDotMeBefore();
|
uint32 TempDontDotMeBefore = tar->DontDotMeBefore();
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore);
|
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore);
|
||||||
|
}
|
||||||
|
|
||||||
if (TempDontDotMeBefore != tar->DontDotMeBefore())
|
if (TempDontDotMeBefore != tar->DontDotMeBefore())
|
||||||
tar->SetDontDotMeBefore(TempDontDotMeBefore);
|
tar->SetDontDotMeBefore(TempDontDotMeBefore);
|
||||||
@@ -929,7 +947,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
|
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||||
|
}
|
||||||
if (castedSpell)
|
if (castedSpell)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -956,7 +976,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
|
if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||||
|
}
|
||||||
|
|
||||||
if (castedSpell && GetClass() != BARD) {
|
if (castedSpell && GetClass() != BARD) {
|
||||||
BotGroupSay(
|
BotGroupSay(
|
||||||
@@ -992,8 +1014,10 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)))
|
if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SpellType_Cure: {
|
case SpellType_Cure: {
|
||||||
@@ -1054,7 +1078,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
|||||||
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
|
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||||
|
}
|
||||||
if (castedSpell) {
|
if (castedSpell) {
|
||||||
BotGroupSay(
|
BotGroupSay(
|
||||||
this,
|
this,
|
||||||
@@ -1764,8 +1790,9 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) {
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore();
|
uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore();
|
||||||
|
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
|
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
|
||||||
|
}
|
||||||
|
|
||||||
if (castedSpell) {
|
if (castedSpell) {
|
||||||
BotGroupSay(
|
BotGroupSay(
|
||||||
@@ -3456,3 +3483,17 @@ bool Bot::HasBotSpellEntry(uint16 spellid) {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Bot::IsValidSpellRange(uint16 spell_id, Mob const* tar) {
|
||||||
|
if (!IsValidSpell(spell_id)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tar) {
|
||||||
|
int spellrange = (GetActSpellRange(spell_id, spells[spell_id].range) * GetActSpellRange(spell_id, spells[spell_id].range));
|
||||||
|
if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
@@ -792,6 +792,7 @@ public:
|
|||||||
void SendTradeskillDetails(uint32 recipe_id);
|
void SendTradeskillDetails(uint32 recipe_id);
|
||||||
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
|
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
|
||||||
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, EQ::skills::SkillType tradeskill);
|
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, EQ::skills::SkillType tradeskill);
|
||||||
|
bool CheckTradeskillLoreConflict(int32 recipe_id);
|
||||||
void InitInnates();
|
void InitInnates();
|
||||||
|
|
||||||
void GMKill();
|
void GMKill();
|
||||||
|
|||||||
@@ -2144,7 +2144,7 @@ void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app)
|
|||||||
theme = LDoNThemes::RUJ;
|
theme = LDoNThemes::RUJ;
|
||||||
} else if (item->LDoNTheme & LDoNThemeBits::MMCBit) {
|
} else if (item->LDoNTheme & LDoNThemeBits::MMCBit) {
|
||||||
theme = LDoNThemes::MMC;
|
theme = LDoNThemes::MMC;
|
||||||
} else if (item->LDoNTheme & LDoNThemeBits::RUJBit) {
|
} else if (item->LDoNTheme & LDoNThemeBits::MIRBit) {
|
||||||
theme = LDoNThemes::MIR;
|
theme = LDoNThemes::MIR;
|
||||||
} else if (item->LDoNTheme & LDoNThemeBits::GUKBit) {
|
} else if (item->LDoNTheme & LDoNThemeBits::GUKBit) {
|
||||||
theme = LDoNThemes::GUK;
|
theme = LDoNThemes::GUK;
|
||||||
@@ -9951,7 +9951,7 @@ void Client::Handle_OP_Mend(const EQApplicationPacket *app)
|
|||||||
Message(Chat::Red, "Ability recovery time not yet met.");
|
Message(Chat::Red, "Ability recovery time not yet met.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
p_timers.Start(pTimerMend, MendReuseTime - 1);
|
p_timers.Start(pTimerMend, (MendReuseTime - GetSkillReuseTime(EQ::skills::SkillMend)));
|
||||||
|
|
||||||
int mendhp = GetMaxHP() / 4;
|
int mendhp = GetMaxHP() / 4;
|
||||||
int currenthp = GetHP();
|
int currenthp = GetHP();
|
||||||
@@ -9963,9 +9963,11 @@ void Client::Handle_OP_Mend(const EQApplicationPacket *app)
|
|||||||
mendhp *= 2;
|
mendhp *= 2;
|
||||||
MessageString(Chat::LightBlue, MEND_CRITICAL);
|
MessageString(Chat::LightBlue, MEND_CRITICAL);
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
MessageString(Chat::LightBlue, MEND_SUCCESS);
|
||||||
|
}
|
||||||
SetHP(GetHP() + mendhp);
|
SetHP(GetHP() + mendhp);
|
||||||
SendHPUpdate();
|
SendHPUpdate();
|
||||||
MessageString(Chat::LightBlue, MEND_SUCCESS);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
/* the purpose of the following is to make the chance to worsen wounds much less common,
|
/* the purpose of the following is to make the chance to worsen wounds much less common,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ std::string GetModifyNPCStatDescription(std::string stat);
|
|||||||
void SendNPCEditSubCommands(Client *c);
|
void SendNPCEditSubCommands(Client *c);
|
||||||
void SendRuleSubCommands(Client *c);
|
void SendRuleSubCommands(Client *c);
|
||||||
void SendGuildSubCommands(Client *c);
|
void SendGuildSubCommands(Client *c);
|
||||||
|
void SendPeekInvSubCommands(Client *c);
|
||||||
|
|
||||||
// Commands
|
// Commands
|
||||||
void command_acceptrules(Client *c, const Seperator *sep);
|
void command_acceptrules(Client *c, const Seperator *sep);
|
||||||
|
|||||||
+25
-18
@@ -53,6 +53,14 @@ Doors::Doors(const DoorsRepository::Doors &door) :
|
|||||||
strn0cpy(m_door_name, door.name.c_str(), sizeof(m_door_name));
|
strn0cpy(m_door_name, door.name.c_str(), sizeof(m_door_name));
|
||||||
strn0cpy(m_destination_zone_name, door.dest_zone.c_str(), sizeof(m_destination_zone_name));
|
strn0cpy(m_destination_zone_name, door.dest_zone.c_str(), sizeof(m_destination_zone_name));
|
||||||
|
|
||||||
|
// destination helpers
|
||||||
|
if (!door.dest_zone.empty() && Strings::ToLower(door.dest_zone) != "none" && !door.dest_zone.empty()) {
|
||||||
|
m_has_destination_zone = true;
|
||||||
|
}
|
||||||
|
if (!door.dest_zone.empty() && !door.zone.empty() && Strings::EqualFold(door.dest_zone, door.zone)) {
|
||||||
|
m_same_destination_zone = true;
|
||||||
|
}
|
||||||
|
|
||||||
m_database_id = door.id;
|
m_database_id = door.id;
|
||||||
m_door_id = door.doorid;
|
m_door_id = door.doorid;
|
||||||
m_incline = door.incline;
|
m_incline = door.incline;
|
||||||
@@ -450,7 +458,7 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
|
|||||||
m_close_timer.Start();
|
m_close_timer.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncmp(m_destination_zone_name, "NONE", strlen("NONE")) == 0) {
|
if (!HasDestinationZone()) {
|
||||||
SetOpenState(true);
|
SetOpenState(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -497,19 +505,15 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// teleport door
|
||||||
* Teleport door
|
if (((m_open_type == 57) || (m_open_type == 58)) && HasDestinationZone()) {
|
||||||
*/
|
bool has_key_required = (required_key_item && ((required_key_item == player_key) || sender->GetGM()));
|
||||||
if (((m_open_type == 57) || (m_open_type == 58)) &&
|
|
||||||
(strncmp(m_destination_zone_name, "NONE", strlen("NONE")) != 0)) {
|
|
||||||
|
|
||||||
/**
|
if (IsDestinationZoneSame() && (!required_key_item)) {
|
||||||
* If click destination is same zone and doesn't require a key
|
|
||||||
*/
|
|
||||||
if ((strncmp(m_destination_zone_name, m_zone_name, strlen(m_zone_name)) == 0) && (!required_key_item)) {
|
|
||||||
if (!disable_add_to_key_ring) {
|
if (!disable_add_to_key_ring) {
|
||||||
sender->KeyRingAdd(player_key);
|
sender->KeyRingAdd(player_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
sender->MovePC(
|
sender->MovePC(
|
||||||
zone->GetZoneID(),
|
zone->GetZoneID(),
|
||||||
zone->GetInstanceID(),
|
zone->GetInstanceID(),
|
||||||
@@ -519,14 +523,7 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
|
|||||||
m_destination.w
|
m_destination.w
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/**
|
else if ((!IsDoorOpen() || m_open_type == 58) && has_key_required) {
|
||||||
* If requires a key
|
|
||||||
*/
|
|
||||||
else if (
|
|
||||||
(!IsDoorOpen() || m_open_type == 58) &&
|
|
||||||
(required_key_item && ((required_key_item == player_key) || sender->GetGM()))
|
|
||||||
) {
|
|
||||||
|
|
||||||
if (!disable_add_to_key_ring) {
|
if (!disable_add_to_key_ring) {
|
||||||
sender->KeyRingAdd(player_key);
|
sender->KeyRingAdd(player_key);
|
||||||
}
|
}
|
||||||
@@ -896,3 +893,13 @@ float Doors::GetHeading()
|
|||||||
{
|
{
|
||||||
return m_position.w;
|
return m_position.w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Doors::HasDestinationZone() const
|
||||||
|
{
|
||||||
|
return m_has_destination_zone;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Doors::IsDestinationZoneSame() const
|
||||||
|
{
|
||||||
|
return m_same_destination_zone;
|
||||||
|
}
|
||||||
|
|||||||
@@ -67,8 +67,13 @@ public:
|
|||||||
float GetZ();
|
float GetZ();
|
||||||
float GetHeading();
|
float GetHeading();
|
||||||
|
|
||||||
|
bool HasDestinationZone() const;
|
||||||
|
bool IsDestinationZoneSame() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
bool m_has_destination_zone = false;
|
||||||
|
bool m_same_destination_zone = false;
|
||||||
uint32 m_database_id;
|
uint32 m_database_id;
|
||||||
uint8 m_door_id;
|
uint8 m_door_id;
|
||||||
char m_zone_name[32];
|
char m_zone_name[32];
|
||||||
|
|||||||
@@ -116,7 +116,7 @@ void command_gearup(Client *c, const Seperator *sep)
|
|||||||
if (t->IsClient()) {
|
if (t->IsClient()) {
|
||||||
has_item = t->CastToClient()->GetInv().HasItem(item_id, 1, invWhereWorn) != INVALID_INDEX;
|
has_item = t->CastToClient()->GetInv().HasItem(item_id, 1, invWhereWorn) != INVALID_INDEX;
|
||||||
} else if (t->IsBot()) {
|
} else if (t->IsBot()) {
|
||||||
has_item = t->CastToBot()->HasBotItem(item_id);
|
has_item = t->CastToBot()->HasBotItem(item_id) != INVALID_INDEX;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool can_wear_item = false;
|
bool can_wear_item = false;
|
||||||
|
|||||||
+291
-173
@@ -3,6 +3,12 @@
|
|||||||
|
|
||||||
void command_peekinv(Client *c, const Seperator *sep)
|
void command_peekinv(Client *c, const Seperator *sep)
|
||||||
{
|
{
|
||||||
|
auto arguments = sep->argnum;
|
||||||
|
if (!arguments) {
|
||||||
|
SendPeekInvSubCommands(c);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// this can be cleaned up once inventory is cleaned up
|
// this can be cleaned up once inventory is cleaned up
|
||||||
enum {
|
enum {
|
||||||
peekNone = 0x0000,
|
peekNone = 0x0000,
|
||||||
@@ -15,63 +21,83 @@ void command_peekinv(Client *c, const Seperator *sep)
|
|||||||
peekShBank = 0x0040,
|
peekShBank = 0x0040,
|
||||||
peekTrade = 0x0080,
|
peekTrade = 0x0080,
|
||||||
peekWorld = 0x0100,
|
peekWorld = 0x0100,
|
||||||
peekOutOfScope = (peekWorld * 2) // less than
|
peekOutOfScope = (peekWorld * 2)
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *scope_prefix[] = {"equip", "gen", "cursor", "limbo", "trib", "bank", "shbank", "trade", "world"};
|
|
||||||
|
|
||||||
static const int16 scope_range[][2] = {
|
static const int16 scope_range[][2] = {
|
||||||
{EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END},
|
{ EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END },
|
||||||
{EQ::invslot::GENERAL_BEGIN, EQ::invslot::GENERAL_END},
|
{ EQ::invslot::GENERAL_BEGIN, EQ::invslot::GENERAL_END },
|
||||||
{EQ::invslot::slotCursor, EQ::invslot::slotCursor},
|
{ EQ::invslot::slotCursor, EQ::invslot::slotCursor },
|
||||||
{EQ::invslot::SLOT_INVALID, EQ::invslot::SLOT_INVALID},
|
{ EQ::invslot::SLOT_INVALID, EQ::invslot::SLOT_INVALID },
|
||||||
{EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END},
|
{ EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END },
|
||||||
{EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END},
|
{ EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END },
|
||||||
{EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END},
|
{ EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END },
|
||||||
{EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END},
|
{ EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END },
|
||||||
{EQ::invslot::SLOT_BEGIN, (EQ::invtype::WORLD_SIZE - 1)}
|
{ EQ::invslot::SLOT_BEGIN, (EQ::invtype::WORLD_SIZE - 1) }
|
||||||
};
|
};
|
||||||
|
|
||||||
static const bool scope_bag[] = {false, true, true, true, false, true, true, true, true};
|
static const bool scope_bag[] = {
|
||||||
|
false, // Equip
|
||||||
|
true, // General
|
||||||
|
true, // Cursor
|
||||||
|
true, // Cursor Limbo
|
||||||
|
false, // Tribute
|
||||||
|
true, // Bank
|
||||||
|
true, // Shared Bank
|
||||||
|
true, // Trade
|
||||||
|
true // World
|
||||||
|
};
|
||||||
|
|
||||||
if (!c) {
|
int scope_mask = peekNone;
|
||||||
|
|
||||||
|
const bool is_all = !strcasecmp(sep->arg[1], "all");
|
||||||
|
const bool is_all_bank = !strcasecmp(sep->arg[1], "allbank");
|
||||||
|
const bool is_bank = !strcasecmp(sep->arg[1], "bank");
|
||||||
|
const bool is_cursor = !strcasecmp(sep->arg[1], "cursor");
|
||||||
|
const bool is_cursor_limbo = !strcasecmp(sep->arg[1], "curlimbo");
|
||||||
|
const bool is_equipment = !strcasecmp(sep->arg[1], "equip");
|
||||||
|
const bool is_general = !strcasecmp(sep->arg[1], "gen");
|
||||||
|
const bool is_limbo = !strcasecmp(sep->arg[1], "limbo");
|
||||||
|
const bool is_possessions = !strcasecmp(sep->arg[1], "poss");
|
||||||
|
const bool is_shared_bank = !strcasecmp(sep->arg[1], "shbank");
|
||||||
|
const bool is_trade = !strcasecmp(sep->arg[1], "trade");
|
||||||
|
const bool is_tribute = !strcasecmp(sep->arg[1], "trib");
|
||||||
|
const bool is_world = !strcasecmp(sep->arg[1], "world");
|
||||||
|
|
||||||
|
if (is_all) {
|
||||||
|
scope_mask = (peekOutOfScope - 1);
|
||||||
|
} else if (is_all_bank) {
|
||||||
|
scope_mask |= (peekBank | peekShBank);
|
||||||
|
} else if (is_bank) {
|
||||||
|
scope_mask |= peekBank;
|
||||||
|
} else if (is_cursor) {
|
||||||
|
scope_mask |= peekCursor;
|
||||||
|
} else if (is_cursor_limbo) {
|
||||||
|
scope_mask |= (peekCursor | peekLimbo);
|
||||||
|
} else if (is_equipment) {
|
||||||
|
scope_mask |= peekEquip;
|
||||||
|
} else if (is_general) {
|
||||||
|
scope_mask |= peekGen;
|
||||||
|
} else if (is_limbo) {
|
||||||
|
scope_mask |= peekLimbo;
|
||||||
|
} else if (is_possessions) {
|
||||||
|
scope_mask |= (peekEquip | peekGen | peekCursor);
|
||||||
|
} else if (is_shared_bank) {
|
||||||
|
scope_mask |= peekShBank;
|
||||||
|
} else if (is_tribute) {
|
||||||
|
scope_mask |= peekTrib;
|
||||||
|
} else if (is_trade) {
|
||||||
|
scope_mask |= peekTrade;
|
||||||
|
} else if (is_world) {
|
||||||
|
scope_mask |= peekWorld;
|
||||||
|
} else {
|
||||||
|
SendPeekInvSubCommands(c);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (c->GetTarget() && !c->GetTarget()->IsClient()) {
|
auto t = c;
|
||||||
c->Message(Chat::White, "You must target a PC for this command.");
|
if (c->GetTarget() && c->GetTarget()->IsClient()) {
|
||||||
return;
|
t = c->GetTarget()->CastToClient();
|
||||||
}
|
|
||||||
|
|
||||||
int scopeMask = peekNone;
|
|
||||||
|
|
||||||
if (strcasecmp(sep->arg[1], "all") == 0) { scopeMask = (peekOutOfScope - 1); }
|
|
||||||
else if (strcasecmp(sep->arg[1], "equip") == 0) { scopeMask |= peekEquip; }
|
|
||||||
else if (strcasecmp(sep->arg[1], "gen") == 0) { scopeMask |= peekGen; }
|
|
||||||
else if (strcasecmp(sep->arg[1], "cursor") == 0) { scopeMask |= peekCursor; }
|
|
||||||
else if (strcasecmp(sep->arg[1], "poss") == 0) { scopeMask |= (peekEquip | peekGen | peekCursor); }
|
|
||||||
else if (strcasecmp(sep->arg[1], "limbo") == 0) { scopeMask |= peekLimbo; }
|
|
||||||
else if (strcasecmp(sep->arg[1], "curlim") == 0) { scopeMask |= (peekCursor | peekLimbo); }
|
|
||||||
else if (strcasecmp(sep->arg[1], "trib") == 0) { scopeMask |= peekTrib; }
|
|
||||||
else if (strcasecmp(sep->arg[1], "bank") == 0) { scopeMask |= peekBank; }
|
|
||||||
else if (strcasecmp(sep->arg[1], "shbank") == 0) { scopeMask |= peekShBank; }
|
|
||||||
else if (strcasecmp(sep->arg[1], "allbank") == 0) { scopeMask |= (peekBank | peekShBank); }
|
|
||||||
else if (strcasecmp(sep->arg[1], "trade") == 0) { scopeMask |= peekTrade; }
|
|
||||||
else if (strcasecmp(sep->arg[1], "world") == 0) { scopeMask |= peekWorld; }
|
|
||||||
|
|
||||||
if (!scopeMask) {
|
|
||||||
c->Message(
|
|
||||||
Chat::White,
|
|
||||||
"Usage: #peekinv [equip|gen|cursor|poss|limbo|curlim|trib|bank|shbank|allbank|trade|world|all]"
|
|
||||||
);
|
|
||||||
c->Message(Chat::White, "- Displays a portion of the targeted user's inventory");
|
|
||||||
c->Message(Chat::White, "- Caution: 'all' is a lot of information!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Client *targetClient = c;
|
|
||||||
if (c->GetTarget()) {
|
|
||||||
targetClient = c->GetTarget()->CastToClient();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const EQ::ItemInstance *inst_main = nullptr;
|
const EQ::ItemInstance *inst_main = nullptr;
|
||||||
@@ -82,62 +108,81 @@ void command_peekinv(Client *c, const Seperator *sep)
|
|||||||
EQ::SayLinkEngine linker;
|
EQ::SayLinkEngine linker;
|
||||||
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
|
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
|
||||||
|
|
||||||
c->Message(Chat::White, "Displaying inventory for %s...", targetClient->GetName());
|
|
||||||
|
|
||||||
Object *objectTradeskill = targetClient->GetTradeskillObject();
|
|
||||||
|
|
||||||
bool itemsFound = false;
|
|
||||||
|
|
||||||
for (int scopeIndex = 0, scopeBit = peekEquip; scopeBit < peekOutOfScope; ++scopeIndex, scopeBit <<= 1) {
|
|
||||||
if (scopeBit & ~scopeMask) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scopeBit & peekWorld) {
|
|
||||||
if (objectTradeskill == nullptr) {
|
|
||||||
c->Message(Chat::Default, "No world tradeskill object selected...");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
"[WorldObject DBID: %i (entityid: %i)]",
|
fmt::format(
|
||||||
objectTradeskill->GetDBID(),
|
"Displaying inventory of {}.",
|
||||||
objectTradeskill->GetID());
|
c->GetTargetDescription(t)
|
||||||
}
|
).c_str()
|
||||||
}
|
);
|
||||||
|
|
||||||
for (int16 indexMain = scope_range[scopeIndex][0]; indexMain <= scope_range[scopeIndex][1]; ++indexMain) {
|
auto o = t->GetTradeskillObject();
|
||||||
if (indexMain == EQ::invslot::SLOT_INVALID) {
|
auto found_items = false;
|
||||||
|
|
||||||
|
for (int scope_index = 0, scope_bit = peekEquip; scope_bit < peekOutOfScope; ++scope_index, scope_bit <<= 1) {
|
||||||
|
if (scope_bit & ~scope_mask) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
inst_main = ((scopeBit & peekWorld) ? objectTradeskill->GetItem(indexMain) : targetClient->GetInv().GetItem(
|
if (scope_bit & peekWorld) {
|
||||||
indexMain
|
if (!o) {
|
||||||
));
|
c->Message(Chat::White, "No world Tradeskill object selected.");
|
||||||
if (inst_main) {
|
continue;
|
||||||
itemsFound = true;
|
} else {
|
||||||
item_data = inst_main->GetItem();
|
c->Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"[World Object] Database ID: {} Entity ID: {}",
|
||||||
|
o->GetDBID(),
|
||||||
|
o->GetID()
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
else {
|
}
|
||||||
|
|
||||||
|
for (int16 index_main = scope_range[scope_index][0]; index_main <= scope_range[scope_index][1]; ++index_main) {
|
||||||
|
if (index_main == EQ::invslot::SLOT_INVALID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
inst_main = (
|
||||||
|
(scope_bit & peekWorld) ?
|
||||||
|
o->GetItem(index_main) :
|
||||||
|
t->GetInv().GetItem(index_main)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (inst_main) {
|
||||||
|
found_items = true;
|
||||||
|
item_data = inst_main->GetItem();
|
||||||
|
} else {
|
||||||
item_data = nullptr;
|
item_data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
linker.SetItemInst(inst_main);
|
linker.SetItemInst(inst_main);
|
||||||
|
|
||||||
|
if (item_data) {
|
||||||
c->Message(
|
c->Message(
|
||||||
(item_data == nullptr),
|
Chat::White,
|
||||||
"%sSlot: %i, Item: %i (%s), Charges: %i",
|
fmt::format(
|
||||||
scope_prefix[scopeIndex],
|
"Slot {} | {} ({}){}",
|
||||||
((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain),
|
((scope_bit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main),
|
||||||
((item_data == nullptr) ? 0 : item_data->ID),
|
linker.GenerateLink(),
|
||||||
linker.GenerateLink().c_str(),
|
item_data->ID,
|
||||||
((inst_main == nullptr) ? 0 : inst_main->GetCharges())
|
(
|
||||||
|
inst_main->IsStackable() && inst_main->GetCharges() > 0 ?
|
||||||
|
fmt::format(
|
||||||
|
" (Stack of {})",
|
||||||
|
inst_main->GetCharges()
|
||||||
|
) :
|
||||||
|
""
|
||||||
|
)
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (inst_main && inst_main->IsClassCommon()) {
|
if (inst_main && inst_main->IsClassCommon()) {
|
||||||
for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) {
|
for (uint8 augment_index = EQ::invaug::SOCKET_BEGIN; augment_index <= EQ::invaug::SOCKET_END; ++augment_index) {
|
||||||
inst_aug = inst_main->GetItem(indexAug);
|
inst_aug = inst_main->GetItem(augment_index);
|
||||||
if (!inst_aug) { // extant only
|
if (!inst_aug) { // extant only
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -146,25 +191,33 @@ void command_peekinv(Client *c, const Seperator *sep)
|
|||||||
linker.SetItemInst(inst_aug);
|
linker.SetItemInst(inst_aug);
|
||||||
|
|
||||||
c->Message(
|
c->Message(
|
||||||
(item_data == nullptr),
|
Chat::White,
|
||||||
".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
fmt::format(
|
||||||
scope_prefix[scopeIndex],
|
"Slot {} (Augment Slot {}) | {} ({}){}",
|
||||||
INVALID_INDEX,
|
((scope_bit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main),
|
||||||
((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain),
|
augment_index,
|
||||||
indexAug,
|
linker.GenerateLink(),
|
||||||
((item_data == nullptr) ? 0 : item_data->ID),
|
item_data->ID,
|
||||||
linker.GenerateLink().c_str(),
|
(
|
||||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
inst_aug->IsStackable() && inst_aug->GetCharges() > 0 ?
|
||||||
|
fmt::format(
|
||||||
|
" (Stack of {})",
|
||||||
|
inst_aug->GetCharges()
|
||||||
|
) :
|
||||||
|
""
|
||||||
|
)
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) {
|
if (!scope_bag[scope_index] || !(inst_main && inst_main->IsClassBag())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) {
|
for (uint8 sub_index = EQ::invbag::SLOT_BEGIN; sub_index <= EQ::invbag::SLOT_END; ++sub_index) {
|
||||||
inst_sub = inst_main->GetItem(indexSub);
|
inst_sub = inst_main->GetItem(sub_index);
|
||||||
if (!inst_sub) { // extant only
|
if (!inst_sub) { // extant only
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -173,20 +226,32 @@ void command_peekinv(Client *c, const Seperator *sep)
|
|||||||
linker.SetItemInst(inst_sub);
|
linker.SetItemInst(inst_sub);
|
||||||
|
|
||||||
c->Message(
|
c->Message(
|
||||||
(item_data == nullptr),
|
Chat::White,
|
||||||
"..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i",
|
fmt::format(
|
||||||
scope_prefix[scopeIndex],
|
"Slot {} Bag Slot {} | {} ({}){}",
|
||||||
((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(indexMain, indexSub)),
|
(
|
||||||
((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain),
|
(scope_bit & peekWorld) ?
|
||||||
indexSub,
|
INVALID_INDEX :
|
||||||
((item_data == nullptr) ? 0 : item_data->ID),
|
EQ::InventoryProfile::CalcSlotId(index_main, sub_index)
|
||||||
linker.GenerateLink().c_str(),
|
),
|
||||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
((scope_bit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main),
|
||||||
|
sub_index,
|
||||||
|
linker.GenerateLink(),
|
||||||
|
item_data->ID,
|
||||||
|
(
|
||||||
|
inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ?
|
||||||
|
fmt::format(
|
||||||
|
" (Stack of {})",
|
||||||
|
inst_sub->GetCharges()
|
||||||
|
) :
|
||||||
|
""
|
||||||
|
)
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (inst_sub->IsClassCommon()) {
|
if (inst_sub->IsClassCommon()) {
|
||||||
for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) {
|
for (uint8 augment_index = EQ::invaug::SOCKET_BEGIN; augment_index <= EQ::invaug::SOCKET_END; ++augment_index) {
|
||||||
inst_aug = inst_sub->GetItem(indexAug);
|
inst_aug = inst_sub->GetItem(augment_index);
|
||||||
if (!inst_aug) { // extant only
|
if (!inst_aug) { // extant only
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -195,58 +260,73 @@ void command_peekinv(Client *c, const Seperator *sep)
|
|||||||
linker.SetItemInst(inst_aug);
|
linker.SetItemInst(inst_aug);
|
||||||
|
|
||||||
c->Message(
|
c->Message(
|
||||||
(item_data == nullptr),
|
Chat::White,
|
||||||
"...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
fmt::format(
|
||||||
scope_prefix[scopeIndex],
|
"Slot {} Bag Slot {} (Augment Slot {}) | {} ({}){}",
|
||||||
INVALID_INDEX,
|
(
|
||||||
((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(
|
(scope_bit & peekWorld) ?
|
||||||
indexMain,
|
INVALID_INDEX :
|
||||||
indexSub
|
EQ::InventoryProfile::CalcSlotId(index_main,sub_index)
|
||||||
)),
|
),
|
||||||
indexSub,
|
sub_index,
|
||||||
indexAug,
|
augment_index,
|
||||||
((item_data == nullptr) ? 0 : item_data->ID),
|
linker.GenerateLink(),
|
||||||
linker.GenerateLink().c_str(),
|
item_data->ID,
|
||||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
(
|
||||||
|
inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ?
|
||||||
|
fmt::format(
|
||||||
|
" (Stack of {})",
|
||||||
|
inst_sub->GetCharges()
|
||||||
|
) :
|
||||||
|
""
|
||||||
|
)
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scopeBit & peekLimbo) {
|
if (scope_bit & peekLimbo) {
|
||||||
int limboIndex = 0;
|
int limboIndex = 0;
|
||||||
for (auto it = targetClient->GetInv().cursor_cbegin();
|
for (auto it = t->GetInv().cursor_cbegin(); (it != t->GetInv().cursor_cend()); ++it, ++limboIndex) {
|
||||||
(it != targetClient->GetInv().cursor_cend());
|
if (it == t->GetInv().cursor_cbegin()) {
|
||||||
++it, ++limboIndex) {
|
|
||||||
if (it == targetClient->GetInv().cursor_cbegin()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
inst_main = *it;
|
inst_main = *it;
|
||||||
if (inst_main) {
|
if (inst_main) {
|
||||||
itemsFound = true;
|
found_items = true;
|
||||||
item_data = inst_main->GetItem();
|
item_data = inst_main->GetItem();
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
item_data = nullptr;
|
item_data = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
linker.SetItemInst(inst_main);
|
linker.SetItemInst(inst_main);
|
||||||
|
|
||||||
|
if (item_data) {
|
||||||
c->Message(
|
c->Message(
|
||||||
(item_data == nullptr),
|
Chat::White,
|
||||||
"%sSlot: %i, Item: %i (%s), Charges: %i",
|
fmt::format(
|
||||||
scope_prefix[scopeIndex],
|
"Slot {} | {} ({}){}",
|
||||||
(8000 + limboIndex),
|
(8000 + limboIndex),
|
||||||
((item_data == nullptr) ? 0 : item_data->ID),
|
item_data->ID,
|
||||||
linker.GenerateLink().c_str(),
|
linker.GenerateLink(),
|
||||||
((inst_main == nullptr) ? 0 : inst_main->GetCharges())
|
(
|
||||||
|
inst_main->IsStackable() && inst_main->GetCharges() > 0 ?
|
||||||
|
fmt::format(
|
||||||
|
" (Stack of {})",
|
||||||
|
inst_main->GetCharges()
|
||||||
|
) :
|
||||||
|
""
|
||||||
|
)
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (inst_main && inst_main->IsClassCommon()) {
|
if (inst_main && inst_main->IsClassCommon()) {
|
||||||
for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) {
|
for (uint8 augment_index = EQ::invaug::SOCKET_BEGIN; augment_index <= EQ::invaug::SOCKET_END; ++augment_index) {
|
||||||
inst_aug = inst_main->GetItem(indexAug);
|
inst_aug = inst_main->GetItem(augment_index);
|
||||||
if (!inst_aug) { // extant only
|
if (!inst_aug) { // extant only
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -255,25 +335,32 @@ void command_peekinv(Client *c, const Seperator *sep)
|
|||||||
linker.SetItemInst(inst_aug);
|
linker.SetItemInst(inst_aug);
|
||||||
|
|
||||||
c->Message(
|
c->Message(
|
||||||
(item_data == nullptr),
|
Chat::White,
|
||||||
".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
fmt::format(
|
||||||
scope_prefix[scopeIndex],
|
"Slot {} (Augment Slot {}) | {} ({}){}",
|
||||||
INVALID_INDEX,
|
|
||||||
(8000 + limboIndex),
|
(8000 + limboIndex),
|
||||||
indexAug,
|
augment_index,
|
||||||
((item_data == nullptr) ? 0 : item_data->ID),
|
linker.GenerateLink(),
|
||||||
linker.GenerateLink().c_str(),
|
item_data->ID,
|
||||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
(
|
||||||
|
inst_aug->IsStackable() && inst_aug->GetCharges() > 0 ?
|
||||||
|
fmt::format(
|
||||||
|
" (Stack of {})",
|
||||||
|
inst_aug->GetCharges()
|
||||||
|
) :
|
||||||
|
""
|
||||||
|
)
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag())) {
|
if (!scope_bag[scope_index] || !(inst_main && inst_main->IsClassBag())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) {
|
for (uint8 sub_index = EQ::invbag::SLOT_BEGIN; sub_index <= EQ::invbag::SLOT_END; ++sub_index) {
|
||||||
inst_sub = inst_main->GetItem(indexSub);
|
inst_sub = inst_main->GetItem(sub_index);
|
||||||
if (!inst_sub) {
|
if (!inst_sub) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -282,23 +369,32 @@ void command_peekinv(Client *c, const Seperator *sep)
|
|||||||
|
|
||||||
linker.SetItemInst(inst_sub);
|
linker.SetItemInst(inst_sub);
|
||||||
|
|
||||||
|
if (item_data) {
|
||||||
c->Message(
|
c->Message(
|
||||||
(item_data == nullptr),
|
Chat::White,
|
||||||
"..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i",
|
fmt::format(
|
||||||
scope_prefix[scopeIndex],
|
"Slot {} Bag Slot {} | {} ({}){}",
|
||||||
INVALID_INDEX,
|
|
||||||
(8000 + limboIndex),
|
(8000 + limboIndex),
|
||||||
indexSub,
|
sub_index,
|
||||||
((item_data == nullptr) ? 0 : item_data->ID),
|
linker.GenerateLink(),
|
||||||
linker.GenerateLink().c_str(),
|
item_data->ID,
|
||||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
(
|
||||||
|
inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ?
|
||||||
|
fmt::format(
|
||||||
|
" (Stack of {})",
|
||||||
|
inst_sub->GetCharges()
|
||||||
|
) :
|
||||||
|
""
|
||||||
|
)
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (inst_sub->IsClassCommon()) {
|
if (inst_sub->IsClassCommon()) {
|
||||||
for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN;
|
for (uint8 augment_index = EQ::invaug::SOCKET_BEGIN;
|
||||||
indexAug <= EQ::invaug::SOCKET_END;
|
augment_index <= EQ::invaug::SOCKET_END;
|
||||||
++indexAug) {
|
++augment_index) {
|
||||||
inst_aug = inst_sub->GetItem(indexAug);
|
inst_aug = inst_sub->GetItem(augment_index);
|
||||||
if (!inst_aug) { // extant only
|
if (!inst_aug) { // extant only
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -307,16 +403,23 @@ void command_peekinv(Client *c, const Seperator *sep)
|
|||||||
linker.SetItemInst(inst_aug);
|
linker.SetItemInst(inst_aug);
|
||||||
|
|
||||||
c->Message(
|
c->Message(
|
||||||
(item_data == nullptr),
|
Chat::White,
|
||||||
"...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
fmt::format(
|
||||||
scope_prefix[scopeIndex],
|
"Slot {} Bag Slot {} (Augment Slot {}) | {} ({}){}",
|
||||||
INVALID_INDEX,
|
|
||||||
(8000 + limboIndex),
|
(8000 + limboIndex),
|
||||||
indexSub,
|
sub_index,
|
||||||
indexAug,
|
augment_index,
|
||||||
((item_data == nullptr) ? 0 : item_data->ID),
|
linker.GenerateLink(),
|
||||||
linker.GenerateLink().c_str(),
|
item_data->ID,
|
||||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
(
|
||||||
|
inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ?
|
||||||
|
fmt::format(
|
||||||
|
" (Stack of {})",
|
||||||
|
inst_sub->GetCharges()
|
||||||
|
) :
|
||||||
|
""
|
||||||
|
)
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,8 +428,23 @@ void command_peekinv(Client *c, const Seperator *sep)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!itemsFound) {
|
if (!found_items) {
|
||||||
c->Message(Chat::White, "No items found.");
|
c->Message(Chat::White, "No items found.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendPeekInvSubCommands(Client* c) {
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv equip - Shows items in Equipment slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv gen - Shows items in General slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv cursor - Shows items in Cursor slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv poss - Shows items in Equipment, General, and Cursor slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv limbo - Shows items in Limbo slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv curlim - Shows items in Cursor and Limbo slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv trib - Shows items in Tribute slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv bank - Shows items in Bank slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv shbank - Shows items in Shared Bank slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv allbank - Shows items in Bank and Shared Bank slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv trade - Shows items in Trade slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv world - Shows items in World slots");
|
||||||
|
c->Message(Chat::White, "Usage: #peekinv all - Shows items in all slots");
|
||||||
|
}
|
||||||
|
|||||||
@@ -794,6 +794,23 @@ bool Client::SummonItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (player_event_logs.IsEventEnabled(PlayerEvent::ITEM_CREATION)) {
|
||||||
|
auto e = PlayerEvent::ItemCreationEvent{};
|
||||||
|
e.item_id = item->ID;
|
||||||
|
e.item_name = item->Name;
|
||||||
|
e.to_slot = to_slot;
|
||||||
|
e.charges = charges;
|
||||||
|
e.aug1 = aug1;
|
||||||
|
e.aug2 = aug2;
|
||||||
|
e.aug3 = aug3;
|
||||||
|
e.aug4 = aug4;
|
||||||
|
e.aug5 = aug5;
|
||||||
|
e.aug6 = aug6;
|
||||||
|
e.attuned = attuned;
|
||||||
|
|
||||||
|
RecordPlayerEventLog(PlayerEvent::ITEM_CREATION, e);
|
||||||
|
}
|
||||||
|
|
||||||
// put item into inventory
|
// put item into inventory
|
||||||
if (to_slot == EQ::invslot::slotCursor) {
|
if (to_slot == EQ::invslot::slotCursor) {
|
||||||
PushItemOnCursor(*inst);
|
PushItemOnCursor(*inst);
|
||||||
|
|||||||
+3
-3
@@ -65,8 +65,8 @@ Lua_Mob Lua_Bot::GetOwner() {
|
|||||||
return Lua_Mob(self->GetOwner());
|
return Lua_Mob(self->GetOwner());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Lua_Bot::HasBotItem(uint32 item_id) {
|
int16 Lua_Bot::HasBotItem(uint32 item_id) {
|
||||||
Lua_Safe_Call_Bool();
|
Lua_Safe_Call_Int();
|
||||||
return self->HasBotItem(item_id);
|
return self->HasBotItem(item_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +507,7 @@ luabind::scope lua_register_bot() {
|
|||||||
.def("GetRawItemAC", (int(Lua_Bot::*)(void))&Lua_Bot::GetRawItemAC)
|
.def("GetRawItemAC", (int(Lua_Bot::*)(void))&Lua_Bot::GetRawItemAC)
|
||||||
.def("GetSpellDamage", (int(Lua_Bot::*)(void))&Lua_Bot::GetSpellDamage)
|
.def("GetSpellDamage", (int(Lua_Bot::*)(void))&Lua_Bot::GetSpellDamage)
|
||||||
.def("HasAugmentEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasAugmentEquippedByID)
|
.def("HasAugmentEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasAugmentEquippedByID)
|
||||||
.def("HasBotItem", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
|
.def("HasBotItem", (int16(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
|
||||||
.def("HasBotSpellEntry", (bool(Lua_Bot::*)(uint16)) & Lua_Bot::HasBotSpellEntry)
|
.def("HasBotSpellEntry", (bool(Lua_Bot::*)(uint16)) & Lua_Bot::HasBotSpellEntry)
|
||||||
.def("HasItemEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasItemEquippedByID)
|
.def("HasItemEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasItemEquippedByID)
|
||||||
.def("IsGrouped", (bool(Lua_Bot::*)(void))&Lua_Bot::IsGrouped)
|
.def("IsGrouped", (bool(Lua_Bot::*)(void))&Lua_Bot::IsGrouped)
|
||||||
|
|||||||
+1
-1
@@ -43,7 +43,7 @@ public:
|
|||||||
uint32 GetBotItemIDBySlot(uint16 slot_id);
|
uint32 GetBotItemIDBySlot(uint16 slot_id);
|
||||||
int GetExpansionBitmask();
|
int GetExpansionBitmask();
|
||||||
Lua_Mob GetOwner();
|
Lua_Mob GetOwner();
|
||||||
bool HasBotItem(uint32 item_id);
|
int16 HasBotItem(uint32 item_id);
|
||||||
void OwnerMessage(std::string message);
|
void OwnerMessage(std::string message);
|
||||||
bool ReloadBotDataBuckets();
|
bool ReloadBotDataBuckets();
|
||||||
bool ReloadBotOwnerDataBuckets();
|
bool ReloadBotOwnerDataBuckets();
|
||||||
|
|||||||
@@ -3038,6 +3038,20 @@ void Lua_Client::UseAugmentContainer(int container_slot)
|
|||||||
self->UseAugmentContainer(container_slot);
|
self->UseAugmentContainer(container_slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool Lua_Client::IsAutoAttackEnabled()
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->AutoAttackEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lua_Client::IsAutoFireEnabled()
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->AutoFireEnabled();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
luabind::scope lua_register_client() {
|
luabind::scope lua_register_client() {
|
||||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||||
.def(luabind::constructor<>())
|
.def(luabind::constructor<>())
|
||||||
@@ -3296,6 +3310,8 @@ luabind::scope lua_register_client() {
|
|||||||
.def("IncreaseSkill", (void(Lua_Client::*)(int))&Lua_Client::IncreaseSkill)
|
.def("IncreaseSkill", (void(Lua_Client::*)(int))&Lua_Client::IncreaseSkill)
|
||||||
.def("IncreaseSkill", (void(Lua_Client::*)(int,int))&Lua_Client::IncreaseSkill)
|
.def("IncreaseSkill", (void(Lua_Client::*)(int,int))&Lua_Client::IncreaseSkill)
|
||||||
.def("IncrementAA", (void(Lua_Client::*)(int))&Lua_Client::IncrementAA)
|
.def("IncrementAA", (void(Lua_Client::*)(int))&Lua_Client::IncrementAA)
|
||||||
|
.def("IsAutoAttackEnabled", (bool(Lua_Client::*)(void))&Lua_Client::IsAutoAttackEnabled)
|
||||||
|
.def("IsAutoFireEnabled", (bool(Lua_Client::*)(void))&Lua_Client::IsAutoFireEnabled)
|
||||||
.def("IsCrouching", (bool(Lua_Client::*)(void))&Lua_Client::IsCrouching)
|
.def("IsCrouching", (bool(Lua_Client::*)(void))&Lua_Client::IsCrouching)
|
||||||
.def("IsDead", &Lua_Client::IsDead)
|
.def("IsDead", &Lua_Client::IsDead)
|
||||||
.def("IsDueling", (bool(Lua_Client::*)(void))&Lua_Client::IsDueling)
|
.def("IsDueling", (bool(Lua_Client::*)(void))&Lua_Client::IsDueling)
|
||||||
|
|||||||
@@ -466,6 +466,8 @@ public:
|
|||||||
void SetItemCooldown(uint32 item_id, uint32 in_time);
|
void SetItemCooldown(uint32 item_id, uint32 in_time);
|
||||||
uint32 GetItemCooldown(uint32 item_id);
|
uint32 GetItemCooldown(uint32 item_id);
|
||||||
void UseAugmentContainer(int container_slot);
|
void UseAugmentContainer(int container_slot);
|
||||||
|
bool IsAutoAttackEnabled();
|
||||||
|
bool IsAutoFireEnabled();
|
||||||
|
|
||||||
void ApplySpell(int spell_id);
|
void ApplySpell(int spell_id);
|
||||||
void ApplySpell(int spell_id, int duration);
|
void ApplySpell(int spell_id, int duration);
|
||||||
|
|||||||
+12
-4
@@ -231,6 +231,8 @@ int main(int argc, char** argv) {
|
|||||||
worldserver.SetLauncherName("NONE");
|
worldserver.SetLauncherName("NONE");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto mutex = new Mutex;
|
||||||
|
|
||||||
LogInfo("Connecting to MySQL");
|
LogInfo("Connecting to MySQL");
|
||||||
if (!database.Connect(
|
if (!database.Connect(
|
||||||
Config->DatabaseHost.c_str(),
|
Config->DatabaseHost.c_str(),
|
||||||
@@ -242,9 +244,7 @@ int main(int argc, char** argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Multi-tenancy: Content Database
|
||||||
* Multi-tenancy: Content Database
|
|
||||||
*/
|
|
||||||
if (!Config->ContentDbHost.empty()) {
|
if (!Config->ContentDbHost.empty()) {
|
||||||
if (!content_db.Connect(
|
if (!content_db.Connect(
|
||||||
Config->ContentDbHost.c_str() ,
|
Config->ContentDbHost.c_str() ,
|
||||||
@@ -258,7 +258,12 @@ int main(int argc, char** argv) {
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
content_db.SetMysql(database.getMySQL());
|
content_db.SetMySQL(database);
|
||||||
|
// when database and content_db share the same underlying mysql connection
|
||||||
|
// it needs to be protected by a shared mutex otherwise we produce concurrency issues
|
||||||
|
// when database actions are occurring in different threads
|
||||||
|
database.SetMutex(mutex);
|
||||||
|
content_db.SetMutex(mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register Log System and Settings */
|
/* Register Log System and Settings */
|
||||||
@@ -613,6 +618,9 @@ int main(int argc, char** argv) {
|
|||||||
safe_delete(parse);
|
safe_delete(parse);
|
||||||
LogInfo("Proper zone shutdown complete.");
|
LogInfo("Proper zone shutdown complete.");
|
||||||
LogSys.CloseFileLogs();
|
LogSys.CloseFileLogs();
|
||||||
|
|
||||||
|
safe_delete(mutex);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+35
-8
@@ -103,6 +103,7 @@ Mob::Mob(
|
|||||||
attack_dw_timer(2000),
|
attack_dw_timer(2000),
|
||||||
ranged_timer(2000),
|
ranged_timer(2000),
|
||||||
hp_regen_per_second_timer(1000),
|
hp_regen_per_second_timer(1000),
|
||||||
|
m_z_clip_check_timer(1000),
|
||||||
tic_timer(6000),
|
tic_timer(6000),
|
||||||
mana_timer(2000),
|
mana_timer(2000),
|
||||||
spellend_timer(0),
|
spellend_timer(0),
|
||||||
@@ -2374,14 +2375,14 @@ void Mob::ShowBuffList(Client* client) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mob::GMMove(float x, float y, float z, float heading) {
|
void Mob::GMMove(float x, float y, float z, float heading, bool save_guard_spot) {
|
||||||
m_Position.x = x;
|
m_Position.x = x;
|
||||||
m_Position.y = y;
|
m_Position.y = y;
|
||||||
m_Position.z = z;
|
m_Position.z = z;
|
||||||
SetHeading(heading);
|
SetHeading(heading);
|
||||||
mMovementManager->SendCommandToClients(this, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny);
|
mMovementManager->SendCommandToClients(this, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny);
|
||||||
|
|
||||||
if (IsNPC()) {
|
if (IsNPC() && save_guard_spot) {
|
||||||
CastToNPC()->SaveGuardSpot(glm::vec4(x, y, z, heading));
|
CastToNPC()->SaveGuardSpot(glm::vec4(x, y, z, heading));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3714,10 +3715,32 @@ bool Mob::HateSummon() {
|
|||||||
// probably should be like half melee range, but we can't get melee range nicely because reasons :)
|
// probably should be like half melee range, but we can't get melee range nicely because reasons :)
|
||||||
new_pos = target->TryMoveAlong(new_pos, 5.0f, angle);
|
new_pos = target->TryMoveAlong(new_pos, 5.0f, angle);
|
||||||
|
|
||||||
if (target->IsClient())
|
if (target->IsClient()) {
|
||||||
target->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), new_pos.x, new_pos.y, new_pos.z, new_pos.w, 0, SummonPC);
|
target->CastToClient()->MovePC(
|
||||||
else
|
zone->GetZoneID(),
|
||||||
target->GMMove(new_pos.x, new_pos.y, new_pos.z, new_pos.w);
|
zone->GetInstanceID(),
|
||||||
|
new_pos.x,
|
||||||
|
new_pos.y,
|
||||||
|
new_pos.z,
|
||||||
|
new_pos.w,
|
||||||
|
0,
|
||||||
|
SummonPC
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
bool target_is_client_pet = (
|
||||||
|
target->IsPet() &&
|
||||||
|
target->IsPetOwnerClient()
|
||||||
|
);
|
||||||
|
bool set_new_guard_spot = !(IsNPC() && target_is_client_pet);
|
||||||
|
|
||||||
|
target->GMMove(
|
||||||
|
new_pos.x,
|
||||||
|
new_pos.y,
|
||||||
|
new_pos.z,
|
||||||
|
new_pos.w,
|
||||||
|
set_new_guard_spot
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else if(summon_level == 2) {
|
} else if(summon_level == 2) {
|
||||||
@@ -4166,7 +4189,7 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
|
|||||||
twinproc = true;
|
twinproc = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id))) { // NPC innate procs don't take this path ever
|
if (IsBeneficialSpell(spell_id) && (!IsNPC() || (IsNPC() && CastToNPC()->GetInnateProcSpellID() != spell_id)) && spells[spell_id].target_type != ST_TargetsTarget) { // NPC innate procs don't take this path ever
|
||||||
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
|
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
|
||||||
if (twinproc) {
|
if (twinproc) {
|
||||||
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
|
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
|
||||||
@@ -4182,7 +4205,11 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint32 Mob::GetZoneID() const {
|
uint32 Mob::GetZoneID() const {
|
||||||
return(zone->GetZoneID());
|
return zone->GetZoneID();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 Mob::GetInstanceVersion() const {
|
||||||
|
return zone->GetInstanceVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
int Mob::GetHaste()
|
int Mob::GetHaste()
|
||||||
|
|||||||
+4
-1
@@ -686,7 +686,7 @@ public:
|
|||||||
float GetMovespeed() const { return IsRunning() ? GetRunspeed() : GetWalkspeed(); }
|
float GetMovespeed() const { return IsRunning() ? GetRunspeed() : GetWalkspeed(); }
|
||||||
bool IsRunning() const { return m_is_running; }
|
bool IsRunning() const { return m_is_running; }
|
||||||
void SetRunning(bool val) { m_is_running = val; }
|
void SetRunning(bool val) { m_is_running = val; }
|
||||||
virtual void GMMove(float x, float y, float z, float heading = 0.01);
|
virtual void GMMove(float x, float y, float z, float heading = 0.01, bool save_guard_spot = true);
|
||||||
virtual void GMMove(const glm::vec4 &position);
|
virtual void GMMove(const glm::vec4 &position);
|
||||||
void SetDelta(const glm::vec4& delta);
|
void SetDelta(const glm::vec4& delta);
|
||||||
void MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct* spu);
|
void MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct* spu);
|
||||||
@@ -1239,6 +1239,7 @@ public:
|
|||||||
bool Charmed() const { return typeofpet == petCharmed; }
|
bool Charmed() const { return typeofpet == petCharmed; }
|
||||||
static uint32 GetLevelHP(uint8 tlevel);
|
static uint32 GetLevelHP(uint8 tlevel);
|
||||||
uint32 GetZoneID() const; //for perl
|
uint32 GetZoneID() const; //for perl
|
||||||
|
uint16 GetInstanceVersion() const; //for perl
|
||||||
virtual int32 CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc = false);
|
virtual int32 CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc = false);
|
||||||
virtual int32 CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible = 0);
|
virtual int32 CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible = 0);
|
||||||
|
|
||||||
@@ -1427,6 +1428,8 @@ protected:
|
|||||||
int _GetRunSpeed() const;
|
int _GetRunSpeed() const;
|
||||||
int _GetFearSpeed() const;
|
int _GetFearSpeed() const;
|
||||||
|
|
||||||
|
Timer m_z_clip_check_timer;
|
||||||
|
|
||||||
virtual bool AI_EngagedCastCheck() { return(false); }
|
virtual bool AI_EngagedCastCheck() { return(false); }
|
||||||
virtual bool AI_PursueCastCheck() { return(false); }
|
virtual bool AI_PursueCastCheck() { return(false); }
|
||||||
virtual bool AI_IdleCastCheck() { return(false); }
|
virtual bool AI_IdleCastCheck() { return(false); }
|
||||||
|
|||||||
+27
-127
@@ -404,16 +404,16 @@ void NPC::AI_Init()
|
|||||||
AIautocastspell_timer.reset(nullptr);
|
AIautocastspell_timer.reset(nullptr);
|
||||||
casting_spell_AIindex = static_cast<uint8>(AIspells.size());
|
casting_spell_AIindex = static_cast<uint8>(AIspells.size());
|
||||||
|
|
||||||
roambox_max_x = 0;
|
m_roambox.max_x = 0;
|
||||||
roambox_max_y = 0;
|
m_roambox.max_y = 0;
|
||||||
roambox_min_x = 0;
|
m_roambox.min_x = 0;
|
||||||
roambox_min_y = 0;
|
m_roambox.min_y = 0;
|
||||||
roambox_distance = 0;
|
m_roambox.distance = 0;
|
||||||
roambox_destination_x = 0;
|
m_roambox.dest_x = 0;
|
||||||
roambox_destination_y = 0;
|
m_roambox.dest_y = 0;
|
||||||
roambox_destination_z = 0;
|
m_roambox.dest_z = 0;
|
||||||
roambox_min_delay = 2500;
|
m_roambox.delay = 2500;
|
||||||
roambox_delay = 2500;
|
m_roambox.min_delay = 2500;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::AI_Init()
|
void Client::AI_Init()
|
||||||
@@ -1071,6 +1071,20 @@ void Mob::AI_Process() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (engaged) {
|
if (engaged) {
|
||||||
|
if (IsNPC() && m_z_clip_check_timer.Check()) {
|
||||||
|
auto t = GetTarget();
|
||||||
|
if (t) {
|
||||||
|
float self_z = GetZ() - GetZOffset();
|
||||||
|
float target_z = t->GetPosition().z - t->GetZOffset();
|
||||||
|
if (DistanceNoZ(GetPosition(), t->GetPosition()) < 75 &&
|
||||||
|
std::abs(self_z - target_z) >= 25 && !CheckLosFN(t)) {
|
||||||
|
float new_z = FindDestGroundZ(t->GetPosition());
|
||||||
|
GMMove(t->GetPosition().x, t->GetPosition().y, new_z + GetZOffset(), t->GetPosition().w, false);
|
||||||
|
FaceTarget(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
|
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
|
||||||
SendAddPlayerState(PlayerState::Aggressive);
|
SendAddPlayerState(PlayerState::Aggressive);
|
||||||
|
|
||||||
@@ -1584,123 +1598,9 @@ void NPC::AI_DoMovement() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// Roambox logic sets precedence
|
||||||
* Roambox logic sets precedence
|
if (m_roambox.distance > 0) {
|
||||||
*/
|
HandleRoambox();
|
||||||
if (roambox_distance > 0) {
|
|
||||||
|
|
||||||
// Check if we're already moving to a WP
|
|
||||||
// If so, if we're not moving we have arrived and need to set delay
|
|
||||||
|
|
||||||
if (GetCWP() == EQ::WaypointStatus::RoamBoxPauseInProgress && !IsMoving()) {
|
|
||||||
// We have arrived
|
|
||||||
|
|
||||||
int roambox_move_delay = EQ::ClampLower(GetRoamboxDelay(), GetRoamboxMinDelay());
|
|
||||||
int move_delay_max = (roambox_move_delay > 0 ? roambox_move_delay : (int) GetRoamboxMinDelay() * 4);
|
|
||||||
int random_timer = RandomTimer(
|
|
||||||
GetRoamboxMinDelay(),
|
|
||||||
move_delay_max
|
|
||||||
);
|
|
||||||
|
|
||||||
LogNPCRoamBoxDetail(
|
|
||||||
"({}) Timer calc | random_timer [{}] roambox_move_delay [{}] move_min [{}] move_max [{}]",
|
|
||||||
GetCleanName(),
|
|
||||||
random_timer,
|
|
||||||
roambox_move_delay,
|
|
||||||
(int) GetRoamboxMinDelay(),
|
|
||||||
move_delay_max
|
|
||||||
);
|
|
||||||
|
|
||||||
time_until_can_move = Timer::GetCurrentTime() + random_timer;
|
|
||||||
SetCurrentWP(0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a new destination
|
|
||||||
if (!IsMoving() && time_until_can_move < Timer::GetCurrentTime()) {
|
|
||||||
auto move_x = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
|
|
||||||
auto move_y = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
|
|
||||||
|
|
||||||
roambox_destination_x = EQ::Clamp((GetX() + move_x), roambox_min_x, roambox_max_x);
|
|
||||||
roambox_destination_y = EQ::Clamp((GetY() + move_y), roambox_min_y, roambox_max_y);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If our roambox was configured with large distances, chances of hitting the min or max end of
|
|
||||||
* the clamp is high, this causes NPC's to gather on the border of a box, to reduce clustering
|
|
||||||
* either lower the roambox distance or the code will do a simple random between min - max when it
|
|
||||||
* hits the min or max of the clamp
|
|
||||||
*/
|
|
||||||
if (roambox_destination_x == roambox_min_x || roambox_destination_x == roambox_max_x) {
|
|
||||||
roambox_destination_x = static_cast<float>(zone->random.Real(roambox_min_x, roambox_max_x));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (roambox_destination_y == roambox_min_y || roambox_destination_y == roambox_max_y) {
|
|
||||||
roambox_destination_y = static_cast<float>(zone->random.Real(roambox_min_y, roambox_max_y));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If mob was not spawned in water, let's not randomly roam them into water
|
|
||||||
* if the roam box was sloppily configured
|
|
||||||
*/
|
|
||||||
if (!GetWasSpawnedInWater()) {
|
|
||||||
roambox_destination_z = GetGroundZ(roambox_destination_x, roambox_destination_y);
|
|
||||||
if (zone->HasMap() && zone->HasWaterMap()) {
|
|
||||||
auto position = glm::vec3(
|
|
||||||
roambox_destination_x,
|
|
||||||
roambox_destination_y,
|
|
||||||
roambox_destination_z
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If someone brought us into water when we naturally wouldn't path there, return to spawn
|
|
||||||
*/
|
|
||||||
if (zone->watermap->InLiquid(position) && zone->watermap->InLiquid(m_Position)) {
|
|
||||||
roambox_destination_x = m_SpawnPoint.x;
|
|
||||||
roambox_destination_y = m_SpawnPoint.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (zone->watermap->InLiquid(position)) {
|
|
||||||
LogNPCRoamBoxDetail("[{}] | My destination is in water and I don't belong there!", GetCleanName());
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { // Mob was in water, make sure new spot is in water also
|
|
||||||
roambox_destination_z = m_Position.z;
|
|
||||||
auto position = glm::vec3(
|
|
||||||
roambox_destination_x,
|
|
||||||
roambox_destination_y,
|
|
||||||
m_Position.z + 15
|
|
||||||
);
|
|
||||||
if (zone->HasWaterMap() && !zone->watermap->InLiquid(position)) {
|
|
||||||
roambox_destination_x = m_SpawnPoint.x;
|
|
||||||
roambox_destination_y = m_SpawnPoint.y;
|
|
||||||
roambox_destination_z = m_SpawnPoint.z;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LogNPCRoamBox("[{}] | Pathing to [{}] [{}] [{}]", GetCleanName(),
|
|
||||||
roambox_destination_x, roambox_destination_y,
|
|
||||||
roambox_destination_z);
|
|
||||||
|
|
||||||
LogNPCRoamBox(
|
|
||||||
"NPC ({}) distance [{}] X (min/max) [{} / {}] Y (min/max) [{} / {}] | Dest x/y/z [{} / {} / {}]",
|
|
||||||
GetCleanName(),
|
|
||||||
roambox_distance,
|
|
||||||
roambox_min_x,
|
|
||||||
roambox_max_x,
|
|
||||||
roambox_min_y,
|
|
||||||
roambox_max_y,
|
|
||||||
roambox_destination_x,
|
|
||||||
roambox_destination_y,
|
|
||||||
roambox_destination_z
|
|
||||||
);
|
|
||||||
|
|
||||||
SetCurrentWP(EQ::WaypointStatus::RoamBoxPauseInProgress);
|
|
||||||
NavigateTo(roambox_destination_x, roambox_destination_y, roambox_destination_z);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (roamer) {
|
else if (roamer) {
|
||||||
|
|||||||
+7
-9
@@ -795,21 +795,19 @@ void Mob::DisplayInfo(Mob *mob)
|
|||||||
window_text += WriteDisplayInfoSection(mob, "Proximity", npc_proximity, 1, true);
|
window_text += WriteDisplayInfoSection(mob, "Proximity", npc_proximity, 1, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
int8 npc_type = npc_scale_manager->GetNPCScalingType(npc);
|
|
||||||
std::string npc_type_string = npc_scale_manager->GetNPCScalingTypeName(npc);
|
|
||||||
|
|
||||||
client->Message(
|
client->Message(
|
||||||
0,
|
Chat::White,
|
||||||
"| # Target: %s Type: %i (%s)",
|
fmt::format(
|
||||||
|
"| # Target: {} Type: {} ({})",
|
||||||
npc->GetCleanName(),
|
npc->GetCleanName(),
|
||||||
npc_type,
|
npc_scale_manager->GetNPCScalingType(npc),
|
||||||
npc_type_string.c_str());
|
npc_scale_manager->GetNPCScalingTypeName(npc)
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
NPCCommandsMenu(client, npc);
|
NPCCommandsMenu(client, npc);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::cout << "Window Length: " << window_text.length() << std::endl;
|
|
||||||
|
|
||||||
if (client->GetDisplayMobInfoWindow()) {
|
if (client->GetDisplayMobInfoWindow()) {
|
||||||
client->SendFullPopup(
|
client->SendFullPopup(
|
||||||
"GM: Entity Info",
|
"GM: Entity Info",
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ public:
|
|||||||
|
|
||||||
if (RuleB(Map, FixZWhenPathing)) {
|
if (RuleB(Map, FixZWhenPathing)) {
|
||||||
m_distance_moved_since_correction += distance_moved;
|
m_distance_moved_since_correction += distance_moved;
|
||||||
if (m_distance_moved_since_correction > RuleR(Map, DistanceCanTravelBeforeAdjustment)) {
|
if (m_distance_moved_since_correction > (mob->IsEngaged() ? 1 : 10)) {
|
||||||
m_distance_moved_since_correction = 0.0;
|
m_distance_moved_since_correction = 0.0;
|
||||||
mob->FixZ();
|
mob->FixZ();
|
||||||
}
|
}
|
||||||
|
|||||||
+171
-19
@@ -159,6 +159,9 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
|
|||||||
if (race == RACE_LAVA_DRAGON_49) {
|
if (race == RACE_LAVA_DRAGON_49) {
|
||||||
size = 5;
|
size = 5;
|
||||||
}
|
}
|
||||||
|
if (race == RACE_WURM_158) {
|
||||||
|
size = 15;
|
||||||
|
}
|
||||||
|
|
||||||
taunting = false;
|
taunting = false;
|
||||||
proximity = nullptr;
|
proximity = nullptr;
|
||||||
@@ -255,15 +258,18 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
|
|||||||
}
|
}
|
||||||
|
|
||||||
guard_anim = eaStanding;
|
guard_anim = eaStanding;
|
||||||
roambox_distance = 0;
|
|
||||||
roambox_max_x = -2;
|
m_roambox.max_x = -2;
|
||||||
roambox_max_y = -2;
|
m_roambox.max_y = -2;
|
||||||
roambox_min_x = -2;
|
m_roambox.min_x = -2;
|
||||||
roambox_min_y = -2;
|
m_roambox.min_y = -2;
|
||||||
roambox_destination_x = -2;
|
m_roambox.distance = 0;
|
||||||
roambox_destination_y = -2;
|
m_roambox.dest_x = -2;
|
||||||
roambox_min_delay = 1000;
|
m_roambox.dest_y = -2;
|
||||||
roambox_delay = 1000;
|
m_roambox.dest_z = 0;
|
||||||
|
m_roambox.delay = 1000;
|
||||||
|
m_roambox.min_delay = 1000;
|
||||||
|
|
||||||
p_depop = false;
|
p_depop = false;
|
||||||
loottable_id = npc_type_data->loottable_id;
|
loottable_id = npc_type_data->loottable_id;
|
||||||
skip_global_loot = npc_type_data->skip_global_loot;
|
skip_global_loot = npc_type_data->skip_global_loot;
|
||||||
@@ -440,52 +446,52 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
|
|||||||
|
|
||||||
float NPC::GetRoamboxMaxX() const
|
float NPC::GetRoamboxMaxX() const
|
||||||
{
|
{
|
||||||
return roambox_max_x;
|
return m_roambox.max_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
float NPC::GetRoamboxMaxY() const
|
float NPC::GetRoamboxMaxY() const
|
||||||
{
|
{
|
||||||
return roambox_max_y;
|
return m_roambox.max_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
float NPC::GetRoamboxMinX() const
|
float NPC::GetRoamboxMinX() const
|
||||||
{
|
{
|
||||||
return roambox_min_x;
|
return m_roambox.min_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
float NPC::GetRoamboxMinY() const
|
float NPC::GetRoamboxMinY() const
|
||||||
{
|
{
|
||||||
return roambox_min_y;
|
return m_roambox.min_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
float NPC::GetRoamboxDistance() const
|
float NPC::GetRoamboxDistance() const
|
||||||
{
|
{
|
||||||
return roambox_distance;
|
return m_roambox.distance;
|
||||||
}
|
}
|
||||||
|
|
||||||
float NPC::GetRoamboxDestinationX() const
|
float NPC::GetRoamboxDestinationX() const
|
||||||
{
|
{
|
||||||
return roambox_destination_x;
|
return m_roambox.dest_x;
|
||||||
}
|
}
|
||||||
|
|
||||||
float NPC::GetRoamboxDestinationY() const
|
float NPC::GetRoamboxDestinationY() const
|
||||||
{
|
{
|
||||||
return roambox_destination_y;
|
return m_roambox.dest_y;
|
||||||
}
|
}
|
||||||
|
|
||||||
float NPC::GetRoamboxDestinationZ() const
|
float NPC::GetRoamboxDestinationZ() const
|
||||||
{
|
{
|
||||||
return roambox_destination_z;
|
return m_roambox.dest_z;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 NPC::GetRoamboxDelay() const
|
uint32 NPC::GetRoamboxDelay() const
|
||||||
{
|
{
|
||||||
return roambox_delay;
|
return m_roambox.delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 NPC::GetRoamboxMinDelay() const
|
uint32 NPC::GetRoamboxMinDelay() const
|
||||||
{
|
{
|
||||||
return roambox_min_delay;
|
return m_roambox.min_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
NPC::~NPC()
|
NPC::~NPC()
|
||||||
@@ -3811,3 +3817,149 @@ void NPC::SendPositionToClients()
|
|||||||
}
|
}
|
||||||
safe_delete(p);
|
safe_delete(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NPC::HandleRoambox()
|
||||||
|
{
|
||||||
|
bool has_arrived = GetCWP() == EQ::WaypointStatus::RoamBoxPauseInProgress && !IsMoving();
|
||||||
|
if (has_arrived) {
|
||||||
|
int roambox_move_delay = EQ::ClampLower(GetRoamboxDelay(), GetRoamboxMinDelay());
|
||||||
|
int move_delay_max = (roambox_move_delay > 0 ? roambox_move_delay : (int) GetRoamboxMinDelay() * 4);
|
||||||
|
int random_timer = RandomTimer(
|
||||||
|
GetRoamboxMinDelay(),
|
||||||
|
move_delay_max
|
||||||
|
);
|
||||||
|
|
||||||
|
LogNPCRoamBoxDetail(
|
||||||
|
"({}) random_timer [{}] roambox_move_delay [{}] move_min [{}] move_max [{}]",
|
||||||
|
GetCleanName(),
|
||||||
|
random_timer,
|
||||||
|
roambox_move_delay,
|
||||||
|
(int) GetRoamboxMinDelay(),
|
||||||
|
move_delay_max
|
||||||
|
);
|
||||||
|
|
||||||
|
time_until_can_move = Timer::GetCurrentTime() + random_timer;
|
||||||
|
SetCurrentWP(0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ready_to_set_new_destination = !IsMoving() && time_until_can_move < Timer::GetCurrentTime();
|
||||||
|
if (ready_to_set_new_destination) {
|
||||||
|
// make several attempts to find a valid next move in the box
|
||||||
|
bool can_path = false;
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
auto move_x = static_cast<float>(zone->random.Real(-m_roambox.distance, m_roambox.distance));
|
||||||
|
auto move_y = static_cast<float>(zone->random.Real(-m_roambox.distance, m_roambox.distance));
|
||||||
|
auto requested_x = EQ::Clamp((GetX() + move_x), m_roambox.min_x, m_roambox.max_x);
|
||||||
|
auto requested_y = EQ::Clamp((GetY() + move_y), m_roambox.min_y, m_roambox.max_y);
|
||||||
|
auto requested_z = GetGroundZ(requested_x, requested_y);
|
||||||
|
|
||||||
|
std::vector<float> heights = {0, 250, -250};
|
||||||
|
for (auto &h: heights) {
|
||||||
|
if (CheckLosFN(requested_x, requested_y, requested_z + h, GetSize())) {
|
||||||
|
LogNPCRoamBox("[{}] Found line of sight to path attempt [{}] at height [{}]", GetCleanName(), i, h);
|
||||||
|
can_path = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!can_path) {
|
||||||
|
LogNPCRoamBox("[{}] | Failed line of sight to path attempt [{}]", GetCleanName(), i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_roambox.dest_x = requested_x;
|
||||||
|
m_roambox.dest_y = requested_y;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If our roambox was configured with large distances, chances of hitting the min or max end of
|
||||||
|
* the clamp is high, this causes NPC's to gather on the border of a box, to reduce clustering
|
||||||
|
* either lower the roambox distance or the code will do a simple random between min - max when it
|
||||||
|
* hits the min or max of the clamp
|
||||||
|
*/
|
||||||
|
if (m_roambox.dest_x == m_roambox.min_x || m_roambox.dest_x == m_roambox.max_x) {
|
||||||
|
m_roambox.dest_x = static_cast<float>(zone->random.Real(m_roambox.min_x, m_roambox.max_x));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_roambox.dest_y == m_roambox.min_y || m_roambox.dest_y == m_roambox.max_y) {
|
||||||
|
m_roambox.dest_y = static_cast<float>(zone->random.Real(m_roambox.min_y, m_roambox.max_y));
|
||||||
|
}
|
||||||
|
|
||||||
|
// If mob was not spawned in water, let's not randomly roam them into water
|
||||||
|
// if the roam box was sloppily configured
|
||||||
|
if (!GetWasSpawnedInWater()) {
|
||||||
|
m_roambox.dest_z = GetGroundZ(m_roambox.dest_x, m_roambox.dest_y);
|
||||||
|
if (zone->HasMap() && zone->HasWaterMap()) {
|
||||||
|
auto position = glm::vec3(
|
||||||
|
m_roambox.dest_x,
|
||||||
|
m_roambox.dest_y,
|
||||||
|
m_roambox.dest_z
|
||||||
|
);
|
||||||
|
|
||||||
|
// If someone brought us into water when we naturally wouldn't path there, return to spawn
|
||||||
|
if (zone->watermap->InLiquid(position) && zone->watermap->InLiquid(m_Position)) {
|
||||||
|
m_roambox.dest_x = m_SpawnPoint.x;
|
||||||
|
m_roambox.dest_y = m_SpawnPoint.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zone->watermap->InLiquid(position)) {
|
||||||
|
LogNPCRoamBoxDetail("[{}] | My destination is in water and I don't belong there!", GetCleanName());
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { // Mob was in water, make sure new spot is in water also
|
||||||
|
m_roambox.dest_z = m_Position.z;
|
||||||
|
auto position = glm::vec3(
|
||||||
|
m_roambox.dest_x,
|
||||||
|
m_roambox.dest_y,
|
||||||
|
m_Position.z + 15
|
||||||
|
);
|
||||||
|
if (zone->HasWaterMap() && !zone->watermap->InLiquid(position)) {
|
||||||
|
m_roambox.dest_x = m_SpawnPoint.x;
|
||||||
|
m_roambox.dest_y = m_SpawnPoint.y;
|
||||||
|
m_roambox.dest_z = m_SpawnPoint.z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogNPCRoamBox(
|
||||||
|
"[{}] | Pathing to [{}] [{}] [{}]",
|
||||||
|
GetCleanName(),
|
||||||
|
m_roambox.dest_x,
|
||||||
|
m_roambox.dest_y,
|
||||||
|
m_roambox.dest_z
|
||||||
|
);
|
||||||
|
|
||||||
|
LogNPCRoamBox(
|
||||||
|
"NPC ({}) distance [{}] X (min/max) [{} / {}] Y (min/max) [{} / {}] | Dest x/y/z [{} / {} / {}]",
|
||||||
|
GetCleanName(),
|
||||||
|
m_roambox.distance,
|
||||||
|
m_roambox.min_x,
|
||||||
|
m_roambox.max_x,
|
||||||
|
m_roambox.min_y,
|
||||||
|
m_roambox.max_y,
|
||||||
|
m_roambox.dest_x,
|
||||||
|
m_roambox.dest_y,
|
||||||
|
m_roambox.dest_z
|
||||||
|
);
|
||||||
|
|
||||||
|
if (can_path) {
|
||||||
|
SetCurrentWP(EQ::WaypointStatus::RoamBoxPauseInProgress);
|
||||||
|
NavigateTo(m_roambox.dest_x, m_roambox.dest_y, m_roambox.dest_z);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// failed to find path, reset timer
|
||||||
|
int roambox_move_delay = EQ::ClampLower(GetRoamboxDelay(), GetRoamboxMinDelay());
|
||||||
|
int move_delay_max = (roambox_move_delay > 0 ? roambox_move_delay : (int) GetRoamboxMinDelay() * 4);
|
||||||
|
int random_timer = RandomTimer(
|
||||||
|
GetRoamboxMinDelay(),
|
||||||
|
move_delay_max
|
||||||
|
);
|
||||||
|
time_until_can_move = Timer::GetCurrentTime() + random_timer;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|||||||
+17
-10
@@ -80,6 +80,19 @@ struct AISpellsVar_Struct {
|
|||||||
uint8 idle_beneficial_chance;
|
uint8 idle_beneficial_chance;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Roambox {
|
||||||
|
float max_x;
|
||||||
|
float max_y;
|
||||||
|
float min_x;
|
||||||
|
float min_y;
|
||||||
|
float distance;
|
||||||
|
float dest_x;
|
||||||
|
float dest_y;
|
||||||
|
float dest_z;
|
||||||
|
uint32 delay;
|
||||||
|
uint32 min_delay;
|
||||||
|
};
|
||||||
|
|
||||||
class SwarmPet;
|
class SwarmPet;
|
||||||
class Client;
|
class Client;
|
||||||
class Group;
|
class Group;
|
||||||
@@ -538,6 +551,8 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
void HandleRoambox();
|
||||||
|
|
||||||
const NPCType* NPCTypedata;
|
const NPCType* NPCTypedata;
|
||||||
NPCType* NPCTypedata_ours; //special case for npcs with uniquely created data.
|
NPCType* NPCTypedata_ours; //special case for npcs with uniquely created data.
|
||||||
|
|
||||||
@@ -635,16 +650,8 @@ protected:
|
|||||||
glm::vec4 m_GuardPoint;
|
glm::vec4 m_GuardPoint;
|
||||||
glm::vec4 m_GuardPointSaved;
|
glm::vec4 m_GuardPointSaved;
|
||||||
EmuAppearance guard_anim;
|
EmuAppearance guard_anim;
|
||||||
float roambox_max_x;
|
|
||||||
float roambox_max_y;
|
Roambox m_roambox = {};
|
||||||
float roambox_min_x;
|
|
||||||
float roambox_min_y;
|
|
||||||
float roambox_distance;
|
|
||||||
float roambox_destination_x;
|
|
||||||
float roambox_destination_y;
|
|
||||||
float roambox_destination_z;
|
|
||||||
uint32 roambox_delay;
|
|
||||||
uint32 roambox_min_delay;
|
|
||||||
|
|
||||||
uint16 skills[EQ::skills::HIGHEST_SKILL + 1];
|
uint16 skills[EQ::skills::HIGHEST_SKILL + 1];
|
||||||
|
|
||||||
|
|||||||
+87
-24
@@ -30,21 +30,27 @@ void NpcScaleManager::ScaleNPC(
|
|||||||
NPC *npc,
|
NPC *npc,
|
||||||
bool always_scale,
|
bool always_scale,
|
||||||
bool override_special_abilities
|
bool override_special_abilities
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
if (npc->IsSkipAutoScale() || npc->GetNPCTypeID() == 0) {
|
if (npc->IsSkipAutoScale() || npc->GetNPCTypeID() == 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int8 npc_type = GetNPCScalingType(npc);
|
auto npc_type = GetNPCScalingType(npc);
|
||||||
int npc_level = npc->GetLevel();
|
auto npc_level = npc->GetLevel();
|
||||||
bool is_auto_scaled = IsAutoScaled(npc);
|
auto is_auto_scaled = IsAutoScaled(npc);
|
||||||
|
auto zone_id = zone->GetZoneID();
|
||||||
|
auto instance_version = zone->GetInstanceVersion();
|
||||||
|
|
||||||
global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(npc_type, npc_level);
|
global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(
|
||||||
|
npc_type,
|
||||||
|
npc_level,
|
||||||
|
zone_id,
|
||||||
|
instance_version
|
||||||
|
);
|
||||||
|
|
||||||
if (!scale_data.level) {
|
if (!scale_data.level) {
|
||||||
LogNPCScaling(
|
LogNPCScaling(
|
||||||
"NPC: [{}] - scaling data not found for type: [{}] level: [{}]",
|
"NPC: [{}] - scaling data not found for type [{}] level [{}]",
|
||||||
npc->GetCleanName(),
|
npc->GetCleanName(),
|
||||||
npc_type,
|
npc_type,
|
||||||
npc_level
|
npc_level
|
||||||
@@ -209,11 +215,13 @@ void NpcScaleManager::ResetNPCScaling(NPC* npc)
|
|||||||
bool NpcScaleManager::LoadScaleData()
|
bool NpcScaleManager::LoadScaleData()
|
||||||
{
|
{
|
||||||
auto rows = NpcScaleGlobalBaseRepository::All(content_db);
|
auto rows = NpcScaleGlobalBaseRepository::All(content_db);
|
||||||
for (auto &s: rows) {
|
for (const auto &s : rows) {
|
||||||
global_npc_scale scale_data;
|
global_npc_scale scale_data;
|
||||||
|
|
||||||
scale_data.type = s.type;
|
scale_data.type = s.type;
|
||||||
scale_data.level = s.level;
|
scale_data.level = s.level;
|
||||||
|
scale_data.zone_id = s.zone_id;
|
||||||
|
scale_data.instance_version = s.instance_version;
|
||||||
scale_data.ac = s.ac;
|
scale_data.ac = s.ac;
|
||||||
scale_data.hp = s.hp;
|
scale_data.hp = s.hp;
|
||||||
scale_data.accuracy = s.accuracy;
|
scale_data.accuracy = s.accuracy;
|
||||||
@@ -246,7 +254,12 @@ bool NpcScaleManager::LoadScaleData()
|
|||||||
|
|
||||||
npc_global_base_scaling_data.insert(
|
npc_global_base_scaling_data.insert(
|
||||||
std::make_pair(
|
std::make_pair(
|
||||||
std::make_pair(scale_data.type, scale_data.level),
|
std::make_tuple(
|
||||||
|
scale_data.type,
|
||||||
|
scale_data.level,
|
||||||
|
scale_data.zone_id,
|
||||||
|
scale_data.instance_version
|
||||||
|
),
|
||||||
scale_data
|
scale_data
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -262,9 +275,45 @@ bool NpcScaleManager::LoadScaleData()
|
|||||||
* @param npc_level
|
* @param npc_level
|
||||||
* @return NpcScaleManager::global_npc_scale
|
* @return NpcScaleManager::global_npc_scale
|
||||||
*/
|
*/
|
||||||
NpcScaleManager::global_npc_scale NpcScaleManager::GetGlobalScaleDataForTypeLevel(int8 npc_type, int npc_level)
|
NpcScaleManager::global_npc_scale NpcScaleManager::GetGlobalScaleDataForTypeLevel(
|
||||||
{
|
int8 npc_type,
|
||||||
auto iter = npc_global_base_scaling_data.find(std::make_pair(npc_type, npc_level));
|
uint8 npc_level,
|
||||||
|
uint32 zone_id,
|
||||||
|
uint16 instance_version
|
||||||
|
) {
|
||||||
|
auto iter = npc_global_base_scaling_data.find(
|
||||||
|
std::make_tuple(
|
||||||
|
npc_type,
|
||||||
|
npc_level,
|
||||||
|
zone_id,
|
||||||
|
instance_version
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (iter != npc_global_base_scaling_data.end()) {
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = npc_global_base_scaling_data.find(
|
||||||
|
std::make_tuple(
|
||||||
|
npc_type,
|
||||||
|
npc_level,
|
||||||
|
zone_id,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (iter != npc_global_base_scaling_data.end()) {
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
iter = npc_global_base_scaling_data.find(
|
||||||
|
std::make_tuple(
|
||||||
|
npc_type,
|
||||||
|
npc_level,
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
);
|
||||||
if (iter != npc_global_base_scaling_data.end()) {
|
if (iter != npc_global_base_scaling_data.end()) {
|
||||||
return iter->second;
|
return iter->second;
|
||||||
}
|
}
|
||||||
@@ -487,14 +536,21 @@ bool NpcScaleManager::IsAutoScaled(NPC* npc)
|
|||||||
*/
|
*/
|
||||||
bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
|
bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
|
||||||
{
|
{
|
||||||
int8 npc_type = GetNPCScalingType(npc);
|
auto npc_type = GetNPCScalingType(npc);
|
||||||
int npc_level = npc->GetLevel();
|
auto npc_level = npc->GetLevel();
|
||||||
|
auto zone_id = zone->GetZoneID();
|
||||||
|
auto instance_version = zone->GetInstanceVersion();
|
||||||
|
|
||||||
global_npc_scale g = GetGlobalScaleDataForTypeLevel(npc_type, npc_level);
|
global_npc_scale g = GetGlobalScaleDataForTypeLevel(
|
||||||
|
npc_type,
|
||||||
|
npc_level,
|
||||||
|
zone_id,
|
||||||
|
instance_version
|
||||||
|
);
|
||||||
|
|
||||||
if (!g.level) {
|
if (!g.level) {
|
||||||
LogNPCScaling(
|
LogNPCScaling(
|
||||||
"NPC: [{}] - scaling data not found for type: [{}] level: [{}]",
|
"NPC: [{}] - scaling data not found for type [{}] level [{}]",
|
||||||
npc->GetCleanName(),
|
npc->GetCleanName(),
|
||||||
npc_type,
|
npc_type,
|
||||||
npc_level
|
npc_level
|
||||||
@@ -503,7 +559,7 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto n = NpcTypesRepository::FindOne(content_db, (int) npc->GetNPCTypeID());
|
auto n = NpcTypesRepository::FindOne(content_db, static_cast<int>(npc->GetNPCTypeID()));
|
||||||
if (n.id > 0) {
|
if (n.id > 0) {
|
||||||
n.AC = g.ac;
|
n.AC = g.ac;
|
||||||
n.hp = g.hp;
|
n.hp = g.hp;
|
||||||
@@ -528,8 +584,8 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
|
|||||||
n.maxdmg = g.max_dmg;
|
n.maxdmg = g.max_dmg;
|
||||||
n.hp_regen_rate = g.hp_regen_rate;
|
n.hp_regen_rate = g.hp_regen_rate;
|
||||||
n.attack_delay = g.attack_delay;
|
n.attack_delay = g.attack_delay;
|
||||||
n.spellscale = (float) g.spell_scale;
|
n.spellscale = static_cast<float>(g.spell_scale);
|
||||||
n.healscale = (float) g.heal_scale;
|
n.healscale = static_cast<float>(g.heal_scale);
|
||||||
n.special_abilities = g.special_abilities;
|
n.special_abilities = g.special_abilities;
|
||||||
|
|
||||||
return NpcTypesRepository::UpdateOne(content_db, n);
|
return NpcTypesRepository::UpdateOne(content_db, n);
|
||||||
@@ -545,14 +601,21 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
|
|||||||
*/
|
*/
|
||||||
bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc)
|
bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc)
|
||||||
{
|
{
|
||||||
int8 npc_type = GetNPCScalingType(npc);
|
auto npc_type = GetNPCScalingType(npc);
|
||||||
int npc_level = npc->GetLevel();
|
auto npc_level = npc->GetLevel();
|
||||||
|
auto zone_id = zone->GetZoneID();
|
||||||
|
auto instance_version = zone->GetInstanceVersion();
|
||||||
|
|
||||||
global_npc_scale d = GetGlobalScaleDataForTypeLevel(npc_type, npc_level);
|
global_npc_scale d = GetGlobalScaleDataForTypeLevel(
|
||||||
|
npc_type,
|
||||||
|
npc_level,
|
||||||
|
zone_id,
|
||||||
|
instance_version
|
||||||
|
);
|
||||||
|
|
||||||
if (!d.level) {
|
if (!d.level) {
|
||||||
LogNPCScaling(
|
LogNPCScaling(
|
||||||
"NPC: [{}] - scaling data not found for type: [{}] level: [{}]",
|
"NPC: [{}] - scaling data not found for type [{}] level [{}]",
|
||||||
npc->GetCleanName(),
|
npc->GetCleanName(),
|
||||||
npc_type,
|
npc_type,
|
||||||
npc_level
|
npc_level
|
||||||
@@ -561,7 +624,7 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto n = NpcTypesRepository::FindOne(content_db, (int) npc->GetNPCTypeID());
|
auto n = NpcTypesRepository::FindOne(content_db, static_cast<int>(npc->GetNPCTypeID()));
|
||||||
if (n.id > 0) {
|
if (n.id > 0) {
|
||||||
n.AC = 0;
|
n.AC = 0;
|
||||||
n.hp = 0;
|
n.hp = 0;
|
||||||
|
|||||||
@@ -22,12 +22,16 @@
|
|||||||
#define EQEMU_NPC_SCALE_MANAGER_H
|
#define EQEMU_NPC_SCALE_MANAGER_H
|
||||||
|
|
||||||
#include "npc.h"
|
#include "npc.h"
|
||||||
|
#include "zone.h"
|
||||||
|
extern Zone* zone;
|
||||||
|
|
||||||
class NpcScaleManager {
|
class NpcScaleManager {
|
||||||
public:
|
public:
|
||||||
struct global_npc_scale {
|
struct global_npc_scale {
|
||||||
int type;
|
int8 type;
|
||||||
int level;
|
uint8 level;
|
||||||
|
uint32 zone_id;
|
||||||
|
uint16 instance_version;
|
||||||
int ac;
|
int ac;
|
||||||
int64 hp;
|
int64 hp;
|
||||||
int accuracy;
|
int accuracy;
|
||||||
@@ -91,9 +95,14 @@ public:
|
|||||||
bool IsAutoScaled(NPC* npc);
|
bool IsAutoScaled(NPC* npc);
|
||||||
bool LoadScaleData();
|
bool LoadScaleData();
|
||||||
|
|
||||||
global_npc_scale GetGlobalScaleDataForTypeLevel(int8 npc_type, int npc_level);
|
global_npc_scale GetGlobalScaleDataForTypeLevel(
|
||||||
|
int8 npc_type,
|
||||||
|
uint8 npc_level,
|
||||||
|
uint32 zone_id,
|
||||||
|
uint16 instance_version
|
||||||
|
);
|
||||||
|
|
||||||
std::map<std::pair<int, int>, global_npc_scale> npc_global_base_scaling_data;
|
std::map<std::tuple<int8, uint8, uint32, uint16>, global_npc_scale> npc_global_base_scaling_data;
|
||||||
|
|
||||||
int8 GetNPCScalingType(NPC * &npc);
|
int8 GetNPCScalingType(NPC * &npc);
|
||||||
std::string GetNPCScalingTypeName(NPC * &npc);
|
std::string GetNPCScalingTypeName(NPC * &npc);
|
||||||
|
|||||||
+1
-1
@@ -90,7 +90,7 @@ uint32 Perl_Bot_CountBotItem(Bot* self, uint32 item_id)
|
|||||||
return self->CountBotItem(item_id);
|
return self->CountBotItem(item_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Perl_Bot_HasBotItem(Bot* self, uint32 item_id)
|
int16 Perl_Bot_HasBotItem(Bot* self, uint32 item_id)
|
||||||
{
|
{
|
||||||
return self->HasBotItem(item_id);
|
return self->HasBotItem(item_id);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2755,6 +2755,11 @@ void Perl_Client_Signal(Client* self, int signal_id)
|
|||||||
self->Signal(signal_id);
|
self->Signal(signal_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Perl_Client_SignalClient(Client* self, int signal_id) // @categories Script Utility
|
||||||
|
{
|
||||||
|
self->Signal(signal_id);
|
||||||
|
}
|
||||||
|
|
||||||
std::string Perl_Client_GetGuildPublicNote(Client* self)
|
std::string Perl_Client_GetGuildPublicNote(Client* self)
|
||||||
{
|
{
|
||||||
return self->GetGuildPublicNote();
|
return self->GetGuildPublicNote();
|
||||||
@@ -2892,6 +2897,16 @@ void Perl_Client_UseAugmentContainer(Client* self, int container_slot)
|
|||||||
self->UseAugmentContainer(container_slot);
|
self->UseAugmentContainer(container_slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Perl_Client_IsAutoAttackEnabled(Client* self)
|
||||||
|
{
|
||||||
|
return self->AutoAttackEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Perl_Client_IsAutoFireEnabled(Client* self)
|
||||||
|
{
|
||||||
|
return self->AutoFireEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
void perl_register_client()
|
void perl_register_client()
|
||||||
{
|
{
|
||||||
perl::interpreter perl(PERL_GET_THX);
|
perl::interpreter perl(PERL_GET_THX);
|
||||||
@@ -3154,6 +3169,8 @@ void perl_register_client()
|
|||||||
package.add("IncreaseSkill", (void(*)(Client*, int))&Perl_Client_IncreaseSkill);
|
package.add("IncreaseSkill", (void(*)(Client*, int))&Perl_Client_IncreaseSkill);
|
||||||
package.add("IncreaseSkill", (void(*)(Client*, int, int))&Perl_Client_IncreaseSkill);
|
package.add("IncreaseSkill", (void(*)(Client*, int, int))&Perl_Client_IncreaseSkill);
|
||||||
package.add("IncrementAA", &Perl_Client_IncrementAA);
|
package.add("IncrementAA", &Perl_Client_IncrementAA);
|
||||||
|
package.add("IsAutoAttackEnabled", &Perl_Client_IsAutoAttackEnabled);
|
||||||
|
package.add("IsAutoFireEnabled", &Perl_Client_IsAutoFireEnabled);
|
||||||
package.add("IsBecomeNPC", &Perl_Client_IsBecomeNPC);
|
package.add("IsBecomeNPC", &Perl_Client_IsBecomeNPC);
|
||||||
package.add("IsCrouching", &Perl_Client_IsCrouching);
|
package.add("IsCrouching", &Perl_Client_IsCrouching);
|
||||||
package.add("IsDueling", &Perl_Client_IsDueling);
|
package.add("IsDueling", &Perl_Client_IsDueling);
|
||||||
@@ -3362,6 +3379,7 @@ void perl_register_client()
|
|||||||
package.add("SetTitleSuffix", (void(*)(Client*, std::string, bool))&Perl_Client_SetTitleSuffix);
|
package.add("SetTitleSuffix", (void(*)(Client*, std::string, bool))&Perl_Client_SetTitleSuffix);
|
||||||
package.add("SetZoneFlag", &Perl_Client_SetZoneFlag);
|
package.add("SetZoneFlag", &Perl_Client_SetZoneFlag);
|
||||||
package.add("Signal", &Perl_Client_Signal);
|
package.add("Signal", &Perl_Client_Signal);
|
||||||
|
package.add("SignalClient", &Perl_Client_SignalClient);
|
||||||
package.add("SilentMessage", &Perl_Client_SilentMessage);
|
package.add("SilentMessage", &Perl_Client_SilentMessage);
|
||||||
package.add("Sit", &Perl_Client_Sit);
|
package.add("Sit", &Perl_Client_Sit);
|
||||||
package.add("SlotConvert2", &Perl_Client_SlotConvert2);
|
package.add("SlotConvert2", &Perl_Client_SlotConvert2);
|
||||||
|
|||||||
@@ -584,6 +584,16 @@ bool Perl_NPC_GetCombatState(NPC* self) // @categories Script Utility
|
|||||||
return self->GetCombatEvent();
|
return self->GetCombatEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Perl_NPC_SetSimpleRoamBox(NPC* self, float box_size) // @categories Script Utility
|
||||||
|
{
|
||||||
|
self->SetSimpleRoamBox(box_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Perl_NPC_SetSimpleRoamBox(NPC* self, float box_size, float move_distance) // @categories Script Utility
|
||||||
|
{
|
||||||
|
self->SetSimpleRoamBox(box_size, move_distance);
|
||||||
|
}
|
||||||
|
|
||||||
void Perl_NPC_SetSimpleRoamBox(NPC* self, float box_size, float move_distance, int move_delay) // @categories Script Utility
|
void Perl_NPC_SetSimpleRoamBox(NPC* self, float box_size, float move_distance, int move_delay) // @categories Script Utility
|
||||||
{
|
{
|
||||||
self->SetSimpleRoamBox(box_size, move_distance, move_delay);
|
self->SetSimpleRoamBox(box_size, move_distance, move_delay);
|
||||||
|
|||||||
+20
-8
@@ -88,19 +88,28 @@ void QuestManager::Process() {
|
|||||||
end = QTimerList.end();
|
end = QTimerList.end();
|
||||||
while (cur != end) {
|
while (cur != end) {
|
||||||
if (cur->Timer_.Enabled() && cur->Timer_.Check()) {
|
if (cur->Timer_.Enabled() && cur->Timer_.Check()) {
|
||||||
if(entity_list.IsMobInZone(cur->mob)) {
|
if (cur->mob && entity_list.IsMobInZone(cur->mob)) {
|
||||||
if(cur->mob->IsNPC()) {
|
if (cur->mob->IsNPC()) {
|
||||||
if (parse->HasQuestSub(cur->mob->GetNPCTypeID(), EVENT_TIMER)) {
|
if (parse->HasQuestSub(cur->mob->GetNPCTypeID(), EVENT_TIMER)) {
|
||||||
parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0);
|
parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0);
|
||||||
}
|
}
|
||||||
} else if (cur->mob->IsEncounter()) {
|
}
|
||||||
parse->EventEncounter(EVENT_TIMER, cur->mob->CastToEncounter()->GetEncounterName(), cur->name, 0, nullptr);
|
else if (cur->mob->IsEncounter()) {
|
||||||
} else if (cur->mob->IsClient()) {
|
parse->EventEncounter(
|
||||||
|
EVENT_TIMER,
|
||||||
|
cur->mob->CastToEncounter()->GetEncounterName(),
|
||||||
|
cur->name,
|
||||||
|
0,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (cur->mob->IsClient()) {
|
||||||
if (parse->PlayerHasQuestSub(EVENT_TIMER)) {
|
if (parse->PlayerHasQuestSub(EVENT_TIMER)) {
|
||||||
//this is inheriently unsafe if we ever make it so more than npc/client start timers
|
//this is inheriently unsafe if we ever make it so more than npc/client start timers
|
||||||
parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0);
|
parse->EventPlayer(EVENT_TIMER, cur->mob->CastToClient(), cur->name, 0);
|
||||||
}
|
}
|
||||||
} else if (cur->mob->IsBot()) {
|
}
|
||||||
|
else if (cur->mob->IsBot()) {
|
||||||
if (parse->BotHasQuestSub(EVENT_TIMER)) {
|
if (parse->BotHasQuestSub(EVENT_TIMER)) {
|
||||||
parse->EventBot(EVENT_TIMER, cur->mob->CastToBot(), nullptr, cur->name, 0);
|
parse->EventBot(EVENT_TIMER, cur->mob->CastToBot(), nullptr, cur->name, 0);
|
||||||
}
|
}
|
||||||
@@ -110,12 +119,15 @@ void QuestManager::Process() {
|
|||||||
//number of timers... worst case we have to check a bunch of timers twice
|
//number of timers... worst case we have to check a bunch of timers twice
|
||||||
cur = QTimerList.begin();
|
cur = QTimerList.begin();
|
||||||
end = QTimerList.end(); //dunno if this is needed, cant hurt...
|
end = QTimerList.end(); //dunno if this is needed, cant hurt...
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
cur = QTimerList.erase(cur);
|
cur = QTimerList.erase(cur);
|
||||||
}
|
}
|
||||||
} else
|
}
|
||||||
|
else {
|
||||||
++cur;
|
++cur;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto cur_iter = STimerList.begin();
|
auto cur_iter = STimerList.begin();
|
||||||
while(cur_iter != STimerList.end()) {
|
while(cur_iter != STimerList.end()) {
|
||||||
|
|||||||
+43
-31
@@ -469,23 +469,12 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if Combine would result in Lore conflict
|
// Check if Combine would result in Lore conflict
|
||||||
for (const auto& e : spec.onsuccess) {
|
if (user->CheckTradeskillLoreConflict(spec.recipe_id)) {
|
||||||
auto success_item_inst = database.GetItem(e.first);
|
|
||||||
if (success_item_inst->LoreGroup > 0) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (user->CheckLoreConflict(success_item_inst)) {
|
|
||||||
EQ::SayLinkEngine linker;
|
|
||||||
linker.SetLinkType(EQ::saylink::SayLinkItemData);
|
|
||||||
linker.SetItemData(success_item_inst);
|
|
||||||
auto item_link = linker.GenerateLink();
|
|
||||||
user->MessageString(Chat::Red, TRADESKILL_COMBINE_LORE, item_link.c_str());
|
|
||||||
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
|
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
|
||||||
user->QueuePacket(outapp);
|
user->QueuePacket(outapp);
|
||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// final check for any additional quest requirements .. "check_zone" in this case - exported as variable [validate_type]
|
// final check for any additional quest requirements .. "check_zone" in this case - exported as variable [validate_type]
|
||||||
if (parse->PlayerHasQuestSub(EVENT_COMBINE_VALIDATE)) {
|
if (parse->PlayerHasQuestSub(EVENT_COMBINE_VALIDATE)) {
|
||||||
@@ -622,9 +611,9 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
|
|||||||
}
|
}
|
||||||
|
|
||||||
//pull the list of components
|
//pull the list of components
|
||||||
std::string query = StringFormat("SELECT tre.item_id, tre.componentcount "
|
const auto query = fmt::format("SELECT item_id, componentcount "
|
||||||
"FROM tradeskill_recipe_entries AS tre "
|
"FROM tradeskill_recipe_entries "
|
||||||
"WHERE tre.componentcount > 0 AND tre.recipe_id = %u",
|
"WHERE componentcount > 0 AND recipe_id = {}",
|
||||||
rac->recipe_id);
|
rac->recipe_id);
|
||||||
auto results = content_db.QueryDatabase(query);
|
auto results = content_db.QueryDatabase(query);
|
||||||
if (!results.Success()) {
|
if (!results.Success()) {
|
||||||
@@ -698,6 +687,13 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if Combine would result in Lore conflict
|
||||||
|
if (user->CheckTradeskillLoreConflict(rac->recipe_id)) {
|
||||||
|
user->QueuePacket(outapp);
|
||||||
|
safe_delete(outapp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//now we know they have everything...
|
//now we know they have everything...
|
||||||
|
|
||||||
//remove all the items from the players inventory, with updates...
|
//remove all the items from the players inventory, with updates...
|
||||||
@@ -727,22 +723,6 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DBTradeskillRecipe_Struct recipe_struct;
|
|
||||||
|
|
||||||
// Check if Combine would result in Lore conflict
|
|
||||||
for (const auto& e : recipe_struct.onsuccess) {
|
|
||||||
auto success_item_inst = database.GetItem(e.first);
|
|
||||||
if (user->CheckLoreConflict(success_item_inst)) {
|
|
||||||
EQ::SayLinkEngine linker;
|
|
||||||
linker.SetLinkType(EQ::saylink::SayLinkItemData);
|
|
||||||
linker.SetItemData(success_item_inst);
|
|
||||||
auto item_link = linker.GenerateLink();
|
|
||||||
user->MessageString(Chat::Red, TRADESKILL_COMBINE_LORE, item_link.c_str());
|
|
||||||
user->QueuePacket(outapp);
|
|
||||||
safe_delete(outapp);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//otherwise, we found it all...
|
//otherwise, we found it all...
|
||||||
outp->reply_code = 0x00000000; //success for finding it...
|
outp->reply_code = 0x00000000; //success for finding it...
|
||||||
user->QueuePacket(outapp);
|
user->QueuePacket(outapp);
|
||||||
@@ -1882,3 +1862,35 @@ bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Client::CheckTradeskillLoreConflict(int32 recipe_id)
|
||||||
|
{
|
||||||
|
const auto& recipe_entries = TradeskillRecipeEntriesRepository::GetWhere(
|
||||||
|
content_db,
|
||||||
|
fmt::format(
|
||||||
|
"recipe_id = {} ORDER BY id ASC",
|
||||||
|
recipe_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
if (recipe_entries.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& e : recipe_entries) {
|
||||||
|
auto item_inst = database.GetItem(e.item_id);
|
||||||
|
if (item_inst) {
|
||||||
|
if (item_inst->LoreGroup == 0 || e.componentcount > 0 || e.iscontainer) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (CheckLoreConflict(item_inst)) {
|
||||||
|
EQ::SayLinkEngine linker;
|
||||||
|
linker.SetLinkType(EQ::saylink::SayLinkItemData);
|
||||||
|
linker.SetItemData(item_inst);
|
||||||
|
auto item_link = linker.GenerateLink();
|
||||||
|
MessageString(Chat::Red, TRADESKILL_COMBINE_LORE, item_link.c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
+22
-23
@@ -66,14 +66,14 @@ void NPC::AI_SetRoambox(
|
|||||||
uint32 min_delay
|
uint32 min_delay
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
roambox_distance = distance;
|
m_roambox.distance = distance;
|
||||||
roambox_max_x = max_x;
|
m_roambox.max_x = max_x;
|
||||||
roambox_min_x = min_x;
|
m_roambox.min_x = min_x;
|
||||||
roambox_max_y = max_y;
|
m_roambox.max_y = max_y;
|
||||||
roambox_min_y = min_y;
|
m_roambox.min_y = min_y;
|
||||||
roambox_destination_x = roambox_max_x + 1; // this will trigger a recalc
|
m_roambox.dest_x = max_x + 1; // this will trigger a recalc
|
||||||
roambox_delay = delay;
|
m_roambox.delay = delay;
|
||||||
roambox_min_delay = min_delay;
|
m_roambox.min_delay = min_delay;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NPC::DisplayWaypointInfo(Client *client) {
|
void NPC::DisplayWaypointInfo(Client *client) {
|
||||||
@@ -781,7 +781,19 @@ float Mob::GetFixedZ(const glm::vec3 &destination, int32 z_find_offset) {
|
|||||||
return new_z;
|
return new_z;
|
||||||
}
|
}
|
||||||
|
|
||||||
new_z = FindDestGroundZ(destination, (-GetZOffset() / 2));
|
new_z = FindDestGroundZ(destination, ((-GetZOffset() / 2) + z_find_offset));
|
||||||
|
|
||||||
|
if (RuleB(Map, MobPathingVisualDebug)) {
|
||||||
|
DrawDebugCoordinateNode(
|
||||||
|
fmt::format("{} search z node", GetCleanName()),
|
||||||
|
glm::vec4{
|
||||||
|
m_Position.x,
|
||||||
|
m_Position.y,
|
||||||
|
((-GetZOffset() / 2) + z_find_offset),
|
||||||
|
m_Position.w
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
if (new_z != BEST_Z_INVALID) {
|
if (new_z != BEST_Z_INVALID) {
|
||||||
new_z += GetZOffset();
|
new_z += GetZOffset();
|
||||||
|
|
||||||
@@ -790,20 +802,6 @@ float Mob::GetFixedZ(const glm::vec3 &destination, int32 z_find_offset) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// prevent ceiling clipping
|
|
||||||
// if client is close in distance (not counting Z) and we clipped up into a ceiling
|
|
||||||
// this helps us snap back down (or up) if it were to happen
|
|
||||||
// other fixes were put in place to prevent clipping into the ceiling to begin with
|
|
||||||
if (std::abs(new_z - m_Position.z) > 15) {
|
|
||||||
LogFixZ("TRIGGER clipping detection");
|
|
||||||
auto t = GetTarget();
|
|
||||||
if (t && DistanceNoZ(GetPosition(), t->GetPosition()) < 20) {
|
|
||||||
new_z = FindDestGroundZ(t->GetPosition(), -t->GetZOffset());
|
|
||||||
new_z += GetZOffset();
|
|
||||||
GMMove(t->GetPosition().x, t->GetPosition().y, new_z, t->GetPosition().w);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto duration = timer.elapsed();
|
auto duration = timer.elapsed();
|
||||||
|
|
||||||
LogFixZ("[{}] returned [{}] at [{}] [{}] [{}] - Took [{}]",
|
LogFixZ("[{}] returned [{}] at [{}] [{}] [{}] - Took [{}]",
|
||||||
@@ -936,6 +934,7 @@ float Mob::GetZOffset() const {
|
|||||||
case RACE_AMYGDALAN_663:
|
case RACE_AMYGDALAN_663:
|
||||||
offset = 5.0f;
|
offset = 5.0f;
|
||||||
break;
|
break;
|
||||||
|
case RACE_SPECTRAL_IKSAR_147:
|
||||||
case RACE_SANDMAN_664:
|
case RACE_SANDMAN_664:
|
||||||
offset = 4.0f;
|
offset = 4.0f;
|
||||||
break;
|
break;
|
||||||
|
|||||||
Reference in New Issue
Block a user