Merge branch 'master' of https://github.com/EQEmu/Server into inv_possessions_rework

This commit is contained in:
Uleat 2018-09-23 01:02:54 -04:00
commit 7c5b1e8fd2
58 changed files with 1157 additions and 654 deletions

23
.editorconfig Normal file
View File

@ -0,0 +1,23 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Unix-style newlines with a newline ending every file
[*]
end_of_line = lf
insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{js,py}]
charset = utf-8
[*.cpp]
indent_style = tab
[*.h]
indent_style = tab
# Tab indentation (no size specified)
[Makefile]
indent_style = tab

View File

@ -28,6 +28,9 @@
#EQEMU_MAP_DIR
CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
IF(POLICY CMP0074)
cmake_policy(SET CMP0074 NEW)
ENDIF()
#FindMySQL is located here so lets make it so CMake can find it
SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/" ${CMAKE_MODULE_PATH})

View File

@ -376,7 +376,11 @@ struct NewZone_Struct {
/*0692*/ uint8 unknown692[8];
/*0700*/ float fog_density;
/*0704*/ uint32 SuspendBuffs;
/*0704*/
/*0708*/ uint32 FastRegenHP;
/*0712*/ uint32 FastRegenMana;
/*0716*/ uint32 FastRegenEndurance;
/*0720*/ uint32 NPCAggroMaxDist;
/*0724*/
};
/*

View File

@ -90,6 +90,7 @@ enum LogCategory {
FixZ,
Food,
Traps,
NPCRoamBox,
MaxCategoryID /* Don't Remove this*/
};
@ -144,7 +145,8 @@ static const char* LogCategoryName[LogCategory::MaxCategoryID] = {
"HP Update",
"FixZ",
"Food",
"Traps"
"Traps",
"NPC Roam Box"
};
}

View File

@ -124,7 +124,7 @@ namespace EQEmu
class OutBuffer : public std::stringstream {
public:
inline size_t size() { return tellp(); }
inline size_t size() { return static_cast<size_t>(tellp()); }
void overwrite(OutBuffer::pos_type position, const char *_Str, std::streamsize _Count);
uchar* detach();
};

View File

@ -73,7 +73,7 @@ uint32 SwapBits21And22(uint32 mask);
uint32 Catch22(uint32 mask);
// macro to catch fp errors (provided by noudness)
#define FCMP(a,b) (fabs(a-b) < FLT_EPSILON)
#define FCMP(a,b) (std::abs(a-b) < FLT_EPSILON)
#define _ITOA_BUFLEN 25
const char *itoa(int num); //not thread safe

View File

