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 #EQEMU_MAP_DIR
CMAKE_MINIMUM_REQUIRED(VERSION 2.8) 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 #FindMySQL is located here so lets make it so CMake can find it
SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/" ${CMAKE_MODULE_PATH}) SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/" ${CMAKE_MODULE_PATH})

View File

@ -376,7 +376,11 @@ struct NewZone_Struct {
/*0692*/ uint8 unknown692[8]; /*0692*/ uint8 unknown692[8];
/*0700*/ float fog_density; /*0700*/ float fog_density;
/*0704*/ uint32 SuspendBuffs; /*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, FixZ,
Food, Food,
Traps, Traps,
NPCRoamBox,
MaxCategoryID /* Don't Remove this*/ MaxCategoryID /* Don't Remove this*/
}; };
@ -144,7 +145,8 @@ static const char* LogCategoryName[LogCategory::MaxCategoryID] = {
"HP Update", "HP Update",
"FixZ", "FixZ",
"Food", "Food",
"Traps" "Traps",
"NPC Roam Box"
}; };
} }

View File

@ -124,7 +124,7 @@ namespace EQEmu
class OutBuffer : public std::stringstream { class OutBuffer : public std::stringstream {
public: 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); void overwrite(OutBuffer::pos_type position, const char *_Str, std::streamsize _Count);
uchar* detach(); uchar* detach();
}; };

View File

@ -73,7 +73,7 @@ uint32 SwapBits21And22(uint32 mask);
uint32 Catch22(uint32 mask); uint32 Catch22(uint32 mask);
// macro to catch fp errors (provided by noudness) // 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 #define _ITOA_BUFLEN 25
const char *itoa(int num); //not thread safe const char *itoa(int num); //not thread safe

View File

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

View File

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

View File

