mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-27 00:32:28 +00:00
Compare commits
45 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d3ca636a70 | |||
| 01855d40df | |||
| 748602b04e | |||
| a97a9a0d1c | |||
| a78c754c0e | |||
| ef214f91e9 | |||
| 04a74df0b2 | |||
| c15bfe12eb | |||
| 5702f7bcd1 | |||
| 9a5bf53e11 | |||
| 69c6a7b89a | |||
| 2f0dbc5d15 | |||
| 93c79817cd | |||
| 3296287d70 | |||
| 774a7fa779 | |||
| 1ff4541a9f | |||
| 3448758c03 | |||
| ff4ccfa98f | |||
| d2c3c14ae0 | |||
| a470931fdd | |||
| 078db3460d | |||
| 0980a780d0 | |||
| 7f01bb509c | |||
| 4bb189cbf4 | |||
| b03e8ff0fb | |||
| 6179b7481e | |||
| 5f68e4a41a | |||
| e103422ca5 | |||
| 0cbfad975d | |||
| 2a20c69c69 | |||
| de2dfc1a7e | |||
| bad631df59 | |||
| e8f1aa253a | |||
| 889e57a5af | |||
| 5cfdeb928e | |||
| 7519b0225e | |||
| 04fdc54522 | |||
| f39155952f | |||
| 5acc181d64 | |||
| 2ae0b7dd3e | |||
| b0d4f095ef | |||
| 7c7a88650b | |||
| afaa8f4100 | |||
| 0d72295cc9 | |||
| 9d4f231619 |
@@ -1,3 +1,92 @@
|
||||
## [22.4.5] - 03/03/2023
|
||||
|
||||
### Bots
|
||||
|
||||
* Add additional Heroic Sta/Wis/Int bonuses for Bots. ([#3013](https://github.com/EQEmu/Server/pull/3013)) @Aeadoin 2023-03-01
|
||||
* Cleanup AI_IdleCastCheck Logic ([#3004](https://github.com/EQEmu/Server/pull/3004)) @Aeadoin 2023-02-26
|
||||
|
||||
### Code
|
||||
|
||||
* Delete unused zone/skills.h ([#3007](https://github.com/EQEmu/Server/pull/3007)) @Kinglykrab 2023-02-27
|
||||
* Remove DumpPacketProfile() from client.h ([#3000](https://github.com/EQEmu/Server/pull/3000)) @Kinglykrab 2023-02-26
|
||||
* Remove GetCombinedAC_TEST() from client.h ([#2999](https://github.com/EQEmu/Server/pull/2999)) @Kinglykrab 2023-02-26
|
||||
* Remove GetDamageMultiplier() from client.h ([#3001](https://github.com/EQEmu/Server/pull/3001)) @Kinglykrab 2023-02-26
|
||||
* Remove NumberOfAvailableTitles() from titles.h ([#3006](https://github.com/EQEmu/Server/pull/3006)) @Kinglykrab 2023-02-27
|
||||
* Remove ReturnItemPacket from client.h/inventory.cpp ([#3002](https://github.com/EQEmu/Server/pull/3002)) @Kinglykrab 2023-02-26
|
||||
* Remove class EGNode from mob.h ([#3003](https://github.com/EQEmu/Server/pull/3003)) @Kinglykrab 2023-02-26
|
||||
* Remove unused ClientFactory in client.h ([#2998](https://github.com/EQEmu/Server/pull/2998)) @Kinglykrab 2023-02-26
|
||||
* Remove unused iterator from LoadCharacterDisciplines ([#3012](https://github.com/EQEmu/Server/pull/3012)) @Aeadoin 2023-03-02
|
||||
|
||||
### Crash
|
||||
|
||||
* Fix crash in CheckTradeskillLoreConflict ([#3009](https://github.com/EQEmu/Server/pull/3009)) @Aeadoin 2023-02-28
|
||||
|
||||
### Fixes
|
||||
|
||||
* Account for bad data in Tradeskill Recipe Entries ([#2991](https://github.com/EQEmu/Server/pull/2991)) @Aeadoin 2023-02-25
|
||||
* Fix DoAnim quest method default speed ([#3016](https://github.com/EQEmu/Server/pull/3016)) @Kinglykrab 2023-03-01
|
||||
* Fix an issue where EVENT_TIMER timers would not be cleaned up after zone ([#3018](https://github.com/EQEmu/Server/pull/3018)) @noudess 2023-03-03
|
||||
* Fix for Discipline Loading from Database causing issues with slot_ids ([#3008](https://github.com/EQEmu/Server/pull/3008)) @Aeadoin 2023-02-28
|
||||
* Fix for Lore Components where component is returned. ([#3005](https://github.com/EQEmu/Server/pull/3005)) @Aeadoin 2023-02-27
|
||||
* Fix issue where quest saylink responses would occur before the NPC's response ([#3010](https://github.com/EQEmu/Server/pull/3010)) @Akkadius 2023-03-01
|
||||
* Fix log messages when players join channel ([#2992](https://github.com/EQEmu/Server/pull/2992)) @Valorith 2023-03-03
|
||||
* Fix npcfeature and playerfeature ([#3017](https://github.com/EQEmu/Server/pull/3017)) @Kinglykrab 2023-03-02
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add GetDefaultRaceSize() to Perl/Lua ([#2993](https://github.com/EQEmu/Server/pull/2993)) @Kinglykrab 2023-02-27
|
||||
* Add HasSpecialAbilities() to Perl/Lua ([#2994](https://github.com/EQEmu/Server/pull/2994)) @Kinglykrab 2023-02-27
|
||||
* Add IsBerserk() to Perl/Lua ([#2997](https://github.com/EQEmu/Server/pull/2997)) @Kinglykrab 2023-03-01
|
||||
* Add IsFindable() and IsTrackable() to Perl/Lua ([#2996](https://github.com/EQEmu/Server/pull/2996)) @Kinglykrab 2023-03-01
|
||||
* Add IsUnderwaterOnly() to Perl/Lua ([#2995](https://github.com/EQEmu/Server/pull/2995)) @Kinglykrab 2023-03-01
|
||||
|
||||
## [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
|
||||
|
||||
@@ -86,7 +86,7 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
content_db.SetMysql(database.getMySQL());
|
||||
content_db.SetMySQL(database);
|
||||
}
|
||||
|
||||
LogSys.SetDatabase(&database)
|
||||
|
||||
@@ -83,7 +83,7 @@ int main(int argc, char **argv) {
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
content_db.SetMysql(database.getMySQL());
|
||||
content_db.SetMySQL(database);
|
||||
}
|
||||
|
||||
LogSys.SetDatabase(&database)
|
||||
|
||||
+45
-47
@@ -34,14 +34,16 @@
|
||||
|
||||
DBcore::DBcore()
|
||||
{
|
||||
mysql_init(&mysql);
|
||||
pHost = nullptr;
|
||||
pUser = nullptr;
|
||||
pPassword = nullptr;
|
||||
pDatabase = nullptr;
|
||||
pCompress = false;
|
||||
pSSL = false;
|
||||
pStatus = Closed;
|
||||
mysql = mysql_init(nullptr);
|
||||
mysqlOwner = true;
|
||||
pHost = nullptr;
|
||||
pUser = nullptr;
|
||||
pPassword = nullptr;
|
||||
pDatabase = nullptr;
|
||||
pCompress = false;
|
||||
pSSL = false;
|
||||
pStatus = Closed;
|
||||
m_mutex = new Mutex;
|
||||
}
|
||||
|
||||
DBcore::~DBcore()
|
||||
@@ -51,16 +53,10 @@ DBcore::~DBcore()
|
||||
* are re-using the default database connection pointer when we dont have an
|
||||
* external configuration setup ex: (content_database)
|
||||
*/
|
||||
std::string mysql_connection_host;
|
||||
if (mysql.host) {
|
||||
mysql_connection_host = mysql.host;
|
||||
if (mysqlOwner) {
|
||||
mysql_close(mysql);
|
||||
}
|
||||
|
||||
if (GetOriginHost() != mysql_connection_host) {
|
||||
return;
|
||||
}
|
||||
|
||||
mysql_close(&mysql);
|
||||
safe_delete_array(pHost);
|
||||
safe_delete_array(pUser);
|
||||
safe_delete_array(pPassword);
|
||||
@@ -70,19 +66,17 @@ DBcore::~DBcore()
|
||||
// Sends the MySQL server a keepalive
|
||||
void DBcore::ping()
|
||||
{
|
||||
if (!m_query_lock.try_lock()) {
|
||||
if (!m_mutex->trylock()) {
|
||||
// well, if's it's locked, someone's using it. If someone's using it, it doesnt need a keepalive
|
||||
return;
|
||||
}
|
||||
mysql_ping(&mysql);
|
||||
m_query_lock.unlock();
|
||||
mysql_ping(mysql);
|
||||
m_mutex->unlock();
|
||||
}
|
||||
|
||||
MySQLRequestResult DBcore::QueryDatabase(std::string query, bool retryOnFailureOnce)
|
||||
{
|
||||
m_query_lock.lock();
|
||||
auto r = QueryDatabase(query.c_str(), query.length(), retryOnFailureOnce);
|
||||
m_query_lock.unlock();
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -98,14 +92,16 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
||||
BenchTimer timer;
|
||||
timer.reset();
|
||||
|
||||
LockMutex lock(m_mutex);
|
||||
|
||||
// Reconnect if we are not connected before hand.
|
||||
if (pStatus != Connected) {
|
||||
Open();
|
||||
}
|
||||
|
||||
// request query. != 0 indicates some kind of error.
|
||||
if (mysql_real_query(&mysql, query, querylen) != 0) {
|
||||
unsigned int errorNumber = mysql_errno(&mysql);
|
||||
if (mysql_real_query(mysql, query, querylen) != 0) {
|
||||
unsigned int errorNumber = mysql_errno(mysql);
|
||||
|
||||
if (errorNumber == CR_SERVER_GONE_ERROR) {
|
||||
pStatus = Error;
|
||||
@@ -129,26 +125,26 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
||||
|
||||
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];
|
||||
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
|
||||
*/
|
||||
if (mysql_errno(&mysql) > 0 && strlen(query) > 0) {
|
||||
LogMySQLError("[{}] [{}]\n[{}]", mysql_errno(&mysql), mysql_error(&mysql), query);
|
||||
if (mysql_errno(mysql) > 0 && strlen(query) > 0) {
|
||||
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.
|
||||
MYSQL_RES *res = mysql_store_result(&mysql);
|
||||
MYSQL_RES *res = mysql_store_result(mysql);
|
||||
uint32 rowCount = 0;
|
||||
|
||||
if (res != nullptr) {
|
||||
@@ -157,10 +153,10 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
||||
|
||||
MySQLRequestResult requestResult(
|
||||
res,
|
||||
(uint32) mysql_affected_rows(&mysql),
|
||||
(uint32) mysql_affected_rows(mysql),
|
||||
rowCount,
|
||||
(uint32) mysql_field_count(&mysql),
|
||||
(uint32) mysql_insert_id(&mysql)
|
||||
(uint32) mysql_field_count(mysql),
|
||||
(uint32) mysql_insert_id(mysql)
|
||||
);
|
||||
|
||||
if (LogSys.log_settings[Logs::MySQLQuery].is_category_enabled == 1) {
|
||||
@@ -206,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.
|
||||
// LockMutex lock(&MDatabase);
|
||||
return mysql_real_escape_string(&mysql, tobuf, frombuf, fromlen);
|
||||
return mysql_real_escape_string(mysql, tobuf, frombuf, fromlen);
|
||||
}
|
||||
|
||||
bool DBcore::Open(
|
||||
@@ -221,7 +217,7 @@ bool DBcore::Open(
|
||||
bool iSSL
|
||||
)
|
||||
{
|
||||
LockMutex lock(&MDatabase);
|
||||
LockMutex lock(m_mutex);
|
||||
safe_delete_array(pHost);
|
||||
safe_delete_array(pUser);
|
||||
safe_delete_array(pPassword);
|
||||
@@ -241,13 +237,13 @@ bool DBcore::Open(uint32 *errnum, char *errbuf)
|
||||
if (errbuf) {
|
||||
errbuf[0] = 0;
|
||||
}
|
||||
LockMutex lock(&MDatabase);
|
||||
LockMutex lock(m_mutex);
|
||||
if (GetStatus() == Connected) {
|
||||
return true;
|
||||
}
|
||||
if (GetStatus() == Error) {
|
||||
mysql_close(&mysql);
|
||||
mysql_init(&mysql); // Initialize structure again
|
||||
mysql_close(mysql);
|
||||
mysql_init(mysql); // Initialize structure again
|
||||
}
|
||||
if (!pHost) {
|
||||
return false;
|
||||
@@ -264,7 +260,7 @@ bool DBcore::Open(uint32 *errnum, char *errbuf)
|
||||
if (pSSL) {
|
||||
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;
|
||||
|
||||
std::string connected_origin_host = pHost;
|
||||
@@ -274,21 +270,16 @@ bool DBcore::Open(uint32 *errnum, char *errbuf)
|
||||
}
|
||||
else {
|
||||
if (errnum) {
|
||||
*errnum = mysql_errno(&mysql);
|
||||
*errnum = mysql_errno(mysql);
|
||||
}
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void DBcore::SetMysql(MYSQL *mysql)
|
||||
{
|
||||
DBcore::mysql = *mysql;
|
||||
}
|
||||
|
||||
const std::string &DBcore::GetOriginHost() const
|
||||
{
|
||||
return origin_host;
|
||||
@@ -303,7 +294,14 @@ 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);
|
||||
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;
|
||||
}
|
||||
|
||||
+10
-4
@@ -31,14 +31,19 @@ public:
|
||||
std::string Escape(const std::string& s);
|
||||
uint32 DoEscapeString(char *tobuf, const char *frombuf, uint32 fromlen);
|
||||
void ping();
|
||||
MYSQL *getMySQL() { return &mysql; }
|
||||
void SetMysql(MYSQL *mysql);
|
||||
|
||||
const std::string &GetOriginHost() const;
|
||||
void SetOriginHost(const std::string &origin_host);
|
||||
|
||||
bool DoesTableExist(std::string table_name);
|
||||
|
||||
void SetMySQL(const DBcore &o)
|
||||
{
|
||||
mysql = o.mysql;
|
||||
mysqlOwner = false;
|
||||
}
|
||||
void SetMutex(Mutex *mutex);
|
||||
|
||||
protected:
|
||||
bool Open(
|
||||
const char *iHost,
|
||||
@@ -55,8 +60,9 @@ protected:
|
||||
private:
|
||||
bool Open(uint32 *errnum = nullptr, char *errbuf = nullptr);
|
||||
|
||||
MYSQL mysql;
|
||||
Mutex MDatabase;
|
||||
MYSQL* mysql;
|
||||
bool mysqlOwner;
|
||||
Mutex *m_mutex;
|
||||
eStatus pStatus;
|
||||
|
||||
std::mutex m_query_lock{};
|
||||
|
||||
@@ -140,10 +140,6 @@ void PlayerEventLogs::AddToQueue(const PlayerEventLogsRepository::PlayerEventLog
|
||||
m_batch_queue_lock.lock();
|
||||
m_record_batch_queue.emplace_back(log);
|
||||
m_batch_queue_lock.unlock();
|
||||
|
||||
if (m_record_batch_queue.size() >= RuleI(Logging, BatchPlayerEventProcessChunkSize)) {
|
||||
ProcessBatchQueue();
|
||||
}
|
||||
}
|
||||
|
||||
// fills common event data in the SendEvent function
|
||||
@@ -607,10 +603,10 @@ std::string PlayerEventLogs::GetDiscordPayloadFromEvent(const PlayerEvent::Playe
|
||||
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()
|
||||
{
|
||||
if (m_process_batch_events_timer.Check()) {
|
||||
if (m_process_batch_events_timer.Check() || m_record_batch_queue.size() >= RuleI(Logging, BatchPlayerEventProcessChunkSize)) {
|
||||
ProcessBatchQueue();
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -132,7 +132,7 @@ enum { //reuse times
|
||||
InstillDoubtReuseTime = 9,
|
||||
FishingReuseTime = 11,
|
||||
ForagingReuseTime = 50,
|
||||
MendReuseTime = 290,
|
||||
MendReuseTime = 360,
|
||||
BashReuseTime = 5,
|
||||
BackstabReuseTime = 9,
|
||||
KickReuseTime = 5,
|
||||
|
||||
@@ -69,6 +69,7 @@ public:
|
||||
int32_t expansion_bitmask;
|
||||
uint8_t enforce_spell_settings;
|
||||
uint8_t archery_setting;
|
||||
uint32_t caster_range;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -129,6 +130,7 @@ public:
|
||||
"expansion_bitmask",
|
||||
"enforce_spell_settings",
|
||||
"archery_setting",
|
||||
"caster_range",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -185,6 +187,7 @@ public:
|
||||
"expansion_bitmask",
|
||||
"enforce_spell_settings",
|
||||
"archery_setting",
|
||||
"caster_range",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -275,6 +278,7 @@ public:
|
||||
e.expansion_bitmask = -1;
|
||||
e.enforce_spell_settings = 0;
|
||||
e.archery_setting = 0;
|
||||
e.caster_range = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -361,6 +365,7 @@ public:
|
||||
e.expansion_bitmask = static_cast<int32_t>(atoi(row[47]));
|
||||
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.caster_range = static_cast<uint32_t>(strtoul(row[50], nullptr, 10));
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -443,6 +448,7 @@ public:
|
||||
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[49] + " = " + std::to_string(e.archery_setting));
|
||||
v.push_back(columns[50] + " = " + std::to_string(e.caster_range));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -514,6 +520,7 @@ public:
|
||||
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.archery_setting));
|
||||
v.push_back(std::to_string(e.caster_range));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -593,6 +600,7 @@ public:
|
||||
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.archery_setting));
|
||||
v.push_back(std::to_string(e.caster_range));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -676,6 +684,7 @@ public:
|
||||
e.expansion_bitmask = static_cast<int32_t>(atoi(row[47]));
|
||||
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.caster_range = static_cast<uint32_t>(strtoul(row[50], nullptr, 10));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -750,6 +759,7 @@ public:
|
||||
e.expansion_bitmask = static_cast<int32_t>(atoi(row[47]));
|
||||
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.caster_range = static_cast<uint32_t>(strtoul(row[50], nullptr, 10));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
|
||||
class BaseNpcScaleGlobalBaseRepository {
|
||||
public:
|
||||
struct NpcScaleGlobalBase {
|
||||
int32_t type;
|
||||
int32_t level;
|
||||
uint32_t zone_id;
|
||||
int32_t instance_version;
|
||||
int32_t ac;
|
||||
int32_t hp;
|
||||
int32_t accuracy;
|
||||
@@ -59,6 +62,8 @@ public:
|
||||
return {
|
||||
"type",
|
||||
"level",
|
||||
"zone_id",
|
||||
"instance_version",
|
||||
"ac",
|
||||
"hp",
|
||||
"accuracy",
|
||||
@@ -93,6 +98,8 @@ public:
|
||||
return {
|
||||
"type",
|
||||
"level",
|
||||
"zone_id",
|
||||
"instance_version",
|
||||
"ac",
|
||||
"hp",
|
||||
"accuracy",
|
||||
@@ -161,6 +168,8 @@ public:
|
||||
|
||||
e.type = 0;
|
||||
e.level = 0;
|
||||
e.zone_id = 0;
|
||||
e.instance_version = -1;
|
||||
e.ac = 0;
|
||||
e.hp = 0;
|
||||
e.accuracy = 0;
|
||||
@@ -212,8 +221,9 @@ public:
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
npc_scale_global_base_id
|
||||
)
|
||||
);
|
||||
@@ -224,32 +234,34 @@ public:
|
||||
|
||||
e.type = static_cast<int32_t>(atoi(row[0]));
|
||||
e.level = static_cast<int32_t>(atoi(row[1]));
|
||||
e.ac = static_cast<int32_t>(atoi(row[2]));
|
||||
e.hp = static_cast<int32_t>(atoi(row[3]));
|
||||
e.accuracy = static_cast<int32_t>(atoi(row[4]));
|
||||
e.slow_mitigation = static_cast<int32_t>(atoi(row[5]));
|
||||
e.attack = static_cast<int32_t>(atoi(row[6]));
|
||||
e.strength = static_cast<int32_t>(atoi(row[7]));
|
||||
e.stamina = static_cast<int32_t>(atoi(row[8]));
|
||||
e.dexterity = static_cast<int32_t>(atoi(row[9]));
|
||||
e.agility = static_cast<int32_t>(atoi(row[10]));
|
||||
e.intelligence = static_cast<int32_t>(atoi(row[11]));
|
||||
e.wisdom = static_cast<int32_t>(atoi(row[12]));
|
||||
e.charisma = static_cast<int32_t>(atoi(row[13]));
|
||||
e.magic_resist = static_cast<int32_t>(atoi(row[14]));
|
||||
e.cold_resist = static_cast<int32_t>(atoi(row[15]));
|
||||
e.fire_resist = static_cast<int32_t>(atoi(row[16]));
|
||||
e.poison_resist = static_cast<int32_t>(atoi(row[17]));
|
||||
e.disease_resist = static_cast<int32_t>(atoi(row[18]));
|
||||
e.corruption_resist = static_cast<int32_t>(atoi(row[19]));
|
||||
e.physical_resist = static_cast<int32_t>(atoi(row[20]));
|
||||
e.min_dmg = static_cast<int32_t>(atoi(row[21]));
|
||||
e.max_dmg = static_cast<int32_t>(atoi(row[22]));
|
||||
e.hp_regen_rate = static_cast<int32_t>(atoi(row[23]));
|
||||
e.attack_delay = static_cast<int32_t>(atoi(row[24]));
|
||||
e.spell_scale = static_cast<int32_t>(atoi(row[25]));
|
||||
e.heal_scale = static_cast<int32_t>(atoi(row[26]));
|
||||
e.special_abilities = row[27] ? row[27] : "";
|
||||
e.zone_id = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.instance_version = static_cast<int32_t>(atoi(row[3]));
|
||||
e.ac = static_cast<int32_t>(atoi(row[4]));
|
||||
e.hp = static_cast<int32_t>(atoi(row[5]));
|
||||
e.accuracy = static_cast<int32_t>(atoi(row[6]));
|
||||
e.slow_mitigation = static_cast<int32_t>(atoi(row[7]));
|
||||
e.attack = static_cast<int32_t>(atoi(row[8]));
|
||||
e.strength = static_cast<int32_t>(atoi(row[9]));
|
||||
e.stamina = static_cast<int32_t>(atoi(row[10]));
|
||||
e.dexterity = static_cast<int32_t>(atoi(row[11]));
|
||||
e.agility = static_cast<int32_t>(atoi(row[12]));
|
||||
e.intelligence = static_cast<int32_t>(atoi(row[13]));
|
||||
e.wisdom = static_cast<int32_t>(atoi(row[14]));
|
||||
e.charisma = static_cast<int32_t>(atoi(row[15]));
|
||||
e.magic_resist = static_cast<int32_t>(atoi(row[16]));
|
||||
e.cold_resist = static_cast<int32_t>(atoi(row[17]));
|
||||
e.fire_resist = static_cast<int32_t>(atoi(row[18]));
|
||||
e.poison_resist = static_cast<int32_t>(atoi(row[19]));
|
||||
e.disease_resist = static_cast<int32_t>(atoi(row[20]));
|
||||
e.corruption_resist = static_cast<int32_t>(atoi(row[21]));
|
||||
e.physical_resist = static_cast<int32_t>(atoi(row[22]));
|
||||
e.min_dmg = static_cast<int32_t>(atoi(row[23]));
|
||||
e.max_dmg = static_cast<int32_t>(atoi(row[24]));
|
||||
e.hp_regen_rate = static_cast<int32_t>(atoi(row[25]));
|
||||
e.attack_delay = static_cast<int32_t>(atoi(row[26]));
|
||||
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;
|
||||
}
|
||||
@@ -285,32 +297,34 @@ public:
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.type));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.level));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.ac));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.hp));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.accuracy));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.slow_mitigation));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.attack));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.strength));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.stamina));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.dexterity));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.agility));
|
||||
v.push_back(columns[11] + " = " + std::to_string(e.intelligence));
|
||||
v.push_back(columns[12] + " = " + std::to_string(e.wisdom));
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.charisma));
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.magic_resist));
|
||||
v.push_back(columns[15] + " = " + std::to_string(e.cold_resist));
|
||||
v.push_back(columns[16] + " = " + std::to_string(e.fire_resist));
|
||||
v.push_back(columns[17] + " = " + std::to_string(e.poison_resist));
|
||||
v.push_back(columns[18] + " = " + std::to_string(e.disease_resist));
|
||||
v.push_back(columns[19] + " = " + std::to_string(e.corruption_resist));
|
||||
v.push_back(columns[20] + " = " + std::to_string(e.physical_resist));
|
||||
v.push_back(columns[21] + " = " + std::to_string(e.min_dmg));
|
||||
v.push_back(columns[22] + " = " + std::to_string(e.max_dmg));
|
||||
v.push_back(columns[23] + " = " + std::to_string(e.hp_regen_rate));
|
||||
v.push_back(columns[24] + " = " + std::to_string(e.attack_delay));
|
||||
v.push_back(columns[25] + " = " + std::to_string(e.spell_scale));
|
||||
v.push_back(columns[26] + " = " + std::to_string(e.heal_scale));
|
||||
v.push_back(columns[27] + " = '" + Strings::Escape(e.special_abilities) + "'");
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.zone_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.instance_version));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.ac));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.hp));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.accuracy));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.slow_mitigation));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.attack));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.strength));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.stamina));
|
||||
v.push_back(columns[11] + " = " + std::to_string(e.dexterity));
|
||||
v.push_back(columns[12] + " = " + std::to_string(e.agility));
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.intelligence));
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.wisdom));
|
||||
v.push_back(columns[15] + " = " + std::to_string(e.charisma));
|
||||
v.push_back(columns[16] + " = " + std::to_string(e.magic_resist));
|
||||
v.push_back(columns[17] + " = " + std::to_string(e.cold_resist));
|
||||
v.push_back(columns[18] + " = " + std::to_string(e.fire_resist));
|
||||
v.push_back(columns[19] + " = " + std::to_string(e.poison_resist));
|
||||
v.push_back(columns[20] + " = " + std::to_string(e.disease_resist));
|
||||
v.push_back(columns[21] + " = " + std::to_string(e.corruption_resist));
|
||||
v.push_back(columns[22] + " = " + std::to_string(e.physical_resist));
|
||||
v.push_back(columns[23] + " = " + std::to_string(e.min_dmg));
|
||||
v.push_back(columns[24] + " = " + std::to_string(e.max_dmg));
|
||||
v.push_back(columns[25] + " = " + std::to_string(e.hp_regen_rate));
|
||||
v.push_back(columns[26] + " = " + std::to_string(e.attack_delay));
|
||||
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(
|
||||
fmt::format(
|
||||
@@ -334,6 +348,8 @@ public:
|
||||
|
||||
v.push_back(std::to_string(e.type));
|
||||
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.hp));
|
||||
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.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.hp));
|
||||
v.push_back(std::to_string(e.accuracy));
|
||||
@@ -452,32 +470,34 @@ public:
|
||||
|
||||
e.type = static_cast<int32_t>(atoi(row[0]));
|
||||
e.level = static_cast<int32_t>(atoi(row[1]));
|
||||
e.ac = static_cast<int32_t>(atoi(row[2]));
|
||||
e.hp = static_cast<int32_t>(atoi(row[3]));
|
||||
e.accuracy = static_cast<int32_t>(atoi(row[4]));
|
||||
e.slow_mitigation = static_cast<int32_t>(atoi(row[5]));
|
||||
e.attack = static_cast<int32_t>(atoi(row[6]));
|
||||
e.strength = static_cast<int32_t>(atoi(row[7]));
|
||||
e.stamina = static_cast<int32_t>(atoi(row[8]));
|
||||
e.dexterity = static_cast<int32_t>(atoi(row[9]));
|
||||
e.agility = static_cast<int32_t>(atoi(row[10]));
|
||||
e.intelligence = static_cast<int32_t>(atoi(row[11]));
|
||||
e.wisdom = static_cast<int32_t>(atoi(row[12]));
|
||||
e.charisma = static_cast<int32_t>(atoi(row[13]));
|
||||
e.magic_resist = static_cast<int32_t>(atoi(row[14]));
|
||||
e.cold_resist = static_cast<int32_t>(atoi(row[15]));
|
||||
e.fire_resist = static_cast<int32_t>(atoi(row[16]));
|
||||
e.poison_resist = static_cast<int32_t>(atoi(row[17]));
|
||||
e.disease_resist = static_cast<int32_t>(atoi(row[18]));
|
||||
e.corruption_resist = static_cast<int32_t>(atoi(row[19]));
|
||||
e.physical_resist = static_cast<int32_t>(atoi(row[20]));
|
||||
e.min_dmg = static_cast<int32_t>(atoi(row[21]));
|
||||
e.max_dmg = static_cast<int32_t>(atoi(row[22]));
|
||||
e.hp_regen_rate = static_cast<int32_t>(atoi(row[23]));
|
||||
e.attack_delay = static_cast<int32_t>(atoi(row[24]));
|
||||
e.spell_scale = static_cast<int32_t>(atoi(row[25]));
|
||||
e.heal_scale = static_cast<int32_t>(atoi(row[26]));
|
||||
e.special_abilities = row[27] ? row[27] : "";
|
||||
e.zone_id = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.instance_version = static_cast<int32_t>(atoi(row[3]));
|
||||
e.ac = static_cast<int32_t>(atoi(row[4]));
|
||||
e.hp = static_cast<int32_t>(atoi(row[5]));
|
||||
e.accuracy = static_cast<int32_t>(atoi(row[6]));
|
||||
e.slow_mitigation = static_cast<int32_t>(atoi(row[7]));
|
||||
e.attack = static_cast<int32_t>(atoi(row[8]));
|
||||
e.strength = static_cast<int32_t>(atoi(row[9]));
|
||||
e.stamina = static_cast<int32_t>(atoi(row[10]));
|
||||
e.dexterity = static_cast<int32_t>(atoi(row[11]));
|
||||
e.agility = static_cast<int32_t>(atoi(row[12]));
|
||||
e.intelligence = static_cast<int32_t>(atoi(row[13]));
|
||||
e.wisdom = static_cast<int32_t>(atoi(row[14]));
|
||||
e.charisma = static_cast<int32_t>(atoi(row[15]));
|
||||
e.magic_resist = static_cast<int32_t>(atoi(row[16]));
|
||||
e.cold_resist = static_cast<int32_t>(atoi(row[17]));
|
||||
e.fire_resist = static_cast<int32_t>(atoi(row[18]));
|
||||
e.poison_resist = static_cast<int32_t>(atoi(row[19]));
|
||||
e.disease_resist = static_cast<int32_t>(atoi(row[20]));
|
||||
e.corruption_resist = static_cast<int32_t>(atoi(row[21]));
|
||||
e.physical_resist = static_cast<int32_t>(atoi(row[22]));
|
||||
e.min_dmg = static_cast<int32_t>(atoi(row[23]));
|
||||
e.max_dmg = static_cast<int32_t>(atoi(row[24]));
|
||||
e.hp_regen_rate = static_cast<int32_t>(atoi(row[25]));
|
||||
e.attack_delay = static_cast<int32_t>(atoi(row[26]));
|
||||
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);
|
||||
}
|
||||
@@ -504,32 +524,34 @@ public:
|
||||
|
||||
e.type = static_cast<int32_t>(atoi(row[0]));
|
||||
e.level = static_cast<int32_t>(atoi(row[1]));
|
||||
e.ac = static_cast<int32_t>(atoi(row[2]));
|
||||
e.hp = static_cast<int32_t>(atoi(row[3]));
|
||||
e.accuracy = static_cast<int32_t>(atoi(row[4]));
|
||||
e.slow_mitigation = static_cast<int32_t>(atoi(row[5]));
|
||||
e.attack = static_cast<int32_t>(atoi(row[6]));
|
||||
e.strength = static_cast<int32_t>(atoi(row[7]));
|
||||
e.stamina = static_cast<int32_t>(atoi(row[8]));
|
||||
e.dexterity = static_cast<int32_t>(atoi(row[9]));
|
||||
e.agility = static_cast<int32_t>(atoi(row[10]));
|
||||
e.intelligence = static_cast<int32_t>(atoi(row[11]));
|
||||
e.wisdom = static_cast<int32_t>(atoi(row[12]));
|
||||
e.charisma = static_cast<int32_t>(atoi(row[13]));
|
||||
e.magic_resist = static_cast<int32_t>(atoi(row[14]));
|
||||
e.cold_resist = static_cast<int32_t>(atoi(row[15]));
|
||||
e.fire_resist = static_cast<int32_t>(atoi(row[16]));
|
||||
e.poison_resist = static_cast<int32_t>(atoi(row[17]));
|
||||
e.disease_resist = static_cast<int32_t>(atoi(row[18]));
|
||||
e.corruption_resist = static_cast<int32_t>(atoi(row[19]));
|
||||
e.physical_resist = static_cast<int32_t>(atoi(row[20]));
|
||||
e.min_dmg = static_cast<int32_t>(atoi(row[21]));
|
||||
e.max_dmg = static_cast<int32_t>(atoi(row[22]));
|
||||
e.hp_regen_rate = static_cast<int32_t>(atoi(row[23]));
|
||||
e.attack_delay = static_cast<int32_t>(atoi(row[24]));
|
||||
e.spell_scale = static_cast<int32_t>(atoi(row[25]));
|
||||
e.heal_scale = static_cast<int32_t>(atoi(row[26]));
|
||||
e.special_abilities = row[27] ? row[27] : "";
|
||||
e.zone_id = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.instance_version = static_cast<int32_t>(atoi(row[3]));
|
||||
e.ac = static_cast<int32_t>(atoi(row[4]));
|
||||
e.hp = static_cast<int32_t>(atoi(row[5]));
|
||||
e.accuracy = static_cast<int32_t>(atoi(row[6]));
|
||||
e.slow_mitigation = static_cast<int32_t>(atoi(row[7]));
|
||||
e.attack = static_cast<int32_t>(atoi(row[8]));
|
||||
e.strength = static_cast<int32_t>(atoi(row[9]));
|
||||
e.stamina = static_cast<int32_t>(atoi(row[10]));
|
||||
e.dexterity = static_cast<int32_t>(atoi(row[11]));
|
||||
e.agility = static_cast<int32_t>(atoi(row[12]));
|
||||
e.intelligence = static_cast<int32_t>(atoi(row[13]));
|
||||
e.wisdom = static_cast<int32_t>(atoi(row[14]));
|
||||
e.charisma = static_cast<int32_t>(atoi(row[15]));
|
||||
e.magic_resist = static_cast<int32_t>(atoi(row[16]));
|
||||
e.cold_resist = static_cast<int32_t>(atoi(row[17]));
|
||||
e.fire_resist = static_cast<int32_t>(atoi(row[18]));
|
||||
e.poison_resist = static_cast<int32_t>(atoi(row[19]));
|
||||
e.disease_resist = static_cast<int32_t>(atoi(row[20]));
|
||||
e.corruption_resist = static_cast<int32_t>(atoi(row[21]));
|
||||
e.physical_resist = static_cast<int32_t>(atoi(row[22]));
|
||||
e.min_dmg = static_cast<int32_t>(atoi(row[23]));
|
||||
e.max_dmg = static_cast<int32_t>(atoi(row[24]));
|
||||
e.hp_regen_rate = static_cast<int32_t>(atoi(row[25]));
|
||||
e.attack_delay = static_cast<int32_t>(atoi(row[26]));
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -319,7 +319,6 @@ RULE_CATEGORY_END()
|
||||
RULE_CATEGORY(Map)
|
||||
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_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, 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")
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// these get injected during the build pipeline
|
||||
#define CURRENT_VERSION "22.4.3-dev" // always append -dev to the current version for custom-builds
|
||||
#define CURRENT_VERSION "22.4.5-dev" // always append -dev to the current version for custom-builds
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
@@ -42,8 +42,8 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9220
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9037
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9221
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9038
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "22.4.3",
|
||||
"version": "22.4.5",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "worldserver.h"
|
||||
#include "../common/path_manager.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include <list>
|
||||
#include <signal.h>
|
||||
#include <thread>
|
||||
@@ -47,6 +48,7 @@ WorldServer *worldserver = 0;
|
||||
EQEmuLogSys LogSys;
|
||||
PathManager path;
|
||||
ZoneStore zone_store;
|
||||
PlayerEventLogs player_event_logs;
|
||||
|
||||
void CatchSignal(int sig_num)
|
||||
{
|
||||
@@ -106,6 +108,9 @@ int main()
|
||||
/* Load Looking For Guild Manager */
|
||||
lfguildmanager.LoadDatabase();
|
||||
|
||||
Timer player_event_process_timer(1000);
|
||||
player_event_logs.SetDatabase(&database)->Init();
|
||||
|
||||
auto loop_fn = [&](EQ::Timer* t) {
|
||||
Timer::SetCurrentTime();
|
||||
|
||||
@@ -117,6 +122,10 @@ int main()
|
||||
if (LFGuildExpireTimer.Check()) {
|
||||
lfguildmanager.ExpireEntries();
|
||||
}
|
||||
|
||||
if (player_event_process_timer.Check()) {
|
||||
player_event_logs.Process();
|
||||
}
|
||||
};
|
||||
|
||||
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 "queryservconfig.h"
|
||||
#include "worldserver.h"
|
||||
#include "../common/events/player_events.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <stdarg.h>
|
||||
@@ -89,6 +91,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
case 0: {
|
||||
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: {
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ int main(int argc, char **argv)
|
||||
return 1;
|
||||
}
|
||||
} else {
|
||||
content_db.SetMysql(database.getMySQL());
|
||||
content_db.SetMySQL(database);
|
||||
}
|
||||
|
||||
LogSys.SetDatabase(&database)
|
||||
|
||||
+5
-5
@@ -48,10 +48,10 @@ ChatChannel::ChatChannel(std::string inName, std::string inOwner, std::string in
|
||||
m_moderated = false;
|
||||
|
||||
LogDebug(
|
||||
"New ChatChannel created: Name: [{}], Owner: [{}], Password: [{}], MinStatus: [{}]",
|
||||
m_name.c_str(),
|
||||
m_owner.c_str(),
|
||||
m_password.c_str(),
|
||||
"New ChatChannel created: Name: [{}] Owner: [{}] Password: [{}] MinStatus: [{}]",
|
||||
m_name,
|
||||
m_owner,
|
||||
m_password,
|
||||
m_minimum_status
|
||||
);
|
||||
|
||||
@@ -667,7 +667,7 @@ ChatChannel *ChatChannelList::RemoveClientFromChannel(const std::string& in_chan
|
||||
}
|
||||
|
||||
LogDebug("Client [{}] removed from channel [{}]. Channel is owned by {}. Command directed: {}", c->GetName(), channel_name, required_channel->GetOwnerName(), command_directed);
|
||||
if (c->GetName() == required_channel->GetOwnerName() && command_directed) { // Check if the client that is leaving is the the channel owner
|
||||
if (c->GetName() == required_channel->GetOwnerName() && command_directed) { // Check if the client that is leaving is the channel owner
|
||||
LogDebug("Owner left the channel [{}], removing channel from database...", channel_name);
|
||||
database.DeleteChatChannel(channel_name); // Remove the channel from the database.
|
||||
LogDebug("Flagging [{}] channel as temporary...", channel_name);
|
||||
|
||||
+4
-1
@@ -793,7 +793,10 @@ void Clientlist::ProcessOPMailCommand(Client *c, std::string command_string, boo
|
||||
case CommandJoin:
|
||||
if (!command_directed) {
|
||||
//Append saved channels to params
|
||||
parameters = parameters + ", " + database.CurrentPlayerChannels(c->GetName());
|
||||
const auto saved_channels = database.CurrentPlayerChannels(c->GetName());
|
||||
if (!saved_channels.empty()) {
|
||||
parameters += fmt::format(", {}", Strings::Join(saved_channels, ", "));
|
||||
}
|
||||
parameters = RemoveDuplicateChannels(parameters);
|
||||
}
|
||||
c->JoinChannels(parameters, command_directed);
|
||||
|
||||
+10
-9
@@ -336,16 +336,17 @@ void UCSDatabase::DeleteChatChannel(const std::string& channel_name)
|
||||
LogInfo("Deleting channel [{}] from the database.", channel_name);
|
||||
}
|
||||
|
||||
std::string UCSDatabase::CurrentPlayerChannels(const std::string& player_name) {
|
||||
int current_player_channel_count = CurrentPlayerChannelCount(player_name);
|
||||
if (current_player_channel_count == 0) {
|
||||
return "";
|
||||
std::vector<std::string> UCSDatabase::CurrentPlayerChannels(const std::string& player_name) {
|
||||
auto rows = ChatchannelsRepository::GetWhere(*this, fmt::format("`owner` = '{}'", Strings::Escape(player_name)));
|
||||
if (rows.empty()) {
|
||||
return {};
|
||||
}
|
||||
const auto rquery = fmt::format("SELECT GROUP_CONCAT(`name` SEPARATOR ', ') FROM chatchannels WHERE `owner` = '{}'; ", Strings::Escape(player_name));
|
||||
auto results = QueryDatabase(rquery);
|
||||
auto row = results.begin();
|
||||
std::string channels = row[0];
|
||||
LogDebug("Player [{}] has the following permanent channels saved to the database: [{}].", player_name, channels);
|
||||
std::vector<std::string> channels = {};
|
||||
channels.reserve(rows.size());
|
||||
for (auto &e: rows) {
|
||||
channels.emplace_back(e.name);
|
||||
}
|
||||
LogDebug("Player [{}] has the following [{}] permanent channels saved to the database: [{}].", player_name, rows.size(), Strings::Join(channels, ", "));
|
||||
return channels;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ public:
|
||||
void SaveChatChannel(const std::string& channel_name, const std::string& channel_owner, const std::string& channel_password, const uint16& min_status);
|
||||
void DeleteChatChannel(const std::string& channel_name);
|
||||
int CurrentPlayerChannelCount(const std::string& player_name);
|
||||
std::string CurrentPlayerChannels(const std::string& player_name);
|
||||
std::vector<std::string> CurrentPlayerChannels(const std::string& player_name);
|
||||
void GetAccountStatus(Client *c);
|
||||
void SetChannelPassword(const std::string& channel_name, const std::string& password);
|
||||
void SetChannelOwner(const std::string& channel_name, const std::string& owner);
|
||||
|
||||
@@ -474,6 +474,7 @@
|
||||
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|
|
||||
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:
|
||||
# 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|
|
||||
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|
|
||||
9038|2023_02_16_add_caster_range.sql|SHOW COLUMNS FROM `bot_data` LIKE 'caster_range'|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# 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);
|
||||
}
|
||||
+7
-11
@@ -131,12 +131,6 @@ inline void UpdateWindowTitle(std::string new_title)
|
||||
#endif
|
||||
}
|
||||
|
||||
void PlayerEventQueueListener() {
|
||||
while (RunLoops) {
|
||||
player_event_logs.Process();
|
||||
Sleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
if (!RuleB(Logging, PlayerEventsQSProcess)) {
|
||||
LogInfo("[PlayerEventQueueListener] Booting queue processor");
|
||||
std::thread(PlayerEventQueueListener).detach();
|
||||
}
|
||||
|
||||
auto loop_fn = [&](EQ::Timer* t) {
|
||||
Timer::SetCurrentTime();
|
||||
|
||||
@@ -435,6 +425,10 @@ int main(int argc, char **argv)
|
||||
|
||||
client_list.Process();
|
||||
|
||||
if (player_event_process_timer.Check()) {
|
||||
player_event_logs.Process();
|
||||
}
|
||||
|
||||
if (PurgeInstanceTimer.Check()) {
|
||||
database.PurgeExpiredInstances();
|
||||
database.PurgeAllDeletedDataBuckets();
|
||||
@@ -484,6 +478,8 @@ int main(int argc, char **argv)
|
||||
LogInfo("Signaling HTTP service to stop");
|
||||
LogSys.CloseFileLogs();
|
||||
|
||||
WorldBoot::Shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+14
-4
@@ -30,6 +30,8 @@
|
||||
extern ZSList zoneserver_list;
|
||||
extern WorldConfig Config;
|
||||
|
||||
auto mutex = new Mutex;
|
||||
|
||||
void WorldBoot::GMSayHookCallBackProcessWorld(uint16 log_category, const char *func, std::string message)
|
||||
{
|
||||
// we don't want to loop up with chat messages
|
||||
@@ -136,9 +138,7 @@ bool WorldBoot::LoadDatabaseConnections()
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-tenancy: Content database
|
||||
*/
|
||||
// Multi-tenancy - content database
|
||||
if (!c->ContentDbHost.empty()) {
|
||||
if (!content_db.Connect(
|
||||
c->ContentDbHost.c_str(),
|
||||
@@ -153,7 +153,12 @@ bool WorldBoot::LoadDatabaseConnections()
|
||||
}
|
||||
}
|
||||
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;
|
||||
@@ -652,3 +657,8 @@ void WorldBoot::CheckForPossibleConfigurationIssues()
|
||||
}
|
||||
}
|
||||
|
||||
void WorldBoot::Shutdown()
|
||||
{
|
||||
safe_delete(mutex);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ public:
|
||||
static void RegisterLoginservers();
|
||||
static bool DatabaseLoadRoutines(int argc, char **argv);
|
||||
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:repository"] = &WorldserverCLI::TestRepository;
|
||||
function_map["test:repository2"] = &WorldserverCLI::TestRepository2;
|
||||
function_map["test:db-concurrency"] = &WorldserverCLI::TestDatabaseConcurrency;
|
||||
|
||||
EQEmuCommand::HandleMenu(function_map, cmd, argc, argv);
|
||||
}
|
||||
|
||||
#include "cli/database_concurrency.cpp"
|
||||
#include "cli/copy_character.cpp"
|
||||
#include "cli/database_dump.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 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 TestDatabaseConcurrency(int argc, char **argv, argh::parser &cmd, std::string &description);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -255,7 +255,6 @@ SET(zone_headers
|
||||
quest_parser_collection.h
|
||||
raids.h
|
||||
raycast_mesh.h
|
||||
skills.h
|
||||
shared_task_zone_messaging.h
|
||||
spawn2.cpp
|
||||
spawn2.h
|
||||
|
||||
+39
-32
@@ -71,6 +71,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
||||
m_enforce_spell_settings = 0;
|
||||
m_bot_archery_setting = 0;
|
||||
m_expansion_bitmask = -1;
|
||||
m_bot_caster_range = 0;
|
||||
SetBotID(0);
|
||||
SetBotSpellID(0);
|
||||
SetSpawnStatus(false);
|
||||
@@ -2240,16 +2241,16 @@ void Bot::AI_Bot_Init()
|
||||
AIautocastspell_timer.reset(nullptr);
|
||||
casting_spell_AIindex = static_cast<uint8>(AIBot_spells.size());
|
||||
|
||||
roambox_max_x = 0;
|
||||
roambox_max_y = 0;
|
||||
roambox_min_x = 0;
|
||||
roambox_min_y = 0;
|
||||
roambox_distance = 0;
|
||||
roambox_destination_x = 0;
|
||||
roambox_destination_y = 0;
|
||||
roambox_destination_z = 0;
|
||||
roambox_min_delay = 2500;
|
||||
roambox_delay = 2500;
|
||||
m_roambox.max_x = 0;
|
||||
m_roambox.max_y = 0;
|
||||
m_roambox.min_x = 0;
|
||||
m_roambox.min_y = 0;
|
||||
m_roambox.distance = 0;
|
||||
m_roambox.dest_x = 0;
|
||||
m_roambox.dest_y = 0;
|
||||
m_roambox.dest_z = 0;
|
||||
m_roambox.delay = 2500;
|
||||
m_roambox.min_delay = 2500;
|
||||
}
|
||||
|
||||
void Bot::SpellProcess() {
|
||||
@@ -2469,6 +2470,7 @@ void Bot::AI_Process()
|
||||
}
|
||||
|
||||
// We also need a leash owner and follow mob (subset of primary AI criteria)
|
||||
bot_group->VerifyGroup();
|
||||
Client* leash_owner = (bot_group->GetLeader() && bot_group->GetLeader()->IsClient() ? bot_group->GetLeader()->CastToClient() : bot_owner);
|
||||
if (!leash_owner) {
|
||||
return;
|
||||
@@ -3115,26 +3117,7 @@ void Bot::AI_Process()
|
||||
}
|
||||
}
|
||||
float melee_distance_min = melee_distance / 2.0f;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
float caster_distance_max = GetBotCasterMaxRange(melee_distance_max);
|
||||
|
||||
bool atArcheryRange = IsArcheryRange(tar);
|
||||
|
||||
@@ -3157,11 +3140,11 @@ void Bot::AI_Process()
|
||||
ChangeBotArcherWeapons(IsBotArcher());
|
||||
}
|
||||
}
|
||||
|
||||
bool stop_melee_level = GetLevel() >= GetStopMeleeLevel();
|
||||
if (IsBotArcher() && atArcheryRange) {
|
||||
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;
|
||||
}
|
||||
else if (tar_distance <= melee_distance) {
|
||||
@@ -4565,6 +4548,7 @@ bool Bot::AddBotToGroup(Bot* bot, Group* group) {
|
||||
group->SendUpdate(groupActUpdate, TempLeader);
|
||||
}
|
||||
}
|
||||
group->VerifyGroup();
|
||||
Result = true;
|
||||
}
|
||||
}
|
||||
@@ -6150,8 +6134,11 @@ void Bot::ProcessBotOwnerRefDelete(Mob* botOwner) {
|
||||
int64 Bot::CalcMaxMana() {
|
||||
switch(GetCasterClass()) {
|
||||
case 'I':
|
||||
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement());
|
||||
max_mana += (GetHeroicINT() * 10);
|
||||
case 'W': {
|
||||
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement());
|
||||
max_mana += (GetHeroicWIS() * 10);
|
||||
break;
|
||||
}
|
||||
case 'N': {
|
||||
@@ -7101,6 +7088,7 @@ int32 Bot::LevelRegen() {
|
||||
|
||||
int64 Bot::CalcHPRegen() {
|
||||
int32 regen = (LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen);
|
||||
regen += GetHeroicSTA() / 20;
|
||||
regen += (aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration());
|
||||
regen = ((regen * RuleI(Character, HPRegenMultiplier)) / 100);
|
||||
return regen;
|
||||
@@ -7172,6 +7160,7 @@ int64 Bot::CalcMaxHP() {
|
||||
int32 bot_hp = 0;
|
||||
uint32 nd = 10000;
|
||||
bot_hp += (GenerateBaseHitPoints() + itembonuses.HP);
|
||||
bot_hp += (GetHeroicSTA() * 10);
|
||||
nd += aabonuses.MaxHP;
|
||||
bot_hp = ((float)bot_hp * (float)nd / (float)10000);
|
||||
bot_hp += (spellbonuses.HP + aabonuses.HP);
|
||||
@@ -9825,4 +9814,22 @@ void Bot::SendSpellAnim(uint16 target_id, uint16 spell_id)
|
||||
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 };
|
||||
|
||||
+5
-2
@@ -348,6 +348,8 @@ public:
|
||||
void SetStopMeleeLevel(uint8 level);
|
||||
void SetGuardMode();
|
||||
void SetHoldMode();
|
||||
uint32 GetBotCasterRange() { return m_bot_caster_range; }
|
||||
bool IsValidSpellRange(uint16 spell_id, Mob const* tar);
|
||||
|
||||
// Bot AI Methods
|
||||
void AI_Bot_Init();
|
||||
@@ -490,7 +492,7 @@ public:
|
||||
EQ::constants::StanceType GetBotStance() { return _botStance; }
|
||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||
bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; }
|
||||
|
||||
float GetBotCasterMaxRange(float melee_distance_max);
|
||||
bool IsGroupHealer() { return m_CastingRoles.GroupHealer; }
|
||||
bool IsGroupSlower() { return m_CastingRoles.GroupSlower; }
|
||||
bool IsGroupNuker() { return m_CastingRoles.GroupNuker; }
|
||||
@@ -623,6 +625,7 @@ public:
|
||||
else
|
||||
_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 SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
|
||||
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
|
||||
@@ -820,7 +823,7 @@ private:
|
||||
bool m_pulling_flag;
|
||||
bool m_returning_flag;
|
||||
eStandingPetOrder m_previous_pet_order;
|
||||
|
||||
uint32 m_bot_caster_range;
|
||||
BotCastingRoles m_CastingRoles;
|
||||
std::map<std::string,std::string> bot_data_buckets;
|
||||
std::map<std::string,std::string> bot_owner_data_buckets;
|
||||
|
||||
@@ -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("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("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("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) ||
|
||||
@@ -10848,3 +10849,53 @@ void bot_command_enforce_spell_list(Client* c, const Seperator *sep)
|
||||
).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_bot(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_cure(Client *c, const Seperator *sep);
|
||||
void bot_command_defensive(Client *c, const Seperator *sep);
|
||||
|
||||
+69
-27
@@ -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->SetBotArcherySetting((l.archery_setting ? true : false));
|
||||
|
||||
loaded_bot->SetBotCasterRange(l.caster_range);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -545,6 +547,7 @@ bool BotDatabase::SaveNewBot(Bot* bot_inst, uint32& bot_id)
|
||||
e.expansion_bitmask = bot_inst->GetExpansionBitmask();
|
||||
e.enforce_spell_settings = bot_inst->GetBotEnforceSpellSetting();
|
||||
e.archery_setting = bot_inst->IsBotArcher() ? 1 : 0;
|
||||
e.caster_range = bot_inst->GetBotCasterRange();
|
||||
|
||||
auto b = BotDataRepository::InsertOne(database, e);
|
||||
if (!b.bot_id) {
|
||||
@@ -639,10 +642,11 @@ bool BotDatabase::DeleteBot(const uint32 bot_id)
|
||||
|
||||
bool BotDatabase::LoadBuffs(Bot* bot_inst)
|
||||
{
|
||||
if (!bot_inst)
|
||||
if (!bot_inst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
query = StringFormat(
|
||||
query = fmt::format(
|
||||
"SELECT"
|
||||
" `spell_id`,"
|
||||
" `caster_level`,"
|
||||
@@ -663,45 +667,58 @@ bool BotDatabase::LoadBuffs(Bot* bot_inst)
|
||||
" `extra_di_chance`,"
|
||||
" `instrument_mod`"
|
||||
" FROM `bot_buffs`"
|
||||
" WHERE `bot_id` = '%u'",
|
||||
" WHERE `bot_id` = {}",
|
||||
bot_inst->GetBotID()
|
||||
);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
if (!results.RowCount())
|
||||
}
|
||||
|
||||
if (!results.RowCount()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Buffs_Struct* bot_buffs = bot_inst->GetBuffs();
|
||||
if (!bot_buffs)
|
||||
|
||||
if (!bot_buffs) {
|
||||
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;
|
||||
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].casterlevel = atoi(row[1]);
|
||||
bot_buffs[buff_count].spellid = atoul(row[0]);
|
||||
bot_buffs[buff_count].casterlevel = atoul(row[1]);
|
||||
//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 = atoi(row[4]);
|
||||
else if (CalculateDiseaseCounters(bot_buffs[buff_count].spellid) > 0)
|
||||
bot_buffs[buff_count].counters = atoi(row[5]);
|
||||
else if (CalculateCurseCounters(bot_buffs[buff_count].spellid) > 0)
|
||||
bot_buffs[buff_count].counters = atoi(row[6]);
|
||||
else if (CalculateCorruptionCounters(bot_buffs[buff_count].spellid) > 0)
|
||||
bot_buffs[buff_count].counters = atoi(row[7]);
|
||||
bot_buffs[buff_count].counters = 0;
|
||||
if (CalculatePoisonCounters(bot_buffs[buff_count].spellid) > 0) {
|
||||
bot_buffs[buff_count].counters = atoul(row[4]);
|
||||
} else if (CalculateDiseaseCounters(bot_buffs[buff_count].spellid) > 0) {
|
||||
bot_buffs[buff_count].counters = atoul(row[5]);
|
||||
} else if (CalculateCurseCounters(bot_buffs[buff_count].spellid) > 0) {
|
||||
bot_buffs[buff_count].counters = atoul(row[6]);
|
||||
} 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].melee_rune = atoi(row[9]);
|
||||
bot_buffs[buff_count].magic_rune = atoi(row[10]);
|
||||
bot_buffs[buff_count].dot_rune = atoi(row[11]);
|
||||
bot_buffs[buff_count].persistant_buff = ((atoi(row[12])) ? (true) : (false));
|
||||
bot_buffs[buff_count].caston_x = atoi(row[13]);
|
||||
bot_buffs[buff_count].caston_y = atoi(row[14]);
|
||||
bot_buffs[buff_count].caston_z = atoi(row[15]);
|
||||
bot_buffs[buff_count].ExtraDIChance = atoi(row[16]);
|
||||
bot_buffs[buff_count].instrument_mod = atoi(row[17]);
|
||||
bot_buffs[buff_count].hit_number = atoul(row[8]);
|
||||
bot_buffs[buff_count].melee_rune = atoul(row[9]);
|
||||
bot_buffs[buff_count].magic_rune = atoul(row[10]);
|
||||
bot_buffs[buff_count].dot_rune = atoul(row[11]);
|
||||
bot_buffs[buff_count].persistant_buff = (Strings::ToBool(row[12])) != 0;
|
||||
bot_buffs[buff_count].caston_x = Strings::ToInt(row[13]);
|
||||
bot_buffs[buff_count].caston_y = Strings::ToInt(row[14]);
|
||||
bot_buffs[buff_count].caston_z = Strings::ToInt(row[15]);
|
||||
bot_buffs[buff_count].ExtraDIChance = Strings::ToInt(row[16]);
|
||||
bot_buffs[buff_count].instrument_mod = atoul(row[17]);
|
||||
bot_buffs[buff_count].casterid = 0;
|
||||
++buff_count;
|
||||
}
|
||||
@@ -3151,6 +3168,30 @@ std::string BotDatabase::GetBotNameByID(const uint32 bot_id)
|
||||
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 */
|
||||
const char* BotDatabase::fail::LoadBotsList() { return "Failed to bots list"; }
|
||||
const char* BotDatabase::fail::LoadOwnerID() { return "Failed to load owner ID"; }
|
||||
@@ -3205,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::SaveAllFollowDistances() { return "Failed to save all follow distances"; }
|
||||
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 */
|
||||
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, 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 */
|
||||
bool QueryBotGroupExistence(const std::string& botgroup_name);
|
||||
|
||||
@@ -250,6 +252,7 @@ public:
|
||||
static const char* SaveFollowDistance();
|
||||
static const char* SaveAllFollowDistances();
|
||||
static const char* SaveStopMeleeLevel();
|
||||
static const char* SaveBotCasterRange();
|
||||
|
||||
/* fail::Bot bot-group functions */
|
||||
static const char* QueryBotGroupExistence();
|
||||
|
||||
+106
-46
@@ -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)) {
|
||||
break;
|
||||
}
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost);
|
||||
|
||||
if (IsValidSpellRange(botSpell.SpellId, addMob)) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost);
|
||||
}
|
||||
if (castedSpell) {
|
||||
BotGroupSay(
|
||||
this,
|
||||
@@ -260,7 +262,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
|
||||
uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore();
|
||||
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == BARD) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
|
||||
}
|
||||
|
||||
if (castedSpell) {
|
||||
/*if (TempDontHealMeBeforeTime != tar->DontHealMeBefore())
|
||||
@@ -340,7 +344,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
}
|
||||
uint32 TempDontRootMeBefore = tar->DontRootMeBefore();
|
||||
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontRootMeBefore);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontRootMeBefore);
|
||||
}
|
||||
|
||||
if (TempDontRootMeBefore != tar->DontRootMeBefore()) {
|
||||
tar->SetDontRootMeBefore(TempDontRootMeBefore);
|
||||
@@ -488,7 +494,10 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if (IsInvulnerabilitySpell(botSpell.SpellId)) {
|
||||
tar = this; //target self for invul type spells
|
||||
}
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == BARD) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -573,7 +582,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
}
|
||||
}
|
||||
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -594,7 +605,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
// TODO: Check target to see if there is anything to dispel
|
||||
|
||||
if (tar->CountDispellableBuffs() > 0) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -768,7 +781,6 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if (CheckSpellRecastTimers(this, itr->SpellIndex)) {
|
||||
uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore();
|
||||
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontBuffMeBefore);
|
||||
|
||||
if (TempDontBuffMeBefore != tar->DontBuffMeBefore())
|
||||
tar->SetDontBuffMeBefore(TempDontBuffMeBefore);
|
||||
}
|
||||
@@ -797,7 +809,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)))
|
||||
break;
|
||||
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -820,7 +834,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
|
||||
uint32 TempDontSnareMeBefore = tar->DontSnareMeBefore();
|
||||
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontSnareMeBefore);
|
||||
}
|
||||
|
||||
if (TempDontSnareMeBefore != tar->DontSnareMeBefore())
|
||||
tar->SetDontSnareMeBefore(TempDontSnareMeBefore);
|
||||
@@ -856,7 +872,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
|
||||
uint32 TempDontDotMeBefore = tar->DontDotMeBefore();
|
||||
|
||||
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore);
|
||||
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore);
|
||||
|
||||
if (TempDontDotMeBefore != tar->DontDotMeBefore())
|
||||
tar->SetDontDotMeBefore(TempDontDotMeBefore);
|
||||
@@ -888,7 +904,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
|
||||
uint32 TempDontDotMeBefore = tar->DontDotMeBefore();
|
||||
|
||||
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(selectedBotSpell.SpellIndex, tar, selectedBotSpell.ManaCost, &TempDontDotMeBefore);
|
||||
}
|
||||
|
||||
if (TempDontDotMeBefore != tar->DontDotMeBefore())
|
||||
tar->SetDontDotMeBefore(TempDontDotMeBefore);
|
||||
@@ -929,7 +947,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
|
||||
continue;
|
||||
|
||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||
}
|
||||
if (castedSpell)
|
||||
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))
|
||||
break;
|
||||
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
}
|
||||
|
||||
if (castedSpell && GetClass() != BARD) {
|
||||
BotGroupSay(
|
||||
@@ -992,7 +1014,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)))
|
||||
break;
|
||||
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1006,7 +1030,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
|
||||
uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore();
|
||||
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontCureMeBeforeTime);
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontCureMeBeforeTime);
|
||||
|
||||
if (castedSpell) {
|
||||
if (botClass != BARD) {
|
||||
@@ -1054,7 +1078,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
|
||||
continue;
|
||||
|
||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||
}
|
||||
if (castedSpell) {
|
||||
BotGroupSay(
|
||||
this,
|
||||
@@ -1096,7 +1122,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
|
||||
continue;
|
||||
|
||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||
if (castedSpell)
|
||||
break;
|
||||
}
|
||||
@@ -1128,7 +1154,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0)
|
||||
continue;
|
||||
|
||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||
castedSpell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost);
|
||||
if (castedSpell)
|
||||
break;
|
||||
}
|
||||
@@ -1277,8 +1303,7 @@ bool Bot::AI_IdleCastCheck() {
|
||||
|
||||
if (HasGroup() && GetGroup()->GetLeader() && GetGroup()->GetLeader()->IsClient()) {
|
||||
test_against = GetGroup()->GetLeader()->CastToClient();
|
||||
}
|
||||
else if (GetOwner() && GetOwner()->IsClient()) {
|
||||
} else if (GetOwner() && GetOwner()->IsClient()) {
|
||||
test_against = GetOwner()->CastToClient();
|
||||
}
|
||||
|
||||
@@ -1291,17 +1316,12 @@ bool Bot::AI_IdleCastCheck() {
|
||||
// Healers WITHOUT pets will check if a heal is needed before buffing.
|
||||
case CLERIC:
|
||||
case PALADIN:
|
||||
case RANGER:
|
||||
case MONK:
|
||||
case ROGUE:
|
||||
case WARRIOR:
|
||||
case BERSERKER: {
|
||||
case RANGER: {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1311,13 +1331,46 @@ bool Bot::AI_IdleCastCheck() {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
case MONK:
|
||||
case ROGUE:
|
||||
case WARRIOR:
|
||||
case BERSERKER: {
|
||||
if (!AICastSpell(this, 100, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
// Pets class will first cast their pet, then buffs
|
||||
case DRUID:
|
||||
|
||||
case MAGICIAN:
|
||||
case SHADOWKNIGHT:
|
||||
case SHAMAN:
|
||||
case NECROMANCER:
|
||||
case ENCHANTER:
|
||||
case ENCHANTER: {
|
||||
if (!AICastSpell(this, 100, SpellType_Pet)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Cure)) {
|
||||
if (!AICastSpell(GetPet(), 100, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
case DRUID:
|
||||
case SHAMAN:
|
||||
case BEASTLORD: {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Pet)) {
|
||||
@@ -1339,16 +1392,12 @@ bool Bot::AI_IdleCastCheck() {
|
||||
}
|
||||
case WIZARD: { // This can eventually be move into the BEASTLORD case handler once pre-combat is fully implemented
|
||||
if (pre_combat) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Pet)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Pet)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_PreCombatBuff)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
}
|
||||
}
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_PreCombatBuff)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1357,15 +1406,11 @@ bool Bot::AI_IdleCastCheck() {
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Pet)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
}
|
||||
}
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1764,8 +1809,9 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) {
|
||||
return false;
|
||||
|
||||
uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore();
|
||||
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
|
||||
if (IsValidSpellRange(botSpell.SpellId, tar)) {
|
||||
castedSpell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime);
|
||||
}
|
||||
|
||||
if (castedSpell) {
|
||||
BotGroupSay(
|
||||
@@ -3456,3 +3502,17 @@ bool Bot::HasBotSpellEntry(uint16 spellid) {
|
||||
|
||||
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;
|
||||
}
|
||||
+1
-12
@@ -218,11 +218,6 @@ struct ClientReward
|
||||
uint32 amount;
|
||||
};
|
||||
|
||||
class ClientFactory {
|
||||
public:
|
||||
Client *MakeClient(std::shared_ptr<EQStreamInterface> ieqs);
|
||||
};
|
||||
|
||||
class Client : public Mob
|
||||
{
|
||||
public:
|
||||
@@ -343,8 +338,6 @@ public:
|
||||
bool HasRecipeLearned(uint32 recipe_id);
|
||||
bool CanIncreaseTradeskill(EQ::skills::SkillType tradeskill);
|
||||
|
||||
EQApplicationPacket* ReturnItemPacket(int16 slot_id, const EQ::ItemInstance* inst, ItemPacketType packet_type);
|
||||
|
||||
bool GetRevoked() const { return revoked; }
|
||||
void SetRevoked(bool rev) { revoked = rev; }
|
||||
inline uint32 GetIP() const { return ip; }
|
||||
@@ -697,7 +690,6 @@ public:
|
||||
void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity, bool quest = false);
|
||||
void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp);
|
||||
int32 GetRawItemAC();
|
||||
uint16 GetCombinedAC_TEST();
|
||||
|
||||
inline uint32 LSAccountID() const { return lsaccountid; }
|
||||
inline uint32 GetWID() const { return WID; }
|
||||
@@ -792,6 +784,7 @@ public:
|
||||
void SendTradeskillDetails(uint32 recipe_id);
|
||||
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
|
||||
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 GMKill();
|
||||
@@ -850,9 +843,6 @@ public:
|
||||
inline void SetBecomeNPC(bool flag) { npcflag = flag; }
|
||||
inline void SetBecomeNPCLevel(uint8 level) { npclevel = level; }
|
||||
EQStreamInterface* Connection() { return eqs; }
|
||||
#ifdef PACKET_PROFILER
|
||||
void DumpPacketProfile() { if(eqs) eqs->DumpPacketProfile(); }
|
||||
#endif
|
||||
uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; // returns item id
|
||||
uint32 GetEquipmentColor(uint8 material_slot) const;
|
||||
virtual void UpdateEquipmentLight() { m_Light.Type[EQ::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQ::lightsource::LightEquipment] = EQ::lightsource::TypeToLevel(m_Light.Type[EQ::lightsource::LightEquipment]); }
|
||||
@@ -1594,7 +1584,6 @@ public:
|
||||
void SetAccountFlag(std::string flag, std::string val);
|
||||
std::string GetAccountFlag(std::string flag);
|
||||
void SetGMStatus(int16 new_status);
|
||||
float GetDamageMultiplier(EQ::skills::SkillType how_long_has_this_been_missing);
|
||||
void Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto_consume);
|
||||
void PlayMP3(const char* fname);
|
||||
void ExpeditionSay(const char *str, int ExpID);
|
||||
|
||||
@@ -8737,12 +8737,12 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
if (!response.empty()) {
|
||||
ChannelMessageReceived(ChatChannel_Say, 0, 100, response.c_str(), nullptr, true);
|
||||
|
||||
if (!silentsaylink) {
|
||||
Message(Chat::LightGray, "You say, '%s'", response.c_str());
|
||||
}
|
||||
|
||||
ChannelMessageReceived(ChatChannel_Say, 0, 100, response.c_str(), nullptr, true);
|
||||
|
||||
return;
|
||||
}
|
||||
else {
|
||||
@@ -9951,7 +9951,7 @@ void Client::Handle_OP_Mend(const EQApplicationPacket *app)
|
||||
Message(Chat::Red, "Ability recovery time not yet met.");
|
||||
return;
|
||||
}
|
||||
p_timers.Start(pTimerMend, MendReuseTime - 1);
|
||||
p_timers.Start(pTimerMend, (MendReuseTime - GetSkillReuseTime(EQ::skills::SkillMend)));
|
||||
|
||||
int mendhp = GetMaxHP() / 4;
|
||||
int currenthp = GetHP();
|
||||
@@ -9963,9 +9963,11 @@ void Client::Handle_OP_Mend(const EQApplicationPacket *app)
|
||||
mendhp *= 2;
|
||||
MessageString(Chat::LightBlue, MEND_CRITICAL);
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::LightBlue, MEND_SUCCESS);
|
||||
}
|
||||
SetHP(GetHP() + mendhp);
|
||||
SendHPUpdate();
|
||||
MessageString(Chat::LightBlue, MEND_SUCCESS);
|
||||
}
|
||||
else {
|
||||
/* 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 SendRuleSubCommands(Client *c);
|
||||
void SendGuildSubCommands(Client *c);
|
||||
void SendPeekInvSubCommands(Client *c);
|
||||
|
||||
// Commands
|
||||
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_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_door_id = door.doorid;
|
||||
m_incline = door.incline;
|
||||
@@ -450,7 +458,7 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
|
||||
m_close_timer.Start();
|
||||
}
|
||||
|
||||
if (strncmp(m_destination_zone_name, "NONE", strlen("NONE")) == 0) {
|
||||
if (!HasDestinationZone()) {
|
||||
SetOpenState(true);
|
||||
}
|
||||
}
|
||||
@@ -497,19 +505,15 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleport door
|
||||
*/
|
||||
if (((m_open_type == 57) || (m_open_type == 58)) &&
|
||||
(strncmp(m_destination_zone_name, "NONE", strlen("NONE")) != 0)) {
|
||||
// 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 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 (IsDestinationZoneSame() && (!required_key_item)) {
|
||||
if (!disable_add_to_key_ring) {
|
||||
sender->KeyRingAdd(player_key);
|
||||
}
|
||||
|
||||
sender->MovePC(
|
||||
zone->GetZoneID(),
|
||||
zone->GetInstanceID(),
|
||||
@@ -519,14 +523,7 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
|
||||
m_destination.w
|
||||
);
|
||||
}
|
||||
/**
|
||||
* If requires a key
|
||||
*/
|
||||
else if (
|
||||
(!IsDoorOpen() || m_open_type == 58) &&
|
||||
(required_key_item && ((required_key_item == player_key) || sender->GetGM()))
|
||||
) {
|
||||
|
||||
else if ((!IsDoorOpen() || m_open_type == 58) && has_key_required) {
|
||||
if (!disable_add_to_key_ring) {
|
||||
sender->KeyRingAdd(player_key);
|
||||
}
|
||||
@@ -896,3 +893,13 @@ float Doors::GetHeading()
|
||||
{
|
||||
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 GetHeading();
|
||||
|
||||
bool HasDestinationZone() const;
|
||||
bool IsDestinationZoneSame() const;
|
||||
|
||||
private:
|
||||
|
||||
bool m_has_destination_zone = false;
|
||||
bool m_same_destination_zone = false;
|
||||
uint32 m_database_id;
|
||||
uint8 m_door_id;
|
||||
char m_zone_name[32];
|
||||
|
||||
@@ -1063,12 +1063,12 @@ void Perl__playertexture(int texture_id)
|
||||
quest_manager.playertexture(texture_id);
|
||||
}
|
||||
|
||||
void Perl__playerfeature(char* feature, int value)
|
||||
void Perl__playerfeature(const char* feature, int value)
|
||||
{
|
||||
quest_manager.playerfeature(feature, value);
|
||||
}
|
||||
|
||||
void Perl__npcfeature(char* feature, int value)
|
||||
void Perl__npcfeature(const char* feature, int value)
|
||||
{
|
||||
quest_manager.npcfeature(feature, value);
|
||||
}
|
||||
|
||||
+292
-174
@@ -3,6 +3,12 @@
|
||||
|
||||
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
|
||||
enum {
|
||||
peekNone = 0x0000,
|
||||
@@ -15,63 +21,83 @@ void command_peekinv(Client *c, const Seperator *sep)
|
||||
peekShBank = 0x0040,
|
||||
peekTrade = 0x0080,
|
||||
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] = {
|
||||
{EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END},
|
||||
{EQ::invslot::GENERAL_BEGIN, EQ::invslot::GENERAL_END},
|
||||
{EQ::invslot::slotCursor, EQ::invslot::slotCursor},
|
||||
{EQ::invslot::SLOT_INVALID, EQ::invslot::SLOT_INVALID},
|
||||
{EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END},
|
||||
{EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END},
|
||||
{EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END},
|
||||
{EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END},
|
||||
{EQ::invslot::SLOT_BEGIN, (EQ::invtype::WORLD_SIZE - 1)}
|
||||
{ EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END },
|
||||
{ EQ::invslot::GENERAL_BEGIN, EQ::invslot::GENERAL_END },
|
||||
{ EQ::invslot::slotCursor, EQ::invslot::slotCursor },
|
||||
{ EQ::invslot::SLOT_INVALID, EQ::invslot::SLOT_INVALID },
|
||||
{ EQ::invslot::TRIBUTE_BEGIN, EQ::invslot::TRIBUTE_END },
|
||||
{ EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END },
|
||||
{ EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END },
|
||||
{ EQ::invslot::TRADE_BEGIN, EQ::invslot::TRADE_END },
|
||||
{ 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;
|
||||
}
|
||||
|
||||
if (c->GetTarget() && !c->GetTarget()->IsClient()) {
|
||||
c->Message(Chat::White, "You must target a PC for this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
auto t = c;
|
||||
if (c->GetTarget() && c->GetTarget()->IsClient()) {
|
||||
t = c->GetTarget()->CastToClient();
|
||||
}
|
||||
|
||||
const EQ::ItemInstance *inst_main = nullptr;
|
||||
@@ -82,62 +108,81 @@ void command_peekinv(Client *c, const Seperator *sep)
|
||||
EQ::SayLinkEngine linker;
|
||||
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
|
||||
|
||||
c->Message(Chat::White, "Displaying inventory for %s...", targetClient->GetName());
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Displaying inventory of {}.",
|
||||
c->GetTargetDescription(t)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
Object *objectTradeskill = targetClient->GetTradeskillObject();
|
||||
auto o = t->GetTradeskillObject();
|
||||
auto found_items = false;
|
||||
|
||||
bool itemsFound = false;
|
||||
|
||||
for (int scopeIndex = 0, scopeBit = peekEquip; scopeBit < peekOutOfScope; ++scopeIndex, scopeBit <<= 1) {
|
||||
if (scopeBit & ~scopeMask) {
|
||||
for (int scope_index = 0, scope_bit = peekEquip; scope_bit < peekOutOfScope; ++scope_index, scope_bit <<= 1) {
|
||||
if (scope_bit & ~scope_mask) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scopeBit & peekWorld) {
|
||||
if (objectTradeskill == nullptr) {
|
||||
c->Message(Chat::Default, "No world tradeskill object selected...");
|
||||
if (scope_bit & peekWorld) {
|
||||
if (!o) {
|
||||
c->Message(Chat::White, "No world Tradeskill object selected.");
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
"[WorldObject DBID: %i (entityid: %i)]",
|
||||
objectTradeskill->GetDBID(),
|
||||
objectTradeskill->GetID());
|
||||
fmt::format(
|
||||
"[World Object] Database ID: {} Entity ID: {}",
|
||||
o->GetDBID(),
|
||||
o->GetID()
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
for (int16 indexMain = scope_range[scopeIndex][0]; indexMain <= scope_range[scopeIndex][1]; ++indexMain) {
|
||||
if (indexMain == EQ::invslot::SLOT_INVALID) {
|
||||
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 = ((scopeBit & peekWorld) ? objectTradeskill->GetItem(indexMain) : targetClient->GetInv().GetItem(
|
||||
indexMain
|
||||
));
|
||||
inst_main = (
|
||||
(scope_bit & peekWorld) ?
|
||||
o->GetItem(index_main) :
|
||||
t->GetInv().GetItem(index_main)
|
||||
);
|
||||
|
||||
if (inst_main) {
|
||||
itemsFound = true;
|
||||
found_items = true;
|
||||
item_data = inst_main->GetItem();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
item_data = nullptr;
|
||||
}
|
||||
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"%sSlot: %i, Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain),
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_main == nullptr) ? 0 : inst_main->GetCharges())
|
||||
);
|
||||
if (item_data) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} | {} ({}){}",
|
||||
((scope_bit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main),
|
||||
linker.GenerateLink(),
|
||||
item_data->ID,
|
||||
(
|
||||
inst_main->IsStackable() && inst_main->GetCharges() > 0 ?
|
||||
fmt::format(
|
||||
" (Stack of {})",
|
||||
inst_main->GetCharges()
|
||||
) :
|
||||
""
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
if (inst_main && inst_main->IsClassCommon()) {
|
||||
for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) {
|
||||
inst_aug = inst_main->GetItem(indexAug);
|
||||
for (uint8 augment_index = EQ::invaug::SOCKET_BEGIN; augment_index <= EQ::invaug::SOCKET_END; ++augment_index) {
|
||||
inst_aug = inst_main->GetItem(augment_index);
|
||||
if (!inst_aug) { // extant only
|
||||
continue;
|
||||
}
|
||||
@@ -146,25 +191,33 @@ void command_peekinv(Client *c, const Seperator *sep)
|
||||
linker.SetItemInst(inst_aug);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain),
|
||||
indexAug,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} (Augment Slot {}) | {} ({}){}",
|
||||
((scope_bit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main),
|
||||
augment_index,
|
||||
linker.GenerateLink(),
|
||||
item_data->ID,
|
||||
(
|
||||
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;
|
||||
}
|
||||
|
||||
for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) {
|
||||
inst_sub = inst_main->GetItem(indexSub);
|
||||
for (uint8 sub_index = EQ::invbag::SLOT_BEGIN; sub_index <= EQ::invbag::SLOT_END; ++sub_index) {
|
||||
inst_sub = inst_main->GetItem(sub_index);
|
||||
if (!inst_sub) { // extant only
|
||||
continue;
|
||||
}
|
||||
@@ -173,20 +226,32 @@ void command_peekinv(Client *c, const Seperator *sep)
|
||||
linker.SetItemInst(inst_sub);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(indexMain, indexSub)),
|
||||
((scopeBit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + indexMain) : indexMain),
|
||||
indexSub,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} Bag Slot {} | {} ({}){}",
|
||||
(
|
||||
(scope_bit & peekWorld) ?
|
||||
INVALID_INDEX :
|
||||
EQ::InventoryProfile::CalcSlotId(index_main, sub_index)
|
||||
),
|
||||
((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()) {
|
||||
for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) {
|
||||
inst_aug = inst_sub->GetItem(indexAug);
|
||||
for (uint8 augment_index = EQ::invaug::SOCKET_BEGIN; augment_index <= EQ::invaug::SOCKET_END; ++augment_index) {
|
||||
inst_aug = inst_sub->GetItem(augment_index);
|
||||
if (!inst_aug) { // extant only
|
||||
continue;
|
||||
}
|
||||
@@ -195,58 +260,73 @@ void command_peekinv(Client *c, const Seperator *sep)
|
||||
linker.SetItemInst(inst_aug);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
((scopeBit & peekWorld) ? INVALID_INDEX : EQ::InventoryProfile::CalcSlotId(
|
||||
indexMain,
|
||||
indexSub
|
||||
)),
|
||||
indexSub,
|
||||
indexAug,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} Bag Slot {} (Augment Slot {}) | {} ({}){}",
|
||||
(
|
||||
(scope_bit & peekWorld) ?
|
||||
INVALID_INDEX :
|
||||
EQ::InventoryProfile::CalcSlotId(index_main,sub_index)
|
||||
),
|
||||
sub_index,
|
||||
augment_index,
|
||||
linker.GenerateLink(),
|
||||
item_data->ID,
|
||||
(
|
||||
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;
|
||||
for (auto it = targetClient->GetInv().cursor_cbegin();
|
||||
(it != targetClient->GetInv().cursor_cend());
|
||||
++it, ++limboIndex) {
|
||||
if (it == targetClient->GetInv().cursor_cbegin()) {
|
||||
for (auto it = t->GetInv().cursor_cbegin(); (it != t->GetInv().cursor_cend()); ++it, ++limboIndex) {
|
||||
if (it == t->GetInv().cursor_cbegin()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
inst_main = *it;
|
||||
if (inst_main) {
|
||||
itemsFound = true;
|
||||
found_items = true;
|
||||
item_data = inst_main->GetItem();
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
item_data = nullptr;
|
||||
}
|
||||
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"%sSlot: %i, Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
(8000 + limboIndex),
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_main == nullptr) ? 0 : inst_main->GetCharges())
|
||||
);
|
||||
if (item_data) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} | {} ({}){}",
|
||||
(8000 + limboIndex),
|
||||
item_data->ID,
|
||||
linker.GenerateLink(),
|
||||
(
|
||||
inst_main->IsStackable() && inst_main->GetCharges() > 0 ?
|
||||
fmt::format(
|
||||
" (Stack of {})",
|
||||
inst_main->GetCharges()
|
||||
) :
|
||||
""
|
||||
)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
if (inst_main && inst_main->IsClassCommon()) {
|
||||
for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN; indexAug <= EQ::invaug::SOCKET_END; ++indexAug) {
|
||||
inst_aug = inst_main->GetItem(indexAug);
|
||||
for (uint8 augment_index = EQ::invaug::SOCKET_BEGIN; augment_index <= EQ::invaug::SOCKET_END; ++augment_index) {
|
||||
inst_aug = inst_main->GetItem(augment_index);
|
||||
if (!inst_aug) { // extant only
|
||||
continue;
|
||||
}
|
||||
@@ -255,25 +335,32 @@ void command_peekinv(Client *c, const Seperator *sep)
|
||||
linker.SetItemInst(inst_aug);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
(8000 + limboIndex),
|
||||
indexAug,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} (Augment Slot {}) | {} ({}){}",
|
||||
(8000 + limboIndex),
|
||||
augment_index,
|
||||
linker.GenerateLink(),
|
||||
item_data->ID,
|
||||
(
|
||||
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;
|
||||
}
|
||||
|
||||
for (uint8 indexSub = EQ::invbag::SLOT_BEGIN; indexSub <= EQ::invbag::SLOT_END; ++indexSub) {
|
||||
inst_sub = inst_main->GetItem(indexSub);
|
||||
for (uint8 sub_index = EQ::invbag::SLOT_BEGIN; sub_index <= EQ::invbag::SLOT_END; ++sub_index) {
|
||||
inst_sub = inst_main->GetItem(sub_index);
|
||||
if (!inst_sub) {
|
||||
continue;
|
||||
}
|
||||
@@ -282,23 +369,32 @@ void command_peekinv(Client *c, const Seperator *sep)
|
||||
|
||||
linker.SetItemInst(inst_sub);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
(8000 + limboIndex),
|
||||
indexSub,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
);
|
||||
if (item_data) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} Bag Slot {} | {} ({}){}",
|
||||
(8000 + limboIndex),
|
||||
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()) {
|
||||
for (uint8 indexAug = EQ::invaug::SOCKET_BEGIN;
|
||||
indexAug <= EQ::invaug::SOCKET_END;
|
||||
++indexAug) {
|
||||
inst_aug = inst_sub->GetItem(indexAug);
|
||||
for (uint8 augment_index = EQ::invaug::SOCKET_BEGIN;
|
||||
augment_index <= EQ::invaug::SOCKET_END;
|
||||
++augment_index) {
|
||||
inst_aug = inst_sub->GetItem(augment_index);
|
||||
if (!inst_aug) { // extant only
|
||||
continue;
|
||||
}
|
||||
@@ -307,16 +403,23 @@ void command_peekinv(Client *c, const Seperator *sep)
|
||||
linker.SetItemInst(inst_aug);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
(8000 + limboIndex),
|
||||
indexSub,
|
||||
indexAug,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} Bag Slot {} (Augment Slot {}) | {} ({}){}",
|
||||
(8000 + limboIndex),
|
||||
sub_index,
|
||||
augment_index,
|
||||
linker.GenerateLink(),
|
||||
item_data->ID,
|
||||
(
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -3376,31 +3376,6 @@ void Client::SendItemPacket(int16 slot_id, const EQ::ItemInstance* inst, ItemPac
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
|
||||
EQApplicationPacket* Client::ReturnItemPacket(int16 slot_id, const EQ::ItemInstance* inst, ItemPacketType packet_type)
|
||||
{
|
||||
if (!inst)
|
||||
return nullptr;
|
||||
|
||||
// Serialize item into |-delimited string
|
||||
std::string packet = inst->Serialize(slot_id);
|
||||
|
||||
EmuOpcode opcode = OP_Unknown;
|
||||
EQApplicationPacket* outapp = nullptr;
|
||||
BulkItemPacket_Struct* itempacket = nullptr;
|
||||
|
||||
// Construct packet
|
||||
opcode = OP_ItemPacket;
|
||||
outapp = new EQApplicationPacket(opcode, packet.length()+1);
|
||||
itempacket = (BulkItemPacket_Struct*)outapp->pBuffer;
|
||||
memcpy(itempacket->SerializedItem, packet.c_str(), packet.length());
|
||||
|
||||
#if EQDEBUG >= 9
|
||||
DumpPacket(outapp);
|
||||
#endif
|
||||
|
||||
return outapp;
|
||||
}
|
||||
|
||||
static int16 BandolierSlotToWeaponSlot(int BandolierSlot)
|
||||
{
|
||||
switch (BandolierSlot)
|
||||
|
||||
@@ -3038,6 +3038,19 @@ void Lua_Client::UseAugmentContainer(int 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() {
|
||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -3296,6 +3309,8 @@ luabind::scope lua_register_client() {
|
||||
.def("IncreaseSkill", (void(Lua_Client::*)(int))&Lua_Client::IncreaseSkill)
|
||||
.def("IncreaseSkill", (void(Lua_Client::*)(int,int))&Lua_Client::IncreaseSkill)
|
||||
.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("IsDead", &Lua_Client::IsDead)
|
||||
.def("IsDueling", (bool(Lua_Client::*)(void))&Lua_Client::IsDueling)
|
||||
|
||||
@@ -466,6 +466,8 @@ public:
|
||||
void SetItemCooldown(uint32 item_id, uint32 in_time);
|
||||
uint32 GetItemCooldown(uint32 item_id);
|
||||
void UseAugmentContainer(int container_slot);
|
||||
bool IsAutoAttackEnabled();
|
||||
bool IsAutoFireEnabled();
|
||||
|
||||
void ApplySpell(int spell_id);
|
||||
void ApplySpell(int spell_id, int duration);
|
||||
|
||||
@@ -2823,6 +2823,22 @@ Lua_HateList Lua_Mob::GetHateListBots(uint32 distance) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Lua_Mob::IsFindable() {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->IsFindable();
|
||||
}
|
||||
|
||||
|
||||
bool Lua_Mob::IsTrackable() {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->IsTrackable();
|
||||
}
|
||||
|
||||
float Lua_Mob::GetDefaultRaceSize() {
|
||||
Lua_Safe_Call_Real();
|
||||
return self->GetDefaultRaceSize();
|
||||
}
|
||||
|
||||
luabind::scope lua_register_mob() {
|
||||
return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -3022,6 +3038,7 @@ luabind::scope lua_register_mob() {
|
||||
.def("GetDEX", &Lua_Mob::GetDEX)
|
||||
.def("GetDR", &Lua_Mob::GetDR)
|
||||
.def("GetDamageAmount", (uint32(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetDamageAmount)
|
||||
.def("GetDefaultRaceSize", &Lua_Mob::GetDefaultRaceSize)
|
||||
.def("GetDeity", &Lua_Mob::GetDeity)
|
||||
.def("GetDisplayAC", &Lua_Mob::GetDisplayAC)
|
||||
.def("GetDrakkinDetails", &Lua_Mob::GetDrakkinDetails)
|
||||
@@ -3164,6 +3181,7 @@ luabind::scope lua_register_mob() {
|
||||
.def("IsEngaged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEngaged)
|
||||
.def("IsEnraged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEnraged)
|
||||
.def("IsFeared", (bool(Lua_Mob::*)(void))&Lua_Mob::IsFeared)
|
||||
.def("IsFindable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsFindable)
|
||||
.def("IsHorse", &Lua_Mob::IsHorse)
|
||||
.def("IsImmuneToSpell", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::IsImmuneToSpell)
|
||||
.def("IsInvisible", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsInvisible)
|
||||
@@ -3179,6 +3197,7 @@ luabind::scope lua_register_mob() {
|
||||
.def("IsStunned", (bool(Lua_Mob::*)(void))&Lua_Mob::IsStunned)
|
||||
.def("IsTargetable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsTargetable)
|
||||
.def("IsTargeted", &Lua_Mob::IsTargeted)
|
||||
.def("IsTrackable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsTrackable)
|
||||
.def("IsWarriorClass", &Lua_Mob::IsWarriorClass)
|
||||
.def("Kill", (void(Lua_Mob::*)(void))&Lua_Mob::Kill)
|
||||
.def("Mesmerize", (void(Lua_Mob::*)(void))&Lua_Mob::Mesmerize)
|
||||
|
||||
@@ -513,6 +513,9 @@ public:
|
||||
void CopyHateList(Lua_Mob to);
|
||||
bool IsAttackAllowed(Lua_Mob target);
|
||||
bool IsAttackAllowed(Lua_Mob target, bool is_spell_attack);
|
||||
bool IsFindable();
|
||||
bool IsTrackable();
|
||||
float GetDefaultRaceSize();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -767,6 +767,16 @@ void Lua_NPC::ScaleNPC(uint8 npc_level, bool override_special_abilities)
|
||||
self->ScaleNPC(npc_level, true, override_special_abilities);
|
||||
}
|
||||
|
||||
bool Lua_NPC::IsUnderwaterOnly() {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->IsUnderwaterOnly();
|
||||
}
|
||||
|
||||
bool Lua_NPC::HasSpecialAbilities() {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->HasSpecialAbilities();
|
||||
}
|
||||
|
||||
luabind::scope lua_register_npc() {
|
||||
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -860,6 +870,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget)
|
||||
.def("IsRareSpawn", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRareSpawn)
|
||||
.def("IsTaunting", (bool(Lua_NPC::*)(void))&Lua_NPC::IsTaunting)
|
||||
.def("IsUnderwaterOnly", (bool(Lua_NPC::*)(void))&Lua_NPC::IsUnderwaterOnly)
|
||||
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop)
|
||||
.def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop)
|
||||
.def("ModifyNPCStat", (void(Lua_NPC::*)(std::string,std::string))&Lua_NPC::ModifyNPCStat)
|
||||
|
||||
@@ -174,6 +174,8 @@ public:
|
||||
void SetLDoNTrapDetected(bool is_detected);
|
||||
void ScaleNPC(uint8 npc_level);
|
||||
void ScaleNPC(uint8 npc_level, bool override_special_abilities);
|
||||
bool IsUnderwaterOnly();
|
||||
bool HasSpecialAbilities();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+12
-4
@@ -231,6 +231,8 @@ int main(int argc, char** argv) {
|
||||
worldserver.SetLauncherName("NONE");
|
||||
}
|
||||
|
||||
auto mutex = new Mutex;
|
||||
|
||||
LogInfo("Connecting to MySQL");
|
||||
if (!database.Connect(
|
||||
Config->DatabaseHost.c_str(),
|
||||
@@ -242,9 +244,7 @@ int main(int argc, char** argv) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Multi-tenancy: Content Database
|
||||
*/
|
||||
// Multi-tenancy: Content Database
|
||||
if (!Config->ContentDbHost.empty()) {
|
||||
if (!content_db.Connect(
|
||||
Config->ContentDbHost.c_str() ,
|
||||
@@ -258,7 +258,12 @@ int main(int argc, char** argv) {
|
||||
return 1;
|
||||
}
|
||||
} 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 */
|
||||
@@ -613,6 +618,9 @@ int main(int argc, char** argv) {
|
||||
safe_delete(parse);
|
||||
LogInfo("Proper zone shutdown complete.");
|
||||
LogSys.CloseFileLogs();
|
||||
|
||||
safe_delete(mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+9
-3
@@ -518,7 +518,9 @@ Mob::Mob(
|
||||
}
|
||||
|
||||
Mob::~Mob()
|
||||
{
|
||||
{
|
||||
quest_manager.stopalltimers(this);
|
||||
|
||||
mMovementManager->RemoveMob(this);
|
||||
|
||||
AI_Stop();
|
||||
@@ -4189,7 +4191,7 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
|
||||
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);
|
||||
if (twinproc) {
|
||||
SpellFinished(spell_id, this, EQ::spells::CastingSlot::Item, 0, -1, spells[spell_id].resist_difficulty, true, level_override);
|
||||
@@ -4205,7 +4207,11 @@ void Mob::ExecWeaponProc(const EQ::ItemInstance *inst, uint16 spell_id, Mob *on,
|
||||
}
|
||||
|
||||
uint32 Mob::GetZoneID() const {
|
||||
return(zone->GetZoneID());
|
||||
return zone->GetZoneID();
|
||||
}
|
||||
|
||||
uint16 Mob::GetInstanceVersion() const {
|
||||
return zone->GetInstanceVersion();
|
||||
}
|
||||
|
||||
int Mob::GetHaste()
|
||||
|
||||
+1
-1
@@ -42,7 +42,6 @@ char* strn0cpy(char* dest, const char* source, uint32 size);
|
||||
|
||||
#define MAX_SPECIAL_ATTACK_PARAMS 8
|
||||
|
||||
class EGNode;
|
||||
class Client;
|
||||
class EQApplicationPacket;
|
||||
class Group;
|
||||
@@ -1239,6 +1238,7 @@ public:
|
||||
bool Charmed() const { return typeofpet == petCharmed; }
|
||||
static uint32 GetLevelHP(uint8 tlevel);
|
||||
uint32 GetZoneID() const; //for perl
|
||||
uint16 GetInstanceVersion() const; //for perl
|
||||
virtual int32 CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc = false);
|
||||
virtual int32 CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible = 0);
|
||||
|
||||
|
||||
+22
-130
@@ -404,16 +404,16 @@ void NPC::AI_Init()
|
||||
AIautocastspell_timer.reset(nullptr);
|
||||
casting_spell_AIindex = static_cast<uint8>(AIspells.size());
|
||||
|
||||
roambox_max_x = 0;
|
||||
roambox_max_y = 0;
|
||||
roambox_min_x = 0;
|
||||
roambox_min_y = 0;
|
||||
roambox_distance = 0;
|
||||
roambox_destination_x = 0;
|
||||
roambox_destination_y = 0;
|
||||
roambox_destination_z = 0;
|
||||
roambox_min_delay = 2500;
|
||||
roambox_delay = 2500;
|
||||
m_roambox.max_x = 0;
|
||||
m_roambox.max_y = 0;
|
||||
m_roambox.min_x = 0;
|
||||
m_roambox.min_y = 0;
|
||||
m_roambox.distance = 0;
|
||||
m_roambox.dest_x = 0;
|
||||
m_roambox.dest_y = 0;
|
||||
m_roambox.dest_z = 0;
|
||||
m_roambox.delay = 2500;
|
||||
m_roambox.min_delay = 2500;
|
||||
}
|
||||
|
||||
void Client::AI_Init()
|
||||
@@ -1073,9 +1073,15 @@ void Mob::AI_Process() {
|
||||
if (engaged) {
|
||||
if (IsNPC() && m_z_clip_check_timer.Check()) {
|
||||
auto t = GetTarget();
|
||||
if (t && DistanceNoZ(GetPosition(), t->GetPosition()) < 75 && std::abs(GetPosition().z - t->GetPosition().z) > 15 && !CheckLosFN(t)) {
|
||||
GMMove(t->GetPosition().x, t->GetPosition().y, t->GetPosition().z, t->GetPosition().w);
|
||||
FaceTarget(t);
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1592,123 +1598,9 @@ void NPC::AI_DoMovement() {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Roambox logic sets precedence
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
// Roambox logic sets precedence
|
||||
if (m_roambox.distance > 0) {
|
||||
HandleRoambox();
|
||||
return;
|
||||
}
|
||||
else if (roamer) {
|
||||
|
||||
+8
-10
@@ -795,21 +795,19 @@ void Mob::DisplayInfo(Mob *mob)
|
||||
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(
|
||||
0,
|
||||
"| # Target: %s Type: %i (%s)",
|
||||
npc->GetCleanName(),
|
||||
npc_type,
|
||||
npc_type_string.c_str());
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"| # Target: {} Type: {} ({})",
|
||||
npc->GetCleanName(),
|
||||
npc_scale_manager->GetNPCScalingType(npc),
|
||||
npc_scale_manager->GetNPCScalingTypeName(npc)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
NPCCommandsMenu(client, npc);
|
||||
}
|
||||
|
||||
// std::cout << "Window Length: " << window_text.length() << std::endl;
|
||||
|
||||
if (client->GetDisplayMobInfoWindow()) {
|
||||
client->SendFullPopup(
|
||||
"GM: Entity Info",
|
||||
|
||||
@@ -236,7 +236,7 @@ public:
|
||||
|
||||
if (RuleB(Map, FixZWhenPathing)) {
|
||||
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;
|
||||
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) {
|
||||
size = 5;
|
||||
}
|
||||
if (race == RACE_WURM_158) {
|
||||
size = 15;
|
||||
}
|
||||
|
||||
taunting = false;
|
||||
proximity = nullptr;
|
||||
@@ -255,15 +258,18 @@ NPC::NPC(const NPCType *npc_type_data, Spawn2 *in_respawn, const glm::vec4 &posi
|
||||
}
|
||||
|
||||
guard_anim = eaStanding;
|
||||
roambox_distance = 0;
|
||||
roambox_max_x = -2;
|
||||
roambox_max_y = -2;
|
||||
roambox_min_x = -2;
|
||||
roambox_min_y = -2;
|
||||
roambox_destination_x = -2;
|
||||
roambox_destination_y = -2;
|
||||
roambox_min_delay = 1000;
|
||||
roambox_delay = 1000;
|
||||
|
||||
m_roambox.max_x = -2;
|
||||
m_roambox.max_y = -2;
|
||||
m_roambox.min_x = -2;
|
||||
m_roambox.min_y = -2;
|
||||
m_roambox.distance = 0;
|
||||
m_roambox.dest_x = -2;
|
||||
m_roambox.dest_y = -2;
|
||||
m_roambox.dest_z = 0;
|
||||
m_roambox.delay = 1000;
|
||||
m_roambox.min_delay = 1000;
|
||||
|
||||
p_depop = false;
|
||||
loottable_id = npc_type_data->loottable_id;
|
||||
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
|
||||
{
|
||||
return roambox_max_x;
|
||||
return m_roambox.max_x;
|
||||
}
|
||||
|
||||
float NPC::GetRoamboxMaxY() const
|
||||
{
|
||||
return roambox_max_y;
|
||||
return m_roambox.max_y;
|
||||
}
|
||||
|
||||
float NPC::GetRoamboxMinX() const
|
||||
{
|
||||
return roambox_min_x;
|
||||
return m_roambox.min_x;
|
||||
}
|
||||
|
||||
float NPC::GetRoamboxMinY() const
|
||||
{
|
||||
return roambox_min_y;
|
||||
return m_roambox.min_y;
|
||||
}
|
||||
|
||||
float NPC::GetRoamboxDistance() const
|
||||
{
|
||||
return roambox_distance;
|
||||
return m_roambox.distance;
|
||||
}
|
||||
|
||||
float NPC::GetRoamboxDestinationX() const
|
||||
{
|
||||
return roambox_destination_x;
|
||||
return m_roambox.dest_x;
|
||||
}
|
||||
|
||||
float NPC::GetRoamboxDestinationY() const
|
||||
{
|
||||
return roambox_destination_y;
|
||||
return m_roambox.dest_y;
|
||||
}
|
||||
|
||||
float NPC::GetRoamboxDestinationZ() const
|
||||
{
|
||||
return roambox_destination_z;
|
||||
return m_roambox.dest_z;
|
||||
}
|
||||
|
||||
uint32 NPC::GetRoamboxDelay() const
|
||||
{
|
||||
return roambox_delay;
|
||||
return m_roambox.delay;
|
||||
}
|
||||
|
||||
uint32 NPC::GetRoamboxMinDelay() const
|
||||
{
|
||||
return roambox_min_delay;
|
||||
return m_roambox.min_delay;
|
||||
}
|
||||
|
||||
NPC::~NPC()
|
||||
@@ -3811,3 +3817,149 @@ void NPC::SendPositionToClients()
|
||||
}
|
||||
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;
|
||||
};
|
||||
|
||||
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 Client;
|
||||
class Group;
|
||||
@@ -538,6 +551,8 @@ public:
|
||||
|
||||
protected:
|
||||
|
||||
void HandleRoambox();
|
||||
|
||||
const NPCType* NPCTypedata;
|
||||
NPCType* NPCTypedata_ours; //special case for npcs with uniquely created data.
|
||||
|
||||
@@ -635,16 +650,8 @@ protected:
|
||||
glm::vec4 m_GuardPoint;
|
||||
glm::vec4 m_GuardPointSaved;
|
||||
EmuAppearance guard_anim;
|
||||
float roambox_max_x;
|
||||
float roambox_max_y;
|
||||
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;
|
||||
|
||||
Roambox m_roambox = {};
|
||||
|
||||
uint16 skills[EQ::skills::HIGHEST_SKILL + 1];
|
||||
|
||||
|
||||
+87
-24
@@ -30,21 +30,27 @@ void NpcScaleManager::ScaleNPC(
|
||||
NPC *npc,
|
||||
bool always_scale,
|
||||
bool override_special_abilities
|
||||
)
|
||||
{
|
||||
) {
|
||||
if (npc->IsSkipAutoScale() || npc->GetNPCTypeID() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int8 npc_type = GetNPCScalingType(npc);
|
||||
int npc_level = npc->GetLevel();
|
||||
bool is_auto_scaled = IsAutoScaled(npc);
|
||||
auto npc_type = GetNPCScalingType(npc);
|
||||
auto npc_level = npc->GetLevel();
|
||||
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) {
|
||||
LogNPCScaling(
|
||||
"NPC: [{}] - scaling data not found for type: [{}] level: [{}]",
|
||||
"NPC: [{}] - scaling data not found for type [{}] level [{}]",
|
||||
npc->GetCleanName(),
|
||||
npc_type,
|
||||
npc_level
|
||||
@@ -209,11 +215,13 @@ void NpcScaleManager::ResetNPCScaling(NPC* npc)
|
||||
bool NpcScaleManager::LoadScaleData()
|
||||
{
|
||||
auto rows = NpcScaleGlobalBaseRepository::All(content_db);
|
||||
for (auto &s: rows) {
|
||||
for (const auto &s : rows) {
|
||||
global_npc_scale scale_data;
|
||||
|
||||
scale_data.type = s.type;
|
||||
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.hp = s.hp;
|
||||
scale_data.accuracy = s.accuracy;
|
||||
@@ -246,7 +254,12 @@ bool NpcScaleManager::LoadScaleData()
|
||||
|
||||
npc_global_base_scaling_data.insert(
|
||||
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
|
||||
)
|
||||
);
|
||||
@@ -262,9 +275,45 @@ bool NpcScaleManager::LoadScaleData()
|
||||
* @param npc_level
|
||||
* @return NpcScaleManager::global_npc_scale
|
||||
*/
|
||||
NpcScaleManager::global_npc_scale NpcScaleManager::GetGlobalScaleDataForTypeLevel(int8 npc_type, int npc_level)
|
||||
{
|
||||
auto iter = npc_global_base_scaling_data.find(std::make_pair(npc_type, npc_level));
|
||||
NpcScaleManager::global_npc_scale NpcScaleManager::GetGlobalScaleDataForTypeLevel(
|
||||
int8 npc_type,
|
||||
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()) {
|
||||
return iter->second;
|
||||
}
|
||||
@@ -487,14 +536,21 @@ bool NpcScaleManager::IsAutoScaled(NPC* npc)
|
||||
*/
|
||||
bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
|
||||
{
|
||||
int8 npc_type = GetNPCScalingType(npc);
|
||||
int npc_level = npc->GetLevel();
|
||||
auto npc_type = GetNPCScalingType(npc);
|
||||
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) {
|
||||
LogNPCScaling(
|
||||
"NPC: [{}] - scaling data not found for type: [{}] level: [{}]",
|
||||
"NPC: [{}] - scaling data not found for type [{}] level [{}]",
|
||||
npc->GetCleanName(),
|
||||
npc_type,
|
||||
npc_level
|
||||
@@ -503,7 +559,7 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
|
||||
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) {
|
||||
n.AC = g.ac;
|
||||
n.hp = g.hp;
|
||||
@@ -528,8 +584,8 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
|
||||
n.maxdmg = g.max_dmg;
|
||||
n.hp_regen_rate = g.hp_regen_rate;
|
||||
n.attack_delay = g.attack_delay;
|
||||
n.spellscale = (float) g.spell_scale;
|
||||
n.healscale = (float) g.heal_scale;
|
||||
n.spellscale = static_cast<float>(g.spell_scale);
|
||||
n.healscale = static_cast<float>(g.heal_scale);
|
||||
n.special_abilities = g.special_abilities;
|
||||
|
||||
return NpcTypesRepository::UpdateOne(content_db, n);
|
||||
@@ -545,14 +601,21 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
|
||||
*/
|
||||
bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc)
|
||||
{
|
||||
int8 npc_type = GetNPCScalingType(npc);
|
||||
int npc_level = npc->GetLevel();
|
||||
auto npc_type = GetNPCScalingType(npc);
|
||||
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) {
|
||||
LogNPCScaling(
|
||||
"NPC: [{}] - scaling data not found for type: [{}] level: [{}]",
|
||||
"NPC: [{}] - scaling data not found for type [{}] level [{}]",
|
||||
npc->GetCleanName(),
|
||||
npc_type,
|
||||
npc_level
|
||||
@@ -561,7 +624,7 @@ bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc)
|
||||
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) {
|
||||
n.AC = 0;
|
||||
n.hp = 0;
|
||||
|
||||
+38
-29
@@ -22,37 +22,41 @@
|
||||
#define EQEMU_NPC_SCALE_MANAGER_H
|
||||
|
||||
#include "npc.h"
|
||||
#include "zone.h"
|
||||
extern Zone* zone;
|
||||
|
||||
class NpcScaleManager {
|
||||
public:
|
||||
struct global_npc_scale {
|
||||
int type;
|
||||
int level;
|
||||
int ac;
|
||||
int64 hp;
|
||||
int accuracy;
|
||||
int slow_mitigation;
|
||||
int attack;
|
||||
int strength;
|
||||
int stamina;
|
||||
int dexterity;
|
||||
int agility;
|
||||
int intelligence;
|
||||
int wisdom;
|
||||
int charisma;
|
||||
int magic_resist;
|
||||
int cold_resist;
|
||||
int fire_resist;
|
||||
int poison_resist;
|
||||
int disease_resist;
|
||||
int corruption_resist;
|
||||
int physical_resist;
|
||||
int min_dmg;
|
||||
int max_dmg;
|
||||
int64 hp_regen_rate;
|
||||
int attack_delay;
|
||||
int spell_scale;
|
||||
int heal_scale;
|
||||
int8 type;
|
||||
uint8 level;
|
||||
uint32 zone_id;
|
||||
uint16 instance_version;
|
||||
int ac;
|
||||
int64 hp;
|
||||
int accuracy;
|
||||
int slow_mitigation;
|
||||
int attack;
|
||||
int strength;
|
||||
int stamina;
|
||||
int dexterity;
|
||||
int agility;
|
||||
int intelligence;
|
||||
int wisdom;
|
||||
int charisma;
|
||||
int magic_resist;
|
||||
int cold_resist;
|
||||
int fire_resist;
|
||||
int poison_resist;
|
||||
int disease_resist;
|
||||
int corruption_resist;
|
||||
int physical_resist;
|
||||
int min_dmg;
|
||||
int max_dmg;
|
||||
int64 hp_regen_rate;
|
||||
int attack_delay;
|
||||
int spell_scale;
|
||||
int heal_scale;
|
||||
|
||||
std::string special_abilities;
|
||||
};
|
||||
@@ -91,9 +95,14 @@ public:
|
||||
bool IsAutoScaled(NPC* npc);
|
||||
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);
|
||||
std::string GetNPCScalingTypeName(NPC * &npc);
|
||||
|
||||
@@ -2897,6 +2897,16 @@ void Perl_Client_UseAugmentContainer(Client* self, int 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()
|
||||
{
|
||||
perl::interpreter perl(PERL_GET_THX);
|
||||
@@ -3159,6 +3169,8 @@ void perl_register_client()
|
||||
package.add("IncreaseSkill", (void(*)(Client*, int))&Perl_Client_IncreaseSkill);
|
||||
package.add("IncreaseSkill", (void(*)(Client*, int, int))&Perl_Client_IncreaseSkill);
|
||||
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("IsCrouching", &Perl_Client_IsCrouching);
|
||||
package.add("IsDueling", &Perl_Client_IsDueling);
|
||||
|
||||
@@ -2786,6 +2786,26 @@ Bot* Perl_Mob_GetHateRandomBot(Mob* self) // @categories Hate and Aggro
|
||||
return self->GetHateRandomBot();
|
||||
}
|
||||
|
||||
bool Perl_Mob_IsFindable(Mob* self) // @categories Script Utility
|
||||
{
|
||||
return self->IsFindable();
|
||||
}
|
||||
|
||||
bool Perl_Mob_IsTrackable(Mob* self) // @categories Script Utility
|
||||
{
|
||||
return self->IsTrackable();
|
||||
}
|
||||
|
||||
bool Perl_Mob_IsBerserk(Mob* self) // @categories Script Utility
|
||||
{
|
||||
return self->IsBerserk();
|
||||
}
|
||||
|
||||
float Perl_Mob_GetDefaultRaceSize(Mob* self) // @categories Script Utility
|
||||
{
|
||||
return self->GetDefaultRaceSize();
|
||||
}
|
||||
|
||||
void perl_register_mob()
|
||||
{
|
||||
perl::interpreter perl(PERL_GET_THX);
|
||||
@@ -2970,6 +2990,7 @@ void perl_register_mob()
|
||||
package.add("GetClassName", &Perl_Mob_GetClassName);
|
||||
package.add("GetCleanName", &Perl_Mob_GetCleanName);
|
||||
package.add("GetCorruption", &Perl_Mob_GetCorruption);
|
||||
package.add("GetDefaultRaceSize", &Perl_Mob_GetDefaultRaceSize);
|
||||
package.add("GetDEX", &Perl_Mob_GetDEX);
|
||||
package.add("GetDR", &Perl_Mob_GetDR);
|
||||
package.add("GetDamageAmount", &Perl_Mob_GetDamageAmount);
|
||||
@@ -3112,6 +3133,7 @@ void perl_register_mob()
|
||||
package.add("IsAttackAllowed", (bool(*)(Mob*, Mob*, bool))&Perl_Mob_IsAttackAllowed);
|
||||
package.add("IsBeacon", &Perl_Mob_IsBeacon);
|
||||
package.add("IsBeneficialAllowed", &Perl_Mob_IsBeneficialAllowed);
|
||||
package.add("IsBerserk", &Perl_Mob_IsBerserk);
|
||||
package.add("IsBlind", &Perl_Mob_IsBlind);
|
||||
package.add("IsBot", &Perl_Mob_IsBot);
|
||||
package.add("IsCasting", &Perl_Mob_IsCasting);
|
||||
@@ -3122,6 +3144,7 @@ void perl_register_mob()
|
||||
package.add("IsEngaged", &Perl_Mob_IsEngaged);
|
||||
package.add("IsEnraged", &Perl_Mob_IsEnraged);
|
||||
package.add("IsFeared", &Perl_Mob_IsFeared);
|
||||
package.add("IsFindable", &Perl_Mob_IsFindable);
|
||||
package.add("IsHorse", &Perl_Mob_IsHorse);
|
||||
package.add("IsImmuneToSpell", &Perl_Mob_IsImmuneToSpell);
|
||||
package.add("IsInvisible", (bool(*)(Mob*))&Perl_Mob_IsInvisible);
|
||||
@@ -3142,6 +3165,7 @@ void perl_register_mob()
|
||||
package.add("IsStunned", &Perl_Mob_IsStunned);
|
||||
package.add("IsTargetable", &Perl_Mob_IsTargetable);
|
||||
package.add("IsTargeted", &Perl_Mob_IsTargeted);
|
||||
package.add("IsTrackable", &Perl_Mob_IsTrackable);
|
||||
package.add("IsTrap", &Perl_Mob_IsTrap);
|
||||
package.add("IsWarriorClass", &Perl_Mob_IsWarriorClass);
|
||||
package.add("Kill", &Perl_Mob_Kill);
|
||||
|
||||
@@ -765,6 +765,16 @@ void Perl_NPC_ScaleNPC(NPC* self, uint8 npc_level, bool override_special_abiliti
|
||||
return self->ScaleNPC(npc_level, override_special_abilities);
|
||||
}
|
||||
|
||||
bool Perl_NPC_IsUnderwaterOnly(NPC* self) // @categories Script Utility
|
||||
{
|
||||
return self->IsUnderwaterOnly();
|
||||
}
|
||||
|
||||
bool Perl_NPC_HasSpecialAbilities(NPC* self) // @categories Script Utility
|
||||
{
|
||||
return self->HasSpecialAbilities();
|
||||
}
|
||||
|
||||
void perl_register_npc()
|
||||
{
|
||||
perl::interpreter perl(PERL_GET_THX);
|
||||
@@ -852,6 +862,7 @@ void perl_register_npc()
|
||||
package.add("GetSwarmTarget", &Perl_NPC_GetSwarmTarget);
|
||||
package.add("GetWaypointMax", &Perl_NPC_GetWaypointMax);
|
||||
package.add("HasAISpellEffect", &Perl_NPC_HasAISpellEffect);
|
||||
package.add("HasSpecialAbilities", &Perl_NPC_HasSpecialAbilities);
|
||||
package.add("HasItem", &Perl_NPC_HasItem);
|
||||
package.add("IsAnimal", &Perl_NPC_IsAnimal);
|
||||
package.add("IsGuarding", &Perl_NPC_IsGuarding);
|
||||
@@ -862,6 +873,7 @@ void perl_register_npc()
|
||||
package.add("IsRaidTarget", &Perl_NPC_IsRaidTarget);
|
||||
package.add("IsRareSpawn", &Perl_NPC_IsRareSpawn);
|
||||
package.add("IsTaunting", &Perl_NPC_IsTaunting);
|
||||
package.add("IsUnderwaterOnly", (bool(*)(NPC*))&Perl_NPC_IsUnderwaterOnly);
|
||||
package.add("MerchantCloseShop", &Perl_NPC_MerchantCloseShop);
|
||||
package.add("MerchantOpenShop", &Perl_NPC_MerchantOpenShop);
|
||||
package.add("ModifyNPCStat", &Perl_NPC_ModifyNPCStat);
|
||||
|
||||
+23
-11
@@ -88,19 +88,28 @@ void QuestManager::Process() {
|
||||
end = QTimerList.end();
|
||||
while (cur != end) {
|
||||
if (cur->Timer_.Enabled() && cur->Timer_.Check()) {
|
||||
if(entity_list.IsMobInZone(cur->mob)) {
|
||||
if(cur->mob->IsNPC()) {
|
||||
if (cur->mob) {
|
||||
if (cur->mob->IsNPC()) {
|
||||
if (parse->HasQuestSub(cur->mob->GetNPCTypeID(), EVENT_TIMER)) {
|
||||
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->IsClient()) {
|
||||
}
|
||||
else if (cur->mob->IsEncounter()) {
|
||||
parse->EventEncounter(
|
||||
EVENT_TIMER,
|
||||
cur->mob->CastToEncounter()->GetEncounterName(),
|
||||
cur->name,
|
||||
0,
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
else if (cur->mob->IsClient()) {
|
||||
if (parse->PlayerHasQuestSub(EVENT_TIMER)) {
|
||||
//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);
|
||||
}
|
||||
} else if (cur->mob->IsBot()) {
|
||||
}
|
||||
else if (cur->mob->IsBot()) {
|
||||
if (parse->BotHasQuestSub(EVENT_TIMER)) {
|
||||
parse->EventBot(EVENT_TIMER, cur->mob->CastToBot(), nullptr, cur->name, 0);
|
||||
}
|
||||
@@ -109,12 +118,15 @@ void QuestManager::Process() {
|
||||
//we MUST reset our iterator since the quest could have removed/added any
|
||||
//number of timers... worst case we have to check a bunch of timers twice
|
||||
cur = QTimerList.begin();
|
||||
end = QTimerList.end(); //dunno if this is needed, cant hurt...
|
||||
} else {
|
||||
end = QTimerList.end(); //dunno if this is needed, cant hurt...
|
||||
}
|
||||
else {
|
||||
cur = QTimerList.erase(cur);
|
||||
}
|
||||
} else
|
||||
}
|
||||
else {
|
||||
++cur;
|
||||
}
|
||||
}
|
||||
|
||||
auto cur_iter = STimerList.begin();
|
||||
@@ -2118,7 +2130,7 @@ void QuestManager::playertexture(int newtexture)
|
||||
initiator->SendIllusionPacket(initiator->GetRace(), 0xFF, newtexture);
|
||||
}
|
||||
|
||||
void QuestManager::playerfeature(char *feature, int setting)
|
||||
void QuestManager::playerfeature(const char* feature, int setting)
|
||||
{
|
||||
QuestManagerCurrentQuestVars();
|
||||
uint16 Race = initiator->GetRace();
|
||||
@@ -2175,7 +2187,7 @@ void QuestManager::playerfeature(char *feature, int setting)
|
||||
DrakkinHeritage, DrakkinTattoo, DrakkinDetails, Size);
|
||||
}
|
||||
|
||||
void QuestManager::npcfeature(char *feature, int setting)
|
||||
void QuestManager::npcfeature(const char* feature, int setting)
|
||||
{
|
||||
QuestManagerCurrentQuestVars();
|
||||
uint16 Race = owner->GetRace();
|
||||
|
||||
+3
-3
@@ -140,7 +140,7 @@ public:
|
||||
void movepc(int zone_id, float x, float y, float z, float heading);
|
||||
void gmmove(float x, float y, float z);
|
||||
void movegrp(int zoneid, float x, float y, float z);
|
||||
void doanim(int animation_id, int animation_speed = 1, bool ackreq = true, eqFilterType filter = FilterNone);
|
||||
void doanim(int animation_id, int animation_speed = 0, bool ackreq = true, eqFilterType filter = FilterNone);
|
||||
void addskill(int skill_id, int value);
|
||||
void setlanguage(int skill_id, int value);
|
||||
void setskill(int skill_id, int value);
|
||||
@@ -208,8 +208,8 @@ public:
|
||||
void playergender(int gender_id);
|
||||
void playersize(int newsize);
|
||||
void playertexture(int newtexture);
|
||||
void playerfeature(char *feature, int setting);
|
||||
void npcfeature(char *feature, int setting);
|
||||
void playerfeature(const char* feature, int setting);
|
||||
void npcfeature(const char* feature, int setting);
|
||||
void popup(const char *title, const char *text, uint32 popupid, uint32 buttons, uint32 Duration);
|
||||
void taskselector(const std::vector<int>& tasks, bool ignore_cooldown = false);
|
||||
void tasksetselector(int tasksettid, bool ignore_cooldown = false);
|
||||
|
||||
-101
@@ -1,101 +0,0 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
#ifndef SKILLS_H
|
||||
#define SKILLS_H
|
||||
|
||||
#define HIGHEST_SKILL_UNUSED 74
|
||||
|
||||
|
||||
// Correct Skill Numbers as of 4-14-2002
|
||||
#define _1H_BLUNT_UNUSED 0
|
||||
#define _1H_SLASHING_UNUSED 1
|
||||
#define _2H_BLUNT_UNUSED 2
|
||||
#define _2H_SLASHING_UNUSED 3
|
||||
#define ABJURE_UNUSED 4
|
||||
#define ALTERATION_UNUSED 5
|
||||
#define APPLY_POISON_UNUSED 6
|
||||
#define ARCHERY_UNUSED 7
|
||||
#define BACKSTAB_UNUSED 8
|
||||
#define BIND_WOUND_UNUSED 9
|
||||
#define BASH_UNUSED 10
|
||||
#define BLOCKSKILL_UNUSED 11
|
||||
#define BRASS_INSTRUMENTS_UNUSED 12
|
||||
#define CHANNELING_UNUSED 13
|
||||
#define CONJURATION_UNUSED 14
|
||||
#define DEFENSE_UNUSED 15
|
||||
#define DISARM_UNUSED 16
|
||||
#define DISARM_TRAPS_UNUSED 17
|
||||
#define DIVINATION_UNUSED 18
|
||||
#define DODGE_UNUSED 19
|
||||
#define DOUBLE_ATTACK_UNUSED 20
|
||||
#define DRAGON_PUNCH_UNUSED 21
|
||||
#define DUAL_WIELD_UNUSED 22
|
||||
#define EAGLE_STRIKE_UNUSED 23
|
||||
#define EVOCATION_UNUSED 24
|
||||
#define FEIGN_DEATH_UNUSED 25
|
||||
#define FLYING_KICK_UNUSED 26
|
||||
#define FORAGE_UNUSED 27
|
||||
#define HAND_TO_HAND_UNUSED 28
|
||||
#define HIDE_UNUSED 29
|
||||
#define KICK_UNUSED 30
|
||||
#define MEDITATE_UNUSED 31
|
||||
#define MEND_UNUSED 32
|
||||
#define OFFENSE_UNUSED 33
|
||||
#define PARRY_UNUSED 34
|
||||
#define PICK_LOCK_UNUSED 35
|
||||
#define PIERCING_UNUSED 36
|
||||
#define RIPOSTE_UNUSED 37
|
||||
#define ROUND_KICK_UNUSED 38
|
||||
#define SAFE_FALL_UNUSED 39
|
||||
#define SENSE_HEADING_UNUSED 40
|
||||
#define SINGING_UNUSED 41
|
||||
#define SNEAK_UNUSED 42
|
||||
#define SPECIALIZE_ABJURE_UNUSED 43
|
||||
#define SPECIALIZE_ALTERATION_UNUSED 44
|
||||
#define SPECIALIZE_CONJURATION_UNUSED 45
|
||||
#define SPECIALIZE_DIVINATION_UNUSED 46
|
||||
#define SPECIALIZE_EVOCATION_UNUSED 47
|
||||
#define PICK_POCKETS_UNUSED 48
|
||||
#define STRINGED_INSTRUMENTS_UNUSED 49
|
||||
#define SWIMMING_UNUSED 50
|
||||
#define THROWING_UNUSED 51
|
||||
#define TIGER_CLAW_UNUSED 52
|
||||
#define TRACKING_UNUSED 53
|
||||
#define WIND_INSTRUMENTS_UNUSED 54
|
||||
#define FISHING_UNUSED 55
|
||||
#define MAKE_POISON_UNUSED 56
|
||||
#define TINKERING_UNUSED 57
|
||||
#define RESEARCH_UNUSED 58
|
||||
#define ALCHEMY_UNUSED 59
|
||||
#define BAKING_UNUSED 60
|
||||
#define TAILORING_UNUSED 61
|
||||
#define SENSE_TRAPS_UNUSED 62
|
||||
#define BLACKSMITHING_UNUSED 63
|
||||
#define FLETCHING_UNUSED 64
|
||||
#define BREWING_UNUSED 65
|
||||
#define ALCOHOL_TOLERANCE_UNUSED 66
|
||||
#define BEGGING_UNUSED 67
|
||||
#define JEWELRY_MAKING_UNUSED 68
|
||||
#define POTTERY_UNUSED 69
|
||||
#define PERCUSSION_INSTRUMENTS_UNUSED 70
|
||||
#define INTIMIDATION_UNUSED 71
|
||||
#define BERSERKING_UNUSED 72
|
||||
#define TAUNT_UNUSED 73
|
||||
#define FRENZY_UNUSED 74
|
||||
|
||||
#endif
|
||||
@@ -93,18 +93,6 @@ EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *client)
|
||||
return(outapp);
|
||||
}
|
||||
|
||||
int TitleManager::NumberOfAvailableTitles(Client *client)
|
||||
{
|
||||
int count = 0;
|
||||
for (const auto& title : titles) {
|
||||
if (IsClientEligibleForTitle(client, title)) {
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
std::string TitleManager::GetPrefix(int title_id)
|
||||
{
|
||||
if (!title_id) {
|
||||
|
||||
@@ -51,7 +51,6 @@ public:
|
||||
EQApplicationPacket *MakeTitlesPacket(Client *client);
|
||||
std::string GetPrefix(int title_id);
|
||||
std::string GetSuffix(int title_id);
|
||||
int NumberOfAvailableTitles(Client *client);
|
||||
bool IsClientEligibleForTitle(Client *client, TitleEntry title);
|
||||
bool IsNewAATitleAvailable(int aa_points, int class_id);
|
||||
bool IsNewTradeSkillTitleAvailable(int skill_id, int skill_value);
|
||||
|
||||
+78
-35
@@ -469,22 +469,11 @@ void Object::HandleCombine(Client* user, const NewCombine_Struct* in_combine, Ob
|
||||
}
|
||||
|
||||
// Check if Combine would result in Lore conflict
|
||||
for (const auto& e : spec.onsuccess) {
|
||||
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);
|
||||
user->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
}
|
||||
if (user->CheckTradeskillLoreConflict(spec.recipe_id)) {
|
||||
auto outapp = new EQApplicationPacket(OP_TradeSkillCombine, 0);
|
||||
user->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
}
|
||||
|
||||
// final check for any additional quest requirements .. "check_zone" in this case - exported as variable [validate_type]
|
||||
@@ -622,9 +611,9 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
|
||||
}
|
||||
|
||||
//pull the list of components
|
||||
std::string query = StringFormat("SELECT tre.item_id, tre.componentcount "
|
||||
"FROM tradeskill_recipe_entries AS tre "
|
||||
"WHERE tre.componentcount > 0 AND tre.recipe_id = %u",
|
||||
const auto query = fmt::format("SELECT item_id, componentcount "
|
||||
"FROM tradeskill_recipe_entries "
|
||||
"WHERE componentcount > 0 AND recipe_id = {}",
|
||||
rac->recipe_id);
|
||||
auto results = content_db.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
@@ -698,6 +687,13 @@ void Object::HandleAutoCombine(Client* user, const RecipeAutoCombine_Struct* rac
|
||||
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...
|
||||
|
||||
//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...
|
||||
outp->reply_code = 0x00000000; //success for finding it...
|
||||
user->QueuePacket(outapp);
|
||||
@@ -1882,3 +1862,66 @@ bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Client::CheckTradeskillLoreConflict(int32 recipe_id)
|
||||
{
|
||||
auto recipe_entries = TradeskillRecipeEntriesRepository::GetWhere(
|
||||
content_db,
|
||||
fmt::format(
|
||||
"recipe_id = {} ORDER BY componentcount DESC",
|
||||
recipe_id
|
||||
)
|
||||
);
|
||||
if (recipe_entries.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate which items from the recipe we will call CheckLoreConflict on
|
||||
for (const auto &tre : recipe_entries) {
|
||||
if (tre.item_id) {
|
||||
auto tre_inst = database.GetItem(tre.item_id);
|
||||
|
||||
// To compare items we iterate against each item in the recipe that have a loregroup.
|
||||
for (auto &tre_update_item : recipe_entries) {
|
||||
bool fi_is_valid = tre_update_item.item_id && tre_inst && tre_inst->LoreGroup != 0;
|
||||
|
||||
if (fi_is_valid) {
|
||||
auto tre_update_item_inst = database.GetItem(tre_update_item.item_id);
|
||||
bool ei_is_valid = tre_update_item_inst && tre_update_item_inst->LoreGroup != 0;
|
||||
|
||||
if (ei_is_valid) {
|
||||
bool unique_lore_group_match = tre_inst->LoreGroup > 0 && tre_inst->LoreGroup == tre_update_item_inst->LoreGroup;
|
||||
bool component_count_is_valid = tre_update_item.componentcount == 0 && tre.componentcount > 0;
|
||||
|
||||
// If the recipe item is a component, and matches a unique lore group (> 0) or the item_id matches another entry in the recipe
|
||||
// zero out the item_id, this will prevent us from doing a lore check inadvertently where
|
||||
// the item is a component, and returned on success, fail, salvage.
|
||||
// or uses an item that is part of a unique loregroup that returns an item of the same unique loregroup
|
||||
if (ei_is_valid && (tre_update_item.item_id == tre.item_id || unique_lore_group_match) && component_count_is_valid) {
|
||||
tre_update_item.item_id = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tre_inst) {
|
||||
if (tre_inst->LoreGroup == 0 || tre.componentcount > 0 || tre.iscontainer) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CheckLoreConflict(tre_inst)) {
|
||||
EQ::SayLinkEngine linker;
|
||||
linker.SetLinkType(EQ::saylink::SayLinkItemData);
|
||||
linker.SetItemData(tre_inst);
|
||||
auto item_link = linker.GenerateLink();
|
||||
MessageString(Chat::Red, TRADESKILL_COMBINE_LORE, item_link.c_str());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
+9
-8
@@ -66,14 +66,14 @@ void NPC::AI_SetRoambox(
|
||||
uint32 min_delay
|
||||
)
|
||||
{
|
||||
roambox_distance = distance;
|
||||
roambox_max_x = max_x;
|
||||
roambox_min_x = min_x;
|
||||
roambox_max_y = max_y;
|
||||
roambox_min_y = min_y;
|
||||
roambox_destination_x = roambox_max_x + 1; // this will trigger a recalc
|
||||
roambox_delay = delay;
|
||||
roambox_min_delay = min_delay;
|
||||
m_roambox.distance = distance;
|
||||
m_roambox.max_x = max_x;
|
||||
m_roambox.min_x = min_x;
|
||||
m_roambox.max_y = max_y;
|
||||
m_roambox.min_y = min_y;
|
||||
m_roambox.dest_x = max_x + 1; // this will trigger a recalc
|
||||
m_roambox.delay = delay;
|
||||
m_roambox.min_delay = min_delay;
|
||||
}
|
||||
|
||||
void NPC::DisplayWaypointInfo(Client *client) {
|
||||
@@ -934,6 +934,7 @@ float Mob::GetZOffset() const {
|
||||
case RACE_AMYGDALAN_663:
|
||||
offset = 5.0f;
|
||||
break;
|
||||
case RACE_SPECTRAL_IKSAR_147:
|
||||
case RACE_SANDMAN_664:
|
||||
offset = 4.0f;
|
||||
break;
|
||||
|
||||
+17
-13
@@ -13,6 +13,7 @@
|
||||
#include "zonedb.h"
|
||||
#include "aura.h"
|
||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||
#include "../common/repositories/character_disciplines_repository.h"
|
||||
#include "../common/repositories/npc_types_repository.h"
|
||||
|
||||
#include <ctime>
|
||||
@@ -814,22 +815,25 @@ bool ZoneDatabase::LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_
|
||||
}
|
||||
|
||||
bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){
|
||||
std::string query = StringFormat(
|
||||
"SELECT "
|
||||
"disc_id "
|
||||
"FROM "
|
||||
"`character_disciplines`"
|
||||
"WHERE `id` = %u ORDER BY `slot_id`", character_id);
|
||||
auto results = database.QueryDatabase(query);
|
||||
int i = 0;
|
||||
|
||||
auto character_disciplines = CharacterDisciplinesRepository::GetWhere(
|
||||
database, fmt::format(
|
||||
"`id` = {} ORDER BY `slot_id`",
|
||||
character_id
|
||||
)
|
||||
);
|
||||
|
||||
if (character_disciplines.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Initialize Disciplines */
|
||||
memset(pp->disciplines.values, 0, (sizeof(pp->disciplines.values[0]) * MAX_PP_DISCIPLINES));
|
||||
for (auto& row = results.begin(); row != results.end(); ++row) {
|
||||
if (i < MAX_PP_DISCIPLINES)
|
||||
pp->disciplines.values[i] = atoi(row[0]);
|
||||
++i;
|
||||
}
|
||||
for (auto& row : character_disciplines) {
|
||||
if (row.slot_id < MAX_PP_DISCIPLINES && IsValidSpell(row.disc_id)) {
|
||||
pp->disciplines.values[row.slot_id] = row.disc_id;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user