@ -1823,6 +1823,9 @@ namespace RoF
OUT(zone_id);
OUT(zone_instance);
OUT(SuspendBuffs);
OUT(FastRegenHP);
OUT(FastRegenMana);
OUT(FastRegenEndurance);
eq->FogDensity = emu->fog_density;
@ -1839,9 +1842,6 @@ namespace RoF
eq->unknown893 = 0;
eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off
eq->unknown895 = 0;
eq->unknown896 = 180;
eq->unknown900 = 180;
eq->unknown904 = 180;
eq->unknown908 = 2;
eq->unknown912 = 2;
eq->unknown932 = -1; // Set from PoK Example
@ -2807,7 +2807,7 @@ namespace RoF
std::vector<int32> skill;
std::vector<int32> points;
for(auto i = 0; i < emu->total_prereqs; ++i) {
for(auto i = 0u; i < emu->total_prereqs; ++i) {
skill.push_back(inapp->ReadUInt32());
points.push_back(inapp->ReadUInt32());
}
@ -2861,7 +2861,7 @@ namespace RoF
outapp->WriteUInt32(emu->total_effects);
inapp->SetReadPosition(sizeof(AARankInfo_Struct));
for(auto i = 0; i < emu->total_effects; ++i) {
for(auto i = 0u; i < emu->total_effects; ++i) {
outapp->WriteUInt32(inapp->ReadUInt32()); // skill_id
outapp->WriteUInt32(inapp->ReadUInt32()); // base1
outapp->WriteUInt32(inapp->ReadUInt32()); // base2
@ -2917,7 +2917,7 @@ namespace RoF
unsigned char *eq_ptr = __packet->pBuffer;
eq_ptr += sizeof(structs::CharacterSelect_Struct);
for (int counter = 0; counter < character_count; ++counter) {
for (auto counter = 0u; counter < character_count; ++counter) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
eq_cse = (structs::CharacterSelectEntry_Struct *)eq_ptr; // base address
@ -3259,7 +3259,7 @@ namespace RoF
InBuffer += title_size;
TaskDescriptionData1_Struct *emu_tdd1 = (TaskDescriptionData1_Struct *)InBuffer;
emu_tdd1->StartTime = (time(nullptr) - emu_tdd1->StartTime); // RoF has elapsed time here rather than start time
emu_tdd1->StartTime = (static_cast<uint32>(time(nullptr)) - emu_tdd1->StartTime); // RoF has elapsed time here rather than start time
InBuffer += sizeof(TaskDescriptionData1_Struct);
uint32 description_size = strlen(InBuffer) + 1;
@ -3610,10 +3610,10 @@ namespace RoF
// calculate size of names, note the packet DOES NOT have null termed c-strings
std::vector<uint32> name_lengths;
for (int i = 0; i < count; ++i) {
for (auto i = 0u; i < count; ++i) {
InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer;
for (int i = 0; i < ivr->claim_count; i++) {
for (auto i = 0u; i < ivr->claim_count; i++) {
uint32 length = strnlen(ivr->items[i].item_name, 63);
if (length)
name_lengths.push_back(length);
@ -3633,7 +3633,7 @@ namespace RoF
outapp->WriteUInt32(count);
auto name_itr = name_lengths.begin();
for (int i = 0; i < count; i++) {
for (auto i = 0u; i < count; i++) {
InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer;
outapp->WriteUInt32(ivr->claim_id);
@ -3641,7 +3641,7 @@ namespace RoF
outapp->WriteUInt32(ivr->claim_count);
outapp->WriteUInt8(1); // enabled
for (int j = 0; j < ivr->claim_count; j++) {
for (auto j = 0u; j < ivr->claim_count; j++) {
assert(name_itr != name_lengths.end()); // the way it's written, it should never happen, so just assert
outapp->WriteUInt32(*name_itr);
outapp->WriteData(ivr->items[j].item_name, *name_itr);
@ -3869,7 +3869,7 @@ namespace RoF
VARSTRUCT_ENCODE_STRING(Buffer, emu->name);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->spawnId);
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->level);
VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7); // Eye Height?
VARSTRUCT_ENCODE_TYPE(float, Buffer, SpawnSize - 0.7f); // Eye Height?
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->NPC);
structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer;

View File

@ -1891,6 +1891,9 @@ namespace RoF2
OUT(zone_id);
OUT(zone_instance);
OUT(SuspendBuffs);
OUT(FastRegenHP);
OUT(FastRegenMana);
OUT(FastRegenEndurance);
eq->FogDensity = emu->fog_density;
@ -1914,9 +1917,6 @@ namespace RoF2
eq->bNoFear = 0;
eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off
eq->unknown895 = 0;
eq->FastRegenHP = 180;
eq->FastRegenMana = 180;
eq->FastRegenEndurance = 180;
eq->CanPlaceCampsite = 2;
eq->CanPlaceGuildBanner = 2;
eq->FishingRelated = -1; // Set from PoK Example

View File

@ -588,9 +588,9 @@ struct NewZone_Struct {
/*0893*/ uint8 unknown893; // Seen 0 - 00
/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off
/*0895*/ uint8 unknown895; // Seen 0 - 00
/*0896*/ uint32 unknown896; // Seen 180
/*0900*/ uint32 unknown900; // Seen 180
/*0904*/ uint32 unknown904; // Seen 180
/*0896*/ uint32 FastRegenHP; // Seen 180
/*0900*/ uint32 FastRegenMana; // Seen 180
/*0904*/ uint32 FastRegenEndurance; // Seen 180
/*0908*/ uint32 unknown908; // Seen 2
/*0912*/ uint32 unknown912; // Seen 2
/*0916*/ float FogDensity; // Most zones have this set to 0.33 Blightfire had 0.16

View File

@ -1350,6 +1350,10 @@ namespace SoD
OUT(zone_id);
OUT(zone_instance);
OUT(SuspendBuffs);
OUT(FastRegenHP);
OUT(FastRegenMana);
OUT(FastRegenEndurance);
/*fill in some unknowns with observed values, hopefully it will help */
eq->unknown800 = -1;
eq->unknown844 = 600;
@ -1363,9 +1367,6 @@ namespace SoD
eq->unknown893 = 0;
eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off
eq->unknown895 = 0;
eq->unknown896 = 180;
eq->unknown900 = 180;
eq->unknown904 = 180;
eq->unknown908 = 2;
eq->unknown912 = 2;
eq->FogDensity = emu->fog_density;

View File

@ -456,9 +456,9 @@ struct NewZone_Struct {
/*0893*/ uint8 unknown893; //seen 0 - 00
/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off
/*0895*/ uint8 unknown895; //seen 0 - 00
/*0896*/ uint32 unknown896; //seen 180
/*0900*/ uint32 unknown900; //seen 180
/*0904*/ uint32 unknown904; //seen 180
/*0896*/ uint32 FastRegenHP; //seen 180
/*0900*/ uint32 FastRegenMana; //seen 180
/*0904*/ uint32 FastRegenEndurance; //seen 180
/*0908*/ uint32 unknown908; //seen 2
/*0912*/ uint32 unknown912; //seen 2
/*0916*/ float FogDensity; //Of about 10 or so zones tested, all but one have this set to 0.33 Blightfire had 0.16

View File

@ -1027,6 +1027,9 @@ namespace SoF
OUT(zone_id);
OUT(zone_instance);
OUT(SuspendBuffs);
OUT(FastRegenHP);
OUT(FastRegenMana);
OUT(FastRegenEndurance);
/*fill in some unknowns with observed values, hopefully it will help */
eq->unknown796 = -1;
@ -1041,9 +1044,6 @@ namespace SoF
eq->unknown889 = 0;
eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off
eq->unknown891 = 0;
eq->unknown892 = 180;
eq->unknown896 = 180;
eq->unknown900 = 180;
eq->unknown904 = 2;
eq->unknown908 = 2;

View File

@ -460,9 +460,9 @@ struct NewZone_Struct {
/*0893*/ uint8 unknown889; //seen 0 - 00
/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off
/*0895*/ uint8 unknown891; //seen 0 - 00
/*0892*/ uint32 unknown892; //seen 180
/*0896*/ uint32 unknown896; //seen 180
/*0900*/ uint32 unknown900; //seen 180
/*0892*/ uint32 FastRegenHP; //seen 180
/*0896*/ uint32 FastRegenMana; //seen 180
/*0900*/ uint32 FastRegenEndurance; //seen 180
/*0904*/ uint32 unknown904; //seen 2
/*0908*/ uint32 unknown908; //seen 2
/*0912*/

View File

@ -1574,6 +1574,9 @@ namespace UF
OUT(zone_id);
OUT(zone_instance);
OUT(SuspendBuffs);
OUT(FastRegenHP);
OUT(FastRegenMana);
OUT(FastRegenEndurance);
eq->FogDensity = emu->fog_density;
@ -1590,9 +1593,6 @@ namespace UF
eq->unknown893 = 0;
eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off
eq->unknown895 = 0;
eq->unknown896 = 180;
eq->unknown900 = 180;
eq->unknown904 = 180;
eq->unknown908 = 2;
eq->unknown912 = 2;

View File

@ -456,9 +456,9 @@ struct NewZone_Struct {
/*0893*/ uint8 unknown893; //seen 0 - 00
/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off
/*0895*/ uint8 unknown895; //seen 0 - 00
/*0896*/ uint32 unknown896; //seen 180
/*0900*/ uint32 unknown900; //seen 180
/*0904*/ uint32 unknown904; //seen 180
/*0896*/ uint32 FastRegenHP; //seen 180
/*0900*/ uint32 FastRegenMana; //seen 180
/*0904*/ uint32 FastRegenEndurance; //seen 180
/*0908*/ uint32 unknown908; //seen 2
/*0912*/ uint32 unknown912; //seen 2
/*0916*/ float FogDensity; //Of about 10 or so zones tested, all but one have this set to 0.33 Blightfire had 0.16

View File

@ -94,9 +94,6 @@ RULE_INT(Character, SkillUpModifier, 100) //skill ups are at 100%
RULE_BOOL(Character, SharedBankPlat, false) //off by default to prevent duping for now
RULE_BOOL(Character, BindAnywhere, false)
RULE_BOOL(Character, RestRegenEnabled, true) // Enable OOC Regen
RULE_INT(Character, RestRegenHP, 180) // seconds until full from 0. this is actually zone setable, but most or all zones are 180
RULE_INT(Character, RestRegenMana, 180) // seconds until full from 0. this is actually zone setable, but most or all zones are 180
RULE_INT(Character, RestRegenEnd, 180) // seconds until full from 0. this is actually zone setable, but most or all zones are 180
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_INT(Character, KillsPerGroupLeadershipAA, 250) // Number of dark blues or above per Group Leadership AA
@ -419,6 +416,8 @@ RULE_BOOL(Combat, UseIntervalAC, true)
RULE_INT(Combat, PetAttackMagicLevel, 30)
RULE_BOOL(Combat, EnableFearPathing, true)
RULE_REAL(Combat, FleeMultiplier, 2.0) // Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker.
RULE_BOOL(Combat, FleeGray, true) // If true FleeGrayHPRatio will be used.
RULE_INT(Combat, FleeGrayHPRatio, 50) //HP % when a Gray NPC begins to flee.
RULE_INT(Combat, FleeHPRatio, 25) //HP % when a NPC begins to flee.
RULE_BOOL(Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it.
RULE_BOOL(Combat, AdjustProcPerMinute, true)
@ -532,6 +531,11 @@ RULE_INT(NPC, NPCToNPCAggroTimerMin, 500)
RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000)
RULE_BOOL(NPC, UseClassAsLastName, true) // Uses class archetype as LastName for npcs with none
RULE_BOOL(NPC, NewLevelScaling, true) // Better level scaling, use old if new formulas would break your server
RULE_INT(NPC, NPCGatePercent, 5) // % at which the NPC Will attempt to gate at.
RULE_BOOL(NPC, NPCGateNearBind, false) // Will NPC attempt to gate when near bind location?
RULE_INT(NPC, NPCGateDistanceBind, 75) // Distance from bind before NPC will attempt to gate
RULE_BOOL(NPC, NPCHealOnGate, true) // Will the NPC Heal on Gate.
RULE_REAL(NPC, NPCHealOnGateAmount, 25) // How much the npc will heal on gate if enabled.
RULE_CATEGORY_END()
RULE_CATEGORY(Aggro)
@ -695,6 +699,7 @@ RULE_CATEGORY_END()
RULE_CATEGORY(QueryServ)
RULE_BOOL(QueryServ, PlayerLogChat, false) // Logs Player Chat
RULE_BOOL(QueryServ, PlayerLogTrades, false) // Logs Player Trades
RULE_BOOL(QueryServ, PlayerDropItems, false) // Logs Player dropping items
RULE_BOOL(QueryServ, PlayerLogHandins, false) // Logs Player Handins
RULE_BOOL(QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills
RULE_BOOL(QueryServ, PlayerLogDeletes, false) // Logs Player Deletes

View File

@ -203,6 +203,7 @@
#define ServerOP_CZSignalNPC 0x5017
#define ServerOP_CZSetEntityVariableByNPCTypeID 0x5018
#define ServerOP_WWMarquee 0x5019
#define ServerOP_QSPlayerDropItem 0x5020
/* Query Serv Generic Packet Flag/Type Enumeration */
enum { QSG_LFGuild = 0 };
@ -1140,6 +1141,27 @@ struct QSPlayerLogTrade_Struct {
QSTradeItems_Struct items[0];
};
struct QSDropItems_Struct {
uint32 item_id;
uint16 charges;
uint32 aug_1;
uint32 aug_2;
uint32 aug_3;
uint32 aug_4;
uint32 aug_5;
};
struct QSPlayerDropItem_Struct {
uint32 char_id;
bool pickup; // 0 drop, 1 pickup
uint32 zone_id;
int x;
int y;
int z;
uint16 _detail_count;
QSDropItems_Struct items[0];
};
struct QSHandinItems_Struct {
char action_type[7]; // handin, return or reward
uint16 char_slot;

View File

@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9127
#define CURRENT_BINARY_DATABASE_VERSION 9129
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9020
#else

View File

@ -123,6 +123,39 @@ void Database::AddSpeech(const char* from, const char* to, const char* message,
}
void Database::LogPlayerDropItem(QSPlayerDropItem_Struct* QS) {
std::string query = StringFormat("INSERT INTO `qs_player_drop_record` SET `time` = NOW(), "
"`char_id` = '%i', `pickup` = '%i', "
"`zone_id` = '%i', `x` = '%i', `y` = '%i', `z` = '%i' ",
QS->char_id, QS->pickup, QS->zone_id, QS->x, QS->y, QS->z);
auto results = QueryDatabase(query);
if (!results.Success()) {
Log(Logs::Detail, Logs::QS_Server, "Failed Drop Record Insert: %s", results.ErrorMessage().c_str());
Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str());
}
if (QS->_detail_count == 0)
return;
int lastIndex = results.LastInsertedID();
for (int i = 0; i < QS->_detail_count; i++) {
query = StringFormat("INSERT INTO `qs_player_drop_record_entries` SET `event_id` = '%i', "
"`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', "
"`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'",
lastIndex, QS->items[i].item_id,
QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2,
QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5);
results = QueryDatabase(query);
if (!results.Success()) {
Log(Logs::Detail, Logs::QS_Server, "Failed Drop Record Entry Insert: %s", results.ErrorMessage().c_str());
Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str());
}
}
}
void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 detailCount) {
std::string query = StringFormat("INSERT INTO `qs_player_trade_record` SET `time` = NOW(), "

View File

@ -45,6 +45,7 @@ public:
void AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type);
void LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 DetailCount);
void LogPlayerDropItem(QSPlayerDropItem_Struct* QS);
void LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 DetailCount);
void LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members);
void LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items);

View File

@ -98,6 +98,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
database.LogPlayerTrade(QS, QS->_detail_count);
break;
}
case ServerOP_QSPlayerDropItem: {
QSPlayerDropItem_Struct *QS = (QSPlayerDropItem_Struct *) p.Data();
database.LogPlayerDropItem(QS);
break;
}
case ServerOP_QSPlayerLogHandins: {
QSPlayerLogHandin_Struct *QS = (QSPlayerLogHandin_Struct*)p.Data();
database.LogPlayerHandin(QS, QS->_detail_count);

View File

@ -40,7 +40,12 @@ if($Config{osname}=~/freebsd|linux/i){
if($Config{osname}=~/Win|MS/i){
$OS = "Windows";
}
$has_internet_connection = check_internet_connection();
if(-e "skip_internet_connection_check.txt"){
$has_internet_connection = 1;
}
($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime();
if(-e "eqemu_server_skip_update.txt"){
@ -167,19 +172,19 @@ sub show_install_summary_info {
if (-e "install_variables.txt") {
$file_to_open = "install_variables.txt";
}
elsif(-e "../install_variables.txt"){
elsif (-e "../install_variables.txt") {
$file_to_open = "../install_variables.txt";
}
open (INSTALL_VARS, $file_to_open);
while (<INSTALL_VARS>){
open(INSTALL_VARS, $file_to_open);
while (<INSTALL_VARS>) {
chomp;
$o = $_;
@data = split(":", $o);
print " - " . $data[0] . "\t" . $data[1] . "\n";
}
close (INSTALL_VARS);
close(INSTALL_VARS);
if($OS eq "Windows"){
if ($OS eq "Windows") {
print "[Install] Windows Utility Scripts:\n";
print " - t_start_server.bat Starts EQEmu server with 30 dynamic zones, UCS & Queryserv, dynamic zones\n";
print " - t_start_server_with_loginserver.bat Starts EQEmu server with 30 zones with loginserver\n";
@ -187,7 +192,7 @@ sub show_install_summary_info {
print " - t_database_backup.bat Backs up the Database to backups/ folder - do not run during server is online\n";
print " - t_server_crash_report.pl Will parse any zone crashes for reporting to developers\n";
}
if($OS eq "Linux"){
if ($OS eq "Linux") {
print "[Install] Linux Utility Scripts:\n";
print " - server_start.sh Starts EQEmu server (Quiet) with 30 dynamic zones, UCS & Queryserv, dynamic zones\n";
print " - server_start_dev.sh Starts EQEmu server with 10 dynamic zones, UCS & Queryserv, dynamic zones all verbose\n";
@ -826,6 +831,7 @@ sub show_menu_prompt {
elsif($input eq "setup_bots"){ setup_bots(); $dc = 1; }
elsif($input eq "linux_login_server_setup"){ do_linux_login_server_setup(); $dc = 1; }
elsif($input eq "quest_heading_convert"){ quest_heading_convert(); $dc = 1; }
elsif($input eq "source_peq_db"){ fetch_peq_db_full(); $dc = 1; }
elsif($input eq "exit"){
exit;
}

View File

@ -54,36 +54,39 @@ export apt_options="-y -qq" # Set autoconfirm and silent install
################################################################
read -n1 -r -p "Press any key to continue..." key
if [ ! -f ./install_variables.txt ]; then
#::: Setting up user environment (eqemu)
echo "First, we need to set your passwords..."
echo "Make sure that you remember these and keep them somewhere"
echo ""
echo ""
groupadd eqemu
useradd -g eqemu -d $eqemu_server_directory eqemu
passwd eqemu
read -n1 -r -p "Press any key to continue..." key
#::: Make server directory and go to it
mkdir $eqemu_server_directory
cd $eqemu_server_directory
#::: Setting up user environment (eqemu)
echo "First, we need to set your passwords..."
echo "Make sure that you remember these and keep them somewhere"
echo ""
echo ""
groupadd eqemu
useradd -g eqemu -d $eqemu_server_directory eqemu
passwd eqemu
#::: Setup MySQL root user PW
read -p "Enter MySQL root (Database) password: " eqemu_db_root_password
#::: Make server directory and go to it
mkdir $eqemu_server_directory
cd $eqemu_server_directory
#::: Write install variables (later use)
echo "mysql_root:$eqemu_db_root_password" > install_variables.txt
#::: Setup MySQL root user PW
read -p "Enter MySQL root (Database) password: " eqemu_db_root_password
#::: Setup MySQL server
read -p "Enter Database Name (single word, no special characters, lower case):" eqemu_db_name
read -p "Enter (Database) MySQL EQEmu Server username: " eqemu_db_username
read -p "Enter (Database) MySQL EQEmu Server password: " eqemu_db_password
#::: Write install variables (later use)
echo "mysql_root:$eqemu_db_root_password" > install_variables.txt
#::: Write install variables (later use)
echo "mysql_eqemu_db_name:$eqemu_db_name" >> install_variables.txt
echo "mysql_eqemu_user:$eqemu_db_username" >> install_variables.txt
echo "mysql_eqemu_password:$eqemu_db_password" >> install_variables.txt
#::: Setup MySQL server
read -p "Enter Database Name (single word, no special characters, lower case):" eqemu_db_name
read -p "Enter (Database) MySQL EQEmu Server username: " eqemu_db_username
read -p "Enter (Database) MySQL EQEmu Server password: " eqemu_db_password
#::: Write install variables (later use)
echo "mysql_eqemu_db_name:$eqemu_db_name" >> install_variables.txt
echo "mysql_eqemu_user:$eqemu_db_username" >> install_variables.txt
echo "mysql_eqemu_password:$eqemu_db_password" >> install_variables.txt
fi
if [[ "$OS" == "Debian" ]]; then
# Install pre-req packages

View File

@ -379,8 +379,10 @@
9123|2018_07_07_data_buckets.sql|SHOW TABLES LIKE 'data_buckets'|empty|
9124|2018_07_09_tasks.sql|SHOW COLUMNS FROM `tasks` LIKE 'type'|empty|
9125|2018_07_20_task_emote.sql|SHOW COLUMNS FROM `tasks` LIKE 'completion_emote'|empty|
9126|2018_08_13_inventory_version_update.sql|SHOW TABLES LIKE 'inventory_versions'|empty|
9127|2018_08_13_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `step` = 0|not_empty|
9126|2018_09_07_FastRegen.sql|SHOW COLUMNS FROM `zone` LIKE 'fast_regen_hp'|empty|
9127|2018_09_07_NPCMaxAggroDist.sql|SHOW COLUMNS FROM `zone` LIKE 'npc_max_aggro_dist'|empty|
9128|2018_08_13_inventory_version_update.sql|SHOW TABLES LIKE 'inventory_versions'|empty|
9129|2018_08_13_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `step` = 0|not_empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,3 @@
ALTER TABLE `zone` ADD `fast_regen_hp` INT NOT NULL DEFAULT '180';
ALTER TABLE `zone` ADD `fast_regen_mana` INT NOT NULL DEFAULT '180';
ALTER TABLE `zone` ADD `fast_regen_endurance` INT NOT NULL DEFAULT '180';

View File

@ -0,0 +1 @@
ALTER TABLE `zone` ADD `npc_max_aggro_dist` INT NOT NULL DEFAULT '600';

View File

@ -1,11 +1,10 @@
aa_ability
aa_actions
aa_effects
aa_required_level_cost
aa_ranks
aa_rank_effects
aa_rank_prereqs
task_activities
aa_ranks
aa_required_level_cost
adventure_template
adventure_template_entry
adventure_template_entry_flavor
@ -18,6 +17,7 @@ char_create_combinations
char_create_point_allocations
class_skill
damageshieldtypes
data_buckets
doors
faction_list
faction_list_mod
@ -33,38 +33,39 @@ ground_spawns
horses
instance_list
items
ldon_trap_templates
ldon_trap_entries
ldon_trap_templates
level_exp_mods
logsys_categories
lootdrop
lootdrop_entries
loottable
loottable_entries
merc_armorinfo
merc_weaponinfo
merc_stats
merc_buffs
merc_inventory
merc_merchant_entries
merc_merchant_template_entries
merc_merchant_templates
merc_stance_entries
merc_templates
merc_npc_types
merc_name_types
merc_subtypes
merc_types
merc_npc_types
merc_spell_list_entries
merc_spell_lists
merc_buffs
mercs
merc_inventory
merc_stance_entries
merc_stats
merc_subtypes
merc_templates
merc_types
merc_weaponinfo
merchantlist
mercs
npc_emotes
npc_faction
npc_faction_entries
npc_spells
npc_spells_entries
npc_spells_effects
npc_spells_effects_entries
npc_spells_entries
npc_types
npc_types_metadata
npc_types_tint
@ -77,14 +78,15 @@ races
saylink
skill_caps
spawn2
spawn_conditions
spawn_condition_values
spawn_conditions
spawn_events
spawnentry
spawngroup
spells_new
start_zones
starting_items
task_activities
tasks
tasksets
titles

View File

@ -530,6 +530,28 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S
//uint32 x = 0;
int whomlen = 0;
if (whom) {
// fixes for client converting some queries into a race query instead of zone
if (whom->wrace == 221) {
whom->wrace = 0xFFFF;
strcpy(whom->whom, "scarlet");
}
if (whom->wrace == 327) {
whom->wrace = 0xFFFF;
strcpy(whom->whom, "crystal");
}
if (whom->wrace == 103) {
whom->wrace = 0xFFFF;
strcpy(whom->whom, "kedge");
}
if (whom->wrace == 230) {
whom->wrace = 0xFFFF;
strcpy(whom->whom, "akheva");
}
if (whom->wrace == 229) {
whom->wrace = 0xFFFF;
strcpy(whom->whom, "netherbian");
}
whomlen = strlen(whom->whom);
if(whom->wrace == 0x001A) // 0x001A is the old Froglok race number and is sent by the client for /who all froglok
whom->wrace = FROGLOK; // This is what EQEmu uses for the Froglok Race number.

View File

@ -1295,6 +1295,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
case ServerOP_QSPlayerLogDeletes:
case ServerOP_QSPlayerLogMoves:
case ServerOP_QSPlayerLogMerchantTransactions:
case ServerOP_QSPlayerDropItem:
{
QSLink.SendPacket(pack);
break;

View File

@ -107,16 +107,10 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) {
float iAggroRange = GetAggroRange();
float t1, t2, t3;
t1 = mob->GetX() - GetX();
t2 = mob->GetY() - GetY();
t3 = mob->GetZ() - GetZ();
//Cheap ABS()
if(t1 < 0)
t1 = 0 - t1;
if(t2 < 0)
t2 = 0 - t2;
if(t3 < 0)
t3 = 0 - t3;
t1 = std::abs(mob->GetX() - GetX());
t2 = std::abs(mob->GetY() - GetY());
t3 = std::abs(mob->GetZ() - GetZ());
if(( t1 > iAggroRange)
|| ( t2 > iAggroRange)
|| ( t3 > iAggroRange) ) {
@ -271,16 +265,10 @@ bool Mob::CheckWillAggro(Mob *mob) {
// Image: I moved this up by itself above faction and distance checks because if one of these return true, theres no reason to go through the other information
float t1, t2, t3;
t1 = mob->GetX() - GetX();
t2 = mob->GetY() - GetY();
t3 = mob->GetZ() - GetZ();
//Cheap ABS()
if(t1 < 0)
t1 = 0 - t1;
if(t2 < 0)
t2 = 0 - t2;
if(t3 < 0)
t3 = 0 - t3;
t1 = std::abs(mob->GetX() - GetX());
t2 = std::abs(mob->GetY() - GetY());
t3 = std::abs(mob->GetZ() - GetZ());
if(( t1 > iAggroRange)
|| ( t2 > iAggroRange)
|| ( t3 > iAggroRange)
@ -424,7 +412,7 @@ Mob* EntityList::AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAss
return nullptr;
}
int EntityList::GetHatedCount(Mob *attacker, Mob *exclude)
int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con)
{
// Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
if (!attacker)
@ -434,20 +422,25 @@ int EntityList::GetHatedCount(Mob *attacker, Mob *exclude)
for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
NPC *mob = it->second;
if (!mob || (mob == exclude))
if (!mob || (mob == exclude)) {
continue;
}
if (!mob->IsEngaged())
if (!mob->IsEngaged()) {
continue;
}
if (mob->IsFeared() || mob->IsMezzed())
if (mob->IsFeared() || mob->IsMezzed()) {
continue;
}
if (attacker->GetLevelCon(mob->GetLevel()) == CON_GRAY)
if (!inc_gray_con && attacker->GetLevelCon(mob->GetLevel()) == CON_GRAY) {
continue;
}
if (!mob->CheckAggro(attacker))
if (!mob->CheckAggro(attacker)) {
continue;
}
float AggroRange = mob->GetAggroRange();
@ -455,14 +448,12 @@ int EntityList::GetHatedCount(Mob *attacker, Mob *exclude)
AggroRange *= AggroRange;
if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange)
if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange) {
continue;
}
Count++;
}
return Count;
}
void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
@ -523,7 +514,7 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
Log(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f",
sender->GetName(), attacker->GetName(), mob->GetName(),
attacker->GetName(), DistanceSquared(mob->GetPosition(),
sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ()));
sender->GetPosition()), std::abs(sender->GetZ()+mob->GetZ()));
#endif
mob->AddToHateList(attacker, 25, 0, false);
sender->AddAssistCap();

View File

@ -5455,3 +5455,11 @@ void Mob::DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts)
}
}
}
bool Mob::GetWasSpawnedInWater() const {
return spawned_in_water;
}
void Mob::SetSpawnedInWater(bool spawned_in_water) {
Mob::spawned_in_water = spawned_in_water;
}

View File

@ -7014,9 +7014,9 @@ void Bot::CalcRestState() {
}
}
RestRegenHP = 6 * (GetMaxHP() / RuleI(Character, RestRegenHP));
RestRegenMana = 6 * (GetMaxMana() / RuleI(Character, RestRegenMana));
RestRegenEndurance = 6 * (GetMaxEndurance() / RuleI(Character, RestRegenEnd));
RestRegenHP = 6 * (GetMaxHP() / zone->newzone_data.FastRegenHP);
RestRegenMana = 6 * (GetMaxMana() / zone->newzone_data.FastRegenMana);
RestRegenEndurance = 6 * (GetMaxEndurance() / zone->newzone_data.FastRegenEndurance);
}
int32 Bot::LevelRegen() {

View File

@ -8081,9 +8081,9 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui
{
//The ole switcheroo
if (npc_value[i] > 0)
npc_value[i] = -abs(npc_value[i]);
npc_value[i] = -std::abs(npc_value[i]);
else if (npc_value[i] < 0)
npc_value[i] = abs(npc_value[i]);
npc_value[i] = std::abs(npc_value[i]);
}
// Adjust the amount you can go up or down so the resulting range

View File

@ -876,6 +876,7 @@ public:
void SetStats(uint8 type,int16 set_val);
void IncStats(uint8 type,int16 increase_val);
void DropItem(int16 slot_id, bool recurse = true);
void DropItemQS(EQEmu::ItemInstance* inst, bool pickup);
int GetItemLinkHash(const EQEmu::ItemInstance* inst); // move to ItemData..or make use of the pre-calculated database field

View File

@ -290,9 +290,8 @@ int32 Client::CalcHPRegen(bool bCombat)
// another check for IsClient && !(base + item_regen) && Cur_HP <= 0 do --base; do later
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
auto fast_mod = RuleI(Character, RestRegenHP); // TODO: this is actually zone based
auto max_hp = GetMaxHP();
int fast_regen = 6 * (max_hp / fast_mod);
int fast_regen = 6 * (max_hp / zone->newzone_data.FastRegenHP);
if (base < fast_regen) // weird, but what the client is doing
base = fast_regen;
}
@ -1296,9 +1295,8 @@ int32 Client::CalcManaRegen(bool bCombat)
regen = regen * 100.0f * AreaManaRegen * 0.01f + 0.5f;
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
auto fast_mod = RuleI(Character, RestRegenMana); // TODO: this is actually zone based
auto max_mana = GetMaxMana();
int fast_regen = 6 * (max_mana / fast_mod);
int fast_regen = 6 * (max_mana / zone->newzone_data.FastRegenMana);
if (regen < fast_regen) // weird, but what the client is doing
regen = fast_regen;
}
@ -2264,9 +2262,8 @@ int32 Client::CalcEnduranceRegen(bool bCombat)
int regen = base;
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
auto fast_mod = RuleI(Character, RestRegenEnd); // TODO: this is actually zone based
auto max_end = GetMaxEndurance();
int fast_regen = 6 * (max_end / fast_mod);
int fast_regen = 6 * (max_end / zone->newzone_data.FastRegenEndurance);
if (aa_regen < fast_regen) // weird, but what the client is doing
aa_regen = fast_regen;
}

View File

@ -2950,7 +2950,11 @@ void Client::Handle_OP_Assist(const EQApplicationPacket *app)
Distance(m_Position, assistee->GetPosition()) <= TARGETING_RANGE)) {
SetAssistExemption(true);
eid->entity_id = new_target->GetID();
} else {
eid->entity_id = 0;
}
} else {
eid->entity_id = 0;
}
}

View File

@ -39,6 +39,7 @@
#include "raids.h"
#include "string_ids.h"
#include "worldserver.h"
#include "water_map.h"
#ifdef _WINDOWS
#define snprintf _snprintf
@ -686,6 +687,15 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
}
}
/**
* Set whether NPC was spawned in or out of water
*/
if (zone->HasMap() && zone->HasWaterMap()) {
npc->SetSpawnedInWater(false);
if (zone->watermap->InLiquid(npc->GetPosition())) {
npc->SetSpawnedInWater(true);
}
}
}
void EntityList::AddMerc(Merc *merc, bool SendSpawnPacket, bool dontqueue)
@ -3425,52 +3435,54 @@ void EntityList::ProcessMove(Client *c, const glm::vec3& location)
}
}
void EntityList::ProcessMove(NPC *n, float x, float y, float z)
{
void EntityList::ProcessMove(NPC *n, float x, float y, float z) {
float last_x = n->GetX();
float last_y = n->GetY();
float last_z = n->GetZ();
std::list<quest_proximity_event> events;
for (auto iter = area_list.begin(); iter != area_list.end(); ++iter) {
Area& a = (*iter);
Area &a = (*iter);
bool old_in = true;
bool new_in = true;
if (last_x < a.min_x || last_x > a.max_x ||
last_y < a.min_y || last_y > a.max_y ||
last_z < a.min_z || last_z > a.max_z) {
last_y < a.min_y || last_y > a.max_y ||
last_z < a.min_z || last_z > a.max_z) {
old_in = false;
}
if (x < a.min_x || x > a.max_x ||
y < a.min_y || y > a.max_y ||
z < a.min_z || z > a.max_z) {
y < a.min_y || y > a.max_y ||
z < a.min_z || z > a.max_z) {
new_in = false;
}
if (old_in && !new_in) {
//were in but are no longer.
quest_proximity_event evt;
evt.event_id = EVENT_LEAVE_AREA;
evt.client = nullptr;
evt.npc = n;
evt.area_id = a.id;
evt.event_id = EVENT_LEAVE_AREA;
evt.client = nullptr;
evt.npc = n;
evt.area_id = a.id;
evt.area_type = a.type;
events.push_back(evt);
} else if (!old_in && new_in) {
}
else if (!old_in && new_in) {
//were not in but now are
quest_proximity_event evt;
evt.event_id = EVENT_ENTER_AREA;
evt.client = nullptr;
evt.npc = n;
evt.area_id = a.id;
evt.event_id = EVENT_ENTER_AREA;
evt.client = nullptr;
evt.npc = n;
evt.area_id = a.id;
evt.area_type = a.type;
events.push_back(evt);
}
}
for (auto iter = events.begin(); iter != events.end(); ++iter) {
quest_proximity_event& evt = (*iter);
quest_proximity_event &evt = (*iter);
std::vector<EQEmu::Any> args;
args.push_back(&evt.area_id);
args.push_back(&evt.area_type);

View File

@ -416,7 +416,7 @@ public:
void CheckClientAggro(Client *around);
Mob* AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange);
int GetHatedCount(Mob *attacker, Mob *exclude);
int GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con);
void AIYellForHelp(Mob* sender, Mob* attacker);
bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes);
bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes);