@ -588,9 +588,9 @@ struct NewZone_Struct {
/*0893*/ uint8 unknown893; // Seen 0 - 00 /*0893*/ uint8 unknown893; // Seen 0 - 00
/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off /*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off
/*0895*/ uint8 unknown895; // Seen 0 - 00 /*0895*/ uint8 unknown895; // Seen 0 - 00
/*0896*/ uint32 unknown896; // Seen 180 /*0896*/ uint32 FastRegenHP; // Seen 180
/*0900*/ uint32 unknown900; // Seen 180 /*0900*/ uint32 FastRegenMana; // Seen 180
/*0904*/ uint32 unknown904; // Seen 180 /*0904*/ uint32 FastRegenEndurance; // Seen 180
/*0908*/ uint32 unknown908; // Seen 2 /*0908*/ uint32 unknown908; // Seen 2
/*0912*/ uint32 unknown912; // Seen 2 /*0912*/ uint32 unknown912; // Seen 2
/*0916*/ float FogDensity; // Most zones have this set to 0.33 Blightfire had 0.16 /*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_id);
OUT(zone_instance); OUT(zone_instance);
OUT(SuspendBuffs); OUT(SuspendBuffs);
OUT(FastRegenHP);
OUT(FastRegenMana);
OUT(FastRegenEndurance);
/*fill in some unknowns with observed values, hopefully it will help */ /*fill in some unknowns with observed values, hopefully it will help */
eq->unknown800 = -1; eq->unknown800 = -1;
eq->unknown844 = 600; eq->unknown844 = 600;
@ -1363,9 +1367,6 @@ namespace SoD
eq->unknown893 = 0; eq->unknown893 = 0;
eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off
eq->unknown895 = 0; eq->unknown895 = 0;
eq->unknown896 = 180;
eq->unknown900 = 180;
eq->unknown904 = 180;
eq->unknown908 = 2; eq->unknown908 = 2;
eq->unknown912 = 2; eq->unknown912 = 2;
eq->FogDensity = emu->fog_density; eq->FogDensity = emu->fog_density;

View File

@ -456,9 +456,9 @@ struct NewZone_Struct {
/*0893*/ uint8 unknown893; //seen 0 - 00 /*0893*/ uint8 unknown893; //seen 0 - 00
/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off /*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off
/*0895*/ uint8 unknown895; //seen 0 - 00 /*0895*/ uint8 unknown895; //seen 0 - 00
/*0896*/ uint32 unknown896; //seen 180 /*0896*/ uint32 FastRegenHP; //seen 180
/*0900*/ uint32 unknown900; //seen 180 /*0900*/ uint32 FastRegenMana; //seen 180
/*0904*/ uint32 unknown904; //seen 180 /*0904*/ uint32 FastRegenEndurance; //seen 180
/*0908*/ uint32 unknown908; //seen 2 /*0908*/ uint32 unknown908; //seen 2
/*0912*/ uint32 unknown912; //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 /*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_id);
OUT(zone_instance); OUT(zone_instance);
OUT(SuspendBuffs); OUT(SuspendBuffs);
OUT(FastRegenHP);
OUT(FastRegenMana);
OUT(FastRegenEndurance);
/*fill in some unknowns with observed values, hopefully it will help */ /*fill in some unknowns with observed values, hopefully it will help */
eq->unknown796 = -1; eq->unknown796 = -1;
@ -1041,9 +1044,6 @@ namespace SoF
eq->unknown889 = 0; eq->unknown889 = 0;
eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off eq->fall_damage = 0; // 0 = Fall Damage on, 1 = Fall Damage off
eq->unknown891 = 0; eq->unknown891 = 0;
eq->unknown892 = 180;
eq->unknown896 = 180;
eq->unknown900 = 180;
eq->unknown904 = 2; eq->unknown904 = 2;
eq->unknown908 = 2; eq->unknown908 = 2;

View File

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

View File

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

View File

@ -456,9 +456,9 @@ struct NewZone_Struct {
/*0893*/ uint8 unknown893; //seen 0 - 00 /*0893*/ uint8 unknown893; //seen 0 - 00
/*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off /*0894*/ uint8 fall_damage; // 0 = Fall Damage on, 1 = Fall Damage off
/*0895*/ uint8 unknown895; //seen 0 - 00 /*0895*/ uint8 unknown895; //seen 0 - 00
/*0896*/ uint32 unknown896; //seen 180 /*0896*/ uint32 FastRegenHP; //seen 180
/*0900*/ uint32 unknown900; //seen 180 /*0900*/ uint32 FastRegenMana; //seen 180
/*0904*/ uint32 unknown904; //seen 180 /*0904*/ uint32 FastRegenEndurance; //seen 180
/*0908*/ uint32 unknown908; //seen 2 /*0908*/ uint32 unknown908; //seen 2
/*0912*/ uint32 unknown912; //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 /*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, SharedBankPlat, false) //off by default to prevent duping for now
RULE_BOOL(Character, BindAnywhere, false) RULE_BOOL(Character, BindAnywhere, false)
RULE_BOOL(Character, RestRegenEnabled, true) // Enable OOC Regen 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, 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, 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 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_INT(Combat, PetAttackMagicLevel, 30)
RULE_BOOL(Combat, EnableFearPathing, true) 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_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_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, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it.
RULE_BOOL(Combat, AdjustProcPerMinute, true) RULE_BOOL(Combat, AdjustProcPerMinute, true)
@ -532,6 +531,11 @@ RULE_INT(NPC, NPCToNPCAggroTimerMin, 500)
RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000) RULE_INT(NPC, NPCToNPCAggroTimerMax, 6000)
RULE_BOOL(NPC, UseClassAsLastName, true) // Uses class archetype as LastName for npcs with none 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_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_END()
RULE_CATEGORY(Aggro) RULE_CATEGORY(Aggro)
@ -695,6 +699,7 @@ RULE_CATEGORY_END()
RULE_CATEGORY(QueryServ) RULE_CATEGORY(QueryServ)
RULE_BOOL(QueryServ, PlayerLogChat, false) // Logs Player Chat RULE_BOOL(QueryServ, PlayerLogChat, false) // Logs Player Chat
RULE_BOOL(QueryServ, PlayerLogTrades, false) // Logs Player Trades 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, PlayerLogHandins, false) // Logs Player Handins
RULE_BOOL(QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills RULE_BOOL(QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills
RULE_BOOL(QueryServ, PlayerLogDeletes, false) // Logs Player Deletes RULE_BOOL(QueryServ, PlayerLogDeletes, false) // Logs Player Deletes

View File

@ -203,6 +203,7 @@
#define ServerOP_CZSignalNPC 0x5017 #define ServerOP_CZSignalNPC 0x5017
#define ServerOP_CZSetEntityVariableByNPCTypeID 0x5018 #define ServerOP_CZSetEntityVariableByNPCTypeID 0x5018
#define ServerOP_WWMarquee 0x5019 #define ServerOP_WWMarquee 0x5019
#define ServerOP_QSPlayerDropItem 0x5020
/* Query Serv Generic Packet Flag/Type Enumeration */ /* Query Serv Generic Packet Flag/Type Enumeration */
enum { QSG_LFGuild = 0 }; enum { QSG_LFGuild = 0 };
@ -1140,6 +1141,27 @@ struct QSPlayerLogTrade_Struct {
QSTradeItems_Struct items[0]; 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 { struct QSHandinItems_Struct {
char action_type[7]; // handin, return or reward char action_type[7]; // handin, return or reward
uint16 char_slot; uint16 char_slot;

View File

@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/ */
#define CURRENT_BINARY_DATABASE_VERSION 9127 #define CURRENT_BINARY_DATABASE_VERSION 9129
#ifdef BOTS #ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9020 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9020
#else #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) { void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 detailCount) {
std::string query = StringFormat("INSERT INTO `qs_player_trade_record` SET `time` = NOW(), " 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 AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type);
void LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 DetailCount); void LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 DetailCount);
void LogPlayerDropItem(QSPlayerDropItem_Struct* QS);
void LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 DetailCount); void LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 DetailCount);
void LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members); void LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members);
void LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items); 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); database.LogPlayerTrade(QS, QS->_detail_count);
break; break;
} }
case ServerOP_QSPlayerDropItem: {
QSPlayerDropItem_Struct *QS = (QSPlayerDropItem_Struct *) p.Data();
database.LogPlayerDropItem(QS);
break;
}
case ServerOP_QSPlayerLogHandins: { case ServerOP_QSPlayerLogHandins: {
QSPlayerLogHandin_Struct *QS = (QSPlayerLogHandin_Struct*)p.Data(); QSPlayerLogHandin_Struct *QS = (QSPlayerLogHandin_Struct*)p.Data();
database.LogPlayerHandin(QS, QS->_detail_count); database.LogPlayerHandin(QS, QS->_detail_count);

View File

@ -40,7 +40,12 @@ if($Config{osname}=~/freebsd|linux/i){
if($Config{osname}=~/Win|MS/i){ if($Config{osname}=~/Win|MS/i){
$OS = "Windows"; $OS = "Windows";
} }
$has_internet_connection = check_internet_connection(); $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(); ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime();
if(-e "eqemu_server_skip_update.txt"){ if(-e "eqemu_server_skip_update.txt"){
@ -826,6 +831,7 @@ sub show_menu_prompt {
elsif($input eq "setup_bots"){ setup_bots(); $dc = 1; } 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 "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 "quest_heading_convert"){ quest_heading_convert(); $dc = 1; }
elsif($input eq "source_peq_db"){ fetch_peq_db_full(); $dc = 1; }
elsif($input eq "exit"){ elsif($input eq "exit"){
exit; exit;
} }

View File

@ -54,6 +54,8 @@ export apt_options="-y -qq" # Set autoconfirm and silent install
################################################################ ################################################################
if [ ! -f ./install_variables.txt ]; then
read -n1 -r -p "Press any key to continue..." key read -n1 -r -p "Press any key to continue..." key
#::: Setting up user environment (eqemu) #::: Setting up user environment (eqemu)
@ -84,6 +86,7 @@ read -p "Enter (Database) MySQL EQEmu Server password: " eqemu_db_password
echo "mysql_eqemu_db_name:$eqemu_db_name" >> install_variables.txt echo "mysql_eqemu_db_name:$eqemu_db_name" >> install_variables.txt
echo "mysql_eqemu_user:$eqemu_db_username" >> install_variables.txt echo "mysql_eqemu_user:$eqemu_db_username" >> install_variables.txt
echo "mysql_eqemu_password:$eqemu_db_password" >> install_variables.txt echo "mysql_eqemu_password:$eqemu_db_password" >> install_variables.txt
fi
if [[ "$OS" == "Debian" ]]; then if [[ "$OS" == "Debian" ]]; then
# Install pre-req packages # Install pre-req packages

View File

@ -379,8 +379,10 @@
9123|2018_07_07_data_buckets.sql|SHOW TABLES LIKE 'data_buckets'|empty| 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| 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| 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| 9126|2018_09_07_FastRegen.sql|SHOW COLUMNS FROM `zone` LIKE 'fast_regen_hp'|empty|
9127|2018_08_13_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `step` = 0|not_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: # Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not # This won't be needed after this system is implemented, but it is used database that are not

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

View File

@ -530,6 +530,28 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S
//uint32 x = 0; //uint32 x = 0;
int whomlen = 0; int whomlen = 0;
if (whom) { 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); 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 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. 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_QSPlayerLogDeletes:
case ServerOP_QSPlayerLogMoves: case ServerOP_QSPlayerLogMoves:
case ServerOP_QSPlayerLogMerchantTransactions: case ServerOP_QSPlayerLogMerchantTransactions:
case ServerOP_QSPlayerDropItem:
{ {
QSLink.SendPacket(pack); QSLink.SendPacket(pack);
break; break;

View File

@ -107,16 +107,10 @@ void NPC::DescribeAggro(Client *towho, Mob *mob, bool verbose) {
float iAggroRange = GetAggroRange(); float iAggroRange = GetAggroRange();
float t1, t2, t3; float t1, t2, t3;
t1 = mob->GetX() - GetX(); t1 = std::abs(mob->GetX() - GetX());
t2 = mob->GetY() - GetY(); t2 = std::abs(mob->GetY() - GetY());
t3 = mob->GetZ() - GetZ(); t3 = std::abs(mob->GetZ() - GetZ());
//Cheap ABS()
if(t1 < 0)
t1 = 0 - t1;
if(t2 < 0)
t2 = 0 - t2;
if(t3 < 0)
t3 = 0 - t3;
if(( t1 > iAggroRange) if(( t1 > iAggroRange)
|| ( t2 > iAggroRange) || ( t2 > iAggroRange)
|| ( t3 > 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 // 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; float t1, t2, t3;
t1 = mob->GetX() - GetX(); t1 = std::abs(mob->GetX() - GetX());
t2 = mob->GetY() - GetY(); t2 = std::abs(mob->GetY() - GetY());
t3 = mob->GetZ() - GetZ(); t3 = std::abs(mob->GetZ() - GetZ());
//Cheap ABS()
if(t1 < 0)
t1 = 0 - t1;
if(t2 < 0)
t2 = 0 - t2;
if(t3 < 0)
t3 = 0 - t3;
if(( t1 > iAggroRange) if(( t1 > iAggroRange)
|| ( t2 > iAggroRange) || ( t2 > iAggroRange)
|| ( t3 > iAggroRange) || ( t3 > iAggroRange)
@ -424,7 +412,7 @@ Mob* EntityList::AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAss
return nullptr; 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 // Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
if (!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) { for (auto it = npc_list.begin(); it != npc_list.end(); ++it) {
NPC *mob = it->second; NPC *mob = it->second;
if (!mob || (mob == exclude)) if (!mob || (mob == exclude)) {
continue; continue;
}
if (!mob->IsEngaged()) if (!mob->IsEngaged()) {
continue; continue;
}
if (mob->IsFeared() || mob->IsMezzed()) if (mob->IsFeared() || mob->IsMezzed()) {
continue; continue;
}
if (attacker->GetLevelCon(mob->GetLevel()) == CON_GRAY) if (!inc_gray_con && attacker->GetLevelCon(mob->GetLevel()) == CON_GRAY) {
continue; continue;
}
if (!mob->CheckAggro(attacker)) if (!mob->CheckAggro(attacker)) {
continue; continue;
}
float AggroRange = mob->GetAggroRange(); float AggroRange = mob->GetAggroRange();
@ -455,14 +448,12 @@ int EntityList::GetHatedCount(Mob *attacker, Mob *exclude)
AggroRange *= AggroRange; AggroRange *= AggroRange;
if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange) if (DistanceSquared(mob->GetPosition(), attacker->GetPosition()) > AggroRange) {
continue; continue;
}
Count++; Count++;
} }
return Count; return Count;
} }
void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) { 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", Log(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f",
sender->GetName(), attacker->GetName(), mob->GetName(), sender->GetName(), attacker->GetName(), mob->GetName(),
attacker->GetName(), DistanceSquared(mob->GetPosition(), attacker->GetName(), DistanceSquared(mob->GetPosition(),
sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ())); sender->GetPosition()), std::abs(sender->GetZ()+mob->GetZ()));
#endif #endif
mob->AddToHateList(attacker, 25, 0, false); mob->AddToHateList(attacker, 25, 0, false);
sender->AddAssistCap(); 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)); 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);
} }
int32 Bot::LevelRegen() { 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 //The ole switcheroo
if (npc_value[i] > 0) 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) 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 // 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 SetStats(uint8 type,int16 set_val);
void IncStats(uint8 type,int16 increase_val); void IncStats(uint8 type,int16 increase_val);
void DropItem(int16 slot_id, bool recurse = true); 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 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 // another check for IsClient && !(base + item_regen) && Cur_HP <= 0 do --base; do later
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) { if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
auto fast_mod = RuleI(Character, RestRegenHP); // TODO: this is actually zone based
auto max_hp = GetMaxHP(); 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 if (base < fast_regen) // weird, but what the client is doing
base = fast_regen; base = fast_regen;
} }
@ -1296,9 +1295,8 @@ int32 Client::CalcManaRegen(bool bCombat)
regen = regen * 100.0f * AreaManaRegen * 0.01f + 0.5f; regen = regen * 100.0f * AreaManaRegen * 0.01f + 0.5f;
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) { if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
auto fast_mod = RuleI(Character, RestRegenMana); // TODO: this is actually zone based
auto max_mana = GetMaxMana(); 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 if (regen < fast_regen) // weird, but what the client is doing
regen = fast_regen; regen = fast_regen;
} }
@ -2264,9 +2262,8 @@ int32 Client::CalcEnduranceRegen(bool bCombat)
int regen = base; int regen = base;
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) { if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
auto fast_mod = RuleI(Character, RestRegenEnd); // TODO: this is actually zone based
auto max_end = GetMaxEndurance(); 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 if (aa_regen < fast_regen) // weird, but what the client is doing
aa_regen = fast_regen; 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)) { Distance(m_Position, assistee->GetPosition()) <= TARGETING_RANGE)) {
SetAssistExemption(true); SetAssistExemption(true);
eid->entity_id = new_target->GetID(); 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 "raids.h"
#include "string_ids.h" #include "string_ids.h"
#include "worldserver.h" #include "worldserver.h"
#include "water_map.h"
#ifdef _WINDOWS #ifdef _WINDOWS
#define snprintf _snprintf #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) void EntityList::AddMerc(Merc *merc, bool SendSpawnPacket, bool dontqueue)
@ -3425,14 +3435,15 @@ 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_x = n->GetX();
float last_y = n->GetY(); float last_y = n->GetY();
float last_z = n->GetZ(); float last_z = n->GetZ();
std::list<quest_proximity_event> events; std::list<quest_proximity_event> events;
for (auto iter = area_list.begin(); iter != area_list.end(); ++iter) { for (auto iter = area_list.begin(); iter != area_list.end(); ++iter) {
Area &a = (*iter); Area &a = (*iter);
bool old_in = true; bool old_in = true;
bool new_in = true; bool new_in = true;
@ -3457,7 +3468,8 @@ void EntityList::ProcessMove(NPC *n, float x, float y, float z)
evt.area_id = a.id; evt.area_id = a.id;
evt.area_type = a.type; evt.area_type = a.type;
events.push_back(evt); events.push_back(evt);
} else if (!old_in && new_in) { }
else if (!old_in && new_in) {
//were not in but now are //were not in but now are
quest_proximity_event evt; quest_proximity_event evt;
evt.event_id = EVENT_ENTER_AREA; evt.event_id = EVENT_ENTER_AREA;

View File

@ -416,7 +416,7 @@ public:
void CheckClientAggro(Client *around); void CheckClientAggro(Client *around);
Mob* AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange); 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); void AIYellForHelp(Mob* sender, Mob* attacker);
bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes); bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes);
bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes); bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes);

