This commit is contained in:
Arthur Ice 2014-08-17 10:39:37 -07:00
commit 87efd22394
50 changed files with 3553 additions and 2337 deletions

16
.travis.yml Normal file
View File

@ -0,0 +1,16 @@
language: cpp
compiler: gcc
before_install:
- sudo apt-get update -qq
- sudo apt-get install -y libmysqlclient-dev libperl-dev libboost-dev liblua5.1-0-dev zlib1g-dev
script:
- cmake -G "Unix Makefiles" -DEQEMU_BUILD_TESTS=ON -DEQEMU_ENABLE_BOTS=ON
- make
- ./bin/tests
branches:
only: master
notifications:
email: false
irc:
channels: "irc.eqemulator.net#eqemucoders"
os: linux

View File

@ -1,5 +1,53 @@
EQEmu - Custom Game Implementation for EverQuest
EQEmu
===
Dependencies can be obtained at http://eqemu.github.io
[![Build Status](https://travis-ci.org/EQEmu/Server.svg?branch=master)](https://travis-ci.org/EQEmu/Server)
Overview
---
EQEmu is a custom server implementation for EverQuest
Dependencies
---
For Windows: http://eqemu.github.io
Login Server dependencies for Windows/Linux/OSX: http://eqemu.github.io
For Debian based distros (adjust to your local flavor):
- libmysqlclient-dev
- libperl-dev
- liblua5.1-0-dev (5.2 should work as well)
- libboost-dev
Further instructions on building the source can be found on the
[wiki](http://wiki.eqemulator.org/i?M=Wiki).
Bug reports
---
Please use the [issue tracker](https://github.com/EQEmu/Server/issues) provided by GitHub to send us bug
reports or feature requests.
The [EQEmu Forums](http://www.eqemulator.org/forums/) also have forums to submit
bugs/get help with bugs.
Contributions
---
The preferred way to contribute is to fork the repo and submit a pull request on
GitHub. If you need help with your changes, you can always post on the forums or
try IRC. You can also post unified diffs (`git diff` should do the trick) on the
[Server Code Submissions](http://www.eqemulator.org/forums/forumdisplay.php?f=669)
forum, although pull requests will be much quicker and easier on all parties.
Contact
---
- **User IRC Channel**: `#eqemu` on `irc.eqemulator.net`
- **Developer IRC Channel**: `#eqemucoders` on `irc.eqemulator.net`
- [EQEmulator Forums](http://www.eqemulator.org/forums)
- [EQEmulator Wiki](http://wiki.eqemulator.org/i?M=Wiki)
More Information: https://github.com/EQEmu/Server/wiki

View File

@ -1,5 +1,32 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 08/16/2014 ==
KLS: (addmoreice) Trying out some unstable DB changes. Do backup your database before trying them as master will be considered unstable for a few days at least.
Uleat (Noudness): Fixed a floating-point comparison error that led to the notorious 'client bounce' (this is not related to the
'mob falling' due to not having maps installed.) This fix also eliminates a sizeable amount of unnecessary out-going packets due
to invalid orientation corrections.
Note: This patch is probably of significant enough importance that admins may wish to manually apply this to older server builds.
The number of packets reduced per second should be approximately (num_stationary_close_clients * (num_close_clients - 1)). This will
likely have the most effect in zones like: Nexus, Bazaar, Guilds (Halls) and RAID INSTANCES - where players don't move around a lot.
== 08/15/2014 ==
Uleat: Reactivated the Bot::Spawn() code for sending post-spawn wear change updates..temporary until I can sort out the proper usage.
== 08/13/2014 ==
Uleat (Kingly_Krab): Fix for bot chest armor graphic glitch. (fix also caused RoF #wc to work properly)
== 08/02/2014 ==
Kayen: Implemented spell_news fields
- npc_no_los (check if LOS is required for spells)
- InCombat, OutofCombat - Used together to restrict spells to only be cast while
in/out of combat (beneficial) or if target is in/out of combat (detrimental).
-min_dist, min_dist_mod, max_dist, max_dist_mod - Scales spell power based on targets distance from caster.
*This will require further work to fully implement but will work with 90% of live spells as is.
*If making custom spells do not include effects that can't be scaled (like a spell trigger)
- min_rage sets minimum distance range that must be away from target.
Required SQL: utils/sql/git/required/2014_08_02_spells_new.sql
== 07/31/2014 ==
Uleat: More inventory slot constant conversions. This should be the bulk of everything..but, due to the size of the server code, there
may be some hidden ones. (client_packet.cpp and the client translators still need a thorough review.)

View File

@ -70,7 +70,7 @@ void ExportSpells(SharedDatabase *db) {
}
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = "SELECT * FROM spells_new ORDER BY id";
const char *query = "SELECT * FROM spells_new ORDER BY id";
MYSQL_RES *result;
MYSQL_ROW row;
if(db->RunQuery(query, strlen(query), errbuf, &result)) {
@ -176,7 +176,7 @@ void ExportBaseData(SharedDatabase *db) {
}
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = "SELECT * FROM base_data ORDER BY level, class";
const char *query = "SELECT * FROM base_data ORDER BY level, class";
MYSQL_RES *result;
MYSQL_ROW row;
if(db->RunQuery(query, strlen(query), errbuf, &result)) {

View File

@ -61,7 +61,7 @@ int main(int argc, char **argv) {
int GetSpellColumns(SharedDatabase *db) {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = "DESCRIBE spells_new";
const char *query = "DESCRIBE spells_new";
MYSQL_RES *result;
MYSQL_ROW row;
int res = 0;
@ -175,14 +175,14 @@ void ImportSkillCaps(SharedDatabase *db) {
continue;
}
std::string sql;
int class_id, skill_id, level, cap;
class_id = atoi(split[0].c_str());
skill_id = atoi(split[1].c_str());
level = atoi(split[2].c_str());
cap = atoi(split[3].c_str());
StringFormat(sql, "INSERT INTO skill_caps(class, skillID, level, cap) VALUES(%d, %d, %d, %d)",
std::string sql = StringFormat("INSERT INTO skill_caps(class, skillID, level, cap) VALUES(%d, %d, %d, %d)",
class_id, skill_id, level, cap);
db->RunQuery(sql.c_str(), (uint32)sql.length());
@ -226,7 +226,7 @@ void ImportBaseData(SharedDatabase *db) {
mana_fac = atof(split[8].c_str());
end_fac = atof(split[9].c_str());
StringFormat(sql, "INSERT INTO base_data(level, class, hp, mana, end, unk1, unk2, hp_fac, "
sql = StringFormat("INSERT INTO base_data(level, class, hp, mana, end, unk1, unk2, hp_fac, "
"mana_fac, end_fac) VALUES(%d, %d, %f, %f, %f, %f, %f, %f, %f, %f)",
level, class_id, hp, mana, end, unk1, unk2, hp_fac, mana_fac, end_fac);
@ -234,4 +234,4 @@ void ImportBaseData(SharedDatabase *db) {
}
fclose(f);
}
}

View File

@ -916,8 +916,7 @@ void Inventory::dumpItemCollection(const std::map<int16, ItemInst*> &collection)
if (!inst || !inst->GetItem())
continue;
std::string slot;
StringFormat(slot, "Slot %d: %s (%d)", it->first, it->second->GetItem()->Name, (inst->GetCharges() <= 0) ? 1 : inst->GetCharges());
std::string slot = StringFormat("Slot %d: %s (%d)", it->first, it->second->GetItem()->Name, (inst->GetCharges() <= 0) ? 1 : inst->GetCharges());
std::cout << slot << std::endl;
dumpBagContents(inst, &it);
@ -936,8 +935,7 @@ void Inventory::dumpBagContents(ItemInst *inst, iter_inst *it) {
if (!baginst || !baginst->GetItem())
continue;
std::string subSlot;
StringFormat(subSlot, " Slot %d: %s (%d)", Inventory::CalcSlotId((*it)->first, itb->first),
std::string subSlot = StringFormat(" Slot %d: %s (%d)", Inventory::CalcSlotId((*it)->first, itb->first),
baginst->GetItem()->Name, (baginst->GetCharges() <= 0) ? 1 : baginst->GetCharges());
std::cout << subSlot << std::endl;
}

View File

@ -103,7 +103,8 @@ int NewFloatToEQ13(float d);
int FloatToEQ19(float d);
int FloatToEQH(float d);
// macro to catch fp errors (provided by noudness)
#define FCMP(a,b) (fabs(a-b) < FLT_EPSILON)
#define _ITOA_BUFLEN 25
const char *itoa(int num); //not thread safe

View File

@ -10,35 +10,24 @@ MySQLRequestResult::MySQLRequestResult()
MySQLRequestResult::MySQLRequestResult(MYSQL_RES* result, uint32 rowsAffected, uint32 rowCount, uint32 columnCount, uint32 lastInsertedID, uint32 errorNumber, char *errorBuffer)
: m_CurrentRow(result), m_OneBeyondRow()
{
if (errorBuffer != nullptr)
m_Success = false;
else if (errorBuffer == nullptr && result == nullptr)
{
m_Success = false;
#ifdef _EQDEBUG
std::cout << "DB Query Error: No Result" << std::endl;
#endif
m_ErrorNumber = UINT_MAX;
m_ErrorBuffer = new char[MYSQL_ERRMSG_SIZE];
strcpy(m_ErrorBuffer, "DBcore::RunQuery: No Result");
}
else
m_Success = true;
m_Result = result;
m_ErrorBuffer = errorBuffer;
m_Result = result;
m_RowsAffected = rowsAffected;
m_RowCount = rowCount;
m_ColumnCount = columnCount;
// If we actually need the column length / fields it will be
// requested at that time, no need to pull it in just to cache it.
// Normal usage would have it as nullptr most likely anyways.
m_ColumnLengths = nullptr;
m_Fields = nullptr;
m_LastInsertedID = lastInsertedID;
m_ErrorNumber = errorNumber;
// If we actually need the column length / fields it will be
// requested at that time, no need to pull it in just to cache it.
// Normal usage would have it as nullptr most likely anyways.
m_ColumnLengths = nullptr;
m_Fields = nullptr;
if (errorBuffer != nullptr)
m_Success = false;
m_Success = true;
m_ErrorNumber = errorNumber;
m_ErrorBuffer = errorBuffer;
}
void MySQLRequestResult::FreeInternals()
@ -113,7 +102,7 @@ MySQLRequestResult::MySQLRequestResult(MySQLRequestResult&& moveItem)
m_ColumnLengths = moveItem.m_ColumnLengths;
m_Fields = moveItem.m_Fields;
// Keeps deconstructor from double freeing
// Keeps deconstructor from double freeing
// pre move instance.
moveItem.ZeroOut();
}
@ -140,8 +129,8 @@ MySQLRequestResult& MySQLRequestResult::operator=(MySQLRequestResult&& other)
m_ColumnLengths = other.m_ColumnLengths;
m_Fields = other.m_Fields;
// Keeps deconstructor from double freeing
// Keeps deconstructor from double freeing
// pre move instance.
other.ZeroOut();
return *this;
}
}

View File

@ -1,6 +1,6 @@
#include "MySQLRequestRow.h"
MySQLRequestRow::MySQLRequestRow(const MySQLRequestRow& row)
MySQLRequestRow::MySQLRequestRow(const MySQLRequestRow& row)
: m_Result(row.m_Result), m_MySQLRow(row.m_MySQLRow)
{
}
@ -26,13 +26,17 @@ MySQLRequestRow& MySQLRequestRow::operator=(MySQLRequestRow& moveItem)
moveItem.m_Result = nullptr;
moveItem.m_MySQLRow = nullptr;
return *this;
}
MySQLRequestRow::MySQLRequestRow(MYSQL_RES *result)
: m_Result(result), m_MySQLRow(mysql_fetch_row(m_Result))
: m_Result(result)
{
if (result != nullptr)
m_MySQLRow = mysql_fetch_row(result);
else
m_MySQLRow = nullptr;
}
MySQLRequestRow& MySQLRequestRow::operator++()
@ -41,19 +45,19 @@ MySQLRequestRow& MySQLRequestRow::operator++()
return *this;
}
MySQLRequestRow MySQLRequestRow::operator++(int)
MySQLRequestRow MySQLRequestRow::operator++(int)
{
MySQLRequestRow tmp(*this);
operator++();
MySQLRequestRow tmp(*this);
operator++();
return tmp;
}
bool MySQLRequestRow::operator==(const MySQLRequestRow& rhs)
bool MySQLRequestRow::operator==(const MySQLRequestRow& rhs)
{
return m_MySQLRow == rhs.m_MySQLRow;
}
bool MySQLRequestRow::operator!=(const MySQLRequestRow& rhs)
bool MySQLRequestRow::operator!=(const MySQLRequestRow& rhs)
{
return m_MySQLRow != rhs.m_MySQLRow;
}

View File

@ -38,8 +38,9 @@
// original source:
// https://github.com/facebook/folly/blob/master/folly/String.cpp
//
void vStringFormat(std::string& output, const char* format, va_list args)
const std::string vStringFormat(const char* format, va_list args)
{
std::string output;
va_list tmpargs;
va_copy(tmpargs,args);
@ -48,11 +49,8 @@ void vStringFormat(std::string& output, const char* format, va_list args)
if (characters_used < 0) {
// Looks like we have an invalid format string.
// error out.
std::string errorMessage("Invalid format string; snprintf returned negative with format string: ");
errorMessage.append(format);
throw std::runtime_error(errorMessage);
// return empty string.
return "";
}
else if ((unsigned int)characters_used > output.capacity()) {
output.resize(characters_used+1);
@ -62,12 +60,10 @@ void vStringFormat(std::string& output, const char* format, va_list args)
if (characters_used < 0) {
// We shouldn't have a format error by this point, but I can't imagine what error we
// could have by this point. Still, error out and report it.
std::string errorMessage("Invalid format string or unknown vsnprintf error; vsnprintf returned negative with format string: ");
errorMessage.append(format);
throw std::runtime_error(errorMessage);
// could have by this point. Still, return empty string;
return "";
}
return std::move(output);
}
else {
output.resize(characters_used + 1);
@ -78,24 +74,23 @@ void vStringFormat(std::string& output, const char* format, va_list args)
if (characters_used < 0) {
// We shouldn't have a format error by this point, but I can't imagine what error we
// could have by this point. still error out and report it.
std::string errorMessage("Invalid format string or unknown vsnprintf error; vsnprintf returned negative with format string: ");
errorMessage.append(format);
throw std::runtime_error(errorMessage);
// could have by this point. Still, return empty string;
return "";
}
return std::move(output);
}
}
void StringFormat(std::string& output, const char* format, ...)
const std::string StringFormat(const char* format, ...)
{
va_list args;
va_start(args, format);
vStringFormat(output,format,args);
std::string output = vStringFormat(format,args);
va_end(args);
return std::move(output);
}
// normal strncpy doesnt put a null term on copied strings, this one does
// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp
char* strn0cpy(char* dest, const char* source, uint32 size) {
@ -384,3 +379,15 @@ std::string EscapeString(const std::string &s) {
return ret;
}
bool isAlphaNumeric(const char *text)
{
for (unsigned int charIndex=0; charIndex<strlen(text); charIndex++) {
if ((text[charIndex] < 'a' || text[charIndex] > 'z') &&
(text[charIndex] < 'A' || text[charIndex] > 'Z') &&
(text[charIndex] < '0' || text[charIndex] > '9'))
return false;
}
return true;
}

View File

@ -19,11 +19,12 @@
#include <sstream>
#include <vector>
#include <cstdarg>
#include <string.h>
#include "types.h"
void vStringFormat(std::string& output, const char* format, va_list args);
void StringFormat(std::string& output, const char* format, ...);
const std::string vStringFormat(const char* format, va_list args);
const std::string StringFormat(const char* format, ...);
std::string EscapeString(const std::string &s);
const char *MakeLowerString(const char *source);
@ -51,4 +52,6 @@ char *RemoveApostrophes(const char *s);
std::vector<std::string> SplitString(const std::string &s, char delim);
bool isAlphaNumeric(const char *text);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -130,7 +130,7 @@ public:
uint32 GetAccountIDByChar(const char* charname, uint32* oCharID = 0);
uint32 GetAccountIDByChar(uint32 char_id);
uint32 GetAccountIDByName(const char* accname, int16* status = 0, uint32* lsid = 0);
uint32 GetGuildDBIDByCharID(uint32 char_id);
uint32 GetGuildIDByCharID(uint32 char_id);
void GetAccountName(uint32 accountid, char* name, uint32* oLSAccountID = 0);
void GetCharName(uint32 char_id, char* name);
uint32 GetCharacterInfo(const char* iName, uint32* oAccID = 0, uint32* oZoneID = 0, uint32* oInstanceID = 0,float* oX = 0, float* oY = 0, float* oZ = 0);
@ -207,6 +207,7 @@ public:
char* GetGroupLeadershipInfo(uint32 gid, char* leaderbuf, char* maintank = nullptr, char* assist = nullptr, char* puller = nullptr, char *marknpc = nullptr,
GroupLeadershipAA_Struct* GLAA = nullptr);
void ClearGroupLeader(uint32 gid = 0);
/*
* Raids
@ -223,7 +224,7 @@ public:
bool SetVariable(const char* varname, const char* varvalue);
bool LoadVariables();
uint32 LoadVariables_MQ(char** query);
bool LoadVariables_result(MYSQL_RES* result);
bool LoadVariables_result(MySQLRequestResult results);
/*
* General Queries
@ -262,7 +263,20 @@ private:
uint32 varcache_max;
VarCache_Struct** varcache_array;
uint32 varcache_lastupdate;
/*
* Groups, utility methods.
*/
void ClearAllGroupLeaders();
void ClearAllGroups();
/*
* Raid, utility methods.
*/
void ClearAllRaids();
void ClearAllRaidDetails();
};
bool FetchRowMap(MYSQL_RES *result, std::map<std::string,std::string> &rowmap);
#endif

View File

@ -80,10 +80,10 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo
pStatus = Error;
// error appears to be a disconnect error, may need to try again.
if (errorNumber == CR_SERVER_LOST || errorNumber == CR_SERVER_GONE_ERROR)
if (errorNumber == CR_SERVER_LOST || errorNumber == CR_SERVER_GONE_ERROR)
{
if (retryOnFailureOnce)
if (retryOnFailureOnce)
{
std::cout << "Database Error: Lost connection, attempting to recover...." << std::endl;
MySQLRequestResult requestResult = QueryDatabase(query, querylen, false);
@ -95,18 +95,18 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo
}
}
pStatus = Error;
char *errorBuffer = new char[MYSQL_ERRMSG_SIZE];
snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl;
return MySQLRequestResult(nullptr, 0, 0, 0, 0, (uint32)mysql_errno(&mysql), errorBuffer);
}
}
char *errorBuffer = new char[MYSQL_ERRMSG_SIZE];
snprintf(errorBuffer, MYSQL_ERRMSG_SIZE, "#%i: %s", mysql_errno(&mysql), mysql_error(&mysql));
@ -114,21 +114,26 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo
std::cout << "DB Query Error #" << mysql_errno(&mysql) << ": " << mysql_error(&mysql) << std::endl;
#endif
return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql),errorBuffer);
}
// successful query. get results.
MYSQL_RES* res = mysql_store_result(&mysql);
MySQLRequestResult requestResult(res, (uint32)mysql_affected_rows(&mysql), (uint32)mysql_num_rows(res), (uint32)mysql_field_count(&mysql), (uint32)mysql_insert_id(&mysql));
uint32 rowCount = 0;
if (res != nullptr)
rowCount = (uint32)mysql_num_rows(res);
MySQLRequestResult requestResult(res, (uint32)mysql_affected_rows(&mysql), rowCount, (uint32)mysql_field_count(&mysql), (uint32)mysql_insert_id(&mysql));
#if DEBUG_MYSQL_QUERIES >= 1
if (requestResult.Success())
if (requestResult.Success())
{
std::cout << "query successful";
if (requestResult.Result())
std::cout << ", " << (int) mysql_num_rows(requestResult.Result()) << " rows returned";
std::cout << ", " << requestResult.RowCount() << " rows affected";
std::cout<< std::endl;
}

View File

@ -237,7 +237,7 @@ std::string EmuConstants::InventorySubName(int16 sub) {
return "Unknown Sub";
std::string ret_str;
StringFormat(ret_str, "Container %i", (sub + 1)); // zero-based index..but, count starts at one
ret_str = StringFormat("Container %i", (sub + 1)); // zero-based index..but, count starts at one
return ret_str;
}
@ -250,7 +250,7 @@ std::string EmuConstants::InventoryAugName(int16 aug) {
return "Unknown Aug";
std::string ret_str;
StringFormat(ret_str, "Augment %i", (aug + 1)); // zero-based index..but, count starts at one
ret_str = StringFormat("Augment %i", (aug + 1)); // zero-based index..but, count starts at one
return ret_str;
}
@ -929,7 +929,7 @@ uint64 EQLimits::PossessionsBitmask(uint32 version) {
/*Underfoot*/ 0x000000027FFFFFFF,
/*RoF*/ 0x00000003FFFFFFFF,
/*RoF2*/ 0,
/*NPC*/ 0,
/*Merc*/ 0,
/*Bot*/ 0,
@ -950,7 +950,7 @@ uint64 EQLimits::EquipmentBitmask(uint32 version) {
/*Underfoot*/ 0x00000000007FFFFF,
/*RoF*/ 0x00000000007FFFFF,
/*RoF2*/ 0,
/*NPC*/ 0,
/*Merc*/ 0,
/*Bot*/ 0,
@ -971,7 +971,7 @@ uint64 EQLimits::GeneralBitmask(uint32 version) {
/*Underfoot*/ 0x000000007F800000,
/*RoF*/ 0x00000001FF800000,
/*RoF2*/ 0,
/*NPC*/ 0,
/*Merc*/ 0,
/*Bot*/ 0,
@ -992,7 +992,7 @@ uint64 EQLimits::CursorBitmask(uint32 version) {
/*Underfoot*/ 0x0000000200000000,
/*RoF*/ 0x0000000200000000,
/*RoF2*/ 0,
/*NPC*/ 0,
/*Merc*/ 0,
/*Bot*/ 0,
@ -1013,13 +1013,13 @@ bool EQLimits::AllowsEmptyBagInBag(uint32 version) {
/*Underfoot*/ Underfoot::limits::ALLOWS_EMPTY_BAG_IN_BAG,
/*RoF*/ RoF::limits::ALLOWS_EMPTY_BAG_IN_BAG,
/*RoF2*/ false,
/*NPC*/ false,
/*Merc*/ false,
/*Bot*/ false,
/*Pet*/ false
};
return false; // not implemented
//return local[ValidateMobVersion(version)];
}
@ -1035,7 +1035,7 @@ uint16 EQLimits::ItemCommonSize(uint32 version) {
/*Underfoot*/ EmuConstants::ITEM_COMMON_SIZE,
/*RoF*/ EmuConstants::ITEM_COMMON_SIZE,
/*RoF2*/ 0,
/*NPC*/ EmuConstants::ITEM_COMMON_SIZE,
/*Merc*/ EmuConstants::ITEM_COMMON_SIZE,
/*Bot*/ EmuConstants::ITEM_COMMON_SIZE,
@ -1055,7 +1055,7 @@ uint16 EQLimits::ItemContainerSize(uint32 version) {
/*Underfoot*/ EmuConstants::ITEM_CONTAINER_SIZE,
/*RoF*/ EmuConstants::ITEM_CONTAINER_SIZE,
/*RoF2*/ 0,
/*NPC*/ EmuConstants::ITEM_CONTAINER_SIZE,
/*Merc*/ EmuConstants::ITEM_CONTAINER_SIZE,
/*Bot*/ EmuConstants::ITEM_CONTAINER_SIZE,
@ -1075,7 +1075,7 @@ bool EQLimits::CoinHasWeight(uint32 version) {
/*Underfoot*/ Underfoot::limits::COIN_HAS_WEIGHT,
/*RoF*/ RoF::limits::COIN_HAS_WEIGHT,
/*RoF2*/ true,
/*NPC*/ true,
/*Merc*/ true,
/*Bot*/ true,
@ -1095,7 +1095,7 @@ uint32 EQLimits::BandoliersCount(uint32 version) {
/*Underfoot*/ EmuConstants::BANDOLIERS_COUNT,
/*RoF*/ EmuConstants::BANDOLIERS_COUNT,
/*RoF2*/ 0,
/*NPC*/ NOT_USED,
/*Merc*/ NOT_USED,
/*Bot*/ NOT_USED,
@ -1115,7 +1115,7 @@ uint32 EQLimits::BandolierSize(uint32 version) {
/*Underfoot*/ EmuConstants::BANDOLIER_SIZE,
/*RoF*/ EmuConstants::BANDOLIER_SIZE,
/*RoF2*/ 0,
/*NPC*/ NOT_USED,
/*Merc*/ NOT_USED,
/*Bot*/ NOT_USED,
@ -1135,7 +1135,7 @@ uint32 EQLimits::PotionBeltSize(uint32 version) {
/*Underfoot*/ EmuConstants::POTION_BELT_SIZE,
/*RoF*/ EmuConstants::POTION_BELT_SIZE,
/*RoF2*/ 0,
/*NPC*/ NOT_USED,
/*Merc*/ NOT_USED,
/*Bot*/ NOT_USED,

View File

@ -33,9 +33,7 @@ void log_message(LogType type, const char *fmt, ...) {
}
void log_messageVA(LogType type, const char *fmt, va_list args) {
std::string prefix_buffer;
StringFormat(prefix_buffer, "[%s] ", log_type_info[type].name);
std::string prefix_buffer = StringFormat("[%s] ", log_type_info[type].name);
LogFile->writePVA(EQEMuLog::Debug, prefix_buffer.c_str(), fmt, args);
}

View File

@ -76,6 +76,7 @@ RULE_BOOL ( Character, SharedBankPlat, false) //off by default to prevent duping
RULE_BOOL ( Character, BindAnywhere, false)
RULE_INT ( Character, RestRegenPercent, 0) // Set to >0 to enable rest state bonus HP and mana regen.
RULE_INT ( Character, RestRegenTimeToActivate, 30) // Time in seconds for rest state regen to kick in.
RULE_INT ( Character, RestRegenRaidTimeToActivate, 300) // Time in seconds for rest state regen to kick in with a raid target.
RULE_BOOL ( Character, RestRegenEndurance, false) // Whether rest regen will work for endurance or not.
RULE_INT ( Character, KillsPerGroupLeadershipAA, 250) // Number of dark blues or above per Group Leadership AA
RULE_INT ( Character, KillsPerRaidLeadershipAA, 250) // Number of dark blues or above per Raid Leadership AA

View File

@ -1710,7 +1710,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
for (y = 0; y < 16; y++)
sp[tempid].deities[y]=atoi(row[126+y]);
sp[tempid].uninterruptable=atoi(row[146]);
sp[tempid].uninterruptable=atoi(row[146]) != 0;
sp[tempid].ResistDiff=atoi(row[147]);
sp[tempid].dot_stacking_exempt=atoi(row[148]);
sp[tempid].RecourseLink = atoi(row[150]);
@ -1720,11 +1720,13 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
sp[tempid].descnum = atoi(row[155]);
sp[tempid].effectdescnum = atoi(row[157]);
sp[tempid].npc_no_los = atoi(row[159]) != 0;
sp[tempid].reflectable = atoi(row[161]) != 0;
sp[tempid].bonushate=atoi(row[162]);
sp[tempid].EndurCost=atoi(row[166]);
sp[tempid].EndurTimerIndex=atoi(row[167]);
sp[tempid].IsDisciplineBuff = atoi(row[168]) != 0;
sp[tempid].HateAdded=atoi(row[173]);
sp[tempid].EndurUpkeep=atoi(row[174]);
sp[tempid].numhitstype = atoi(row[175]);
@ -1740,17 +1742,26 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
sp[tempid].viral_targets = atoi(row[191]);
sp[tempid].viral_timer = atoi(row[192]);
sp[tempid].NimbusEffect = atoi(row[193]);
sp[tempid].directional_start = (float)atoi(row[194]);
sp[tempid].directional_end = (float)atoi(row[195]);
sp[tempid].directional_start = static_cast<float>(atoi(row[194]));
sp[tempid].directional_end = static_cast<float>(atoi(row[195]));
sp[tempid].not_extendable = atoi(row[197]) != 0;
sp[tempid].suspendable = atoi(row[200]) != 0;
sp[tempid].viral_range = atoi(row[201]);
sp[tempid].spellgroup=atoi(row[207]);
sp[tempid].rank = atoi(row[208]);
sp[tempid].powerful_flag=atoi(row[209]);
sp[tempid].CastRestriction = atoi(row[211]);
sp[tempid].AllowRest = atoi(row[212]) != 0;
sp[tempid].NotOutofCombat = atoi(row[213]) != 0;
sp[tempid].NotInCombat = atoi(row[214]) != 0;
sp[tempid].InCombat = atoi(row[213]) != 0;
sp[tempid].OutofCombat = atoi(row[214]) != 0;
sp[tempid].aemaxtargets = atoi(row[218]);
sp[tempid].maxtargets = atoi(row[219]);
sp[tempid].persistdeath = atoi(row[224]) != 0;
sp[tempid].min_dist = atof(row[227]);
sp[tempid].min_dist_mod = atof(row[228]);
sp[tempid].max_dist = atof(row[229]);
sp[tempid].max_dist_mod = atof(row[230]);
sp[tempid].min_range = static_cast<float>(atoi(row[231]));
sp[tempid].DamageShieldType = 0;
}
mysql_free_result(result);
@ -1764,7 +1775,7 @@ void SharedDatabase::LoadSpells(void *data, int max_spells) {
int SharedDatabase::GetMaxBaseDataLevel() {
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = "SELECT MAX(level) FROM base_data";
const char *query = "SELECT MAX(level) FROM base_data";
MYSQL_RES *result;
MYSQL_ROW row;
int32 ret = 0;
@ -1815,7 +1826,7 @@ bool SharedDatabase::LoadBaseData() {
void SharedDatabase::LoadBaseData(void *data, int max_level) {
char *base_ptr = reinterpret_cast<char*>(data);
char errbuf[MYSQL_ERRMSG_SIZE];
char *query = "SELECT * FROM base_data ORDER BY level, class ASC";
const char *query = "SELECT * FROM base_data ORDER BY level, class ASC";
MYSQL_RES *result;
MYSQL_ROW row;

View File

@ -1040,6 +1040,15 @@ bool IsCastonFadeDurationSpell(uint16 spell_id)
return false;
}
bool IsPowerDistModSpell(uint16 spell_id)
{
if (IsValidSpell(spell_id) &&
(spells[spell_id].max_dist_mod || spells[spell_id].min_dist_mod) && spells[spell_id].max_dist > spells[spell_id].min_dist)
return true;
return false;
}
uint32 GetPartialMeleeRuneReduction(uint32 spell_id)
{
for (int i = 0; i < EFFECT_COUNT; ++i)

View File

@ -131,10 +131,10 @@ typedef enum {
/* 41 */ ST_Group = 0x29,
/* 42 */ ST_Directional = 0x2a, //ae around this target between two angles
/* 43 */ ST_GroupClientAndPet = 0x2b,
/* 44 */ ST_Beam = 0x2c, //like directional but facing in front of you always
/* 44 */ //ST_Beam = 0x2c, //like directional but facing in front of you always
/* 45 */ //ST_Ring = 0x2d, // Like a mix of PB ae + rain spell(has ae duration)
/* 46 */ ST_TargetsTarget = 0x2e, // uses the target of your target
/* 47 */ //ST_PetMaster = 0x2e, // uses the master as target
/* 47 */ ST_PetMaster = 0x2f, // uses the master as target
} SpellTargetType;
typedef enum {
@ -690,15 +690,15 @@ struct SPDat_Spell_Struct
/* 156 */ //int typedescnum; // eqstr of type description
/* 157 */ int effectdescnum; // eqstr of effect description
/* 158 */ //Category Desc ID 3
/* 159 */ //bool npc_no_los;
/* 159 */ bool npc_no_los;
/* 161 */ bool reflectable;
/* 162 */ int bonushate;
/* 163 */
/* 164 */ // for most spells this appears to mimic ResistDiff
/* 166 */ int EndurCost;
/* 167 */ int8 EndurTimerIndex;
/* 168 */ //int IsDisciplineBuff; //Will goto the combat window when cast
/* 169 */
/* 168 */ bool IsDisciplineBuff; //Will goto the combat window when cast
/* 169 - 172*/ //These are zero for ALL spells
/* 173 */ int HateAdded;
/* 174 */ int EndurUpkeep;
/* 175 */ int numhitstype; // defines which type of behavior will tick down the numhit counter.
@ -721,22 +721,30 @@ struct SPDat_Spell_Struct
/* 197 */ bool not_extendable;
/* 198- 199 */
/* 200 */ bool suspendable; // buff is suspended in suspended buff zones
/* 201 - 202 */
/* 201 */ int viral_range;
/* 202 */
/* 203 */ //int songcap; // individual song cap (how live currently does it, not implemented)
/* 204 - 206 */
/* 207 */ int spellgroup;
/* 208 */ // int rank - increments AA effects with same name
/* 208 */ int rank; //increments AA effects with same name
/* 209 */ int powerful_flag; // Need more investigation to figure out what to call this, for now we know -1 makes charm spells not break before their duration is complete, it does alot more though
/* 210 */ // bool DurationFrozen; ???
/* 211 */ int CastRestriction; //Various restriction categories for spells most seem targetable race related but have also seen others for instance only castable if target hp 20% or lower or only if target out of combat
/* 212 */ bool AllowRest;
/* 213 */ bool NotOutofCombat; //Fail if cast out of combat
/* 214 */ bool NotInCombat; //Fail if cast in combat
/* 215 - 218 */
/* 219 */ //int maxtargets; // is used for beam and ring spells for target # limits (not implemented)
/* 220 - 223 */
/* 213 */ bool InCombat; //Allow spell if target is in combat
/* 214 */ bool OutofCombat; //Allow spell if target is out of combat
/* 215 - 217 */
/* 218 */ int aemaxtargets; //Is used for various AE effects
/* 219 */ int maxtargets; //Is used for beam and ring spells for target # limits (not implemented)
/* 220 - 223 */
/* 224 */ bool persistdeath; // buff doesn't get stripped on death
/* 225 - 236 */ // Not in DB
/* 225 - 226 */
/* 227 */ float min_dist; //spell power modified by distance from caster (Min Distance)
/* 228 */ float min_dist_mod; //spell power modified by distance from caster (Modifier at Min Distance)
/* 229 */ float max_dist; //spell power modified by distance from caster (Max Distance)
/* 230 */ float max_dist_mod; //spell power modified by distance from caster (Modifier at Max Distance)
/* 231 */ float min_range; //Min casting range
/* 232 - 236 */
uint8 DamageShieldType; // This field does not exist in spells_us.txt
};
@ -837,6 +845,7 @@ bool IsPersistDeathSpell(uint16 spell_id);
bool IsSuspendableSpell(uint16 spell_id);
uint32 GetMorphTrigger(uint32 spell_id);
bool IsCastonFadeDurationSpell(uint16 spell_id);
bool IsPowerDistModSpell(uint16 spell_id);
uint32 GetPartialMeleeRuneReduction(uint32 spell_id);
uint32 GetPartialMagicRuneReduction(uint32 spell_id);
uint32 GetPartialMeleeRuneAmount(uint32 spell_id);

1
utils/scripts/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
opcode_handlers_output

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Character:RestRegenRaidTimeToActivate', 300, 'Time in seconds for rest state regen to kick in with a raid target.');

View File

@ -0,0 +1,34 @@
-- AA MGB update
UPDATE altadv_vars SET spellid = 5228 WHERE skill_id = 128;
UPDATE aa_actions SET spell_id = 5228, nonspell_action = 0 WHERE aaid = 128;
-- AA Project Illusion update
UPDATE altadv_vars SET spellid = 5227 WHERE skill_id = 643;
UPDATE aa_actions SET spell_id = 5227, nonspell_action = 0 WHERE aaid = 643;
-- AA Improved Reclaim Energy
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('180', '1', '241', '95', '0');
-- AA Headshot
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '1', '217', '0', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('644', '2', '346', '46', '0');
-- AA Anatomy (Rogue Assassinate)
INSERT INTO `altadv_vars` (`skill_id`, `name`, `cost`, `max_level`, `hotkey_sid`, `hotkey_sid2`, `title_sid`, `desc_sid`, `type`, `spellid`, `prereq_skill`, `prereq_minpoints`, `spell_type`, `spell_refresh`, `classes`, `berserker`, `class_type`, `cost_inc`, `aa_expansion`, `special_category`, `sof_type`, `sof_cost_inc`, `sof_max_level`, `sof_next_skill`, `clientver`, `account_time_required`, `sof_current_level`,`sof_next_id`,`level_inc`) VALUES ('1604', 'Anatomy', '5', '3', '4294967295', '4294967295', '1604', '1604', '1', '4294967295', '0', '0', '0', '0', '512', '0', '60', '1', '10', '4294967295', '3', '0', '3', '1604', '1', '0', '0', '0', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '1', '439', '0', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1604', '2', '345', '48', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '1', '439', '0', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1605', '2', '345', '51', '0');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '1', '439', '0', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('1606', '2', '345', '53', '0');
-- AA Finishing Blow Fix
DELETE FROM aa_effects WHERE aaid = 199 AND slot = 2;
DELETE FROM aa_effects WHERE aaid = 200 AND slot = 2;
DELETE FROM aa_effects WHERE aaid = 201 AND slot = 2;
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '1', '278', '500', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('119', '2', '440', '50', '200');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '1', '278', '500', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('120', '2', '440', '52', '200');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '1', '278', '500', '32000');
INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('121', '2', '440', '54', '200');

View File

@ -0,0 +1,18 @@
-- spells new talbe update
ALTER TABLE `spells_new` CHANGE `NotOutofCombat` `InCombat` INT(11) NOT NULL DEFAULT '0';
ALTER TABLE `spells_new` CHANGE `NotInCombat` `OutofCombat` INT(11) NOT NULL DEFAULT '0';
ALTER TABLE `spells_new` CHANGE `field201` `viral_range` INT(11) NOT NULL DEFAULT '0';
ALTER TABLE `spells_new` CHANGE `field218` `aemaxtargets` INT(11) NOT NULL DEFAULT '0';
ALTER TABLE `spells_new` ADD `field225` int( 11 ) NOT NULL DEFAULT '0' AFTER `persistdeath`;
ALTER TABLE `spells_new` ADD `field226` int( 11 ) NOT NULL DEFAULT '0' AFTER `field225`;
ALTER TABLE `spells_new` ADD `min_dist` float( 0 ) NOT NULL DEFAULT '0' AFTER `field226`;
ALTER TABLE `spells_new` ADD `min_dist_mod` float( 0 ) NOT NULL DEFAULT '0' AFTER `min_dist`;
ALTER TABLE `spells_new` ADD `max_dist` float( 0 ) NOT NULL DEFAULT '0' AFTER `min_dist_mod`;
ALTER TABLE `spells_new` ADD `max_dist_mod` float( 0 ) NOT NULL DEFAULT '0' AFTER `max_dist`;
ALTER TABLE `spells_new` ADD `min_range` int( 11 ) NOT NULL DEFAULT '0' AFTER `max_dist_mod`;
ALTER TABLE `spells_new` ADD `field232` int( 11 ) NOT NULL DEFAULT '0' AFTER `min_range`;
ALTER TABLE `spells_new` ADD `field233` int( 11 ) NOT NULL DEFAULT '0' AFTER `field232`;
ALTER TABLE `spells_new` ADD `field234` int( 11 ) NOT NULL DEFAULT '0' AFTER `field233`;
ALTER TABLE `spells_new` ADD `field235` int( 11 ) NOT NULL DEFAULT '0' AFTER `field234`;
ALTER TABLE `spells_new` ADD `field236` int( 11 ) NOT NULL DEFAULT '0' AFTER `field235`;

View File

@ -0,0 +1 @@
ALTER TABLE `npc_types` ADD `raid_target` TINYINT(1) UNSIGNED NOT NULL DEFAULT '0' AFTER `no_target_hotkey`;

View File

@ -12,8 +12,7 @@
void log_message_clientVA(LogType type, Client *who, const char *fmt, va_list args) {
std::string prefix_buffer;
StringFormat(prefix_buffer,"[%s] %s: ", log_type_info[type].name, who->GetAccountName());
std::string prefix_buffer = StringFormat("[%s] %s: ", log_type_info[type].name, who->GetAccountName());
LogFile->writePVA(EQEMuLog::Debug, prefix_buffer.c_str(), fmt, args);
}
@ -31,11 +30,11 @@ void log_message_zoneVA(LogType type, ZoneServer *who, const char *fmt, va_list
const char *zone_name=who->GetZoneName();
if (zone_name == nullptr)
StringFormat(zone_tag,"[%d]", who->GetID());
zone_tag = StringFormat("[%d]", who->GetID());
else
StringFormat(zone_tag,"[%d] [%s]",who->GetID(),zone_name);
zone_tag = StringFormat("[%d] [%s]",who->GetID(),zone_name);
StringFormat(prefix_buffer, "[%s] %s ", log_type_info[type].name, zone_tag.c_str());
prefix_buffer = StringFormat("[%s] %s ", log_type_info[type].name, zone_tag.c_str());
LogFile->writePVA(EQEMuLog::Debug, prefix_buffer.c_str(), fmt, args);
}

View File

@ -249,6 +249,7 @@
#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1.
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
#define TARGET_TOO_CLOSE 4602 //You are too close to your target. Get farther away.
#define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters.
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
#define PETITION_DELETED 5054 //Your petition was successfully deleted.
@ -316,6 +317,10 @@
#define PET_NOT_FOCUSING 9263 //No longer focusing on one target, Master.
#define PET_NOT_CASTING 9264 //Not casting spells, Master.
#define PET_CASTING 9291 //Casting spells normally, Master.
#define NO_CAST_IN_COMBAT 9190 //You can not cast this spell while in combat.
#define NO_CAST_OUT_OF_COMBAT 9191 //You can not cast this spell while out of combat.
#define NO_ABILITY_IN_COMBAT 9192 //You can not use this ability while in combat.
#define NO_ABILITY_OUT_OF_COMBAT 9194 //You can not use this ability while out of combat.
#define AE_RAMPAGE 11015 //%1 goes on a WILD RAMPAGE!
#define FACE_ACCEPTED 12028 //Facial features accepted.
#define SPELL_LEVEL_TO_LOW 12048 //You will have to achieve level %1 before you can scribe the %2.

View File

@ -311,7 +311,7 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
hitBonus += (attacker->CastToNPC()->GetAccuracyRating() / 10.0f); //Modifier from database
if(skillinuse == SkillArchery)
hitBonus -= hitBonus*(RuleR(Combat, ArcheryHitPenalty)*100.0f);
hitBonus -= hitBonus*RuleR(Combat, ArcheryHitPenalty);
//Calculate final chance to hit
chancetohit += ((chancetohit * (hitBonus - avoidanceBonus)) / 100.0f);

View File

@ -4303,8 +4303,9 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) {
// Get the zone id this bot spawned in
_lastZoneId = GetZoneID();
this->helmtexture = 0xFF;
this->texture = 0xFF;
// this change propagates to Bot::FillSpawnStruct()
this->helmtexture = 0; //0xFF;
this->texture = 0; //0xFF;
if(this->Save())
this->GetBotOwner()->CastToClient()->Message(0, "%s saved.", this->GetCleanName());
@ -4329,18 +4330,18 @@ void Bot::Spawn(Client* botCharacterOwner, std::string* errorMessage) {
this->SendPosition();
/* // fillspawnstruct now properly handles this -U
// there is something askew with spawn struct appearance fields...
// I re-enabled this until I can sort it out -U
uint32 itemID = 0;
uint8 materialFromSlot = 0xFF;
for(int i=EmuConstants::EQUIPMENT_BEGIN; i<=EmuConstants::EQUIPMENT_END; ++i) {
for(int i = EmuConstants::EQUIPMENT_BEGIN; i <= EmuConstants::EQUIPMENT_END; ++i) {
itemID = GetBotItemBySlot(i);
if(itemID != 0) {
materialFromSlot = Inventory::CalcMaterialFromSlot(i);
if(materialFromSlot != 0xFF) {
if(materialFromSlot != 0xFF)
this->SendWearChange(materialFromSlot);
}
}
}*/
}
}
}
@ -4586,8 +4587,8 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
ns->spawn.size = 0;
ns->spawn.NPC = 0; // 0=player,1=npc,2=pc corpse,3=npc corpse
ns->spawn.helm = 0xFF;
ns->spawn.equip_chest2 = 0xFF;
ns->spawn.helm = helmtexture; //0xFF;
ns->spawn.equip_chest2 = texture; //0xFF;
const Item_Struct* item = 0;
const ItemInst* inst = 0;

View File

@ -324,6 +324,9 @@ Client::Client(EQStreamInterface* ieqs)
initial_respawn_selection = 0;
alternate_currency_loaded = false;
EngagedRaidTarget = false;
SavedRaidRestTimer = 0;
}
Client::~Client() {
@ -4330,12 +4333,16 @@ void Client::IncrementAggroCount() {
if(!RuleI(Character, RestRegenPercent))
return;
// If we already had aggro before this method was called, the combat indicator should already be up for SoF clients,
// so we don't need to send it again.
//
if(AggroCount > 1)
return;
// Pause the rest timer
if (AggroCount == 1)
SavedRaidRestTimer = rest_timer.GetRemainingTime();
if(GetClientVersion() >= EQClientSoF) {
@ -4367,14 +4374,27 @@ void Client::DecrementAggroCount() {
// Something else is still aggro on us, can't rest yet.
if(AggroCount) return;
rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000);
uint32 time_until_rest;
if (GetEngagedRaidTarget()) {
time_until_rest = RuleI(Character, RestRegenRaidTimeToActivate) * 1000;
SetEngagedRaidTarget(false);
} else {
if (SavedRaidRestTimer > (RuleI(Character, RestRegenTimeToActivate) * 1000)) {
time_until_rest = SavedRaidRestTimer;
SavedRaidRestTimer = 0;
} else {
time_until_rest = RuleI(Character, RestRegenTimeToActivate) * 1000;
}
}
rest_timer.Start(time_until_rest);
if(GetClientVersion() >= EQClientSoF) {
EQApplicationPacket *outapp = new EQApplicationPacket(OP_RestState, 5);
char *Buffer = (char *)outapp->pBuffer;
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0x00);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, RuleI(Character, RestRegenTimeToActivate));
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, (uint32)(time_until_rest / 1000));
QueuePacket(outapp);
safe_delete(outapp);
}

View File

@ -789,8 +789,8 @@ public:
void SetTint(int16 slot_id, Color_Struct& color);
void SetMaterial(int16 slot_id, uint32 item_id);
void Undye();
uint32 GetItemIDAt(int16 slot_id);
uint32 GetAugmentIDAt(int16 slot_id, uint8 augslot);
int32 GetItemIDAt(int16 slot_id);
int32 GetAugmentIDAt(int16 slot_id, uint8 augslot);
bool PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client_update = false);
bool PushItemOnCursor(const ItemInst& inst, bool client_update = false);
void DeleteItemInInventory(int16 slot_id, int8 quantity = 0, bool client_update = false, bool update_db = true);
@ -1198,6 +1198,9 @@ public:
int mod_food_value(const Item_Struct *item, int change);
int mod_drink_value(const Item_Struct *item, int change);
void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; }
bool GetEngagedRaidTarget() const { return EngagedRaidTarget; }
protected:
friend class Mob;
void CalcItemBonuses(StatBonuses* newbon);
@ -1442,6 +1445,9 @@ private:
unsigned int RestRegenHP;
unsigned int RestRegenMana;
unsigned int RestRegenEndurance;
bool EngagedRaidTarget;
uint32 SavedRaidRestTimer;
std::set<uint32> zone_flags;

View File

@ -1231,7 +1231,6 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
delta_y = ppu->delta_y;
delta_z = ppu->delta_z;
delta_heading = ppu->delta_heading;
heading = EQ19toFloat(ppu->heading);
if(IsTracking() && ((x_pos!=ppu->x_pos) || (y_pos!=ppu->y_pos))){
if(MakeRandomFloat(0, 100) < 70)//should be good
@ -1257,12 +1256,15 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
}
// Outgoing client packet
if (ppu->y_pos != y_pos || ppu->x_pos != x_pos || ppu->heading != heading || ppu->animation != animation)
float tmpheading = EQ19toFloat(ppu->heading);
if (!FCMP(ppu->y_pos, y_pos) || !FCMP(ppu->x_pos, x_pos) || !FCMP(tmpheading, heading) || ppu->animation != animation)
{
x_pos = ppu->x_pos;
y_pos = ppu->y_pos;
z_pos = ppu->z_pos;
animation = ppu->animation;
heading = tmpheading;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
PlayerPositionUpdateServer_Struct* ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer;
@ -9140,7 +9142,8 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) {
m_pp.timeentitledonaccount = database.GetTotalTimeEntitledOnAccount(AccountID()) / 1440;
if(m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate))
// Reset rest timer if the durations have been lowered in the database
if ((m_pp.RestTimer > RuleI(Character, RestRegenTimeToActivate)) && (m_pp.RestTimer > RuleI(Character, RestRegenRaidTimeToActivate)))
m_pp.RestTimer = 0;
//This checksum should disappear once dynamic structs are in... each struct strategy will do it

View File

@ -740,6 +740,8 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
float dist = caster->GetAOERange(spell_id);
float dist2 = dist * dist;
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range;
float dist_targ = 0;
bool bad = IsDetrimentalSpell(spell_id);
bool isnpc = caster->IsNPC();
@ -755,7 +757,11 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
continue;
if (curmob == caster && !affect_caster) //watch for caster too
continue;
if (center->DistNoRoot(*curmob) > dist2) //make sure they are in range
dist_targ = center->DistNoRoot(*curmob);
if (dist_targ > dist2) //make sure they are in range
continue;
if (dist_targ < min_range2) //make sure they are in range
continue;
if (isnpc && curmob->IsNPC()) { //check npc->npc casting
FACTION_VALUE f = curmob->GetReverseFactionCon(caster);
@ -786,6 +792,8 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
continue;
}
curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ);
//if we get here... cast the spell.
if (IsTargetableAESpell(spell_id) && bad) {
if (iCounter < MAX_TARGETS_ALLOWED) {

View File

@ -4538,7 +4538,7 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType)
return ClosestMob;
}
void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, std::list<Mob*> &m_list)
void EntityList::GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list<Mob*> &m_list)
{
auto it = mob_list.begin();
while (it != mob_list.end()) {
@ -4547,15 +4547,15 @@ void EntityList::GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height,
++it;
continue;
}
int32 x_diff = ptr->GetX() - start->GetX();
int32 y_diff = ptr->GetY() - start->GetY();
int32 z_diff = ptr->GetZ() - start->GetZ();
float x_diff = ptr->GetX() - start->GetX();
float y_diff = ptr->GetY() - start->GetY();
float z_diff = ptr->GetZ() - start->GetZ();
x_diff *= x_diff;
y_diff *= y_diff;
z_diff *= z_diff;
if ((x_diff + y_diff) <= (radius * radius))
if ((x_diff + y_diff) <= (radius * radius) && (x_diff + y_diff) >= (min_radius * min_radius))
if(z_diff <= (height * height))
m_list.push_back(ptr);

View File

@ -406,7 +406,7 @@ public:
void GetObjectList(std::list<Object*> &o_list);
void GetDoorsList(std::list<Doors*> &d_list);
void GetSpawnList(std::list<Spawn2*> &d_list);
void GetTargetsForConeArea(Mob *start, uint32 radius, uint32 height, std::list<Mob*> &m_list);
void GetTargetsForConeArea(Mob *start, float min_radius, float radius, float height, std::list<Mob*> &m_list);
void DepopAll(int NPCTypeID, bool StartSpawnTimer = true);

View File

@ -658,6 +658,7 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) {
range = caster->GetAOERange(spell_id);
float range2 = range*range;
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range;
// caster->SpellOnTarget(spell_id, caster);
@ -673,7 +674,8 @@ void Group::CastGroupSpell(Mob* caster, uint16 spell_id) {
else if(members[z] != nullptr)
{
distance = caster->DistNoRoot(*members[z]);
if(distance <= range2) {
if(distance <= range2 && distance >= min_range2) {
members[z]->CalcSpellPowerDistanceMod(spell_id, distance);
caster->SpellOnTarget(spell_id, members[z]);
#ifdef GROUP_BUFF_PETS
if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed())

View File

@ -198,8 +198,11 @@ void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAd
list.push_back(p);
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "1", 0);
if(ent->IsClient())
if (ent->IsClient()) {
if (owner->CastToNPC()->IsRaidTarget())
ent->CastToClient()->SetEngagedRaidTarget(true);
ent->CastToClient()->IncrementAggroCount();
}
}
}
@ -567,20 +570,25 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range)
//So keep a list of entity ids and look up after
std::list<uint32> id_list;
range = range * range;
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range;
float dist_targ = 0;
auto iterator = list.begin();
while (iterator != list.end())
{
tHateEntry *h = (*iterator);
if(range > 0)
{
if(caster->DistNoRoot(*h->ent) <= range)
dist_targ = caster->DistNoRoot(*h->ent);
if(dist_targ <= range && dist_targ >= min_range2)
{
id_list.push_back(h->ent->GetID());
h->ent->CalcSpellPowerDistanceMod(spell_id, dist_targ);
}
}
else
{
id_list.push_back(h->ent->GetID());
h->ent->CalcSpellPowerDistanceMod(spell_id, 0, caster);
}
++iterator;
}

View File

@ -659,7 +659,7 @@ void Client::DropInst(const ItemInst* inst)
}
// Returns a slot's item ID (returns INVALID_ID if not found)
uint32 Client::GetItemIDAt(int16 slot_id) {
int32 Client::GetItemIDAt(int16 slot_id) {
const ItemInst* inst = m_inv[slot_id];
if (inst)
return inst->GetItem()->ID;
@ -670,7 +670,7 @@ uint32 Client::GetItemIDAt(int16 slot_id) {
// Returns an augment's ID that's in an item (returns INVALID_ID if not found)
// Pass in the slot ID of the item and which augslot you want to check (0-4)
uint32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) {
int32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) {
const ItemInst* inst = m_inv[slot_id];
if (inst)
if (inst->GetAugmentItemID(augslot))

View File

@ -182,6 +182,7 @@ Mob::Mob(const char* in_name,
has_numhits = false;
has_MGB = false;
has_ProjectIllusion = false;
SpellPowerDistanceMod = 0;
if(in_aa_title>0)
aa_title = in_aa_title;
@ -3536,7 +3537,7 @@ void Mob::TrySympatheticProc(Mob *target, uint32 spell_id)
}
}
uint32 Mob::GetItemStat(uint32 itemid, const char *identifier)
int32 Mob::GetItemStat(uint32 itemid, const char *identifier)
{
const ItemInst* inst = database.CreateItem(itemid);
if (!inst)
@ -3549,7 +3550,7 @@ uint32 Mob::GetItemStat(uint32 itemid, const char *identifier)
if (!identifier)
return 0;
uint32 stat = 0;
int32 stat = 0;
std::string id = identifier;
for(int i = 0; i < id.length(); ++i)
@ -3558,316 +3559,318 @@ uint32 Mob::GetItemStat(uint32 itemid, const char *identifier)
}
if (id == "itemclass")
stat = uint32(item->ItemClass);
stat = int32(item->ItemClass);
if (id == "id")
stat = uint32(item->ID);
stat = int32(item->ID);
if (id == "idfile")
stat = atoi(&item->IDFile[2]);
if (id == "weight")
stat = uint32(item->Weight);
stat = int32(item->Weight);
if (id == "norent")
stat = uint32(item->NoRent);
stat = int32(item->NoRent);
if (id == "nodrop")
stat = uint32(item->NoDrop);
stat = int32(item->NoDrop);
if (id == "size")
stat = uint32(item->Size);
stat = int32(item->Size);
if (id == "slots")
stat = uint32(item->Slots);
stat = int32(item->Slots);
if (id == "price")
stat = uint32(item->Price);
stat = int32(item->Price);
if (id == "icon")
stat = uint32(item->Icon);
stat = int32(item->Icon);
if (id == "loregroup")
stat = uint32(item->LoreGroup);
stat = int32(item->LoreGroup);
if (id == "loreflag")
stat = uint32(item->LoreFlag);
stat = int32(item->LoreFlag);
if (id == "pendingloreflag")
stat = uint32(item->PendingLoreFlag);
stat = int32(item->PendingLoreFlag);
if (id == "artifactflag")
stat = uint32(item->ArtifactFlag);
stat = int32(item->ArtifactFlag);
if (id == "summonedflag")
stat = uint32(item->SummonedFlag);
stat = int32(item->SummonedFlag);
if (id == "fvnodrop")
stat = uint32(item->FVNoDrop);
stat = int32(item->FVNoDrop);
if (id == "favor")
stat = uint32(item->Favor);
stat = int32(item->Favor);
if (id == "guildfavor")
stat = uint32(item->GuildFavor);
stat = int32(item->GuildFavor);
if (id == "pointtype")
stat = uint32(item->PointType);
stat = int32(item->PointType);
if (id == "bagtype")
stat = uint32(item->BagType);
stat = int32(item->BagType);
if (id == "bagslots")
stat = uint32(item->BagSlots);
stat = int32(item->BagSlots);
if (id == "bagsize")
stat = uint32(item->BagSize);
stat = int32(item->BagSize);
if (id == "bagwr")
stat = uint32(item->BagWR);
stat = int32(item->BagWR);
if (id == "benefitflag")
stat = uint32(item->BenefitFlag);
stat = int32(item->BenefitFlag);
if (id == "tradeskills")
stat = uint32(item->Tradeskills);
stat = int32(item->Tradeskills);
if (id == "cr")
stat = uint32(item->CR);
stat = int32(item->CR);
if (id == "dr")
stat = uint32(item->DR);
stat = int32(item->DR);
if (id == "pr")
stat = uint32(item->PR);
stat = int32(item->PR);
if (id == "mr")
stat = uint32(item->MR);
stat = int32(item->MR);
if (id == "fr")
stat = uint32(item->FR);
stat = int32(item->FR);
if (id == "astr")
stat = uint32(item->AStr);
stat = int32(item->AStr);
if (id == "asta")
stat = uint32(item->ASta);
stat = int32(item->ASta);
if (id == "aagi")
stat = uint32(item->AAgi);
stat = int32(item->AAgi);
if (id == "adex")
stat = uint32(item->ADex);
stat = int32(item->ADex);
if (id == "acha")
stat = uint32(item->ACha);
stat = int32(item->ACha);
if (id == "aint")
stat = uint32(item->AInt);
stat = int32(item->AInt);
if (id == "awis")
stat = uint32(item->AWis);
stat = int32(item->AWis);
if (id == "hp")
stat = uint32(item->HP);
stat = int32(item->HP);
if (id == "mana")
stat = uint32(item->Mana);
stat = int32(item->Mana);
if (id == "ac")
stat = uint32(item->AC);
stat = int32(item->AC);
if (id == "deity")
stat = uint32(item->Deity);
stat = int32(item->Deity);
if (id == "skillmodvalue")
stat = uint32(item->SkillModValue);
stat = int32(item->SkillModValue);
if (id == "skillmodtype")
stat = uint32(item->SkillModType);
stat = int32(item->SkillModType);
if (id == "banedmgrace")
stat = uint32(item->BaneDmgRace);
stat = int32(item->BaneDmgRace);
if (id == "banedmgamt")
stat = uint32(item->BaneDmgAmt);
stat = int32(item->BaneDmgAmt);
if (id == "banedmgbody")
stat = uint32(item->BaneDmgBody);
stat = int32(item->BaneDmgBody);
if (id == "magic")
stat = uint32(item->Magic);
stat = int32(item->Magic);
if (id == "casttime_")
stat = uint32(item->CastTime_);
stat = int32(item->CastTime_);
if (id == "reqlevel")
stat = uint32(item->ReqLevel);
stat = int32(item->ReqLevel);
if (id == "bardtype")
stat = uint32(item->BardType);
stat = int32(item->BardType);
if (id == "bardvalue")
stat = uint32(item->BardValue);
stat = int32(item->BardValue);
if (id == "light")
stat = uint32(item->Light);
stat = int32(item->Light);
if (id == "delay")
stat = uint32(item->Delay);
stat = int32(item->Delay);
if (id == "reclevel")
stat = uint32(item->RecLevel);
stat = int32(item->RecLevel);
if (id == "recskill")
stat = uint32(item->RecSkill);
stat = int32(item->RecSkill);
if (id == "elemdmgtype")
stat = uint32(item->ElemDmgType);
stat = int32(item->ElemDmgType);
if (id == "elemdmgamt")
stat = uint32(item->ElemDmgAmt);
stat = int32(item->ElemDmgAmt);
if (id == "range")
stat = uint32(item->Range);
stat = int32(item->Range);
if (id == "damage")
stat = uint32(item->Damage);
stat = int32(item->Damage);
if (id == "color")
stat = uint32(item->Color);
stat = int32(item->Color);
if (id == "classes")
stat = uint32(item->Classes);
stat = int32(item->Classes);
if (id == "races")
stat = uint32(item->Races);
stat = int32(item->Races);
if (id == "maxcharges")
stat = uint32(item->MaxCharges);
stat = int32(item->MaxCharges);
if (id == "itemtype")
stat = uint32(item->ItemType);
stat = int32(item->ItemType);
if (id == "material")
stat = uint32(item->Material);
stat = int32(item->Material);
if (id == "casttime")
stat = uint32(item->CastTime);
stat = int32(item->CastTime);
if (id == "elitematerial")
stat = uint32(item->EliteMaterial);
stat = int32(item->EliteMaterial);
if (id == "procrate")
stat = uint32(item->ProcRate);
stat = int32(item->ProcRate);
if (id == "combateffects")
stat = uint32(item->CombatEffects);
stat = int32(item->CombatEffects);
if (id == "shielding")
stat = uint32(item->Shielding);
stat = int32(item->Shielding);
if (id == "stunresist")
stat = uint32(item->StunResist);
stat = int32(item->StunResist);
if (id == "strikethrough")
stat = uint32(item->StrikeThrough);
stat = int32(item->StrikeThrough);
if (id == "extradmgskill")
stat = uint32(item->ExtraDmgSkill);
stat = int32(item->ExtraDmgSkill);
if (id == "extradmgamt")
stat = uint32(item->ExtraDmgAmt);
stat = int32(item->ExtraDmgAmt);
if (id == "spellshield")
stat = uint32(item->SpellShield);
stat = int32(item->SpellShield);
if (id == "avoidance")
stat = uint32(item->Avoidance);
stat = int32(item->Avoidance);
if (id == "accuracy")
stat = uint32(item->Accuracy);
stat = int32(item->Accuracy);
if (id == "charmfileid")
stat = uint32(item->CharmFileID);
stat = int32(item->CharmFileID);
if (id == "factionmod1")
stat = uint32(item->FactionMod1);
stat = int32(item->FactionMod1);
if (id == "factionmod2")
stat = uint32(item->FactionMod2);
stat = int32(item->FactionMod2);
if (id == "factionmod3")
stat = uint32(item->FactionMod3);
stat = int32(item->FactionMod3);
if (id == "factionmod4")
stat = uint32(item->FactionMod4);
stat = int32(item->FactionMod4);
if (id == "factionamt1")
stat = uint32(item->FactionAmt1);
stat = int32(item->FactionAmt1);
if (id == "factionamt2")
stat = uint32(item->FactionAmt2);
stat = int32(item->FactionAmt2);
if (id == "factionamt3")
stat = uint32(item->FactionAmt3);
stat = int32(item->FactionAmt3);
if (id == "factionamt4")
stat = uint32(item->FactionAmt4);
stat = int32(item->FactionAmt4);
if (id == "augtype")
stat = uint32(item->AugType);
stat = int32(item->AugType);
if (id == "ldontheme")
stat = uint32(item->LDoNTheme);
stat = int32(item->LDoNTheme);
if (id == "ldonprice")
stat = uint32(item->LDoNPrice);
stat = int32(item->LDoNPrice);
if (id == "ldonsold")
stat = uint32(item->LDoNSold);
stat = int32(item->LDoNSold);
if (id == "banedmgraceamt")
stat = uint32(item->BaneDmgRaceAmt);
stat = int32(item->BaneDmgRaceAmt);
if (id == "augrestrict")
stat = uint32(item->AugRestrict);
stat = int32(item->AugRestrict);
if (id == "endur")
stat = uint32(item->Endur);
stat = int32(item->Endur);
if (id == "dotshielding")
stat = uint32(item->DotShielding);
stat = int32(item->DotShielding);
if (id == "attack")
stat = uint32(item->Attack);
stat = int32(item->Attack);
if (id == "regen")
stat = uint32(item->Regen);
stat = int32(item->Regen);
if (id == "manaregen")
stat = uint32(item->ManaRegen);
stat = int32(item->ManaRegen);
if (id == "enduranceregen")
stat = uint32(item->EnduranceRegen);
stat = int32(item->EnduranceRegen);
if (id == "haste")
stat = uint32(item->Haste);
stat = int32(item->Haste);
if (id == "damageshield")
stat = uint32(item->DamageShield);
stat = int32(item->DamageShield);
if (id == "recastdelay")
stat = uint32(item->RecastDelay);
stat = int32(item->RecastDelay);
if (id == "recasttype")
stat = uint32(item->RecastType);
stat = int32(item->RecastType);
if (id == "augdistiller")
stat = uint32(item->AugDistiller);
stat = int32(item->AugDistiller);
if (id == "attuneable")
stat = uint32(item->Attuneable);
stat = int32(item->Attuneable);
if (id == "nopet")
stat = uint32(item->NoPet);
stat = int32(item->NoPet);
if (id == "potionbelt")
stat = uint32(item->PotionBelt);
stat = int32(item->PotionBelt);
if (id == "stackable")
stat = uint32(item->Stackable);
stat = int32(item->Stackable);
if (id == "notransfer")
stat = uint32(item->NoTransfer);
stat = int32(item->NoTransfer);
if (id == "questitemflag")
stat = uint32(item->QuestItemFlag);
stat = int32(item->QuestItemFlag);
if (id == "stacksize")
stat = uint32(item->StackSize);
stat = int32(item->StackSize);
if (id == "potionbeltslots")
stat = uint32(item->PotionBeltSlots);
stat = int32(item->PotionBeltSlots);
if (id == "book")
stat = uint32(item->Book);
stat = int32(item->Book);
if (id == "booktype")
stat = uint32(item->BookType);
stat = int32(item->BookType);
if (id == "svcorruption")
stat = uint32(item->SVCorruption);
stat = int32(item->SVCorruption);
if (id == "purity")
stat = uint32(item->Purity);
stat = int32(item->Purity);
if (id == "backstabdmg")
stat = uint32(item->BackstabDmg);
stat = int32(item->BackstabDmg);
if (id == "dsmitigation")
stat = uint32(item->DSMitigation);
stat = int32(item->DSMitigation);
if (id == "heroicstr")
stat = uint32(item->HeroicStr);
stat = int32(item->HeroicStr);
if (id == "heroicint")
stat = uint32(item->HeroicInt);
stat = int32(item->HeroicInt);
if (id == "heroicwis")
stat = uint32(item->HeroicWis);
stat = int32(item->HeroicWis);
if (id == "heroicagi")
stat = uint32(item->HeroicAgi);
stat = int32(item->HeroicAgi);
if (id == "heroicdex")
stat = uint32(item->HeroicDex);
stat = int32(item->HeroicDex);
if (id == "heroicsta")
stat = uint32(item->HeroicSta);
stat = int32(item->HeroicSta);
if (id == "heroiccha")
stat = uint32(item->HeroicCha);
stat = int32(item->HeroicCha);
if (id == "heroicmr")
stat = uint32(item->HeroicMR);
stat = int32(item->HeroicMR);
if (id == "heroicfr")
stat = uint32(item->HeroicFR);
stat = int32(item->HeroicFR);
if (id == "heroiccr")
stat = uint32(item->HeroicCR);
stat = int32(item->HeroicCR);
if (id == "heroicdr")
stat = uint32(item->HeroicDR);
stat = int32(item->HeroicDR);
if (id == "heroicpr")
stat = uint32(item->HeroicPR);
stat = int32(item->HeroicPR);
if (id == "heroicsvcorrup")
stat = uint32(item->HeroicSVCorrup);
stat = int32(item->HeroicSVCorrup);
if (id == "healamt")
stat = uint32(item->HealAmt);
stat = int32(item->HealAmt);
if (id == "spelldmg")
stat = uint32(item->SpellDmg);
stat = int32(item->SpellDmg);
if (id == "ldonsellbackrate")
stat = uint32(item->LDoNSellBackRate);
stat = int32(item->LDoNSellBackRate);
if (id == "scriptfileid")
stat = uint32(item->ScriptFileID);
stat = int32(item->ScriptFileID);
if (id == "expendablearrow")
stat = uint32(item->ExpendableArrow);
stat = int32(item->ExpendableArrow);
if (id == "clairvoyance")
stat = uint32(item->Clairvoyance);
stat = int32(item->Clairvoyance);
// Begin Effects
if (id == "clickeffect")
stat = uint32(item->Click.Effect);
stat = int32(item->Click.Effect);
if (id == "clicktype")
stat = uint32(item->Click.Type);
stat = int32(item->Click.Type);
if (id == "clicklevel")
stat = uint32(item->Click.Level);
stat = int32(item->Click.Level);
if (id == "clicklevel2")
stat = uint32(item->Click.Level2);
stat = int32(item->Click.Level2);
if (id == "proceffect")
stat = uint32(item->Proc.Effect);
stat = int32(item->Proc.Effect);
if (id == "proctype")
stat = uint32(item->Proc.Type);
stat = int32(item->Proc.Type);
if (id == "proclevel")
stat = uint32(item->Proc.Level);
stat = int32(item->Proc.Level);
if (id == "proclevel2")
stat = uint32(item->Proc.Level2);
stat = int32(item->Proc.Level2);
if (id == "worneffect")
stat = uint32(item->Worn.Effect);
stat = int32(item->Worn.Effect);
if (id == "worntype")
stat = uint32(item->Worn.Type);
stat = int32(item->Worn.Type);
if (id == "wornlevel")
stat = uint32(item->Worn.Level);
stat = int32(item->Worn.Level);
if (id == "wornlevel2")
stat = uint32(item->Worn.Level2);
stat = int32(item->Worn.Level2);
if (id == "focuseffect")
stat = uint32(item->Focus.Effect);
stat = int32(item->Focus.Effect);
if (id == "focustype")
stat = uint32(item->Focus.Type);
stat = int32(item->Focus.Type);
if (id == "focuslevel")
stat = uint32(item->Focus.Level);
stat = int32(item->Focus.Level);
if (id == "focuslevel2")
stat = uint32(item->Focus.Level2);
stat = int32(item->Focus.Level2);
if (id == "scrolleffect")
stat = uint32(item->Scroll.Effect);
stat = int32(item->Scroll.Effect);
if (id == "scrolltype")
stat = uint32(item->Scroll.Type);
stat = int32(item->Scroll.Type);
if (id == "scrolllevel")
stat = uint32(item->Scroll.Level);
stat = int32(item->Scroll.Level);
if (id == "scrolllevel2")
stat = uint32(item->Scroll.Level2);
stat = int32(item->Scroll.Level2);
safe_delete(inst);
return stat;

View File

@ -524,6 +524,7 @@ public:
//More stuff to sort:
virtual bool IsRaidTarget() { return false; };
virtual bool IsAttackAllowed(Mob *target, bool isSpellAttack = false);
bool IsTargeted() const { return (targeted > 0); }
inline void IsTargeted(int in_tar) { targeted += in_tar; if(targeted < 0) targeted = 0;}
@ -552,7 +553,7 @@ public:
void Shout(const char *format, ...);
void Emote(const char *format, ...);
void QuestJournalledSay(Client *QuestInitiator, const char *str);
uint32 GetItemStat(uint32 itemid, const char *identifier);
int32 GetItemStat(uint32 itemid, const char *identifier);
int16 CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, bool best_focus=false);
uint8 IsFocusEffect(uint16 spellid, int effect_index, bool AA=false,uint32 aa_effect=0);
@ -615,6 +616,9 @@ public:
bool ImprovedTaunt();
bool TryRootFadeByDamage(int buffslot, Mob* attacker);
int16 GetSlowMitigation() const {return slow_mitigation;}
void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr);
inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; };
inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; };
void ModSkillDmgTaken(SkillUseTypes skill_num, int value);
int16 GetModSkillDmgTaken(const SkillUseTypes skill_num);
@ -1114,6 +1118,7 @@ protected:
bool has_numhits;
bool has_MGB;
bool has_ProjectIllusion;
int16 SpellPowerDistanceMod;
// Bind wound
Timer bindwound_timer;

View File

@ -358,6 +358,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
SetEmoteID(d->emoteid);
InitializeBuffSlots();
CalcBonuses();
raid_target = d->raid_target;
}
NPC::~NPC()

View File

@ -395,6 +395,8 @@ public:
void mod_npc_killed_merit(Mob* c);
void mod_npc_killed(Mob* oos);
void AISpellsList(Client *c);
bool IsRaidTarget() const { return raid_target; };
protected:
@ -500,6 +502,8 @@ protected:
//mercenary stuff
std::list<MercType> mercTypeList;
std::list<MercData> mercDataList;
bool raid_target;
private:
uint32 loottable_id;

View File

@ -2955,7 +2955,7 @@ XS(XS_Client_GetItemIDAt)
Perl_croak(aTHX_ "Usage: Client::GetItemIDAt(THIS, slot_id)");
{
Client * THIS;
uint32 RETVAL;
int32 RETVAL;
dXSTARG;
int16 slot_id = (int16)SvIV(ST(1));
@ -2969,7 +2969,7 @@ XS(XS_Client_GetItemIDAt)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetItemIDAt(slot_id);
XSprePUSH; PUSHu((UV)RETVAL);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
@ -2982,7 +2982,7 @@ XS(XS_Client_GetAugmentIDAt)
Perl_croak(aTHX_ "Usage: Client::GetAugmentIDAt(THIS, slot_id, augslot)");
{
Client * THIS;
uint32 RETVAL;
int32 RETVAL;
dXSTARG;
int16 slot_id = (int16)SvIV(ST(1));
int16 augslot = (uint8)SvIV(ST(2));
@ -2997,7 +2997,7 @@ XS(XS_Client_GetAugmentIDAt)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetAugmentIDAt(slot_id, augslot);
XSprePUSH; PUSHu((UV)RETVAL);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}

View File

@ -7298,7 +7298,7 @@ XS(XS_Mob_GetItemStat)
Perl_croak(aTHX_ "Usage: Mob::GetItemStat(THIS, itemid, stat)");
{
Mob * THIS;
uint32 RETVAL;
int32 RETVAL;
uint32 itemid = (uint32)SvUV(ST(1));
Const_char * stat = (Const_char *)SvPV_nolen(ST(2));
dXSTARG;
@ -7313,7 +7313,7 @@ XS(XS_Mob_GetItemStat)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetItemStat(itemid, stat);
XSprePUSH; PUSHu((UV)RETVAL);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}

View File

@ -2015,7 +2015,7 @@ float Mob::GetSpecialProcChances(uint16 hand)
ProcBonus += static_cast<float>(mydex/35) + static_cast<float>(itembonuses.HeroicDEX / 25);
ProcChance += ProcChance * ProcBonus / 100.0f;
} else {
/*PRE 2014 CHANGE Dev Quote - "Elidroth SOE:Proc chance is a function of your base hardcapped Dexterity / 35 + Heroic Dexterity / 25.
/*PRE 2014 CHANGE Dev Quote - "Elidroth SOE:Proc chance is a function of your base hardcapped Dexterity / 35 + Heroic Dexterity / 25.”
Kayen: Most reports suggest a ~ 6% chance to Headshot which consistent with above.*/
ProcChance = (static_cast<float>(mydex/35) + static_cast<float>(itembonuses.HeroicDEX / 25))/100.0f;

View File

@ -186,6 +186,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
buffs[buffslot].numhits = numhit;
}
if (!IsPowerDistModSpell(spell_id))
SetSpellPowerDistanceMod(0);
// iterate through the effects in the spell
for (i = 0; i < EFFECT_COUNT; i++)
{
@ -198,6 +201,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
if(spell_id == SPELL_LAY_ON_HANDS && caster && caster->GetAA(aaImprovedLayOnHands))
effect_value = GetMaxHP();
if (GetSpellPowerDistanceMod())
effect_value = effect_value*(GetSpellPowerDistanceMod()/100);
#ifdef SPELL_EFFECT_SPAM
effect_desc[0] = 0;
#endif
@ -2705,7 +2711,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
if (buffslot >= 0)
break;
if(IsCasting() && MakeRandomInt(0, 100) <= spells[spell_id].base[i])
if(!spells[spell_id].uninterruptable && IsCasting() && MakeRandomInt(0, 100) <= spells[spell_id].base[i])
InterruptSpell();
break;
@ -6444,4 +6450,24 @@ bool Mob::CheckSpellCategory(uint16 spell_id, int category_id, int effect_id){
return false;
}
void Mob::CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster)
{
if (IsPowerDistModSpell(spell_id)){
float distance = 0;
if (caster && !range)
distance = caster->CalculateDistance(GetX(), GetY(), GetZ());
else
distance = sqrt(range);
float dm_range = spells[spell_id].max_dist - spells[spell_id].min_dist;
float dm_mod_interval = spells[spell_id].max_dist_mod - spells[spell_id].min_dist_mod;
float dist_from_min = distance - spells[spell_id].min_dist;
float mod = spells[spell_id].min_dist_mod + (dist_from_min * (dm_mod_interval/dm_range));
mod *= 100.0f;
SetSpellPowerDistanceMod(static_cast<int>(mod));
}
}

View File

@ -1034,7 +1034,7 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot,
mlog(SPELLS__CASTING, "Checking Interruption: spell x: %f spell y: %f cur x: %f cur y: %f channelchance %f channeling skill %d\n", GetSpellX(), GetSpellY(), GetX(), GetY(), channelchance, GetSkill(SkillChanneling));
if(MakeRandomFloat(0, 100) > channelchance) {
if(!spells[spell_id].uninterruptable && MakeRandomFloat(0, 100) > channelchance) {
mlog(SPELLS__CASTING_ERR, "Casting of %d canceled: interrupted.", spell_id);
InterruptSpell();
return;
@ -1384,6 +1384,52 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
return false;
}
//Must be out of combat. (If Beneficial checks casters combat state, Deterimental checks targets)
if (!spells[spell_id].InCombat && spells[spell_id].OutofCombat){
if (IsDetrimentalSpell(spell_id)) {
if ( (spell_target->IsNPC() && spell_target->IsEngaged()) ||
(spell_target->IsClient() && spell_target->CastToClient()->GetAggroCount())){
Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string
return false;
}
}
else if (IsBeneficialSpell(spell_id)) {
if ( (IsNPC() && IsEngaged()) ||
(IsClient() && CastToClient()->GetAggroCount())){
if (IsDiscipline(spell_id))
Message_StringID(13,NO_ABILITY_IN_COMBAT);
else
Message_StringID(13,NO_CAST_IN_COMBAT);
return false;
}
}
}
//Must be in combat. (If Beneficial checks casters combat state, Deterimental checks targets)
else if (spells[spell_id].InCombat && !spells[spell_id].OutofCombat){
if (IsDetrimentalSpell(spell_id)) {
if ( (spell_target->IsNPC() && !spell_target->IsEngaged()) ||
(spell_target->IsClient() && !spell_target->CastToClient()->GetAggroCount())){
Message_StringID(13,SPELL_NO_EFFECT); //Unsure correct string
return false;
}
}
else if (IsBeneficialSpell(spell_id)) {
if ( (IsNPC() && !IsEngaged()) ||
(IsClient() && !CastToClient()->GetAggroCount())){
if (IsDiscipline(spell_id))
Message_StringID(13,NO_ABILITY_OUT_OF_COMBAT);
else
Message_StringID(13,NO_CAST_OUT_OF_COMBAT);
return false;
}
}
}
switch (targetType)
{
// single target spells
@ -1734,6 +1780,24 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
break;
}
case ST_PetMaster:
{
Mob *owner = nullptr;
if (IsPet())
owner = GetOwner();
else if ((IsNPC() && CastToNPC()->GetSwarmOwner()))
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
if (!owner)
return false;
spell_target = owner;
CastAction = SingleTarget;
break;
}
default:
{
mlog(SPELLS__CASTING_ERR, "I dont know Target Type: %d Spell: (%d) %s", spells[spell_id].targettype, spell_id, spells[spell_id].name);
@ -1833,7 +1897,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
}
// check line of sight to target if it's a detrimental spell
if(spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional)
if(!spells[spell_id].npc_no_los && spell_target && IsDetrimentalSpell(spell_id) && !CheckLosFN(spell_target) && !IsHarmonySpell(spell_id) && spells[spell_id].targettype != ST_TargetOptional)
{
mlog(SPELLS__CASTING, "Spell %d: cannot see target %s", spell_target->GetName());
Message_StringID(13,CANT_SEE_TARGET);
@ -1863,12 +1927,21 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
//casting a spell on somebody but ourself, make sure they are in range
float dist2 = DistNoRoot(*spell_target);
float range2 = range * range;
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range;
if(dist2 > range2) {
//target is out of range.
mlog(SPELLS__CASTING, "Spell %d: Spell target is out of range (squared: %f > %f)", spell_id, dist2, range2);
Message_StringID(13, TARGET_OUT_OF_RANGE);
return(false);
}
else if (dist2 < min_range2){
//target is too close range.
mlog(SPELLS__CASTING, "Spell %d: Spell target is too close (squared: %f < %f)", spell_id, dist2, min_range2);
Message_StringID(13, TARGET_TOO_CLOSE);
return(false);
}
spell_target->CalcSpellPowerDistanceMod(spell_id, dist2);
}
//
@ -2052,7 +2125,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
std::list<Mob*> targets_in_range;
std::list<Mob*>::iterator iter;
entity_list.GetTargetsForConeArea(this, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range);
entity_list.GetTargetsForConeArea(this, spells[spell_id].min_range, spells[spell_id].aoerange, spells[spell_id].aoerange / 2, targets_in_range);
iter = targets_in_range.begin();
while(iter != targets_in_range.end())
{
@ -2068,16 +2141,20 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16
if((heading_to_target >= angle_start && heading_to_target <= 360.0f) ||
(heading_to_target >= 0.0f && heading_to_target <= angle_end))
{
if(CheckLosFN(spell_target))
if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){
(*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this);
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust);
}
}
}
else
{
if(heading_to_target >= angle_start && heading_to_target <= angle_end)
{
if(CheckLosFN((*iter)))
if(CheckLosFN((*iter)) || spells[spell_id].npc_no_los){
(*iter)->CalcSpellPowerDistanceMod(spell_id, 0, this);
SpellOnTarget(spell_id, (*iter), false, true, resist_adjust);
}
}
}
++iter;
@ -4625,12 +4702,10 @@ void Mob::Stun(int duration)
if(stunned && stunned_timer.GetRemainingTime() > uint32(duration))
return;
if(casting_spell_id) {
int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting;
if(IsClient())
persistent_casting += aabonuses.PersistantCasting;
if(IsValidSpell(casting_spell_id) && !spells[casting_spell_id].uninterruptable) {
int persistent_casting = spellbonuses.PersistantCasting + itembonuses.PersistantCasting + aabonuses.PersistantCasting;
if(MakeRandomInt(1,99) > persistent_casting)
if(MakeRandomInt(0,99) > persistent_casting)
InterruptSpell();
}

View File

@ -1115,7 +1115,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
"npc_types.emoteid,"
"npc_types.spellscale,"
"npc_types.healscale,"
"npc_types.no_target_hotkey";
"npc_types.no_target_hotkey,"
"npc_types.raid_target";
MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id);
@ -1302,6 +1303,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
tmpNPCType->spellscale = atoi(row[r++]);
tmpNPCType->healscale = atoi(row[r++]);
tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false;
tmpNPCType->raid_target = atoi(row[r++]) == 0 ? false : true;
// If NPC with duplicate NPC id already in table,
// free item we attempted to add.

View File

@ -125,6 +125,7 @@ struct NPCType
float spellscale;
float healscale;
bool no_target_hotkey;
bool raid_target;
};
/*