View File

@ -31,87 +31,117 @@ extern Zone* zone;
//this is called whenever we are damaged to process possible fleeing
void Mob::CheckFlee() {
//if were allready fleeing, dont need to check more...
if(flee_mode && currently_fleeing)
// if mob is dead why would you run?
if(GetHP() == 0) {
return;
}
// if were already fleeing, don't need to check more...
if(flee_mode && currently_fleeing) {
return;
}
//dont bother if we are immune to fleeing
if(GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee)
if(GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) {
return;
}
if(!flee_timer.Check())
return; //only do all this stuff every little while, since
//its not essential that we start running RIGHT away
//see if were possibly hurt enough
float ratio = GetHPRatio();
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
if(ratio >= fleeratio)
// Check if Flee Timer is cleared
if(!flee_timer.Check()) {
return;
}
//we might be hurt enough, check con now..
int hpratio = GetIntHPRatio();
int fleeratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists
Mob *hate_top = GetHateTop();
// Sanity Check for race conditions
if(hate_top == nullptr) {
return;
}
// If no special flee_percent check for Gray or Other con rates
if(GetLevelCon(hate_top->GetLevel(), GetLevel()) == CON_GRAY && fleeratio == 0 && RuleB(Combat, FleeGray)) {
fleeratio = RuleI(Combat, FleeGrayHPRatio);
} else if(fleeratio == 0) {
fleeratio = RuleI(Combat, FleeHPRatio );
}
// Mob does not have low enough health to flee
if(hpratio >= fleeratio) {
return;
}
// Sanity Check this should never happen...
if(!hate_top) {
//this should never happen...
StartFleeing();
return;
}
float other_ratio = hate_top->GetHPRatio();
int other_ratio = hate_top->GetIntHPRatio();
// If the Client is nearing death the NPC will not flee and instead try to kill the client.
if(other_ratio < 20) {
//our hate top is almost dead too... stay and fight
return;
}
//base our flee ratio on our con. this is how the
//attacker sees the mob, since this is all we can observe
// Flee Chance checking based on con.
uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel());
float run_ratio;
int flee_chance;
switch(con) {
//these values are not 100% researched
case CON_GRAY:
run_ratio = fleeratio;
flee_chance = 100;
break;
case CON_GREEN:
run_ratio = fleeratio * 9 / 10;
flee_chance = 90;
break;
case CON_LIGHTBLUE:
run_ratio = fleeratio * 9 / 10;
flee_chance = 90;
break;
case CON_BLUE:
run_ratio = fleeratio * 8 / 10;
flee_chance = 80;
break;
default:
run_ratio = fleeratio * 7 / 10;
flee_chance = 70;
break;
}
if(ratio < run_ratio)
{
if (RuleB(Combat, FleeIfNotAlone) ||
GetSpecialAbility(ALWAYS_FLEE) ||
(!RuleB(Combat, FleeIfNotAlone) && (entity_list.GetHatedCount(hate_top, this) == 0)))
StartFleeing();
// If we got here we are allowed to roll on flee chance if there is not other hated NPC's in the area.
if(RuleB(Combat, FleeIfNotAlone) || GetSpecialAbility(ALWAYS_FLEE) || zone->random.Roll(flee_chance) && entity_list.GetHatedCount(hate_top, this, true) == 0) {
currently_fleeing = true;
StartFleeing();
}
}
void Mob::ProcessFlee()
{
//Stop fleeing if effect is applied after they start to run.
//When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee.
if (flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) &&
!spellbonuses.IsFeared && !spellbonuses.IsBlind) {
!spellbonuses.IsFeared && !spellbonuses.IsBlind) {
currently_fleeing = false;
return;
}
//see if we are still dying, if so, do nothing
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
if (GetHPRatio() < fleeratio)
int hpratio = GetIntHPRatio();
int fleeratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists
Mob *hate_top = GetHateTop();
// If no special flee_percent check for Gray or Other con rates
if(hate_top != nullptr && GetLevelCon(hate_top->GetLevel(), GetLevel()) == CON_GRAY && fleeratio == 0 && RuleB(Combat, FleeGray)) {
fleeratio = RuleI(Combat, FleeGrayHPRatio);
} else if(fleeratio == 0) {
fleeratio = RuleI(Combat, FleeHPRatio );
}
// Mob is still too low. Keep Running
if(hpratio < fleeratio) {
return;
}
//we are not dying anymore... see what we do next
@ -125,42 +155,43 @@ void Mob::ProcessFlee()
}
}
void Mob::CalculateNewFearpoint()
{
if(RuleB(Pathing, Fear) && zone->pathing)
{
void Mob::CalculateNewFearpoint() {
if (RuleB(Pathing, Fear) && zone->pathing) {
auto Node = zone->pathing->GetRandomLocation();
if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) {
++Node.z;
m_FearWalkTarget = Node;
m_FearWalkTarget = Node;
currently_fleeing = true;
return;
}
Log(Logs::Detail, Logs::None, "No path found to selected node. Falling through to old fear point selection.");
Log(Logs::Detail,
Logs::Pathing,
"No path found to selected node. Falling through to old fear point selection.");
}
int loop = 0;
int loop = 0;
float ranx, rany, ranz;
currently_fleeing = true;
while (loop < 100) //Max 100 tries
{
int ran = 250 - (loop*2);
int ran = 250 - (loop * 2);
loop++;
ranx = GetX()+zone->random.Int(0, ran-1)-zone->random.Int(0, ran-1);
rany = GetY()+zone->random.Int(0, ran-1)-zone->random.Int(0, ran-1);
ranz = FindGroundZ(ranx,rany);
ranx = GetX() + zone->random.Int(0, ran - 1) - zone->random.Int(0, ran - 1);
rany = GetY() + zone->random.Int(0, ran - 1) - zone->random.Int(0, ran - 1);
ranz = FindGroundZ(ranx, rany);
if (ranz == BEST_Z_INVALID)
continue;
float fdist = ranz - GetZ();
if (fdist >= -12 && fdist <= 12 && CheckCoordLosNoZLeaps(GetX(),GetY(),GetZ(),ranx,rany,ranz))
{
if (fdist >= -12 && fdist <= 12 && CheckCoordLosNoZLeaps(GetX(), GetY(), GetZ(), ranx, rany, ranz)) {
break;
}
}
if (currently_fleeing)
m_FearWalkTarget = glm::vec3(ranx, rany, ranz);
m_FearWalkTarget = glm::vec3(ranx, rany, ranz);
}

View File

@ -575,12 +575,19 @@ int HateList::AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOption
if (!target || !caster)
return 0;
// tank will be hit ONLY if they are the only target on the hate list
// if there is anyone else on the hate list, the tank will not be hit, even if those others aren't hit either
if (list.size() == 1) {
caster->ProcessAttackRounds(target, opts);
return 1;
}
int hit_count = 0;
// This should prevent crashes if something dies (or mainly more than 1 thing goes away)
// This is a temp solution until the hate lists can be rewritten to not have that issue
std::vector<uint16> id_list;
for (auto &h : list) {
if (h->entity_on_hatelist && h->entity_on_hatelist != caster &&
if (h->entity_on_hatelist && h->entity_on_hatelist != caster && h->entity_on_hatelist != target &&
caster->CombatRange(h->entity_on_hatelist))
id_list.push_back(h->entity_on_hatelist->GetID());
if (count != -1 && id_list.size() > count)

View File

@ -596,15 +596,15 @@ void Client::DropItem(int16 slot_id, bool recurse)
if (LogSys.log_settings[Logs::Inventory].is_category_enabled) {
Log(Logs::General, Logs::Inventory, "DropItem() Hack detected - full item parse:");
Log(Logs::General, Logs::Inventory, "depth: 0, Item: '%s' (id: %u), IsDroppable: %s",
(invalid_drop->GetItem() ? invalid_drop->GetItem()->Name : "null data"), invalid_drop->GetID(), invalid_drop->IsDroppable(false));
(invalid_drop->GetItem() ? invalid_drop->GetItem()->Name : "null data"), invalid_drop->GetID(), (invalid_drop->IsDroppable(false) ? "true" : "false"));
for (auto iter1 : *invalid_drop->GetContents()) { // depth 1
Log(Logs::General, Logs::Inventory, "-depth: 1, Item: '%s' (id: %u), IsDroppable: %s",
(iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"), iter1.second->GetID(), iter1.second->IsDroppable(false));
(iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"), iter1.second->GetID(), (iter1.second->IsDroppable(false) ? "true" : "false"));
for (auto iter2 : *iter1.second->GetContents()) { // depth 2
Log(Logs::General, Logs::Inventory, "--depth: 2, Item: '%s' (id: %u), IsDroppable: %s",
(iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"), iter2.second->GetID(), iter2.second->IsDroppable(false));
(iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"), iter2.second->GetID(), (iter2.second->IsDroppable(false) ? "true" : "false"));
}
}
}
@ -622,21 +622,21 @@ void Client::DropItem(int16 slot_id, bool recurse)
if (LogSys.log_settings[Logs::Inventory].is_category_enabled) {
Log(Logs::General, Logs::Inventory, "DropItem() Processing - full item parse:");
Log(Logs::General, Logs::Inventory, "depth: 0, Item: '%s' (id: %u), IsDroppable: %s",
(inst->GetItem() ? inst->GetItem()->Name : "null data"), inst->GetID(), inst->IsDroppable(false));
(inst->GetItem() ? inst->GetItem()->Name : "null data"), inst->GetID(), (inst->IsDroppable(false) ? "true" : "false"));
if (!inst->IsDroppable(false))
Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName());
for (auto iter1 : *inst->GetContents()) { // depth 1
Log(Logs::General, Logs::Inventory, "-depth: 1, Item: '%s' (id: %u), IsDroppable: %s",
(iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"), iter1.second->GetID(), iter1.second->IsDroppable(false));
(iter1.second->GetItem() ? iter1.second->GetItem()->Name : "null data"), iter1.second->GetID(), (iter1.second->IsDroppable(false) ? "true" : "false"));
if (!iter1.second->IsDroppable(false))
Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName());
for (auto iter2 : *iter1.second->GetContents()) { // depth 2
Log(Logs::General, Logs::Inventory, "--depth: 2, Item: '%s' (id: %u), IsDroppable: %s",
(iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"), iter2.second->GetID(), iter2.second->IsDroppable(false));
(iter2.second->GetItem() ? iter2.second->GetItem()->Name : "null data"), iter2.second->GetID(), (iter2.second->IsDroppable(false) ? "true" : "false"));
if (!iter2.second->IsDroppable(false))
Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName());
@ -674,10 +674,79 @@ void Client::DropItem(int16 slot_id, bool recurse)
object->StartDecay();
Log(Logs::General, Logs::Inventory, "Item drop handled ut assolet");
DropItemQS(inst, false);
safe_delete(inst);
}
void Client::DropItemQS(EQEmu::ItemInstance* inst, bool pickup) {
if (RuleB(QueryServ, PlayerDropItems)) {
QSPlayerDropItem_Struct qs_audit;
std::list<void*> event_details;
memset(&qs_audit, 0, sizeof(QSPlayerDropItem_Struct));
qs_audit.char_id = this->character_id;
qs_audit.pickup = pickup;
qs_audit.zone_id = this->GetZoneID();
qs_audit.x = (int) this->GetX();
qs_audit.y = (int) this->GetY();
qs_audit.z = (int) this->GetZ();
if (inst) {
auto detail = new QSDropItems_Struct;
detail->item_id = inst->GetID();
detail->charges = inst->IsClassBag() ? 1 : inst->GetCharges();
detail->aug_1 = inst->GetAugmentItemID(1);
detail->aug_2 = inst->GetAugmentItemID(2);
detail->aug_3 = inst->GetAugmentItemID(3);
detail->aug_4 = inst->GetAugmentItemID(4);
detail->aug_5 = inst->GetAugmentItemID(5);
event_details.push_back(detail);
if (inst->IsClassBag()) {
for (uint8 sub_slot = EQEmu::invbag::SLOT_BEGIN; (sub_slot <= EQEmu::invbag::SLOT_END); ++sub_slot) { // this is to catch ALL items
const EQEmu::ItemInstance* bag_inst = inst->GetItem(sub_slot);
if (bag_inst) {
detail = new QSDropItems_Struct;
detail->item_id = bag_inst->GetID();
detail->charges = (!bag_inst->IsStackable() ? 1 : bag_inst->GetCharges());
detail->aug_1 = bag_inst->GetAugmentItemID(1);
detail->aug_2 = bag_inst->GetAugmentItemID(2);
detail->aug_3 = bag_inst->GetAugmentItemID(3);
detail->aug_4 = bag_inst->GetAugmentItemID(4);
detail->aug_5 = bag_inst->GetAugmentItemID(5);
event_details.push_back(detail);
}
}
}
}
qs_audit._detail_count = event_details.size();
auto qs_pack = new ServerPacket(
ServerOP_QSPlayerDropItem,
sizeof(QSPlayerDropItem_Struct) +
(sizeof(QSDropItems_Struct) * qs_audit._detail_count));
QSPlayerDropItem_Struct* qs_buf = (QSPlayerDropItem_Struct*) qs_pack->pBuffer;
memcpy(qs_buf, &qs_audit, sizeof(QSPlayerDropItem_Struct));
int offset = 0;
for (auto iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) {
QSDropItems_Struct* detail = reinterpret_cast<QSDropItems_Struct*>(*iter);
qs_buf->items[offset] = *detail;
safe_delete(detail);
}
event_details.clear();
if (worldserver.Connected())
worldserver.SendPacket(qs_pack);
safe_delete(qs_pack);
}
}
// Drop inst
void Client::DropInst(const EQEmu::ItemInstance* inst)
{

View File

@ -32,12 +32,12 @@ int Lua_Raid::RaidCount() {
return self->RaidCount();
}
uint32 Lua_Raid::GetGroup(const char *c) {
int Lua_Raid::GetGroup(const char *c) {
Lua_Safe_Call_Int();
return self->GetGroup(c);
}
uint32 Lua_Raid::GetGroup(Lua_Client c) {
int Lua_Raid::GetGroup(Lua_Client c) {
Lua_Safe_Call_Int();
return self->GetGroup(c);
}
@ -122,6 +122,16 @@ Lua_Client Lua_Raid::GetMember(int index) {
return self->members[index].member;
}
int Lua_Raid::GetGroupNumber(int index) {
Lua_Safe_Call_Int();
if(index >= 72 || index < 0 || self->members[index].GroupNumber == RAID_GROUPLESS) {
return -1;
}
return self->members[index].GroupNumber;
}
luabind::scope lua_register_raid() {
return luabind::class_<Lua_Raid>("Raid")
@ -132,7 +142,8 @@ luabind::scope lua_register_raid() {
.def("CastGroupSpell", (void(Lua_Raid::*)(Lua_Mob,int,uint32))&Lua_Raid::CastGroupSpell)
.def("GroupCount", (int(Lua_Raid::*)(uint32))&Lua_Raid::GroupCount)
.def("RaidCount", (int(Lua_Raid::*)(void))&Lua_Raid::RaidCount)
.def("GetGroup", (uint32(Lua_Raid::*)(const char*))&Lua_Raid::GetGroup)
.def("GetGroup", (int(Lua_Raid::*)(const char*))&Lua_Raid::GetGroup)
.def("GetGroup", (int(Lua_Raid::*)(Lua_Client))&Lua_Raid::GetGroup)
.def("SplitExp", (void(Lua_Raid::*)(uint32,Lua_Mob))&Lua_Raid::SplitExp)
.def("GetTotalRaidDamage", (uint32(Lua_Raid::*)(Lua_Mob))&Lua_Raid::GetTotalRaidDamage)
.def("SplitMoney", (void(Lua_Raid::*)(uint32,uint32,uint32,uint32))&Lua_Raid::SplitMoney)
@ -146,7 +157,8 @@ luabind::scope lua_register_raid() {
.def("TeleportGroup", (int(Lua_Raid::*)(Lua_Mob,uint32,uint32,float,float,float,float,uint32))&Lua_Raid::TeleportGroup)
.def("TeleportRaid", (int(Lua_Raid::*)(Lua_Mob,uint32,uint32,float,float,float,float))&Lua_Raid::TeleportRaid)
.def("GetID", (int(Lua_Raid::*)(void))&Lua_Raid::GetID)
.def("GetMember", (Lua_Client(Lua_Raid::*)(int))&Lua_Raid::GetMember);
.def("GetMember", (Lua_Client(Lua_Raid::*)(int))&Lua_Raid::GetMember)
.def("GetGroupNumber", (int(Lua_Raid::*)(int))&Lua_Raid::GetGroupNumber);
}
#endif

View File

@ -30,8 +30,8 @@ public:
void CastGroupSpell(Lua_Mob caster, int spell_id, uint32 group_id);
int GroupCount(uint32 group_id);
int RaidCount();
uint32 GetGroup(const char *c);
uint32 GetGroup(Lua_Client c);
int GetGroup(const char *c);
int GetGroup(Lua_Client c);
void SplitExp(uint32 exp, Lua_Mob other);
uint32 GetTotalRaidDamage(Lua_Mob other);
void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum);
@ -47,6 +47,7 @@ public:
void TeleportRaid(Lua_Mob sender, uint32 zone_id, uint32 instance_id, float x, float y, float z, float h);
int GetID();
Lua_Client GetMember(int index);
int GetGroupNumber(int index);
};
#endif

View File

@ -1168,11 +1168,11 @@ void Merc::CalcRestState() {
}
}
RestRegenHP = 6 * (GetMaxHP() / RuleI(Character, RestRegenHP));
RestRegenHP = 6 * (GetMaxHP() / zone->newzone_data.FastRegenHP);
RestRegenMana = 6 * (GetMaxMana() / RuleI(Character, RestRegenMana));
RestRegenMana = 6 * (GetMaxMana() / zone->newzone_data.FastRegenMana);
RestRegenEndurance = 6 * (GetMaxEndurance() / RuleI(Character, RestRegenEnd));
RestRegenEndurance = 6 * (GetMaxEndurance() / zone->newzone_data.FastRegenEndurance);
}
bool Merc::HasSkill(EQEmu::skills::SkillType skill_id) const {
@ -1408,7 +1408,7 @@ void Merc::AI_Process() {
if(DivineAura())
return;
int hateCount = entity_list.GetHatedCount(this, nullptr);
int hateCount = entity_list.GetHatedCount(this, nullptr, false);
if(GetHatedCount() < hateCount) {
SetHatedCount(hateCount);
@ -1475,8 +1475,14 @@ void Merc::AI_Process() {
if (RuleB(Mercs, MercsUsePathing) && zone->pathing) {
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
GetRunspeed(), WaypointChanged, NodeReached);
glm::vec3 Goal = UpdatePath(
GetTarget()->GetX(),
GetTarget()->GetY(),
GetTarget()->GetZ(),
GetRunspeed(),
WaypointChanged,
NodeReached
);
if (WaypointChanged)
tar_ndx = 20;
@ -1582,7 +1588,7 @@ void Merc::AI_Process() {
}
}
if(IsMoving())
if (IsMoving())
SendPositionUpdate();
else
SendPosition();

View File

@ -981,15 +981,15 @@ public:
inline bool CheckAggro(Mob* other) {return hate_list.IsEntOnHateList(other);}
float CalculateHeadingToTarget(float in_x, float in_y) { return HeadingAngleToMob(in_x, in_y); }
virtual bool CalculateNewPosition(float x, float y, float z, int speed, bool checkZ = true, bool calcheading = true);
virtual bool CalculateNewPosition(float x, float y, float z, float speed, bool check_z = true, bool calculate_heading = true);
float CalculateDistance(float x, float y, float z);
float GetGroundZ(float new_x, float new_y, float z_offset=0.0);
void SendTo(float new_x, float new_y, float new_z);
void SendToFixZ(float new_x, float new_y, float new_z);
float GetZOffset() const;
float GetDefaultRaceSize() const;
void FixZ(int32 z_find_offset = 5);
float GetFixedZ(glm::vec3 position, int32 z_find_offset = 5);
void FixZ(int32 z_find_offset = 5, bool fix_client_z = false);
float GetFixedZ(glm::vec3 destination, int32 z_find_offset = 5);
void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false);
inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; }
@ -1165,7 +1165,7 @@ protected:
int _GetWalkSpeed() const;
int _GetRunSpeed() const;
int _GetFearSpeed() const;
virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ = true, bool calcHeading = true);
virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool check_z = true, bool calculate_heading = true);
virtual bool AI_EngagedCastCheck() { return(false); }
virtual bool AI_PursueCastCheck() { return(false); }
@ -1414,6 +1414,13 @@ protected:
bool pseudo_rooted;
bool endur_upkeep;
bool degenerating_effects; // true if we have a buff that needs to be recalced every tick
bool spawned_in_water;
public:
bool GetWasSpawnedInWater() const;
void SetSpawnedInWater(bool spawned_in_water);
protected:
// Bind wound
Timer bindwound_timer;
@ -1443,7 +1450,7 @@ protected:
std::unique_ptr<Timer> AI_feign_remember_timer;
std::unique_ptr<Timer> AI_check_signal_timer;
std::unique_ptr<Timer> AI_scan_door_open_timer;
uint32 pLastFightingDelayMoving;
uint32 time_until_can_move;
HateList hate_list;
std::set<uint32> feign_memory_list;
// This is to keep track of mobs we cast faction mod spells on

File diff suppressed because it is too large Load Diff

View File

@ -242,8 +242,8 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
roambox_max_y = -2;
roambox_min_x = -2;
roambox_min_y = -2;
roambox_movingto_x = -2;
roambox_movingto_y = -2;
roambox_destination_x = -2;
roambox_destination_y = -2;
roambox_min_delay = 1000;
roambox_delay = 1000;
p_depop = false;

View File

@ -311,9 +311,17 @@ public:
void SaveGuardSpot(bool iClearGuardSpot = false);
inline bool IsGuarding() const { return(m_GuardPoint.w != 0); }
void SaveGuardSpotCharm();
void RestoreGuardSpotCharm();
void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500, uint32 iMinDelay = 2500);
void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500, uint32 iMinDelay = 2500);
void RestoreGuardSpotCharm();
void AI_SetRoambox(
float max_distance,
float roam_distance_variance,
uint32 delay = 2500,
uint32 min_delay = 2500
);
void AI_SetRoambox(float distance, float max_x, float min_x, float max_y, float min_y, uint32 delay = 2500, uint32 min_delay = 2500);
//mercenary stuff
void LoadMercTypes();
@ -530,8 +538,9 @@ protected:
float roambox_min_x;
float roambox_min_y;
float roambox_distance;
float roambox_movingto_x;
float roambox_movingto_y;
float roambox_destination_x;
float roambox_destination_y;
float roambox_destination_z;
uint32 roambox_delay;
uint32 roambox_min_delay;

View File

@ -528,6 +528,8 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object)
if(cursordelete) // delete the item if it's a duplicate lore. We have to do this because the client expects the item packet
sender->DeleteItemInInventory(EQEmu::invslot::slotCursor);
sender->DropItemQS(m_inst, true);
if(!m_ground_spawn)
safe_delete(m_inst);

View File

@ -141,118 +141,181 @@ uint32 Spawn2::despawnTimer(uint32 despawn_timer)
bool Spawn2::Process() {
IsDespawned = false;
if(!Enabled())
if (!Enabled())
return true;
//grab our spawn group
SpawnGroup* sg = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_);
SpawnGroup *spawn_group = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_);
if(NPCPointerValid() && (sg->despawn == 0 || condition_id != 0))
if (NPCPointerValid() && (spawn_group->despawn == 0 || condition_id != 0)) {
return true;
}
if (timer.Check()) {
if (timer.Check()) {
timer.Disable();
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Timer has triggered", spawn2_id);
//first check our spawn condition, if this isnt active
//then we reset the timer and try again next time.
if(condition_id != SC_AlwaysEnabled
if (condition_id != SC_AlwaysEnabled
&& !zone->spawn_conditions.Check(condition_id, condition_min_value)) {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: spawning prevented by spawn condition %d", spawn2_id, condition_id);
Log(Logs::Detail,
Logs::Spawns,
"Spawn2 %d: spawning prevented by spawn condition %d",
spawn2_id,
condition_id);
Reset();
return(true);
return (true);
}
if (sg == nullptr) {
database.LoadSpawnGroupsByID(spawngroup_id_,&zone->spawn_group_list);
sg = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_);
if (spawn_group == nullptr) {
database.LoadSpawnGroupsByID(spawngroup_id_, &zone->spawn_group_list);
spawn_group = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_);
}
if (sg == nullptr) {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Unable to locate spawn group %d. Disabling.", spawn2_id, spawngroup_id_);
if (spawn_group == nullptr) {
Log(Logs::Detail,
Logs::Spawns,
"Spawn2 %d: Unable to locate spawn group %d. Disabling.",
spawn2_id,
spawngroup_id_);
return false;
}
//have the spawn group pick an NPC for us
uint32 npcid = sg->GetNPCType();
uint32 npcid = spawn_group->GetNPCType();
if (npcid == 0) {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn group %d did not yeild an NPC! not spawning.", spawn2_id, spawngroup_id_);
Reset(); //try again later (why?)
return(true);
Log(Logs::Detail,
Logs::Spawns,
"Spawn2 %d: Spawn group %d did not yeild an NPC! not spawning.",
spawn2_id,
spawngroup_id_);
Reset(); //try again later (why?)
return (true);
}
//try to find our NPC type.
const NPCType* tmp = database.LoadNPCTypesData(npcid);
const NPCType *tmp = database.LoadNPCTypesData(npcid);
if (tmp == nullptr) {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn group %d yeilded an invalid NPC type %d", spawn2_id, spawngroup_id_, npcid);
Reset(); //try again later
return(true);
Log(Logs::Detail,
Logs::Spawns,
"Spawn2 %d: Spawn group %d yeilded an invalid NPC type %d",
spawn2_id,
spawngroup_id_,
npcid);
Reset(); //try again later
return (true);
}
if(tmp->unique_spawn_by_name)
{
if(!entity_list.LimitCheckName(tmp->name))
{
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is unique and one already exists.", spawn2_id, spawngroup_id_, npcid);
timer.Start(5000); //try again in five seconds.
return(true);
if (tmp->unique_spawn_by_name) {
if (!entity_list.LimitCheckName(tmp->name)) {
Log(Logs::Detail,
Logs::Spawns,
"Spawn2 %d: Spawn group %d yeilded NPC type %d, which is unique and one already exists.",
spawn2_id,
spawngroup_id_,
npcid);
timer.Start(5000); //try again in five seconds.
return (true);
}
}
if(tmp->spawn_limit > 0) {
if(!entity_list.LimitCheckType(npcid, tmp->spawn_limit)) {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn group %d yeilded NPC type %d, which is over its spawn limit (%d)", spawn2_id, spawngroup_id_, npcid, tmp->spawn_limit);
timer.Start(5000); //try again in five seconds.
return(true);
if (tmp->spawn_limit > 0) {
if (!entity_list.LimitCheckType(npcid, tmp->spawn_limit)) {
Log(Logs::Detail,
Logs::Spawns,
"Spawn2 %d: Spawn group %d yeilded NPC type %d, which is over its spawn limit (%d)",
spawn2_id,
spawngroup_id_,
npcid,
tmp->spawn_limit);
timer.Start(5000); //try again in five seconds.
return (true);
}
}
bool ignore_despawn = false;
if (npcthis)
{
if (npcthis) {
ignore_despawn = npcthis->IgnoreDespawn();
}
if (ignore_despawn)
{
if (ignore_despawn) {
return true;
}
if (sg->despawn != 0 && condition_id == 0 && !ignore_despawn)
{
if (spawn_group->despawn != 0 && condition_id == 0 && !ignore_despawn) {
zone->Despawn(spawn2_id);
}
if (IsDespawned)
{
if (IsDespawned) {
return true;
}
currentnpcid = npcid;
NPC* npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), FlyMode3);
NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), FlyMode3);
npc->mod_prespawn(this);
npcthis = npc;
npc->AddLootTable();
if (npc->DropsGlobalLoot())
if (npc->DropsGlobalLoot()) {
npc->CheckGlobalLootTables();
}
npc->SetSp2(spawngroup_id_);
npc->SaveGuardPointAnim(anim);
npc->SetAppearance((EmuAppearance)anim);
npc->SetAppearance((EmuAppearance) anim);
entity_list.AddNPC(npc);
//this limit add must be done after the AddNPC since we need the entity ID.
entity_list.LimitAddNPC(npc);
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay && sg->min_delay)
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay,sg->min_delay);
if(zone->InstantGrids()) {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).", spawn2_id, spawngroup_id_, npc->GetName(), npcid, x, y, z);
/**
* Roambox init
*/
if (spawn_group->roamdist && spawn_group->roambox[0] && spawn_group->roambox[1] && spawn_group->roambox[2] &&
spawn_group->roambox[3] && spawn_group->delay && spawn_group->min_delay) {
npc->AI_SetRoambox(
spawn_group->roamdist,
spawn_group->roambox[0],
spawn_group->roambox[1],
spawn_group->roambox[2],
spawn_group->roambox[3],
spawn_group->delay,
spawn_group->min_delay
);
}
if (zone->InstantGrids()) {
Log(Logs::Detail,
Logs::Spawns,
"Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).",
spawn2_id,
spawngroup_id_,
npc->GetName(),
npcid,
x,
y,
z);
LoadGrid();
} else {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f). Grid loading delayed.", spawn2_id, spawngroup_id_, tmp->name, npcid, x, y, z);
}
else {
Log(Logs::Detail,
Logs::Spawns,
"Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f). Grid loading delayed.",
spawn2_id,
spawngroup_id_,
tmp->name,
npcid,
x,
y,
z);
}
}
return true;
}
@ -266,11 +329,11 @@ void Spawn2::Disable()
}
void Spawn2::LoadGrid() {
if(!npcthis)
if (!npcthis)
return;
if(grid_ < 1)
if (grid_ < 1)
return;
if(!entity_list.IsMobInZone(npcthis))
if (!entity_list.IsMobInZone(npcthis))
return;
//dont set an NPC's grid until its loaded for them.
npcthis->SetGrid(grid_);
@ -278,7 +341,6 @@ void Spawn2::LoadGrid() {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Loading grid %d for %s", spawn2_id, grid_, npcthis->GetName());
}
/*
All three of these actions basically say that the mob which was
associated with this spawn point is no longer relavent.

View File

@ -3928,6 +3928,12 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
Mob* tempmob = GetOwner();
SetOwnerID(0);
SetPetType(petNone);
SetHeld(false);
SetGHeld(false);
SetNoCast(false);
SetFocused(false);
SetPetStop(false);
SetPetRegroup(false);
if(tempmob)
{
tempmob->SetPet(0);

View File

@ -4247,6 +4247,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
{
Log(Logs::Detail, Logs::Spells, "Our level (%d) is higher than the limit of this Mez spell (%d)", GetLevel(), spells[spell_id].max[effect_index]);
caster->Message_StringID(MT_Shout, CANNOT_MEZ_WITH_SPELL);
AddToHateList(caster, 1,0,true,false,false,spell_id);
return true;
}
}
@ -4338,6 +4339,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
{
Log(Logs::Detail, Logs::Spells, "Our level (%d) is higher than the limit of this Charm spell (%d)", GetLevel(), spells[spell_id].max[effect_index]);
caster->Message_StringID(MT_Shout, CANNOT_CHARM_YET);
AddToHateList(caster, 1,0,true,false,false,spell_id);
return true;
}
}

View File

@ -30,7 +30,7 @@ bool WaterMapV2::InLava(const glm::vec3& location) const {
}
bool WaterMapV2::InLiquid(const glm::vec3& location) const {
return InWater(location) || InLava(location);
return InWater(location) || InLava(location) || InVWater(location);
}
bool WaterMapV2::InPvP(const glm::vec3& location) const {

View File

@ -42,19 +42,27 @@ struct wp_distance
int index;
};
void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay, uint32 iMinDelay) {
AI_SetRoambox(iDist, GetX() + iRoamDist, GetX() - iRoamDist, GetY() + iRoamDist, GetY() - iRoamDist, iDelay, iMinDelay);
void NPC::AI_SetRoambox(float max_distance, float roam_distance_variance, uint32 delay, uint32 min_delay) {
AI_SetRoambox(
max_distance,
GetX() + roam_distance_variance,
GetX() - roam_distance_variance,
GetY() + roam_distance_variance,
GetY() - roam_distance_variance,
delay,
min_delay
);
}
void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay, uint32 iMinDelay) {
roambox_distance = iDist;
roambox_max_x = iMaxX;
roambox_min_x = iMinX;
roambox_max_y = iMaxY;
roambox_min_y = iMinY;
roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc
roambox_delay = iDelay;
roambox_min_delay = iMinDelay;
void NPC::AI_SetRoambox(float distance, float max_x, float min_x, float max_y, float min_y, uint32 delay, 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;
}
void NPC::DisplayWaypointInfo(Client *c) {
@ -199,7 +207,7 @@ void NPC::MoveTo(const glm::vec4& position, bool saveguardspot)
}
cur_wp_pause = 0;
pLastFightingDelayMoving = 0;
time_until_can_move = 0;
if (AI_walking_timer->Enabled())
AI_walking_timer->Start(100);
}
@ -438,22 +446,16 @@ float Mob::CalculateDistance(float x, float y, float z) {
return (float)sqrtf(((m_Position.x - x)*(m_Position.x - x)) + ((m_Position.y - y)*(m_Position.y - y)) + ((m_Position.z - z)*(m_Position.z - z)));
}
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ, bool calcHeading) {
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool check_z, bool calculate_heading) {
if (GetID() == 0)
return true;
if (speed <= 0)
{
if (speed <= 0) {
SetCurrentSpeed(0);
return true;
}
if ((m_Position.x - x == 0) && (m_Position.y - y == 0)) {//spawn is at target coords
if (m_Position.z - z != 0) {
m_Position.z = z;
Log(Logs::Detail, Logs::AI, "Calc Position2 (%.3f, %.3f, %.3f): Jumping pure Z.", x, y, z);
return true;
}
if ((m_Position.x - x == 0) && (m_Position.y - y == 0)) { //spawn is at target coords
return false;
}
else if ((std::abs(m_Position.x - x) < 0.1) && (std::abs(m_Position.y - y) < 0.1)) {
@ -464,16 +466,17 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Position.x = x;
m_Position.y = y;
m_Position.z = z;
return true;
}
bool send_update = false;
int compare_steps = 20;
if (tar_ndx < compare_steps && m_TargetLocation.x == x && m_TargetLocation.y == y) {
float new_x = m_Position.x + m_TargetV.x*tar_vector;
float new_y = m_Position.y + m_TargetV.y*tar_vector;
float new_z = m_Position.z + m_TargetV.z*tar_vector;
float new_x = m_Position.x + m_TargetV.x * tar_vector;
float new_y = m_Position.y + m_TargetV.y * tar_vector;
float new_z = m_Position.z + m_TargetV.z * tar_vector;
if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
}
@ -482,21 +485,22 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Position.y = new_y;
m_Position.z = new_z;
if(checkZ && fix_z_timer.Check() &&
(!this->IsEngaged() || flee_mode || currently_fleeing))
if (check_z && fix_z_timer.Check() && (!this->IsEngaged() || flee_mode || currently_fleeing)) {
this->FixZ();
}
tar_ndx++;
return true;
}
if (tar_ndx>50) {
if (tar_ndx > 50) {
tar_ndx--;
}
else {
tar_ndx = 0;
}
m_TargetLocation = glm::vec3(x, y, z);
float nx = this->m_Position.x;
@ -530,14 +534,12 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
// mob move fix
if (numsteps<20)
{
if (numsteps>1)
{
if (numsteps < 20) {
if (numsteps > 1) {
tar_vector = 1.0f;
m_TargetV.x = m_TargetV.x / (float)numsteps;
m_TargetV.y = m_TargetV.y / (float)numsteps;
m_TargetV.z = m_TargetV.z / (float)numsteps;
m_TargetV.x = m_TargetV.x / (float) numsteps;
m_TargetV.y = m_TargetV.y / (float) numsteps;
m_TargetV.z = m_TargetV.z / (float) numsteps;
float new_x = m_Position.x + m_TargetV.x;
float new_y = m_Position.y + m_TargetV.y;
@ -549,12 +551,12 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Position.x = new_x;
m_Position.y = new_y;
m_Position.z = new_z;
if (calcHeading)
if (calculate_heading) {
m_Position.w = CalculateHeadingToTarget(x, y);
}
tar_ndx = 20 - numsteps;
}
else
{
else {
if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), x, y, z);
}
@ -578,9 +580,10 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
tar_vector *= (dur / 100.0f);
float new_x = m_Position.x + m_TargetV.x*tar_vector;
float new_y = m_Position.y + m_TargetV.y*tar_vector;
float new_z = m_Position.z + m_TargetV.z*tar_vector;
float new_x = m_Position.x + m_TargetV.x * tar_vector;
float new_y = m_Position.y + m_TargetV.y * tar_vector;
float new_z = m_Position.z + m_TargetV.z * tar_vector;
if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
}
@ -588,11 +591,12 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Position.x = new_x;
m_Position.y = new_y;
m_Position.z = new_z;
if (calcHeading)
if (calculate_heading) {
m_Position.w = CalculateHeadingToTarget(x, y);
}
}
if (checkZ && fix_z_timer.Check() && !this->IsEngaged())
if (check_z && fix_z_timer.Check() && !this->IsEngaged())
this->FixZ();
SetMoving(true);
@ -600,13 +604,11 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f);
if (IsClient())
{
if (IsClient()) {
SendPositionUpdate(1);
CastToClient()->ResetPositionTimer();
}
else
{
else {
SendPositionUpdate();
SetAppearance(eaStanding, false);
}
@ -615,8 +617,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
return true;
}
bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ, bool calcHeading) {
return MakeNewPositionAndSendUpdate(x, y, z, speed);
bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool check_z, bool calculate_heading) {
return MakeNewPositionAndSendUpdate(x, y, z, speed, check_z);
}
void NPC::AssignWaypoints(int32 grid)
@ -746,10 +748,11 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
}
}
float Mob::GetFixedZ(glm::vec3 dest, int32 z_find_offset) {
float Mob::GetFixedZ(glm::vec3 destination, int32 z_find_offset) {
BenchTimer timer;
timer.reset();
float new_z = dest.z;
float new_z = destination.z;
if (zone->HasMap() && RuleB(Map, FixZWhenMoving)) {
@ -765,7 +768,7 @@ float Mob::GetFixedZ(glm::vec3 dest, int32 z_find_offset) {
/*
* Any more than 5 in the offset makes NPC's hop/snap to ceiling in small corridors
*/
new_z = this->FindDestGroundZ(dest, z_find_offset);
new_z = this->FindDestGroundZ(destination, z_find_offset);
if (new_z != BEST_Z_INVALID) {
new_z += this->GetZOffset();
@ -776,31 +779,48 @@ float Mob::GetFixedZ(glm::vec3 dest, int32 z_find_offset) {
auto duration = timer.elapsed();
Log(Logs::Moderate, Logs::FixZ,
"Mob::GetFixedZ() (%s) returned %4.3f at %4.3f, %4.3f, %4.3f - Took %lf",
this->GetCleanName(), new_z, dest.x, dest.y, dest.z, duration);
Log(Logs::Moderate,
Logs::FixZ,
"Mob::GetFixedZ() (%s) returned %4.3f at %4.3f, %4.3f, %4.3f - Took %lf",
this->GetCleanName(),
new_z,
destination.x,
destination.y,
destination.z,
duration);
}
return new_z;
}
void Mob::FixZ(int32 z_find_offset /*= 5*/) {
void Mob::FixZ(int32 z_find_offset /*= 5*/, bool fix_client_z /*= false*/) {
glm::vec3 current_loc(m_Position);
float new_z = GetFixedZ(current_loc, z_find_offset);
if (!IsClient() && new_z != m_Position.z) {
if ((new_z > -2000) && new_z != BEST_Z_INVALID) {
if (RuleB(Map, MobZVisualDebug))
this->SendAppearanceEffect(78, 0, 0, 0, 0);
if (IsClient() && !fix_client_z)
return;
m_Position.z = new_z;
} else {
if (RuleB(Map, MobZVisualDebug))
this->SendAppearanceEffect(103, 0, 0, 0, 0);
float new_z = GetFixedZ(current_loc, z_find_offset);
Log(Logs::General, Logs::FixZ, "%s is failing to find Z %f",
this->GetCleanName(), std::abs(m_Position.z - new_z));
if (new_z == m_Position.z)
return;
if ((new_z > -2000) && new_z != BEST_Z_INVALID) {
if (RuleB(Map, MobZVisualDebug)) {
this->SendAppearanceEffect(78, 0, 0, 0, 0);
}
m_Position.z = new_z;
}
else {
if (RuleB(Map, MobZVisualDebug)) {
this->SendAppearanceEffect(103, 0, 0, 0, 0);
}
Log(Logs::General,
Logs::FixZ,
"%s is failing to find Z %f",
this->GetCleanName(),
std::abs(m_Position.z - new_z));
}
}

View File

@ -905,9 +905,9 @@ bool Zone::Init(bool iStaticZone) {
}
}
zone->zonemap = Map::LoadMapFile(zone->map_name);
zone->zonemap = Map::LoadMapFile(zone->map_name);
zone->watermap = WaterMap::LoadWaterMapfile(zone->map_name);
zone->pathing = IPathfinder::Load(zone->map_name);
zone->pathing = IPathfinder::Load(zone->map_name);
Log(Logs::General, Logs::Status, "Loading spawn conditions...");
if(!spawn_conditions.LoadSpawnConditions(short_name, instanceid)) {

View File

@ -212,10 +212,10 @@ public:
void ReloadWorld(uint32 Option);
void ReloadMerchants();
Map* zonemap;
WaterMap* watermap;
IPathfinder *pathing;
NewZone_Struct newzone_data;
Map *zonemap;
WaterMap *watermap;
IPathfinder *pathing;
NewZone_Struct newzone_data;
SpawnConditionManager spawn_conditions;

View File

@ -143,7 +143,11 @@ bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct
"snow_duration2, " // 53
"snow_duration3, " // 54
"snow_duration4, " // 55
"gravity " // 56
"gravity, " // 56
"fast_regen_hp, " // 57
"fast_regen_mana, " // 58
"fast_regen_endurance, " // 59
"npc_max_aggro_dist " // 60
"FROM zone WHERE zoneidnumber = %i AND version = %i",
zoneid, instance_id);
auto results = QueryDatabase(query);
@ -188,6 +192,11 @@ bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct
Log(Logs::General, Logs::Debug, "Zone Gravity is %f", zone_data->gravity);
allow_mercs = true;
zone_data->FastRegenHP = atoi(row[57]);
zone_data->FastRegenMana = atoi(row[58]);
zone_data->FastRegenEndurance = atoi(row[59]);
zone_data->NPCAggroMaxDist = atoi(row[60]);
int bindable = 0;
bindable = atoi(row[31]);

View File

@ -717,6 +717,10 @@ void Client::GoToSafeCoords(uint16 zone_id, uint16 instance_id) {
void Mob::Gate(uint8 bindnum) {
GoToBind(bindnum);
if (RuleB(NPC, NPCHealOnGate) && this->IsNPC() && this->GetHPRatio() <= RuleR(NPC, NPCHealOnGateAmount)) {
auto HealAmount = (RuleR(NPC, NPCHealOnGateAmount) / 100);
SetHP(int(this->GetMaxHP() * HealAmount));
}
}
void Client::Gate(uint8 bindnum) {