View File

@ -31,71 +31,91 @@ extern Zone* zone;
//this is called whenever we are damaged to process possible fleeing //this is called whenever we are damaged to process possible fleeing
void Mob::CheckFlee() { 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; 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 //dont bother if we are immune to fleeing
if(GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) if(GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) {
return; return;
}
if(!flee_timer.Check()) // Check if Flee Timer is cleared
return; //only do all this stuff every little while, since if(!flee_timer.Check()) {
//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)
return; 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(); 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) { if(!hate_top) {
//this should never happen...
StartFleeing(); StartFleeing();
return; 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) { if(other_ratio < 20) {
//our hate top is almost dead too... stay and fight
return; return;
} }
//base our flee ratio on our con. this is how the // Flee Chance checking based on con.
//attacker sees the mob, since this is all we can observe
uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel()); uint32 con = GetLevelCon(hate_top->GetLevel(), GetLevel());
float run_ratio; int flee_chance;
switch(con) { switch(con) {
//these values are not 100% researched //these values are not 100% researched
case CON_GRAY: case CON_GRAY:
run_ratio = fleeratio; flee_chance = 100;
break; break;
case CON_GREEN: case CON_GREEN:
run_ratio = fleeratio * 9 / 10; flee_chance = 90;
break; break;
case CON_LIGHTBLUE: case CON_LIGHTBLUE:
run_ratio = fleeratio * 9 / 10; flee_chance = 90;
break; break;
case CON_BLUE: case CON_BLUE:
run_ratio = fleeratio * 8 / 10; flee_chance = 80;
break; break;
default: default:
run_ratio = fleeratio * 7 / 10; flee_chance = 70;
break; break;
} }
if(ratio < run_ratio)
{ // 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) || if(RuleB(Combat, FleeIfNotAlone) || GetSpecialAbility(ALWAYS_FLEE) || zone->random.Roll(flee_chance) && entity_list.GetHatedCount(hate_top, this, true) == 0) {
(!RuleB(Combat, FleeIfNotAlone) && (entity_list.GetHatedCount(hate_top, this) == 0))) currently_fleeing = true;
StartFleeing(); StartFleeing();
} }
} }
void Mob::ProcessFlee() void Mob::ProcessFlee()
{ {
@ -107,11 +127,21 @@ void Mob::ProcessFlee()
return; return;
} }
//see if we are still dying, if so, do nothing int hpratio = GetIntHPRatio();
float fleeratio = GetSpecialAbility(FLEE_PERCENT); int fleeratio = GetSpecialAbility(FLEE_PERCENT); // if a special flee_percent exists
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio); Mob *hate_top = GetHateTop();
if (GetHPRatio() < fleeratio)
// 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; return;
}
//we are not dying anymore... see what we do next //we are not dying anymore... see what we do next
@ -125,19 +155,21 @@ void Mob::ProcessFlee()
} }
} }
void Mob::CalculateNewFearpoint() void Mob::CalculateNewFearpoint() {
{ if (RuleB(Pathing, Fear) && zone->pathing) {
if(RuleB(Pathing, Fear) && zone->pathing)
{
auto Node = zone->pathing->GetRandomLocation(); auto Node = zone->pathing->GetRandomLocation();
if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) { if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) {
++Node.z; ++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;
@ -154,8 +186,7 @@ void Mob::CalculateNewFearpoint()
if (ranz == BEST_Z_INVALID) if (ranz == BEST_Z_INVALID)
continue; continue;
float fdist = ranz - GetZ(); 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; break;
} }
} }

View File

@ -575,12 +575,19 @@ int HateList::AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOption
if (!target || !caster) if (!target || !caster)
return 0; 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; int hit_count = 0;
// This should prevent crashes if something dies (or mainly more than 1 thing goes away) // 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 // This is a temp solution until the hate lists can be rewritten to not have that issue
std::vector<uint16> id_list; std::vector<uint16> id_list;
for (auto &h : 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)) caster->CombatRange(h->entity_on_hatelist))
id_list.push_back(h->entity_on_hatelist->GetID()); id_list.push_back(h->entity_on_hatelist->GetID());
if (count != -1 && id_list.size() > count) 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) { 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, "DropItem() Hack detected - full item parse:");
Log(Logs::General, Logs::Inventory, "depth: 0, Item: '%s' (id: %u), IsDroppable: %s", 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 for (auto iter1 : *invalid_drop->GetContents()) { // depth 1
Log(Logs::General, Logs::Inventory, "-depth: 1, Item: '%s' (id: %u), IsDroppable: %s", 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 for (auto iter2 : *iter1.second->GetContents()) { // depth 2
Log(Logs::General, Logs::Inventory, "--depth: 2, Item: '%s' (id: %u), IsDroppable: %s", 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) { if (LogSys.log_settings[Logs::Inventory].is_category_enabled) {
Log(Logs::General, Logs::Inventory, "DropItem() Processing - full item parse:"); Log(Logs::General, Logs::Inventory, "DropItem() Processing - full item parse:");
Log(Logs::General, Logs::Inventory, "depth: 0, Item: '%s' (id: %u), IsDroppable: %s", 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)) if (!inst->IsDroppable(false))
Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName()); Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName());
for (auto iter1 : *inst->GetContents()) { // depth 1 for (auto iter1 : *inst->GetContents()) { // depth 1
Log(Logs::General, Logs::Inventory, "-depth: 1, Item: '%s' (id: %u), IsDroppable: %s", 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)) if (!iter1.second->IsDroppable(false))
Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName()); Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName());
for (auto iter2 : *iter1.second->GetContents()) { // depth 2 for (auto iter2 : *iter1.second->GetContents()) { // depth 2
Log(Logs::General, Logs::Inventory, "--depth: 2, Item: '%s' (id: %u), IsDroppable: %s", 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)) if (!iter2.second->IsDroppable(false))
Log(Logs::General, Logs::Error, "Non-droppable item being processed for drop by '%s'", GetCleanName()); 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(); object->StartDecay();
Log(Logs::General, Logs::Inventory, "Item drop handled ut assolet"); Log(Logs::General, Logs::Inventory, "Item drop handled ut assolet");
DropItemQS(inst, false);
safe_delete(inst); 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 // Drop inst
void Client::DropInst(const EQEmu::ItemInstance* inst) void Client::DropInst(const EQEmu::ItemInstance* inst)
{ {

View File

@ -32,12 +32,12 @@ int Lua_Raid::RaidCount() {
return self->RaidCount(); return self->RaidCount();
} }
uint32 Lua_Raid::GetGroup(const char *c) { int Lua_Raid::GetGroup(const char *c) {
Lua_Safe_Call_Int(); Lua_Safe_Call_Int();
return self->GetGroup(c); return self->GetGroup(c);
} }
uint32 Lua_Raid::GetGroup(Lua_Client c) { int Lua_Raid::GetGroup(Lua_Client c) {
Lua_Safe_Call_Int(); Lua_Safe_Call_Int();
return self->GetGroup(c); return self->GetGroup(c);
} }
@ -122,6 +122,16 @@ Lua_Client Lua_Raid::GetMember(int index) {
return self->members[index].member; 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() { luabind::scope lua_register_raid() {
return luabind::class_<Lua_Raid>("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("CastGroupSpell", (void(Lua_Raid::*)(Lua_Mob,int,uint32))&Lua_Raid::CastGroupSpell)
.def("GroupCount", (int(Lua_Raid::*)(uint32))&Lua_Raid::GroupCount) .def("GroupCount", (int(Lua_Raid::*)(uint32))&Lua_Raid::GroupCount)
.def("RaidCount", (int(Lua_Raid::*)(void))&Lua_Raid::RaidCount) .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("SplitExp", (void(Lua_Raid::*)(uint32,Lua_Mob))&Lua_Raid::SplitExp)
.def("GetTotalRaidDamage", (uint32(Lua_Raid::*)(Lua_Mob))&Lua_Raid::GetTotalRaidDamage) .def("GetTotalRaidDamage", (uint32(Lua_Raid::*)(Lua_Mob))&Lua_Raid::GetTotalRaidDamage)
.def("SplitMoney", (void(Lua_Raid::*)(uint32,uint32,uint32,uint32))&Lua_Raid::SplitMoney) .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("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("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("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 #endif

View File

@ -30,8 +30,8 @@ public:
void CastGroupSpell(Lua_Mob caster, int spell_id, uint32 group_id); void CastGroupSpell(Lua_Mob caster, int spell_id, uint32 group_id);
int GroupCount(uint32 group_id); int GroupCount(uint32 group_id);
int RaidCount(); int RaidCount();
uint32 GetGroup(const char *c); int GetGroup(const char *c);
uint32 GetGroup(Lua_Client c); int GetGroup(Lua_Client c);
void SplitExp(uint32 exp, Lua_Mob other); void SplitExp(uint32 exp, Lua_Mob other);
uint32 GetTotalRaidDamage(Lua_Mob other); uint32 GetTotalRaidDamage(Lua_Mob other);
void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum); 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); void TeleportRaid(Lua_Mob sender, uint32 zone_id, uint32 instance_id, float x, float y, float z, float h);
int GetID(); int GetID();
Lua_Client GetMember(int index); Lua_Client GetMember(int index);
int GetGroupNumber(int index);
}; };
#endif #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 { bool Merc::HasSkill(EQEmu::skills::SkillType skill_id) const {
@ -1408,7 +1408,7 @@ void Merc::AI_Process() {
if(DivineAura()) if(DivineAura())
return; return;
int hateCount = entity_list.GetHatedCount(this, nullptr); int hateCount = entity_list.GetHatedCount(this, nullptr, false);
if(GetHatedCount() < hateCount) { if(GetHatedCount() < hateCount) {
SetHatedCount(hateCount); SetHatedCount(hateCount);
@ -1475,8 +1475,14 @@ void Merc::AI_Process() {
if (RuleB(Mercs, MercsUsePathing) && zone->pathing) { if (RuleB(Mercs, MercsUsePathing) && zone->pathing) {
bool WaypointChanged, NodeReached; bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), glm::vec3 Goal = UpdatePath(
GetRunspeed(), WaypointChanged, NodeReached); GetTarget()->GetX(),
GetTarget()->GetY(),
GetTarget()->GetZ(),
GetRunspeed(),
WaypointChanged,
NodeReached
);
if (WaypointChanged) if (WaypointChanged)
tar_ndx = 20; tar_ndx = 20;

View File

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

View File

@ -30,6 +30,7 @@
#include "string_ids.h" #include "string_ids.h"
#include "water_map.h" #include "water_map.h"
#include "fastmath.h" #include "fastmath.h"
#include "../common/data_verification.h"
#include <glm/gtx/projection.hpp> #include <glm/gtx/projection.hpp>
#include <algorithm> #include <algorithm>
@ -199,12 +200,16 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates
case SpellType_Escape: { case SpellType_Escape: {
// If min_hp !=0 then the spell list has specified // If min_hp !=0 then the spell list has specified
// custom range and we're inside that range if we // custom range and we're inside that range if we
// made it here. The hard coded <=5 is for unspecified. // made it here.
if (AIspells[i].min_hp != 0 || GetHPRatio() <= 5) if (AIspells[i].min_hp != 0 || GetHPRatio() <= (RuleI(NPC, NPCGatePercent))) {
{ auto npcSpawnPoint = CastToNPC()->GetSpawnPoint();
if (!RuleB(NPC, NPCGateNearBind) && DistanceNoZ(m_Position, npcSpawnPoint) < RuleI(NPC, NPCGateDistanceBind)) {
break;
} else {
AIDoSpellCast(i, tar, mana_cost); AIDoSpellCast(i, tar, mana_cost);
return true; return true;
} }
}
break; break;
} }
case SpellType_Slow: case SpellType_Slow:
@ -459,8 +464,9 @@ void NPC::AI_Init()
roambox_min_x = 0; roambox_min_x = 0;
roambox_min_y = 0; roambox_min_y = 0;
roambox_distance = 0; roambox_distance = 0;
roambox_movingto_x = 0; roambox_destination_x = 0;
roambox_movingto_y = 0; roambox_destination_y = 0;
roambox_destination_z = 0;
roambox_min_delay = 2500; roambox_min_delay = 2500;
roambox_delay = 2500; roambox_delay = 2500;
} }
@ -476,9 +482,9 @@ void Mob::AI_Start(uint32 iMoveDelay) {
return; return;
if (iMoveDelay) if (iMoveDelay)
pLastFightingDelayMoving = Timer::GetCurrentTime() + iMoveDelay; time_until_can_move = Timer::GetCurrentTime() + iMoveDelay;
else else
pLastFightingDelayMoving = 0; time_until_can_move = 0;
pAIControlled = true; pAIControlled = true;
AI_think_timer = std::unique_ptr<Timer>(new Timer(AIthink_duration)); AI_think_timer = std::unique_ptr<Timer>(new Timer(AIthink_duration));
@ -777,20 +783,20 @@ void Client::AI_Process()
if (RuleB(Combat, EnableFearPathing)) { if (RuleB(Combat, EnableFearPathing)) {
if (currently_fleeing) { if (currently_fleeing) {
if (fix_z_timer_engaged.Check()) if (fix_z_timer.Check())
this->FixZ(); this->FixZ(5, true);
if (IsRooted()) { if (IsRooted()) {
//make sure everybody knows were not moving, for appearance sake //make sure everybody knows were not moving, for appearance sake
if(IsMoving()) if (IsMoving()) {
{
if (GetTarget()) if (GetTarget())
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetCurrentSpeed(0); SetCurrentSpeed(0);
} }
//continue on to attack code, ensuring that we execute the engaged code //continue on to attack code, ensuring that we execute the engaged code
engaged = true; engaged = true;
} else { }
else {
if (AI_movement_timer->Check()) { if (AI_movement_timer->Check()) {
int speed = GetFearSpeed(); int speed = GetFearSpeed();
animation = speed; animation = speed;
@ -802,16 +808,22 @@ void Client::AI_Process()
// Calculate a new point to run to // Calculate a new point to run to
CalculateNewFearpoint(); CalculateNewFearpoint();
} }
if (!RuleB(Pathing, Fear) || !zone->pathing) if (!RuleB(Pathing, Fear) || !zone->pathing)
CalculateNewPosition(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, speed, true); CalculateNewPosition(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, speed, true);
else else {
{ bool waypoint_changed, node_reached;
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, glm::vec3 Goal = UpdatePath(
speed, WaypointChanged, NodeReached); m_FearWalkTarget.x,
m_FearWalkTarget.y,
m_FearWalkTarget.z,
speed,
waypoint_changed,
node_reached
);
if(WaypointChanged) if (waypoint_changed)
tar_ndx = 20; tar_ndx = 20;
CalculateNewPosition(Goal.x, Goal.y, Goal.z, speed); CalculateNewPosition(Goal.x, Goal.y, Goal.z, speed);
@ -1098,10 +1110,10 @@ void Mob::AI_Process() {
// //
if (RuleB(Combat, EnableFearPathing)) { if (RuleB(Combat, EnableFearPathing)) {
if (currently_fleeing) { if (currently_fleeing) {
if((IsRooted() || (IsBlind() && CombatRange(hate_list.GetClosestEntOnHateList(this)))) && !IsPetStop() && !IsPetRegroup()) { if ((IsRooted() || (IsBlind() && CombatRange(hate_list.GetClosestEntOnHateList(this)))) && !IsPetStop() &&
!IsPetRegroup()) {
//make sure everybody knows were not moving, for appearance sake //make sure everybody knows were not moving, for appearance sake
if(IsMoving()) if (IsMoving()) {
{
if (target) if (target)
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SetCurrentSpeed(0); SetCurrentSpeed(0);
@ -1109,7 +1121,8 @@ void Mob::AI_Process() {
} }
//continue on to attack code, ensuring that we execute the engaged code //continue on to attack code, ensuring that we execute the engaged code
engaged = true; engaged = true;
} else { }
else {
if (AI_movement_timer->Check()) { if (AI_movement_timer->Check()) {
// Check if we have reached the last fear point // Check if we have reached the last fear point
if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) && if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) &&
@ -1117,16 +1130,26 @@ void Mob::AI_Process() {
// Calculate a new point to run to // Calculate a new point to run to
CalculateNewFearpoint(); CalculateNewFearpoint();
} }
if(!RuleB(Pathing, Fear) || !zone->pathing) if (!RuleB(Pathing, Fear) || !zone->pathing) {
{ CalculateNewPosition(
CalculateNewPosition(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, GetFearSpeed(), true); m_FearWalkTarget.x,
m_FearWalkTarget.y,
m_FearWalkTarget.z,
GetFearSpeed(),
true
);
} }
else else {
{
bool WaypointChanged, NodeReached; bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, glm::vec3 Goal = UpdatePath(
GetFearSpeed(), WaypointChanged, NodeReached); m_FearWalkTarget.x,
m_FearWalkTarget.y,
m_FearWalkTarget.z,
GetFearSpeed(),
WaypointChanged,
NodeReached
);
if (WaypointChanged) if (WaypointChanged)
tar_ndx = 20; tar_ndx = 20;
@ -1171,7 +1194,7 @@ void Mob::AI_Process() {
// NPCs will forget people after 10 mins of not interacting with them or out of range // NPCs will forget people after 10 mins of not interacting with them or out of range
// both of these maybe zone specific, hardcoded for now // both of these maybe zone specific, hardcoded for now
if (hate_list_cleanup_timer.Check()) { if (hate_list_cleanup_timer.Check()) {
hate_list.RemoveStaleEntries(600000, 600.0f); hate_list.RemoveStaleEntries(600000, static_cast<float>(zone->newzone_data.NPCAggroMaxDist));
if (hate_list.IsHateListEmpty()) { if (hate_list.IsHateListEmpty()) {
AI_Event_NoLongerEngaged(); AI_Event_NoLongerEngaged();
zone->DelAggroMob(); zone->DelAggroMob();
@ -1183,10 +1206,8 @@ void Mob::AI_Process() {
// from above, so no extra blind checks needed // from above, so no extra blind checks needed
if ((IsRooted() && !GetSpecialAbility(IGNORE_ROOT_AGGRO_RULES)) || IsBlind()) if ((IsRooted() && !GetSpecialAbility(IGNORE_ROOT_AGGRO_RULES)) || IsBlind())
SetTarget(hate_list.GetClosestEntOnHateList(this)); SetTarget(hate_list.GetClosestEntOnHateList(this));
else else {
{ if (AI_target_check_timer->Check()) {
if (AI_target_check_timer->Check())
{
if (IsFocused()) { if (IsFocused()) {
if (!target) { if (!target) {
SetTarget(hate_list.GetEntWithMostHateOnList(this)); SetTarget(hate_list.GetEntWithMostHateOnList(this));
@ -1248,21 +1269,16 @@ void Mob::AI_Process() {
bool is_combat_range = CombatRange(target); bool is_combat_range = CombatRange(target);
if (is_combat_range) if (is_combat_range) {
{ if (AI_movement_timer->Check()) {
if (AI_movement_timer->Check()) if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) {
{
if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition(); SendPosition();
} }
SetCurrentSpeed(0); SetCurrentSpeed(0);
} }
if (IsMoving()) if (IsMoving()) {
{ if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) {
if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition(); SendPosition();
} }
@ -1330,16 +1346,17 @@ void Mob::AI_Process() {
} }
if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) {
if (spellbonuses.PC_Pet_Rampage[0] || itembonuses.PC_Pet_Rampage[0] || aabonuses.PC_Pet_Rampage[0]) { if (spellbonuses.PC_Pet_Rampage[0] || itembonuses.PC_Pet_Rampage[0] ||
int chance = spellbonuses.PC_Pet_Rampage[0] + itembonuses.PC_Pet_Rampage[0] + aabonuses.PC_Pet_Rampage[0]; aabonuses.PC_Pet_Rampage[0]) {
int chance = spellbonuses.PC_Pet_Rampage[0] + itembonuses.PC_Pet_Rampage[0] +
aabonuses.PC_Pet_Rampage[0];
if (zone->random.Roll(chance)) { if (zone->random.Roll(chance)) {
Rampage(nullptr); Rampage(nullptr);
} }
} }
} }
if (GetSpecialAbility(SPECATK_RAMPAGE) && !specialed) if (GetSpecialAbility(SPECATK_RAMPAGE) && !specialed) {
{
int rampage_chance = GetSpecialAbilityParam(SPECATK_RAMPAGE, 0); int rampage_chance = GetSpecialAbilityParam(SPECATK_RAMPAGE, 0);
rampage_chance = rampage_chance > 0 ? rampage_chance : 20; rampage_chance = rampage_chance > 0 ? rampage_chance : 20;
if (zone->random.Roll(rampage_chance)) { if (zone->random.Roll(rampage_chance)) {
@ -1373,8 +1390,7 @@ void Mob::AI_Process() {
} }
} }
if (GetSpecialAbility(SPECATK_AREA_RAMPAGE) && !specialed) if (GetSpecialAbility(SPECATK_AREA_RAMPAGE) && !specialed) {
{
int rampage_chance = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 0); int rampage_chance = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 0);
rampage_chance = rampage_chance > 0 ? rampage_chance : 20; rampage_chance = rampage_chance > 0 ? rampage_chance : 20;
if (zone->random.Roll(rampage_chance)) { if (zone->random.Roll(rampage_chance)) {
@ -1434,7 +1450,7 @@ void Mob::AI_Process() {
Heal(); Heal();
BuffFadeAll(); BuffFadeAll();
AI_walking_timer->Start(100); AI_walking_timer->Start(100);
pLastFightingDelayMoving = Timer::GetCurrentTime(); time_until_can_move = Timer::GetCurrentTime();
return; return;
} }
else if (tar != nullptr) { else if (tar != nullptr) {
@ -1445,8 +1461,7 @@ void Mob::AI_Process() {
} }
// See if we can summon the mob to us // See if we can summon the mob to us
if (!HateSummon()) if (!HateSummon()) {
{
//could not summon them, check ranged... //could not summon them, check ranged...
if (GetSpecialAbility(SPECATK_RANGED_ATK)) if (GetSpecialAbility(SPECATK_RANGED_ATK))
doranged = true; doranged = true;
@ -1456,18 +1471,18 @@ void Mob::AI_Process() {
if (AI_PursueCastCheck()) { if (AI_PursueCastCheck()) {
//we did something, so do not process movement. //we did something, so do not process movement.
} }
else if (AI_movement_timer->Check() && target) else if (AI_movement_timer->Check() && target) {
{
if (!IsRooted()) { if (!IsRooted()) {
Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName()); Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName());
if (!RuleB(Pathing, Aggro) || !zone->pathing) if (!RuleB(Pathing, Aggro) || !zone->pathing)
CalculateNewPosition(target->GetX(), target->GetY(), target->GetZ(), GetRunspeed()); CalculateNewPosition(target->GetX(), target->GetY(), target->GetZ(), GetRunspeed());
else else {
{
bool WaypointChanged, NodeReached; bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(target->GetX(), target->GetY(), target->GetZ(), glm::vec3 Goal = UpdatePath(
GetRunspeed(), WaypointChanged, NodeReached); target->GetX(), target->GetY(), target->GetZ(),
GetRunspeed(), WaypointChanged, NodeReached
);
if (WaypointChanged) if (WaypointChanged)
tar_ndx = 20; tar_ndx = 20;
@ -1503,106 +1518,102 @@ void Mob::AI_Process() {
if (remember_client == nullptr) { if (remember_client == nullptr) {
//they are gone now... //they are gone now...
RememberedCharID = feign_memory_list.erase(RememberedCharID); RememberedCharID = feign_memory_list.erase(RememberedCharID);
} else if (!remember_client->GetFeigned()) { }
else if (!remember_client->GetFeigned()) {
AddToHateList(remember_client->CastToMob(), 1); AddToHateList(remember_client->CastToMob(), 1);
RememberedCharID = feign_memory_list.erase(RememberedCharID); RememberedCharID = feign_memory_list.erase(RememberedCharID);
break; break;
} else { }
else {
//they are still feigned, carry on... //they are still feigned, carry on...
++RememberedCharID; ++RememberedCharID;
} }
} }
} }
if (AI_IdleCastCheck()) if (AI_IdleCastCheck()) {
{
//we processed a spell action, so do nothing else. //we processed a spell action, so do nothing else.
} }
else if (zone->CanDoCombat() && CastToNPC()->WillAggroNPCs() && AI_scan_area_timer->Check()) else if (zone->CanDoCombat() && CastToNPC()->WillAggroNPCs() && AI_scan_area_timer->Check()) {
{
/* /*
* NPC to NPC aggro checking, npc needs npc_aggro flag * NPC to NPC aggro checking, npc needs npc_aggro flag
*/ */
Mob *temp_target = entity_list.AICheckNPCtoNPCAggro(this, GetAggroRange(), GetAssistRange()); Mob *temp_target = entity_list.AICheckNPCtoNPCAggro(this, GetAggroRange(), GetAssistRange());
if (temp_target) { if (temp_target) {
AddToHateList(temp_target); AddToHateList(temp_target);
} }
AI_scan_area_timer->Disable(); AI_scan_area_timer->Disable();
AI_scan_area_timer->Start(RandomTimer(RuleI(NPC, NPCToNPCAggroTimerMin), RuleI(NPC, NPCToNPCAggroTimerMax)), false); AI_scan_area_timer->Start(
RandomTimer(RuleI(NPC, NPCToNPCAggroTimerMin), RuleI(NPC, NPCToNPCAggroTimerMax)),
false
);
} }
else if (AI_movement_timer->Check() && !IsRooted()) else if (AI_movement_timer->Check() && !IsRooted()) {
{ if (IsPet()) {
if (IsPet())
{
// we're a pet, do as we're told // we're a pet, do as we're told
switch (pStandingPetOrder) switch (pStandingPetOrder) {
{ case SPO_Follow: {
case SPO_Follow:
{
Mob *owner = GetOwner(); Mob *owner = GetOwner();
if(owner == nullptr) if (owner == nullptr) {
break; break;
}
//if(owner->IsClient()) glm::vec4 pet_owner_position = owner->GetPosition();
// printf("Pet start pos: (%f, %f, %f)\n", GetX(), GetY(), GetZ()); float distance_to_owner = DistanceSquared(m_Position, pet_owner_position);
float z_distance = pet_owner_position.z - m_Position.z;
glm::vec4 ownerPos = owner->GetPosition(); if (distance_to_owner >= 400 || z_distance > 100) {
float dist = DistanceSquared(m_Position, ownerPos);
float distz = ownerPos.z - m_Position.z;
if (dist >= 400 || distz > 100) int pet_speed = GetWalkspeed();
{
int speed = GetWalkspeed();
if (dist >= 1225) // 35
speed = GetRunspeed();
if (dist >= 202500 || distz > 100) // dist >= 450 /**
{ * Distance: >= 35 (Run if far away)
m_Position = ownerPos; */
if (distance_to_owner >= 1225) {
pet_speed = GetRunspeed();
}
/**
* Distance: >= 450 (Snap to owner)
*/
if (distance_to_owner >= 202500 || z_distance > 100) {
m_Position = pet_owner_position;
SendPositionUpdate(); SendPositionUpdate();
moved = true; moved = true;
} }
else else {
{
CalculateNewPosition(owner->GetX(), owner->GetY(), owner->GetZ(), speed); bool waypoint_changed, node_reached;
glm::vec3 Goal = UpdatePath(
owner->GetX(),
owner->GetY(),
owner->GetZ(),
pet_speed,
waypoint_changed,
node_reached
);
CalculateNewPosition(Goal.x, Goal.y, Goal.z, pet_speed, true);
} }
} }
else else {
{ if (moved) {
if(moved)
{
this->FixZ(); this->FixZ();
SetCurrentSpeed(0); SetCurrentSpeed(0);
moved = false; moved = false;
} }
} }
/*
//fix up Z
float zdiff = GetZ() - owner->GetZ();
if(zdiff < 0)
zdiff = 0 - zdiff;
if(zdiff > 2.0f) {
SendTo(GetX(), GetY(), owner->GetZ());
SendPosition();
}
if(owner->IsClient())
printf("Pet pos: (%f, %f, %f)\n", GetX(), GetY(), GetZ());
*/
break; break;
} }
case SPO_Sit: case SPO_Sit: {
{
SetAppearance(eaSitting, false); SetAppearance(eaSitting, false);
break; break;
} }
case SPO_Guard: case SPO_Guard: {
{
//only NPCs can guard stuff. (forced by where the guard movement code is in the AI) //only NPCs can guard stuff. (forced by where the guard movement code is in the AI)
if (IsNPC()) { if (IsNPC()) {
CastToNPC()->NextGuardPosition(); CastToNPC()->NextGuardPosition();
@ -1610,28 +1621,45 @@ void Mob::AI_Process() {
break; break;
} }
} }
if (IsPetRegroup()) if (IsPetRegroup()) {
return; return;
} }
/* Entity has been assigned another entity to follow */
else if (GetFollowID())
{
Mob* follow = entity_list.GetMob(GetFollowID());
if (!follow) SetFollowID(0);
else
{
float dist2 = DistanceSquared(m_Position, follow->GetPosition());
int followdist = GetFollowDistance();
if (dist2 >= followdist) // Default follow distance is 100
{
int speed = GetWalkspeed();
if (dist2 >= followdist + 150)
speed = GetRunspeed();
CalculateNewPosition(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
} }
else /* Entity has been assigned another entity to follow */
{ else if (GetFollowID()) {
Mob *follow = entity_list.GetMob(static_cast<uint16>(GetFollowID()));
if (!follow) {
SetFollowID(0);
}
else {
float distance = DistanceSquared(m_Position, follow->GetPosition());
int follow_distance = GetFollowDistance();
/**
* Default follow distance is 100
*/
if (distance >= follow_distance) {
int speed = GetWalkspeed();
if (distance >= follow_distance + 150) {
speed = GetRunspeed();
}
bool waypoint_changed, node_reached;
glm::vec3 Goal = UpdatePath(
follow->GetX(),
follow->GetY(),
follow->GetZ(),
speed,
waypoint_changed,
node_reached
);
CalculateNewPosition(Goal.x, Goal.y, Goal.z, speed, true);
}
else {
moved = false; moved = false;
SetCurrentSpeed(0); SetCurrentSpeed(0);
} }
@ -1640,20 +1668,21 @@ void Mob::AI_Process() {
else //not a pet, and not following somebody... else //not a pet, and not following somebody...
{ {
// dont move till a bit after you last fought // dont move till a bit after you last fought
if (pLastFightingDelayMoving < Timer::GetCurrentTime()) if (time_until_can_move < Timer::GetCurrentTime()) {
{ if (this->IsClient()) {
if (this->IsClient())
{ /**
// LD timer expired, drop out of world * LD timer expired, drop out of world
if (this->CastToClient()->IsLD()) */
if (this->CastToClient()->IsLD()) {
this->CastToClient()->Disconnect(); this->CastToClient()->Disconnect();
}
return; return;
} }
if(IsNPC()) if (IsNPC()) {
{ if (RuleB(NPC, SmartLastFightingDelayMoving) && !feign_memory_list.empty()) {
if(RuleB(NPC, SmartLastFightingDelayMoving) && !feign_memory_list.empty())
{
minLastFightingDelayMoving = 0; minLastFightingDelayMoving = 0;
maxLastFightingDelayMoving = 0; maxLastFightingDelayMoving = 0;
} }
@ -1661,92 +1690,144 @@ void Mob::AI_Process() {
CastToNPC()->AI_DoMovement(); CastToNPC()->AI_DoMovement();
} }
} }
} }
} // else if (AImovement_timer->Check()) }
} }
//Do Ranged attack here //Do Ranged attack here
if(doranged) if (doranged) {
{
RangedAttack(target); RangedAttack(target);
} }
} }
void NPC::AI_DoMovement() { void NPC::AI_DoMovement() {
float walksp = GetMovespeed();
if(walksp <= 0.0f)
return; //this is idle movement at walk speed, and we are unable to walk right now.
float move_speed = GetMovespeed();
if (move_speed <= 0.0f) {
return;
}
/**
* Roambox logic sets precedence
*/
if (roambox_distance > 0) { if (roambox_distance > 0) {
if (
roambox_movingto_x > roambox_max_x if (!IsMoving()) {
|| roambox_movingto_x < roambox_min_x auto move_x = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
|| roambox_movingto_y > roambox_max_y auto move_y = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
|| roambox_movingto_y < roambox_min_y
) roambox_destination_x = EQEmu::Clamp((GetX() + move_x), roambox_min_x, roambox_max_x);
{ roambox_destination_y = EQEmu::Clamp((GetY() + move_y), roambox_min_y, roambox_max_y);
float movedist = roambox_distance*roambox_distance;
float movex = zone->random.Real(0, movedist); /**
float movey = movedist - movex; * If our roambox was configured with large distances, chances of hitting the min or max end of
movex = sqrtf(movex); * the clamp is high, this causes NPC's to gather on the border of a box, to reduce clustering
movey = sqrtf(movey); * either lower the roambox distance or the code will do a simple random between min - max when it
movex *= zone->random.Int(0, 1) ? 1 : -1; * hits the min or max of the clamp
movey *= zone->random.Int(0, 1) ? 1 : -1; */
roambox_movingto_x = GetX() + movex; if (roambox_destination_x == roambox_min_x || roambox_destination_x == roambox_max_x) {
roambox_movingto_y = GetY() + movey; roambox_destination_x = static_cast<float>(zone->random.Real(roambox_min_x, roambox_max_x));
//Try to calculate new coord using distance.
if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
roambox_movingto_x -= movex * 2;
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
roambox_movingto_y -= movey * 2;
//New coord is still invalid, ignore distance and just pick a new random coord.
//If we're here we may have a roambox where one side is shorter than the specified distance. Commons, Wkarana, etc.
if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
roambox_movingto_x = zone->random.Real(roambox_min_x+1,roambox_max_x-1);
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
roambox_movingto_y = zone->random.Real(roambox_min_y+1,roambox_max_y-1);
Log(Logs::Detail, Logs::AI,
"Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)",
roambox_distance, roambox_min_x, roambox_max_x, roambox_min_y,
roambox_max_y, roambox_movingto_x, roambox_movingto_y);
} }
Log(Logs::Detail, Logs::AI, "Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)", if (roambox_destination_y == roambox_min_y || roambox_destination_y == roambox_max_y) {
roambox_distance, roambox_min_x, roambox_max_x, roambox_min_y, roambox_max_y, roambox_movingto_x, roambox_movingto_y); roambox_destination_y = static_cast<float>(zone->random.Real(roambox_min_y, roambox_max_y));
}
float new_z = this->FindGroundZ(m_Position.x, m_Position.y, 5) + GetZOffset(); /**
* If mob was not spawned in water, let's not randomly roam them into water
* if the roam box was sloppily configured
*/
if (!this->GetWasSpawnedInWater()) {
if (zone->HasMap() && zone->HasWaterMap()) {
auto position = glm::vec3(
roambox_destination_x,
roambox_destination_y,
(m_Position.z - 15)
);
if (!CalculateNewPosition(roambox_movingto_x, roambox_movingto_y, new_z, walksp, true)) /**
{ * If someone brought us into water when we naturally wouldn't path there, return to spawn
this->FixZ(); // FixZ on final arrival point. */
roambox_movingto_x = roambox_max_x + 1; // force update if (zone->watermap->InLiquid(position) && zone->watermap->InLiquid(m_Position)) {
pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay); roambox_destination_x = m_SpawnPoint.x;
roambox_destination_y = m_SpawnPoint.y;
}
if (zone->watermap->InLiquid(position)) {
Log(Logs::Detail,
Logs::NPCRoamBox, "%s | My destination is in water and I don't belong there!",
this->GetCleanName());
return;
}
}
}
glm::vec3 destination;
destination.x = roambox_destination_x;
destination.y = roambox_destination_y;
destination.z = m_Position.z;
roambox_destination_z = GetFixedZ(destination) + this->GetZOffset();
Log(Logs::Detail,
Logs::NPCRoamBox,
"Calculate | NPC: %s distance %.3f | min_x %.3f | max_x %.3f | final_x %.3f | min_y %.3f | max_y %.3f | final_y %.3f",
this->GetCleanName(),
roambox_distance,
roambox_min_x,
roambox_max_x,
roambox_destination_x,
roambox_min_y,
roambox_max_y,
roambox_destination_y);
}
bool waypoint_changed, node_reached;
glm::vec3 Goal = UpdatePath(
roambox_destination_x,
roambox_destination_y,
roambox_destination_z,
move_speed,
waypoint_changed,
node_reached
);
CalculateNewPosition(Goal.x, Goal.y, Goal.z, move_speed, true);
if (m_Position.x == roambox_destination_x && m_Position.y == roambox_destination_y) {
time_until_can_move = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay);
SetMoving(false); SetMoving(false);
SendPosition(); // makes mobs stop clientside this->FixZ();
SendPosition();
} }
return;
} }
else if (roamer) else if (roamer) {
{ if (AI_walking_timer->Check()) {
if (AI_walking_timer->Check())
{
movetimercompleted = true; movetimercompleted = true;
AI_walking_timer->Disable(); AI_walking_timer->Disable();
} }
int32 gridno = CastToNPC()->GetGrid(); int32 gridno = CastToNPC()->GetGrid();
if (gridno > 0 || cur_wp == -2) { if (gridno > 0 || cur_wp == -2) {
if (movetimercompleted == true) { // time to pause at wp is over if (movetimercompleted == true) { // time to pause at wp is over
AI_SetupNextWaypoint(); AI_SetupNextWaypoint();
} // endif (movetimercompleted==true) } // endif (movetimercompleted==true)
else if (!(AI_walking_timer->Enabled())) else if (!(AI_walking_timer->Enabled())) { // currently moving
{ // currently moving
bool doMove = true; bool doMove = true;
if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) { // are we there yet? then stop
{ // are we there yet? then stop Log(Logs::Detail,
Log(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); Logs::AI,
"We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d",
cur_wp,
GetX(),
GetY(),
GetZ(),
GetGrid());
SetWaypointPause(); SetWaypointPause();
SetAppearance(eaStanding, false); SetAppearance(eaStanding, false);
@ -1776,32 +1857,41 @@ void NPC::AI_DoMovement() {
if (cur_wp == 1) if (cur_wp == 1)
ClearFeignMemory(); ClearFeignMemory();
} }
if (doMove) if (doMove) { // not at waypoint yet or at 0 pause WP, so keep moving
{ // not at waypoint yet or at 0 pause WP, so keep moving
if (!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0)) if (!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0))
CalculateNewPosition(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true); CalculateNewPosition(
else m_CurrentWayPoint.x,
{ m_CurrentWayPoint.y,
m_CurrentWayPoint.z,
move_speed,
true
);
else {
bool WaypointChanged; bool WaypointChanged;
bool NodeReached; bool NodeReached;
glm::vec3 Goal = UpdatePath(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, WaypointChanged, NodeReached); glm::vec3 Goal = UpdatePath(
m_CurrentWayPoint.x,
m_CurrentWayPoint.y,
m_CurrentWayPoint.z,
move_speed,
WaypointChanged,
NodeReached
);
if (WaypointChanged) if (WaypointChanged)
tar_ndx = 20; tar_ndx = 20;
if (NodeReached) if (NodeReached)
entity_list.OpenDoorsNear(CastToNPC()); entity_list.OpenDoorsNear(CastToNPC());
CalculateNewPosition(Goal.x, Goal.y, Goal.z, walksp, true); CalculateNewPosition(Goal.x, Goal.y, Goal.z, move_speed, true);
} }
} }
} }
} // endif (gridno > 0) } // endif (gridno > 0)
// handle new quest grid command processing // handle new quest grid command processing
else if (gridno < 0) else if (gridno < 0) { // this mob is under quest control
{ // this mob is under quest control if (movetimercompleted == true) { // time to pause has ended
if (movetimercompleted==true)
{ // time to pause has ended
SetGrid(0 - GetGrid()); // revert to AI control SetGrid(0 - GetGrid()); // revert to AI control
Log(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid()); Log(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid());
@ -1812,39 +1902,55 @@ void NPC::AI_DoMovement() {
} }
} }
else if (IsGuarding()) else if (IsGuarding()) {
{
bool CP2Moved; bool CP2Moved;
if(!RuleB(Pathing, Guard) || !zone->pathing) if (!RuleB(Pathing, Guard) || !zone->pathing) {
CP2Moved = CalculateNewPosition(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z, walksp); CP2Moved = CalculateNewPosition(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z, move_speed);
else }
{ else {
if(!((m_Position.x == m_GuardPoint.x) && (m_Position.y == m_GuardPoint.y) && (m_Position.z == m_GuardPoint.z))) if (!((m_Position.x == m_GuardPoint.x) && (m_Position.y == m_GuardPoint.y) &&
{ (m_Position.z == m_GuardPoint.z))) {
bool WaypointChanged, NodeReached; bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z, walksp, WaypointChanged, NodeReached);
if(WaypointChanged) glm::vec3 Goal = UpdatePath(
m_GuardPoint.x,
m_GuardPoint.y,
m_GuardPoint.z,
move_speed,
WaypointChanged,
NodeReached
);
if (WaypointChanged) {
tar_ndx = 20; tar_ndx = 20;
}
if(NodeReached) if (NodeReached) {
entity_list.OpenDoorsNear(CastToNPC()); entity_list.OpenDoorsNear(CastToNPC());
CP2Moved = CalculateNewPosition(Goal.x, Goal.y, Goal.z, walksp);
} }
else
CP2Moved = CalculateNewPosition(Goal.x, Goal.y, Goal.z, move_speed);
}
else {
CP2Moved = false; CP2Moved = false;
}
} }
if (!CP2Moved) if (!CP2Moved) {
{
if (moved) { if (moved) {
Log(Logs::Detail, Logs::AI, "Reached guard point (%.3f,%.3f,%.3f)", m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z); Log(Logs::Detail,
Logs::AI,
"Reached guard point (%.3f,%.3f,%.3f)",
m_GuardPoint.x,
m_GuardPoint.y,
m_GuardPoint.z);
ClearFeignMemory(); ClearFeignMemory();
moved = false; moved = false;
if (GetTarget() == nullptr || DistanceSquared(m_Position, GetTarget()->GetPosition()) >= 5*5 ) if (GetTarget() == nullptr || DistanceSquared(m_Position, GetTarget()->GetPosition()) >= 5 * 5) {
{
SetHeading(m_GuardPoint.w); SetHeading(m_GuardPoint.w);
} else { }
else {
FaceTarget(GetTarget()); FaceTarget(GetTarget());
} }
SetCurrentSpeed(0); SetCurrentSpeed(0);
@ -1968,11 +2074,11 @@ void Mob::AI_Event_NoLongerEngaged() {
if (!IsAIControlled()) if (!IsAIControlled())
return; return;
this->AI_walking_timer->Start(RandomTimer(3000,20000)); this->AI_walking_timer->Start(RandomTimer(3000,20000));
pLastFightingDelayMoving = Timer::GetCurrentTime(); time_until_can_move = Timer::GetCurrentTime();
if (minLastFightingDelayMoving == maxLastFightingDelayMoving) if (minLastFightingDelayMoving == maxLastFightingDelayMoving)
pLastFightingDelayMoving += minLastFightingDelayMoving; time_until_can_move += minLastFightingDelayMoving;
else else
pLastFightingDelayMoving += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving); time_until_can_move += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving);
// So mobs don't keep running as a ghost until AIwalking_timer fires // So mobs don't keep running as a ghost until AIwalking_timer fires
// if they were moving prior to losing all hate // if they were moving prior to losing all hate
// except if we're a pet, then we might run into some issues with pets backing off when they should immediately be moving // except if we're a pet, then we might run into some issues with pets backing off when they should immediately be moving
@ -2255,8 +2361,6 @@ void Mob::AreaRampage(ExtraAttackOptions *opts)
m_specialattacks = eSpecialAttacks::AERampage; m_specialattacks = eSpecialAttacks::AERampage;
index_hit = hate_list.AreaRampage(this, GetTarget(), rampage_targets, opts); index_hit = hate_list.AreaRampage(this, GetTarget(), rampage_targets, opts);
if(index_hit == 0)
ProcessAttackRounds(GetTarget(), opts);
m_specialattacks = eSpecialAttacks::None; m_specialattacks = eSpecialAttacks::None;
} }

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_max_y = -2;
roambox_min_x = -2; roambox_min_x = -2;
roambox_min_y = -2; roambox_min_y = -2;
roambox_movingto_x = -2; roambox_destination_x = -2;
roambox_movingto_y = -2; roambox_destination_y = -2;
roambox_min_delay = 1000; roambox_min_delay = 1000;
roambox_delay = 1000; roambox_delay = 1000;
p_depop = false; p_depop = false;

View File

@ -311,9 +311,17 @@ public:
void SaveGuardSpot(bool iClearGuardSpot = false); void SaveGuardSpot(bool iClearGuardSpot = false);
inline bool IsGuarding() const { return(m_GuardPoint.w != 0); } inline bool IsGuarding() const { return(m_GuardPoint.w != 0); }
void SaveGuardSpotCharm(); void SaveGuardSpotCharm();
void RestoreGuardSpotCharm(); 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 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 //mercenary stuff
void LoadMercTypes(); void LoadMercTypes();
@ -530,8 +538,9 @@ protected:
float roambox_min_x; float roambox_min_x;
float roambox_min_y; float roambox_min_y;
float roambox_distance; float roambox_distance;
float roambox_movingto_x; float roambox_destination_x;
float roambox_movingto_y; float roambox_destination_y;
float roambox_destination_z;
uint32 roambox_delay; uint32 roambox_delay;
uint32 roambox_min_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 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->DeleteItemInInventory(EQEmu::invslot::slotCursor);
sender->DropItemQS(m_inst, true);
if(!m_ground_spawn) if(!m_ground_spawn)
safe_delete(m_inst); safe_delete(m_inst);

View File

@ -145,10 +145,11 @@ bool Spawn2::Process() {
return true; return true;
//grab our spawn group //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; return true;
}
if (timer.Check()) { if (timer.Check()) {
timer.Disable(); timer.Disable();
@ -159,25 +160,39 @@ bool Spawn2::Process() {
//then we reset the timer and try again next time. //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)) { && !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(); Reset();
return (true); return (true);
} }
if (sg == nullptr) { if (spawn_group == nullptr) {
database.LoadSpawnGroupsByID(spawngroup_id_, &zone->spawn_group_list); database.LoadSpawnGroupsByID(spawngroup_id_, &zone->spawn_group_list);
sg = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_); spawn_group = zone->spawn_group_list.GetSpawnGroup(spawngroup_id_);
} }
if (sg == nullptr) { if (spawn_group == nullptr) {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Unable to locate spawn group %d. Disabling.", spawn2_id, spawngroup_id_); Log(Logs::Detail,
Logs::Spawns,
"Spawn2 %d: Unable to locate spawn group %d. Disabling.",
spawn2_id,
spawngroup_id_);
return false; return false;
} }
//have the spawn group pick an NPC for us //have the spawn group pick an NPC for us
uint32 npcid = sg->GetNPCType(); uint32 npcid = spawn_group->GetNPCType();
if (npcid == 0) { if (npcid == 0) {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn group %d did not yeild an NPC! not spawning.", spawn2_id, spawngroup_id_); 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?) Reset(); //try again later (why?)
return (true); return (true);
} }
@ -185,16 +200,24 @@ bool Spawn2::Process() {
//try to find our NPC type. //try to find our NPC type.
const NPCType *tmp = database.LoadNPCTypesData(npcid); const NPCType *tmp = database.LoadNPCTypesData(npcid);
if (tmp == nullptr) { if (tmp == nullptr) {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn group %d yeilded an invalid NPC type %d", spawn2_id, spawngroup_id_, npcid); 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 Reset(); //try again later
return (true); return (true);
} }
if(tmp->unique_spawn_by_name) if (tmp->unique_spawn_by_name) {
{ if (!entity_list.LimitCheckName(tmp->name)) {
if(!entity_list.LimitCheckName(tmp->name)) Log(Logs::Detail,
{ Logs::Spawns,
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); "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. timer.Start(5000); //try again in five seconds.
return (true); return (true);
} }
@ -202,30 +225,32 @@ bool Spawn2::Process() {
if (tmp->spawn_limit > 0) { if (tmp->spawn_limit > 0) {
if (!entity_list.LimitCheckType(npcid, tmp->spawn_limit)) { 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); 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. timer.Start(5000); //try again in five seconds.
return (true); return (true);
} }
} }
bool ignore_despawn = false; bool ignore_despawn = false;
if (npcthis) if (npcthis) {
{
ignore_despawn = npcthis->IgnoreDespawn(); ignore_despawn = npcthis->IgnoreDespawn();
} }
if (ignore_despawn) if (ignore_despawn) {
{
return true; 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); zone->Despawn(spawn2_id);
} }
if (IsDespawned) if (IsDespawned) {
{
return true; return true;
} }
@ -236,23 +261,61 @@ bool Spawn2::Process() {
npcthis = npc; npcthis = npc;
npc->AddLootTable(); npc->AddLootTable();
if (npc->DropsGlobalLoot()) if (npc->DropsGlobalLoot()) {
npc->CheckGlobalLootTables(); npc->CheckGlobalLootTables();
}
npc->SetSp2(spawngroup_id_); npc->SetSp2(spawngroup_id_);
npc->SaveGuardPointAnim(anim); npc->SaveGuardPointAnim(anim);
npc->SetAppearance((EmuAppearance) anim); npc->SetAppearance((EmuAppearance) anim);
entity_list.AddNPC(npc); entity_list.AddNPC(npc);
//this limit add must be done after the AddNPC since we need the entity ID. //this limit add must be done after the AddNPC since we need the entity ID.
entity_list.LimitAddNPC(npc); 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); /**
* 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()) { 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); 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(); 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; return true;
} }
@ -278,7 +341,6 @@ void Spawn2::LoadGrid() {
Log(Logs::Detail, Logs::Spawns, "Spawn2 %d: Loading grid %d for %s", spawn2_id, grid_, npcthis->GetName()); 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 All three of these actions basically say that the mob which was
associated with this spawn point is no longer relavent. 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(); Mob* tempmob = GetOwner();
SetOwnerID(0); SetOwnerID(0);
SetPetType(petNone); SetPetType(petNone);
SetHeld(false);
SetGHeld(false);
SetNoCast(false);
SetFocused(false);
SetPetStop(false);
SetPetRegroup(false);
if(tempmob) if(tempmob)
{ {
tempmob->SetPet(0); 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]); 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); caster->Message_StringID(MT_Shout, CANNOT_MEZ_WITH_SPELL);
AddToHateList(caster, 1,0,true,false,false,spell_id);
return true; 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]); 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); caster->Message_StringID(MT_Shout, CANNOT_CHARM_YET);
AddToHateList(caster, 1,0,true,false,false,spell_id);
return true; return true;
} }
} }

View File

@ -30,7 +30,7 @@ bool WaterMapV2::InLava(const glm::vec3& location) const {
} }
bool WaterMapV2::InLiquid(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 { bool WaterMapV2::InPvP(const glm::vec3& location) const {

View File

@ -42,19 +42,27 @@ struct wp_distance
int index; int index;
}; };
void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay, uint32 iMinDelay) { void NPC::AI_SetRoambox(float max_distance, float roam_distance_variance, uint32 delay, uint32 min_delay) {
AI_SetRoambox(iDist, GetX() + iRoamDist, GetX() - iRoamDist, GetY() + iRoamDist, GetY() - iRoamDist, iDelay, iMinDelay); 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) { 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 = iDist; roambox_distance = distance;
roambox_max_x = iMaxX; roambox_max_x = max_x;
roambox_min_x = iMinX; roambox_min_x = min_x;
roambox_max_y = iMaxY; roambox_max_y = max_y;
roambox_min_y = iMinY; roambox_min_y = min_y;
roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc roambox_destination_x = roambox_max_x + 1; // this will trigger a recalc
roambox_delay = iDelay; roambox_delay = delay;
roambox_min_delay = iMinDelay; roambox_min_delay = min_delay;
} }
void NPC::DisplayWaypointInfo(Client *c) { void NPC::DisplayWaypointInfo(Client *c) {
@ -199,7 +207,7 @@ void NPC::MoveTo(const glm::vec4& position, bool saveguardspot)
} }
cur_wp_pause = 0; cur_wp_pause = 0;
pLastFightingDelayMoving = 0; time_until_can_move = 0;
if (AI_walking_timer->Enabled()) if (AI_walking_timer->Enabled())
AI_walking_timer->Start(100); 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))); 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) if (GetID() == 0)
return true; return true;
if (speed <= 0) if (speed <= 0) {
{
SetCurrentSpeed(0); SetCurrentSpeed(0);
return true; return true;
} }
if ((m_Position.x - x == 0) && (m_Position.y - y == 0)) { //spawn is at target coords 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;
}
return false; return false;
} }
else if ((std::abs(m_Position.x - x) < 0.1) && (std::abs(m_Position.y - y) < 0.1)) { 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.x = x;
m_Position.y = y; m_Position.y = y;
m_Position.z = z; m_Position.z = z;
return true; return true;
} }
bool send_update = false;
int compare_steps = 20; int compare_steps = 20;
if (tar_ndx < compare_steps && m_TargetLocation.x == x && m_TargetLocation.y == y) { 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_x = m_Position.x + m_TargetV.x * tar_vector;
float new_y = m_Position.y + m_TargetV.y * 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_z = m_Position.z + m_TargetV.z * tar_vector;
if (IsNPC()) { if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
} }
@ -482,14 +485,14 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Position.y = new_y; m_Position.y = new_y;
m_Position.z = new_z; m_Position.z = new_z;
if(checkZ && fix_z_timer.Check() && if (check_z && fix_z_timer.Check() && (!this->IsEngaged() || flee_mode || currently_fleeing)) {
(!this->IsEngaged() || flee_mode || currently_fleeing))
this->FixZ(); this->FixZ();
tar_ndx++;
return true;
} }
tar_ndx++;
return true;
}
if (tar_ndx > 50) { if (tar_ndx > 50) {
tar_ndx--; tar_ndx--;
@ -497,6 +500,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
else { else {
tar_ndx = 0; tar_ndx = 0;
} }
m_TargetLocation = glm::vec3(x, y, z); m_TargetLocation = glm::vec3(x, y, z);
float nx = this->m_Position.x; float nx = this->m_Position.x;
@ -530,10 +534,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
// mob move fix // mob move fix
if (numsteps<20) if (numsteps < 20) {
{ if (numsteps > 1) {
if (numsteps>1)
{
tar_vector = 1.0f; tar_vector = 1.0f;
m_TargetV.x = m_TargetV.x / (float) numsteps; m_TargetV.x = m_TargetV.x / (float) numsteps;
m_TargetV.y = m_TargetV.y / (float) numsteps; m_TargetV.y = m_TargetV.y / (float) numsteps;
@ -549,12 +551,12 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Position.x = new_x; m_Position.x = new_x;
m_Position.y = new_y; m_Position.y = new_y;
m_Position.z = new_z; m_Position.z = new_z;
if (calcHeading) if (calculate_heading) {
m_Position.w = CalculateHeadingToTarget(x, y); m_Position.w = CalculateHeadingToTarget(x, y);
}
tar_ndx = 20 - numsteps; tar_ndx = 20 - numsteps;
} }
else else {
{
if (IsNPC()) { if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), x, y, z); entity_list.ProcessMove(CastToNPC(), x, y, z);
} }
@ -581,6 +583,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
float new_x = m_Position.x + m_TargetV.x * 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_y = m_Position.y + m_TargetV.y * tar_vector;
float new_z = m_Position.z + m_TargetV.z * tar_vector; float new_z = m_Position.z + m_TargetV.z * tar_vector;
if (IsNPC()) { if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z); 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.x = new_x;
m_Position.y = new_y; m_Position.y = new_y;
m_Position.z = new_z; m_Position.z = new_z;
if (calcHeading) if (calculate_heading) {
m_Position.w = CalculateHeadingToTarget(x, y); 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(); this->FixZ();
SetMoving(true); 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); m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f);
if (IsClient()) if (IsClient()) {
{
SendPositionUpdate(1); SendPositionUpdate(1);
CastToClient()->ResetPositionTimer(); CastToClient()->ResetPositionTimer();
} }
else else {
{
SendPositionUpdate(); SendPositionUpdate();
SetAppearance(eaStanding, false); SetAppearance(eaStanding, false);
} }
@ -615,8 +617,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
return true; return true;
} }
bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ, bool calcHeading) { bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool check_z, bool calculate_heading) {
return MakeNewPositionAndSendUpdate(x, y, z, speed); return MakeNewPositionAndSendUpdate(x, y, z, speed, check_z);
} }
void NPC::AssignWaypoints(int32 grid) 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; BenchTimer timer;
timer.reset(); timer.reset();
float new_z = dest.z;
float new_z = destination.z;
if (zone->HasMap() && RuleB(Map, FixZWhenMoving)) { 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 * 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) { if (new_z != BEST_Z_INVALID) {
new_z += this->GetZOffset(); new_z += this->GetZOffset();
@ -776,31 +779,48 @@ float Mob::GetFixedZ(glm::vec3 dest, int32 z_find_offset) {
auto duration = timer.elapsed(); auto duration = timer.elapsed();
Log(Logs::Moderate, Logs::FixZ, Log(Logs::Moderate,
Logs::FixZ,
"Mob::GetFixedZ() (%s) returned %4.3f at %4.3f, %4.3f, %4.3f - Took %lf", "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); this->GetCleanName(),
new_z,
destination.x,
destination.y,
destination.z,
duration);
} }
return new_z; 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); glm::vec3 current_loc(m_Position);
if (IsClient() && !fix_client_z)
return;
float new_z = GetFixedZ(current_loc, z_find_offset); float new_z = GetFixedZ(current_loc, z_find_offset);
if (!IsClient() && new_z != m_Position.z) { if (new_z == m_Position.z)
return;
if ((new_z > -2000) && new_z != BEST_Z_INVALID) { if ((new_z > -2000) && new_z != BEST_Z_INVALID) {
if (RuleB(Map, MobZVisualDebug)) if (RuleB(Map, MobZVisualDebug)) {
this->SendAppearanceEffect(78, 0, 0, 0, 0); this->SendAppearanceEffect(78, 0, 0, 0, 0);
}
m_Position.z = new_z; 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));
} }
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

@ -143,7 +143,11 @@ bool ZoneDatabase::GetZoneCFG(uint32 zoneid, uint16 instance_id, NewZone_Struct
"snow_duration2, " // 53 "snow_duration2, " // 53
"snow_duration3, " // 54 "snow_duration3, " // 54
"snow_duration4, " // 55 "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", "FROM zone WHERE zoneidnumber = %i AND version = %i",
zoneid, instance_id); zoneid, instance_id);
auto results = QueryDatabase(query); 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); Log(Logs::General, Logs::Debug, "Zone Gravity is %f", zone_data->gravity);
allow_mercs = true; 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; int bindable = 0;
bindable = atoi(row[31]); bindable = atoi(row[31]);

View File

@ -717,6 +717,10 @@ void Client::GoToSafeCoords(uint16 zone_id, uint16 instance_id) {
void Mob::Gate(uint8 bindnum) { void Mob::Gate(uint8 bindnum) {
GoToBind(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) { void Client::Gate(uint8 bindnum) {