Merge fix

This commit is contained in:
KimLS 2017-04-11 21:48:01 -07:00
commit 1d1ee1ccbf
40 changed files with 331 additions and 186 deletions

View File

@ -1,6 +1,7 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 4/1/2017 == == 4/1/2017 ==
Akkadius: Cleaned up some of the NPC to NPC aggro code, only do aggro checks to other NPC's when the NPC is flagged for it
Akkadius: [Performance] Reworked how all log calls are made in the source Akkadius: [Performance] Reworked how all log calls are made in the source
- Before we used Log.Out, we will now use a macro Log( - Before we used Log.Out, we will now use a macro Log(
- Before: Log.Out(Logs::General, Logs::Status, "Importing Spells..."); - Before: Log.Out(Logs::General, Logs::Status, "Importing Spells...");

View File

@ -49,7 +49,7 @@ namespace EQEmu
}; };
enum DeityTypeBit : uint32 { enum DeityTypeBit : uint32 {
bit_DeityAll = 0x00000000, bit_DeityNone = 0x00000000,
bit_DeityAgnostic = 0x00000001, bit_DeityAgnostic = 0x00000001,
bit_DeityBertoxxulous = 0x00000002, bit_DeityBertoxxulous = 0x00000002,
bit_DeityBrellSirilis = 0x00000004, bit_DeityBrellSirilis = 0x00000004,
@ -66,7 +66,8 @@ namespace EQEmu
bit_DeitySolusekRo = 0x00002000, bit_DeitySolusekRo = 0x00002000,
bit_DeityTheTribunal = 0x00004000, bit_DeityTheTribunal = 0x00004000,
bit_DeityTunare = 0x00008000, bit_DeityTunare = 0x00008000,
bit_DeityVeeshan = 0x00010000 bit_DeityVeeshan = 0x00010000,
bit_DeityAll = 0xFFFFFFFF
}; };
extern DeityTypeBit ConvertDeityTypeToDeityTypeBit(DeityType deity_type); extern DeityTypeBit ConvertDeityTypeToDeityTypeBit(DeityType deity_type);

View File

@ -105,6 +105,15 @@ void EQEmuLogSys::LoadLogSettingsDefaults()
log_settings[Logs::Login_Server].log_to_console = Logs::General; log_settings[Logs::Login_Server].log_to_console = Logs::General;
log_settings[Logs::Headless_Client].log_to_console = Logs::General; log_settings[Logs::Headless_Client].log_to_console = Logs::General;
/* Set Category enabled status on defaults */
log_settings[Logs::World_Server].is_category_enabled = 1;
log_settings[Logs::Zone_Server].is_category_enabled = 1;
log_settings[Logs::QS_Server].is_category_enabled = 1;
log_settings[Logs::UCS_Server].is_category_enabled = 1;
log_settings[Logs::Crash].is_category_enabled = 1;
log_settings[Logs::MySQLError].is_category_enabled = 1;
log_settings[Logs::Login_Server].is_category_enabled = 1;
/* Declare process file names for log writing /* Declare process file names for log writing
If there is no process_file_name declared, no log file will be written, simply If there is no process_file_name declared, no log file will be written, simply
*/ */

View File

@ -111,9 +111,6 @@ Zone extensions and features
//path to where sql logs should be placed //path to where sql logs should be placed
#define SQL_LOG_PATH "sql_logs/" #define SQL_LOG_PATH "sql_logs/"
//New aggro system to reduce overhead.
#define REVERSE_AGGRO
//The highest you can #setskill / #setallskill //The highest you can #setskill / #setallskill
#define HIGHEST_CAN_SET_SKILL 400 #define HIGHEST_CAN_SET_SKILL 400

View File

@ -241,48 +241,70 @@ EQEmu::ItemInstance* EQEmu::InventoryProfile::GetCursorItem()
} }
// Swap items in inventory // Swap items in inventory
bool EQEmu::InventoryProfile::SwapItem(int16 slot_a, int16 slot_b, uint16 race_id, uint8 class_id, uint16 deity_id, uint8 level) bool EQEmu::InventoryProfile::SwapItem(int16 slot_a, int16 slot_b, SwapItemFailState& fail_state, uint16 race_id, uint8 class_id, uint16 deity_id, uint8 level)
{ {
fail_state = swapInvalid;
// Temp holding areas for a and b // Temp holding areas for a and b
ItemInstance* inst_a = GetItem(slot_a); ItemInstance* inst_a = GetItem(slot_a);
ItemInstance* inst_b = GetItem(slot_b); ItemInstance* inst_b = GetItem(slot_b);
if (inst_a) { if (inst_a) {
if (!inst_a->IsSlotAllowed(slot_b)) if (!inst_a->IsSlotAllowed(slot_b)) {
fail_state = swapNotAllowed;
return false; return false;
}
if ((slot_b >= legacy::EQUIPMENT_BEGIN && slot_b <= legacy::EQUIPMENT_END) || slot_b == inventory::slotPowerSource) { if ((slot_b >= legacy::EQUIPMENT_BEGIN && slot_b <= legacy::EQUIPMENT_END) || slot_b == inventory::slotPowerSource) {
auto item_a = inst_a->GetItem(); auto item_a = inst_a->GetItem();
if (!item_a) if (!item_a) {
fail_state = swapNullData;
return false; return false;
if (race_id && class_id && !item_a->IsEquipable(race_id, class_id)) }
if (race_id && class_id && !item_a->IsEquipable(race_id, class_id)) {
fail_state = swapRaceClass;
return false; return false;
if (deity_id && item_a->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_a->Deity)) }
if (deity_id && item_a->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_a->Deity)) {
fail_state = swapDeity;
return false; return false;
if (level && item_a->ReqLevel && level < item_a->ReqLevel) }
if (level && item_a->ReqLevel && level < item_a->ReqLevel) {
fail_state = swapLevel;
return false; return false;
}
} }
} }
if (inst_b) { if (inst_b) {
if (!inst_b->IsSlotAllowed(slot_a)) if (!inst_b->IsSlotAllowed(slot_a)) {
fail_state = swapNotAllowed;
return false; return false;
}
if ((slot_a >= legacy::EQUIPMENT_BEGIN && slot_a <= legacy::EQUIPMENT_END) || slot_a == inventory::slotPowerSource) { if ((slot_a >= legacy::EQUIPMENT_BEGIN && slot_a <= legacy::EQUIPMENT_END) || slot_a == inventory::slotPowerSource) {
auto item_b = inst_b->GetItem(); auto item_b = inst_b->GetItem();
if (!item_b) if (!item_b) {
fail_state = swapNullData;
return false; return false;
if (race_id && class_id && !item_b->IsEquipable(race_id, class_id)) }
if (race_id && class_id && !item_b->IsEquipable(race_id, class_id)) {
fail_state = swapRaceClass;
return false; return false;
if (deity_id && item_b->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_b->Deity)) }
if (deity_id && item_b->Deity && !(deity::ConvertDeityTypeToDeityTypeBit((deity::DeityType)deity_id) & item_b->Deity)) {
fail_state = swapDeity;
return false; return false;
if (level && item_b->ReqLevel && level < item_b->ReqLevel) }
if (level && item_b->ReqLevel && level < item_b->ReqLevel) {
fail_state = swapLevel;
return false; return false;
}
} }
} }
_PutItem(slot_a, inst_b); // Copy b->a _PutItem(slot_a, inst_b); // Assign b->a
_PutItem(slot_b, inst_a); // Copy a->b _PutItem(slot_b, inst_a); // Assign a->b
fail_state = swapPass;
return true; return true;
} }

View File

@ -85,15 +85,22 @@ namespace EQEmu
// Public Methods // Public Methods
/////////////////////////////// ///////////////////////////////
InventoryProfile() { m_mob_version = versions::MobVersion::Unknown; m_mob_version_set = false; } InventoryProfile() {
m_mob_version = versions::MobVersion::Unknown;
m_mob_version_set = false;
m_lookup = inventory::Lookup(versions::MobVersion::Unknown);
}
~InventoryProfile(); ~InventoryProfile();
bool SetInventoryVersion(versions::MobVersion inventory_version) { bool SetInventoryVersion(versions::MobVersion inventory_version) {
if (!m_mob_version_set) { if (!m_mob_version_set) {
m_mob_version = versions::ValidateMobVersion(inventory_version); m_mob_version = versions::ValidateMobVersion(inventory_version);
return (m_mob_version_set = true); m_lookup = inventory::Lookup(m_mob_version);
m_mob_version_set = true;
return true;
} }
else { else {
m_lookup = inventory::Lookup(versions::MobVersion::Unknown);
return false; return false;
} }
} }
@ -127,7 +134,8 @@ namespace EQEmu
ItemInstance* GetCursorItem(); ItemInstance* GetCursorItem();
// Swap items in inventory // Swap items in inventory
bool SwapItem(int16 slot_a, int16 slot_b, uint16 race_id = 0, uint8 class_id = 0, uint16 deity_id = 0, uint8 level = 0); enum SwapItemFailState : int8 { swapInvalid = -1, swapPass = 0, swapNotAllowed, swapNullData, swapRaceClass, swapDeity, swapLevel };
bool SwapItem(int16 slot_a, int16 slot_b, SwapItemFailState& fail_state, uint16 race_id = 0, uint8 class_id = 0, uint16 deity_id = 0, uint8 level = 0);
// Remove item from inventory // Remove item from inventory
bool DeleteItem(int16 slot_id, uint8 quantity = 0); bool DeleteItem(int16 slot_id, uint8 quantity = 0);
@ -224,6 +232,7 @@ namespace EQEmu
// Active mob version // Active mob version
versions::MobVersion m_mob_version; versions::MobVersion m_mob_version;
bool m_mob_version_set; bool m_mob_version_set;
const inventory::LookupEntry* m_lookup;
}; };
} }

View File

@ -22,7 +22,8 @@
#include <map> #include <map>
#include <vector> #include <vector>
enum { //values for pTimerType enum : int { //values for pTimerType
pTimerNegativeItemReuse = -1, // these grow down basically, we will have item ID * -1 for the timer ID
pTimerStartAdventureTimer = 1, pTimerStartAdventureTimer = 1,
pTimerSurnameChange = 2, pTimerSurnameChange = 2,
pTimerFeignDeath = 3, pTimerFeignDeath = 3,

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 9107 #define CURRENT_BINARY_DATABASE_VERSION 9110
#ifdef BOTS #ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017
#else #else

View File

@ -44,6 +44,7 @@ int main()
LogSys.LoadLogSettingsDefaults(); LogSys.LoadLogSettingsDefaults();
LogSys.log_settings[Logs::Error].log_to_console = Logs::General; LogSys.log_settings[Logs::Error].log_to_console = Logs::General;
LogSys.log_settings[Logs::Error].is_category_enabled = 1;
Log(Logs::General, Logs::Login_Server, "Logging System Init."); Log(Logs::General, Logs::Login_Server, "Logging System Init.");

View File

@ -361,6 +361,9 @@
9105|2017_02_15_bot_spells_entries.sql|SELECT `id` FROM `npc_spells_entries` WHERE `npc_spells_id` >= 701 AND `npc_spells_id` <= 712|not_empty| 9105|2017_02_15_bot_spells_entries.sql|SELECT `id` FROM `npc_spells_entries` WHERE `npc_spells_id` >= 701 AND `npc_spells_id` <= 712|not_empty|
9106|2017_02_26_npc_spells_update_for_bots.sql|SELECT * FROM `npc_spells` WHERE `id` = '701' AND `name` = 'Cleric Bot'|not_empty| 9106|2017_02_26_npc_spells_update_for_bots.sql|SELECT * FROM `npc_spells` WHERE `id` = '701' AND `name` = 'Cleric Bot'|not_empty|
9107|2017_03_09_inventory_version.sql|SHOW TABLES LIKE 'inventory_version'|empty| 9107|2017_03_09_inventory_version.sql|SHOW TABLES LIKE 'inventory_version'|empty|
9108|2017_04_07_ignore_despawn.sql|SHOW COLUMNS FROM `npc_types` LIKE 'ignore_despawn'|empty|
9109|2017_04_08_doors_disable_timer.sql|SHOW COLUMNS FROM `doors` LIKE 'disable_timer'|empty|
9110|2017_04_10_graveyard.sql|show index from graveyard WHERE key_name = 'zone_id_nonunique'|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 @@
alter table npc_types add column `ignore_despawn` tinyint(2) not null default 0;

View File

@ -0,0 +1 @@
ALTER TABLE `doors` ADD COLUMN `disable_timer` TINYINT(2) NOT NULL DEFAULT '0' AFTER `triggertype`;

View File

@ -0,0 +1,2 @@
alter table graveyard drop index zone_id;
create index zone_id_nonunique on graveyard(zone_id);

View File

@ -162,6 +162,7 @@ SET(zone_headers
lua_bit.h lua_bit.h
lua_client.h lua_client.h
lua_corpse.h lua_corpse.h
lua_door.h
lua_encounter.h lua_encounter.h
lua_entity.h lua_entity.h
lua_entity_list.h lua_entity_list.h

View File

@ -408,25 +408,19 @@ bool Mob::CheckWillAggro(Mob *mob) {
return(false); return(false);
} }
Mob* EntityList::AICheckCloseAggro(Mob* sender, float iAggroRange, float iAssistRange) { Mob* EntityList::AICheckNPCtoNPCAggro(Mob* sender, float iAggroRange, float iAssistRange) {
if (!sender || !sender->IsNPC()) if (!sender || !sender->IsNPC())
return(nullptr); return(nullptr);
#ifdef REVERSE_AGGRO
//with reverse aggro, npc->client is checked elsewhere, no need to check again
auto it = npc_list.begin(); auto it = npc_list.begin();
while (it != npc_list.end()) { while (it != npc_list.end()) {
#else
auto it = mob_list.begin();
while (it != mob_list.end()) {
#endif
Mob *mob = it->second; Mob *mob = it->second;
if (sender->CheckWillAggro(mob)) if (sender->CheckWillAggro(mob))
return mob; return mob;
++it; ++it;
} }
//LogFile->write(EQEMuLog::Debug, "Check aggro for %s no target.", sender->GetName());
return nullptr; return nullptr;
} }

View File

@ -3974,16 +3974,9 @@ void Mob::TrySpellProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData *w
float chance = ProcChance * (static_cast<float>(SpellProcs[i].chance) / 100.0f); float chance = ProcChance * (static_cast<float>(SpellProcs[i].chance) / 100.0f);
if (zone->random.Roll(chance)) { if (zone->random.Roll(chance)) {
Log(Logs::Detail, Logs::Combat, Log(Logs::Detail, Logs::Combat,
"Spell proc %d procing spell %d (%.2f percent chance)", "Spell proc %d procing spell %d (%.2f percent chance)",
i, SpellProcs[i].spellID, chance); i, SpellProcs[i].spellID, chance);
auto outapp = new EQApplicationPacket(OP_BeginCast, sizeof(BeginCast_Struct)); SendBeginCast(SpellProcs[i].spellID, 0);
BeginCast_Struct* begincast = (BeginCast_Struct*)outapp->pBuffer;
begincast->caster_id = GetID();
begincast->spell_id = SpellProcs[i].spellID;
begincast->cast_time = 0;
outapp->priority = 3;
entity_list.QueueCloseClients(this, outapp, false, RuleI(Range, SpellMessages), 0, true);
safe_delete(outapp);
ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override); ExecWeaponProc(nullptr, SpellProcs[i].spellID, on, SpellProcs[i].level_override);
CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0, CheckNumHitsRemaining(NumHit::OffensiveSpellProcs, 0,
SpellProcs[i].base_spellID); SpellProcs[i].base_spellID);

View File

@ -134,9 +134,7 @@ Client::Client(EQStreamInterface* ieqs)
endupkeep_timer(1000), endupkeep_timer(1000),
forget_timer(0), forget_timer(0),
autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000), autosave_timer(RuleI(Character, AutosaveIntervalS) * 1000),
#ifdef REVERSE_AGGRO
client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000), client_scan_npc_aggro_timer(RuleI(Aggro, ClientAggroCheckInterval) * 1000),
#endif
tribute_timer(Tribute_duration), tribute_timer(Tribute_duration),
proximity_timer(ClientProximity_interval), proximity_timer(ClientProximity_interval),
TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000), TaskPeriodic_Timer(RuleI(TaskSystem, PeriodicCheckTimer) * 1000),

View File

@ -1460,9 +1460,7 @@ private:
Timer endupkeep_timer; Timer endupkeep_timer;
Timer forget_timer; // our 2 min everybody forgets you timer Timer forget_timer; // our 2 min everybody forgets you timer
Timer autosave_timer; Timer autosave_timer;
#ifdef REVERSE_AGGRO
Timer client_scan_npc_aggro_timer; Timer client_scan_npc_aggro_timer;
#endif
Timer tribute_timer; Timer tribute_timer;
Timer proximity_timer; Timer proximity_timer;

View File

@ -614,7 +614,6 @@ bool Client::Process() {
} }
} }
#ifdef REVERSE_AGGRO
//At this point, we are still connected, everything important has taken //At this point, we are still connected, everything important has taken
//place, now check to see if anybody wants to aggro us. //place, now check to see if anybody wants to aggro us.
// only if client is not feigned // only if client is not feigned
@ -630,7 +629,6 @@ bool Client::Process() {
} }
Log(Logs::General, Logs::Aggro, "Checking Reverse Aggro (client->npc) scanned_npcs (%i)", npc_scan_count); Log(Logs::General, Logs::Aggro, "Checking Reverse Aggro (client->npc) scanned_npcs (%i)", npc_scan_count);
} }
#endif
if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED))) if (client_state != CLIENT_LINKDEAD && (client_state == CLIENT_ERROR || client_state == DISCONNECTED || client_state == CLIENT_KICKED || !eqs->CheckState(ESTABLISHED)))
{ {

View File

@ -70,6 +70,8 @@ m_Destination(door->dest_x, door->dest_y, door->dest_z, door->dest_heading)
is_ldon_door = door->is_ldon_door; is_ldon_door = door->is_ldon_door;
client_version_mask = door->client_version_mask; client_version_mask = door->client_version_mask;
disable_timer = (door->disable_timer == 1 ? true : false);
} }
Doors::Doors(const char *dmodel, const glm::vec4& position, uint8 dopentype, uint16 dsize) : Doors::Doors(const char *dmodel, const glm::vec4& position, uint8 dopentype, uint16 dsize) :
@ -102,6 +104,8 @@ m_Destination(glm::vec4())
is_ldon_door = 0; is_ldon_door = 0;
client_version_mask = 4294967295u; client_version_mask = 4294967295u;
disable_timer = 0;
} }
@ -135,7 +139,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger)
//door debugging info dump //door debugging info dump
Log(Logs::Detail, Logs::Doors, "%s clicked door %s (dbid %d, eqid %d) at %s", sender->GetName(), door_name, db_id, door_id, to_string(m_Position).c_str()); Log(Logs::Detail, Logs::Doors, "%s clicked door %s (dbid %d, eqid %d) at %s", sender->GetName(), door_name, db_id, door_id, to_string(m_Position).c_str());
Log(Logs::Detail, Logs::Doors, " incline %d, opentype %d, lockpick %d, key %d, nokeyring %d, trigger %d type %d, param %d", incline, opentype, lockpick, keyitem, nokeyring, trigger_door, trigger_type, door_param); Log(Logs::Detail, Logs::Doors, " incline %d, opentype %d, lockpick %d, key %d, nokeyring %d, trigger %d type %d, param %d", incline, opentype, lockpick, keyitem, nokeyring, trigger_door, trigger_type, door_param);
Log(Logs::Detail, Logs::Doors, " size %d, invert %d, dest: %s %s", size, invert_state, dest_zone, to_string(m_Destination).c_str()); Log(Logs::Detail, Logs::Doors, " disable_timer '%s',size %d, invert %d, dest: %s %s", (disable_timer?"true":"false"), size, invert_state, dest_zone, to_string(m_Destination).c_str());
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
MoveDoor_Struct* md = (MoveDoor_Struct*)outapp->pBuffer; MoveDoor_Struct* md = (MoveDoor_Struct*)outapp->pBuffer;
@ -354,13 +358,15 @@ void Doors::HandleClick(Client* sender, uint8 trigger)
entity_list.QueueClients(sender, outapp, false); entity_list.QueueClients(sender, outapp, false);
if(!IsDoorOpen() || (opentype == 58)) if(!IsDoorOpen() || (opentype == 58))
{ {
close_timer.Start(); if (!disable_timer)
close_timer.Start();
SetOpenState(true); SetOpenState(true);
} }
else else
{ {
close_timer.Disable(); close_timer.Disable();
SetOpenState(false); if (!disable_timer)
SetOpenState(false);
} }
//everything past this point assumes we opened the door //everything past this point assumes we opened the door
@ -443,31 +449,34 @@ void Doors::HandleClick(Client* sender, uint8 trigger)
void Doors::NPCOpen(NPC* sender, bool alt_mode) void Doors::NPCOpen(NPC* sender, bool alt_mode)
{ {
if(sender) { if (sender) {
if(GetTriggerType() == 255 || GetTriggerDoorID() > 0 || GetLockpick() != 0 || GetKeyItem() != 0 || opentype == 59 || opentype == 58 || !sender->IsNPC()) { // this object isnt triggered or door is locked - NPCs should not open locked doors! if (GetTriggerType() == 255 || GetTriggerDoorID() > 0 || GetLockpick() != 0 || GetKeyItem() != 0 || opentype == 59 || opentype == 58 || !sender->IsNPC()) { // this object isnt triggered or door is locked - NPCs should not open locked doors!
return; return;
} }
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
MoveDoor_Struct* md=(MoveDoor_Struct*)outapp->pBuffer; MoveDoor_Struct* md = (MoveDoor_Struct*)outapp->pBuffer;
md->doorid = door_id; md->doorid = door_id;
md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR;
entity_list.QueueCloseClients(sender,outapp,false,200); entity_list.QueueCloseClients(sender, outapp, false, 200);
safe_delete(outapp); safe_delete(outapp);
if(!alt_mode) { // original function if (!alt_mode) { // original function
if(!isopen) { if (!is_open) {
close_timer.Start(); if (!disable_timer)
isopen=true; close_timer.Start();
is_open = true;
} }
else { else {
close_timer.Disable(); close_timer.Disable();
isopen=false; if (!disable_timer)
is_open = false;
} }
} }
else { // alternative function else { // alternative function
close_timer.Start(); if (!disable_timer)
isopen=true; close_timer.Start();
is_open = true;
} }
} }
} }
@ -475,49 +484,53 @@ void Doors::NPCOpen(NPC* sender, bool alt_mode)
void Doors::ForceOpen(Mob *sender, bool alt_mode) void Doors::ForceOpen(Mob *sender, bool alt_mode)
{ {
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
MoveDoor_Struct* md=(MoveDoor_Struct*)outapp->pBuffer; MoveDoor_Struct* md = (MoveDoor_Struct*)outapp->pBuffer;
md->doorid = door_id; md->doorid = door_id;
md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR;
entity_list.QueueClients(sender,outapp,false); entity_list.QueueClients(sender, outapp, false);
safe_delete(outapp); safe_delete(outapp);
if(!alt_mode) { // original function if (!alt_mode) { // original function
if(!isopen) { if (!is_open) {
close_timer.Start(); if (!disable_timer)
isopen=true; close_timer.Start();
is_open = true;
} }
else { else {
close_timer.Disable(); close_timer.Disable();
isopen=false; if (!disable_timer)
is_open = false;
} }
} }
else { // alternative function else { // alternative function
close_timer.Start(); if (!disable_timer)
isopen=true; close_timer.Start();
is_open = true;
} }
} }
void Doors::ForceClose(Mob *sender, bool alt_mode) void Doors::ForceClose(Mob *sender, bool alt_mode)
{ {
auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct)); auto outapp = new EQApplicationPacket(OP_MoveDoor, sizeof(MoveDoor_Struct));
MoveDoor_Struct* md=(MoveDoor_Struct*)outapp->pBuffer; MoveDoor_Struct* md = (MoveDoor_Struct*)outapp->pBuffer;
md->doorid = door_id; md->doorid = door_id;
md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; // change from original (open to close) md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; // change from original (open to close)
entity_list.QueueClients(sender,outapp,false); entity_list.QueueClients(sender, outapp, false);
safe_delete(outapp); safe_delete(outapp);
if(!alt_mode) { // original function if (!alt_mode) { // original function
if(!isopen) { if (!is_open) {
close_timer.Start(); if (!disable_timer)
isopen=true; close_timer.Start();
is_open = true;
} }
else { else {
close_timer.Disable(); close_timer.Disable();
isopen=false; is_open = false;
} }
} }
else { // alternative function else { // alternative function
if(isopen) if (is_open)
close_timer.Trigger(); close_timer.Trigger();
} }
} }
@ -532,14 +545,14 @@ void Doors::ToggleState(Mob *sender)
MoveDoor_Struct* md=(MoveDoor_Struct*)outapp->pBuffer; MoveDoor_Struct* md=(MoveDoor_Struct*)outapp->pBuffer;
md->doorid = door_id; md->doorid = door_id;
if(!isopen) { if(!is_open) {
md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR; md->action = invert_state == 0 ? OPEN_DOOR : OPEN_INVDOOR;
isopen=true; is_open=true;
} }
else else
{ {
md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR; md->action = invert_state == 0 ? CLOSE_DOOR : CLOSE_INVDOOR;
isopen=false; is_open=false;
} }
entity_list.QueueClients(sender,outapp,false); entity_list.QueueClients(sender,outapp,false);
@ -552,7 +565,7 @@ void Doors::DumpDoor(){
db_id, door_id, zone_name, door_name, to_string(m_Position).c_str()); db_id, door_id, zone_name, door_name, to_string(m_Position).c_str());
Log(Logs::General, Logs::None, Log(Logs::General, Logs::None,
"opentype:%i guild_id:%i lockpick:%i keyitem:%i nokeyring:%i trigger_door:%i trigger_type:%i door_param:%i open:%s", "opentype:%i guild_id:%i lockpick:%i keyitem:%i nokeyring:%i trigger_door:%i trigger_type:%i door_param:%i open:%s",
opentype, guild_id, lockpick, keyitem, nokeyring, trigger_door, trigger_type, door_param, (isopen) ? "open":"closed"); opentype, guild_id, lockpick, keyitem, nokeyring, trigger_door, trigger_type, door_param, (is_open) ? "open":"closed");
Log(Logs::General, Logs::None, Log(Logs::General, Logs::None,
"dest_zone:%s destination:%s ", "dest_zone:%s destination:%s ",
dest_zone, to_string(m_Destination).c_str()); dest_zone, to_string(m_Destination).c_str());
@ -632,34 +645,32 @@ bool ZoneDatabase::LoadDoors(int32 iDoorCount, Door *into, const char *zone_name
Log(Logs::General, Logs::Status, "Loading Doors from database..."); Log(Logs::General, Logs::Status, "Loading Doors from database...");
// Door tmpDoor; // Door tmpDoor;
std::string query = StringFormat("SELECT id, doorid, zone, name, pos_x, pos_y, pos_z, heading, " std::string query = StringFormat("SELECT id, doorid, zone, name, pos_x, pos_y, pos_z, heading, "
"opentype, guild, lockpick, keyitem, nokeyring, triggerdoor, triggertype, " "opentype, guild, lockpick, keyitem, nokeyring, triggerdoor, triggertype, "
"dest_zone, dest_instance, dest_x, dest_y, dest_z, dest_heading, " "dest_zone, dest_instance, dest_x, dest_y, dest_z, dest_heading, "
"door_param, invert_state, incline, size, is_ldon_door, client_version_mask " "door_param, invert_state, incline, size, is_ldon_door, client_version_mask, disable_timer "
"FROM doors WHERE zone = '%s' AND (version = %u OR version = -1) " "FROM doors WHERE zone = '%s' AND (version = %u OR version = -1) "
"ORDER BY doorid asc", zone_name, version); "ORDER BY doorid asc", zone_name, version);
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
if (!results.Success()){ if (!results.Success()) {
return false; return false;
} }
int32 rowIndex = 0; int32 rowIndex = 0;
for(auto row = results.begin(); row != results.end(); ++row, ++rowIndex) { for (auto row = results.begin(); row != results.end(); ++row, ++rowIndex) {
if(rowIndex >= iDoorCount) { if (rowIndex >= iDoorCount) {
std::cerr << "Error, Door Count of " << iDoorCount << " exceeded." << std::endl; std::cerr << "Error, Door Count of " << iDoorCount << " exceeded." << std::endl;
break; break;
} }
memset(&into[rowIndex], 0, sizeof(Door)); memset(&into[rowIndex], 0, sizeof(Door));
into[rowIndex].db_id = atoi(row[0]); into[rowIndex].db_id = atoi(row[0]);
into[rowIndex].door_id = atoi(row[1]); into[rowIndex].door_id = atoi(row[1]);
Log(Logs::Detail, Logs::Doors, "Door Load: db id: %u, door_id %u", into[rowIndex].db_id, into[rowIndex].door_id); strn0cpy(into[rowIndex].zone_name, row[2], 32);
strn0cpy(into[rowIndex].door_name, row[3], 32);
strn0cpy(into[rowIndex].zone_name,row[2],32);
strn0cpy(into[rowIndex].door_name,row[3],32);
into[rowIndex].pos_x = (float)atof(row[4]); into[rowIndex].pos_x = (float)atof(row[4]);
into[rowIndex].pos_y = (float)atof(row[5]); into[rowIndex].pos_y = (float)atof(row[5]);
@ -676,17 +687,24 @@ bool ZoneDatabase::LoadDoors(int32 iDoorCount, Door *into, const char *zone_name
strn0cpy(into[rowIndex].dest_zone, row[15], 32); strn0cpy(into[rowIndex].dest_zone, row[15], 32);
into[rowIndex].dest_instance_id = atoi(row[16]); into[rowIndex].dest_instance_id = atoi(row[16]);
into[rowIndex].dest_x = (float) atof(row[17]); into[rowIndex].dest_x = (float)atof(row[17]);
into[rowIndex].dest_y = (float) atof(row[18]); into[rowIndex].dest_y = (float)atof(row[18]);
into[rowIndex].dest_z = (float) atof(row[19]); into[rowIndex].dest_z = (float)atof(row[19]);
into[rowIndex].dest_heading = (float) atof(row[20]); into[rowIndex].dest_heading = (float)atof(row[20]);
into[rowIndex].door_param=atoi(row[21]); into[rowIndex].door_param = atoi(row[21]);
into[rowIndex].invert_state=atoi(row[22]); into[rowIndex].invert_state = atoi(row[22]);
into[rowIndex].incline=atoi(row[23]); into[rowIndex].incline = atoi(row[23]);
into[rowIndex].size=atoi(row[24]); into[rowIndex].size = atoi(row[24]);
into[rowIndex].is_ldon_door=atoi(row[25]); into[rowIndex].is_ldon_door = atoi(row[25]);
into[rowIndex].client_version_mask = (uint32)strtoul(row[26], nullptr, 10); into[rowIndex].client_version_mask = (uint32)strtoul(row[26], nullptr, 10);
} into[rowIndex].disable_timer = atoi(row[27]);
Log(Logs::Detail, Logs::Doors, "Door Load: db id: %u, door_id %u disable_timer: %i",
into[rowIndex].db_id,
into[rowIndex].door_id,
into[rowIndex].disable_timer
);
}
return true; return true;
} }
@ -730,6 +748,10 @@ void Doors::SetSize(uint16 in) {
entity_list.RespawnAllDoors(); entity_list.RespawnAllDoors();
} }
void Doors::SetDisableTimer(bool flag) {
disable_timer = flag;
}
void Doors::CreateDatabaseEntry() void Doors::CreateDatabaseEntry()
{ {
if(database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()) - 1 >= 255) if(database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()) - 1 >= 255)

View File

@ -32,8 +32,8 @@ public:
const glm::vec4& GetPosition() const{ return m_Position; } const glm::vec4& GetPosition() const{ return m_Position; }
int GetIncline() { return incline; } int GetIncline() { return incline; }
bool triggered; bool triggered;
void SetOpenState(bool st) { isopen = st; } void SetOpenState(bool st) { is_open = st; }
bool IsDoorOpen() { return isopen; } bool IsDoorOpen() { return is_open; }
uint8 GetTriggerDoorID() { return trigger_door; } uint8 GetTriggerDoorID() { return trigger_door; }
uint8 GetTriggerType() { return trigger_type; } uint8 GetTriggerType() { return trigger_type; }
@ -67,6 +67,10 @@ public:
void SetDoorName(const char* name); void SetDoorName(const char* name);
void SetOpenType(uint8 in); void SetOpenType(uint8 in);
void SetSize(uint16 size); void SetSize(uint16 size);
void SetDisableTimer(bool flag);
bool GetDisableTimer() { return disable_timer; }
void CreateDatabaseEntry(); void CreateDatabaseEntry();
private: private:
@ -88,7 +92,8 @@ private:
uint16 size; uint16 size;
int invert_state; int invert_state;
uint32 entity_id; uint32 entity_id;
bool isopen; bool disable_timer;
bool is_open;
Timer close_timer; Timer close_timer;
//Timer trigger_timer; //Timer trigger_timer;

View File

@ -877,12 +877,12 @@ bool EntityList::MakeDoorSpawnPacket(EQApplicationPacket *app, Client *client)
auto it = door_list.begin(); auto it = door_list.begin();
while (it != door_list.end()) { while (it != door_list.end()) {
if ((it->second->GetClientVersionMask() & mask_test) && if ((it->second->GetClientVersionMask() & mask_test) &&
strlen(it->second->GetDoorName()) > 3) strlen(it->second->GetDoorName()) > 3)
count++; count++;
++it; ++it;
} }
if(count == 0 || count > 500) if (count == 0 || count > 500)
return false; return false;
uint32 length = count * sizeof(Door_Struct); uint32 length = count * sizeof(Door_Struct);
@ -890,31 +890,43 @@ bool EntityList::MakeDoorSpawnPacket(EQApplicationPacket *app, Client *client)
memset(packet_buffer, 0, length); memset(packet_buffer, 0, length);
uchar *ptr = packet_buffer; uchar *ptr = packet_buffer;
Doors *door; Doors *door;
Door_Struct nd; Door_Struct new_door;
it = door_list.begin(); it = door_list.begin();
while (it != door_list.end()) { while (it != door_list.end()) {
door = it->second; door = it->second;
if (door && (door->GetClientVersionMask() & mask_test) && if (door && (door->GetClientVersionMask() & mask_test) &&
strlen(door->GetDoorName()) > 3) { strlen(door->GetDoorName()) > 3) {
memset(&nd, 0, sizeof(nd)); memset(&new_door, 0, sizeof(new_door));
memcpy(nd.name, door->GetDoorName(), 32); memcpy(new_door.name, door->GetDoorName(), 32);
auto position = door->GetPosition(); auto position = door->GetPosition();
nd.xPos = position.x;
nd.yPos = position.y; new_door.xPos = position.x;
nd.zPos = position.z; new_door.yPos = position.y;
nd.heading = position.w; new_door.zPos = position.z;
nd.incline = door->GetIncline(); new_door.heading = position.w;
nd.size = door->GetSize();
nd.doorId = door->GetDoorID(); new_door.incline = door->GetIncline();
nd.opentype = door->GetOpenType(); new_door.size = door->GetSize();
nd.state_at_spawn = door->GetInvertState() ? !door->IsDoorOpen() : door->IsDoorOpen(); new_door.doorId = door->GetDoorID();
nd.invert_state = door->GetInvertState(); new_door.opentype = door->GetOpenType();
nd.door_param = door->GetDoorParam();
memcpy(ptr, &nd, sizeof(nd)); Log(Logs::General, Logs::Doors, "Door timer_disable: %s door_id: %u is_open: %s invert_state: %i",
ptr+=sizeof(nd); (door->GetDisableTimer() ? "true" : "false"),
*(ptr-1)=0x01; door->GetDoorID(),
*(ptr-3)=0x01; (door->IsDoorOpen() ? "true" : "false"),
door->GetInvertState()
);
new_door.state_at_spawn = (door->GetInvertState() ? !door->IsDoorOpen() : door->IsDoorOpen());
new_door.invert_state = door->GetInvertState();
new_door.door_param = door->GetDoorParam();
memcpy(ptr, &new_door, sizeof(new_door));
ptr += sizeof(new_door);
*(ptr - 1) = 0x01;
*(ptr - 3) = 0x01;
} }
++it; ++it;
} }

View File

@ -412,7 +412,7 @@ public:
bool LimitCheckName(const char* npc_name); bool LimitCheckName(const char* npc_name);
void CheckClientAggro(Client *around); void CheckClientAggro(Client *around);
Mob* AICheckCloseAggro(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);
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);

View File

@ -1792,7 +1792,8 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
// Nothing in destination slot: split stack into two // Nothing in destination slot: split stack into two
if ((int16)move_in->number_in_stack >= src_inst->GetCharges()) { if ((int16)move_in->number_in_stack >= src_inst->GetCharges()) {
// Move entire stack // Move entire stack
if(!m_inv.SwapItem(src_slot_id, dst_slot_id)) { return false; } EQEmu::InventoryProfile::SwapItemFailState fail_state = EQEmu::InventoryProfile::swapInvalid;
if (!m_inv.SwapItem(src_slot_id, dst_slot_id, fail_state)) { return false; }
Log(Logs::Detail, Logs::Inventory, "Move entire stack from %d to %d with stack size %d. Dest empty.", src_slot_id, dst_slot_id, move_in->number_in_stack); Log(Logs::Detail, Logs::Inventory, "Move entire stack from %d to %d with stack size %d. Dest empty.", src_slot_id, dst_slot_id, move_in->number_in_stack);
} }
else { else {
@ -1822,7 +1823,21 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
} }
SetMaterial(dst_slot_id,src_inst->GetItem()->ID); SetMaterial(dst_slot_id,src_inst->GetItem()->ID);
} }
if(!m_inv.SwapItem(src_slot_id, dst_slot_id, GetRace(), GetClass(), GetDeity(), GetLevel())) { return false; }
EQEmu::InventoryProfile::SwapItemFailState fail_state = EQEmu::InventoryProfile::swapInvalid;
if (!m_inv.SwapItem(src_slot_id, dst_slot_id, fail_state, GetBaseRace(), GetBaseClass(), GetDeity(), GetLevel())) {
const char* fail_message = "The selected slot was invalid.";
if (fail_state == EQEmu::InventoryProfile::swapRaceClass || fail_state == EQEmu::InventoryProfile::swapDeity)
fail_message = "Your class, deity and/or race may not equip that item.";
else if (fail_state == EQEmu::InventoryProfile::swapLevel)
fail_message = "You are not sufficient level to use this item.";
if (fail_message)
Message(CC_Red, "%s", fail_message);
return false;
}
Log(Logs::Detail, Logs::Inventory, "Moving entire item from slot %d to slot %d", src_slot_id, dst_slot_id); Log(Logs::Detail, Logs::Inventory, "Moving entire item from slot %d to slot %d", src_slot_id, dst_slot_id);
if (src_slot_id <= EQEmu::legacy::EQUIPMENT_END || src_slot_id == EQEmu::inventory::slotPowerSource) { if (src_slot_id <= EQEmu::legacy::EQUIPMENT_END || src_slot_id == EQEmu::inventory::slotPowerSource) {

View File

@ -111,6 +111,16 @@ uint32 Lua_Door::GetOpenType() {
return self->GetOpenType(); return self->GetOpenType();
} }
void Lua_Door::SetDisableTimer(bool flag) {
Lua_Safe_Call_Void();
self->SetDisableTimer(flag);
}
bool Lua_Door::GetDisableTimer() {
Lua_Safe_Call_Bool();
return self->GetDisableTimer();
}
void Lua_Door::SetLockPick(uint32 pick) { void Lua_Door::SetLockPick(uint32 pick) {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
self->SetLockpick(pick); self->SetLockpick(pick);
@ -190,6 +200,8 @@ luabind::scope lua_register_door() {
.def("GetIncline", (uint32(Lua_Door::*)(void))&Lua_Door::GetIncline) .def("GetIncline", (uint32(Lua_Door::*)(void))&Lua_Door::GetIncline)
.def("SetOpenType", (void(Lua_Door::*)(uint32))&Lua_Door::SetOpenType) .def("SetOpenType", (void(Lua_Door::*)(uint32))&Lua_Door::SetOpenType)
.def("GetOpenType", (uint32(Lua_Door::*)(void))&Lua_Door::GetOpenType) .def("GetOpenType", (uint32(Lua_Door::*)(void))&Lua_Door::GetOpenType)
.def("SetDisableTimer", (void(Lua_Door::*)(bool))&Lua_Door::SetDisableTimer)
.def("GetDisableTimer", (bool(Lua_Door::*)(void))&Lua_Door::GetDisableTimer)
.def("SetLockPick", (void(Lua_Door::*)(uint32))&Lua_Door::SetLockPick) .def("SetLockPick", (void(Lua_Door::*)(uint32))&Lua_Door::SetLockPick)
.def("GetLockPick", (uint32(Lua_Door::*)(void))&Lua_Door::GetLockPick) .def("GetLockPick", (uint32(Lua_Door::*)(void))&Lua_Door::GetLockPick)
.def("SetKeyItem", (void(Lua_Door::*)(uint32))&Lua_Door::SetKeyItem) .def("SetKeyItem", (void(Lua_Door::*)(uint32))&Lua_Door::SetKeyItem)

View File

@ -49,6 +49,8 @@ public:
uint32 GetIncline(); uint32 GetIncline();
void SetOpenType(uint32 type); void SetOpenType(uint32 type);
uint32 GetOpenType(); uint32 GetOpenType();
void SetDisableTimer(bool flag);
bool GetDisableTimer();
void SetLockPick(uint32 pick); void SetLockPick(uint32 pick);
uint32 GetLockPick(); uint32 GetLockPick();
void SetKeyItem(uint32 key); void SetKeyItem(uint32 key);

View File

@ -1680,7 +1680,7 @@ luabind::scope lua_register_events() {
luabind::value("loot", static_cast<int>(EVENT_LOOT)), luabind::value("loot", static_cast<int>(EVENT_LOOT)),
luabind::value("zone", static_cast<int>(EVENT_ZONE)), luabind::value("zone", static_cast<int>(EVENT_ZONE)),
luabind::value("level_up", static_cast<int>(EVENT_LEVEL_UP)), luabind::value("level_up", static_cast<int>(EVENT_LEVEL_UP)),
luabind::value("killed_merit ", static_cast<int>(EVENT_KILLED_MERIT )), luabind::value("killed_merit", static_cast<int>(EVENT_KILLED_MERIT)),
luabind::value("cast_on", static_cast<int>(EVENT_CAST_ON)), luabind::value("cast_on", static_cast<int>(EVENT_CAST_ON)),
luabind::value("task_accepted", static_cast<int>(EVENT_TASK_ACCEPTED)), luabind::value("task_accepted", static_cast<int>(EVENT_TASK_ACCEPTED)),
luabind::value("task_stage_complete", static_cast<int>(EVENT_TASK_STAGE_COMPLETE)), luabind::value("task_stage_complete", static_cast<int>(EVENT_TASK_STAGE_COMPLETE)),

View File

@ -40,7 +40,8 @@ int Lua_Inventory::PushCursor(Lua_ItemInst item) {
bool Lua_Inventory::SwapItem(int slot_a, int slot_b) { bool Lua_Inventory::SwapItem(int slot_a, int slot_b) {
Lua_Safe_Call_Bool(); Lua_Safe_Call_Bool();
return self->SwapItem(slot_a, slot_b); EQEmu::InventoryProfile::SwapItemFailState fail_state = EQEmu::InventoryProfile::swapInvalid;
return self->SwapItem(slot_a, slot_b, fail_state);
} }
bool Lua_Inventory::DeleteItem(int slot_id) { bool Lua_Inventory::DeleteItem(int slot_id) {

View File

@ -18,6 +18,11 @@ namespace luabind {
luabind::scope lua_register_inventory(); luabind::scope lua_register_inventory();
// This class should be deprecated due to the nature of inventory actions.
// Direct manipulation of the inventory system bypasses the client management
// of database calls and can lead to lost items, duplicated items and/or
// desync'd inventories, if not handled correctly.
class Lua_Inventory : public Lua_Ptr<EQEmu::InventoryProfile> class Lua_Inventory : public Lua_Ptr<EQEmu::InventoryProfile>
{ {
typedef EQEmu::InventoryProfile NativeType; typedef EQEmu::InventoryProfile NativeType;

View File

@ -823,6 +823,11 @@ bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_use
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust, proc); return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust, proc);
} }
void Lua_Mob::SendBeginCast(int spell_id, int cast_time) {
Lua_Safe_Call_Void();
self->SendBeginCast(spell_id, cast_time);
}
void Lua_Mob::SpellEffect(Lua_Mob caster, int spell_id, double partial) { void Lua_Mob::SpellEffect(Lua_Mob caster, int spell_id, double partial) {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
self->SpellEffect(caster, spell_id, static_cast<float>(partial)); self->SpellEffect(caster, spell_id, static_cast<float>(partial));
@ -2128,6 +2133,7 @@ luabind::scope lua_register_mob() {
.def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32))&Lua_Mob::SpellFinished) .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32))&Lua_Mob::SpellFinished)
.def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32,int))&Lua_Mob::SpellFinished) .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32,int))&Lua_Mob::SpellFinished)
.def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32,int,bool))&Lua_Mob::SpellFinished) .def("SpellFinished", (bool(Lua_Mob::*)(int,Lua_Mob,int,int,uint32,int,bool))&Lua_Mob::SpellFinished)
.def("SendBeginCast", &Lua_Mob::SendBeginCast)
.def("SpellEffect", &Lua_Mob::SpellEffect) .def("SpellEffect", &Lua_Mob::SpellEffect)
.def("GetPet", &Lua_Mob::GetPet) .def("GetPet", &Lua_Mob::GetPet)
.def("GetOwner", &Lua_Mob::GetOwner) .def("GetOwner", &Lua_Mob::GetOwner)

View File

@ -181,6 +181,7 @@ public:
bool SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot); bool SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot);
bool SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust); bool SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust);
bool SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust, bool proc); bool SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust, bool proc);
void SendBeginCast(int spell_id, int cast_time);
void SpellEffect(Lua_Mob caster, int spell_id, double partial); void SpellEffect(Lua_Mob caster, int spell_id, double partial);
Lua_Mob GetPet(); Lua_Mob GetPet();
Lua_Mob GetOwner(); Lua_Mob GetOwner();

View File

@ -279,6 +279,7 @@ public:
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0); uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0);
bool SpellFinished(uint16 spell_id, Mob *target, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, uint16 mana_used = 0, bool SpellFinished(uint16 spell_id, Mob *target, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, uint16 mana_used = 0,
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1);
void SendBeginCast(uint16 spell_id, uint32 casttime);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect = false, virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect = false,
bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1); bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1); virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1);

View File

@ -457,18 +457,18 @@ void Mob::AI_Start(uint32 iMoveDelay) {
AI_movement_timer = std::unique_ptr<Timer>(new Timer(AImovement_duration)); AI_movement_timer = std::unique_ptr<Timer>(new Timer(AImovement_duration));
AI_target_check_timer = std::unique_ptr<Timer>(new Timer(AItarget_check_duration)); AI_target_check_timer = std::unique_ptr<Timer>(new Timer(AItarget_check_duration));
AI_feign_remember_timer = std::unique_ptr<Timer>(new Timer(AIfeignremember_delay)); AI_feign_remember_timer = std::unique_ptr<Timer>(new Timer(AIfeignremember_delay));
AI_scan_area_timer = std::unique_ptr<Timer>(new Timer(RandomTimer(RuleI(NPC, NPCToNPCAggroTimerMin), RuleI(NPC, NPCToNPCAggroTimerMax))));
if(CastToNPC()->WillAggroNPCs())
AI_scan_area_timer = std::unique_ptr<Timer>(new Timer(RandomTimer(RuleI(NPC, NPCToNPCAggroTimerMin), RuleI(NPC, NPCToNPCAggroTimerMax))));
AI_check_signal_timer = std::unique_ptr<Timer>(new Timer(AI_check_signal_timer_delay)); AI_check_signal_timer = std::unique_ptr<Timer>(new Timer(AI_check_signal_timer_delay));
#ifdef REVERSE_AGGRO
if(IsNPC() && !CastToNPC()->WillAggroNPCs())
AI_scan_area_timer->Disable();
#endif
if (GetAggroRange() == 0) if (GetAggroRange() == 0)
pAggroRange = 70; pAggroRange = 70;
if (GetAssistRange() == 0) if (GetAssistRange() == 0)
pAssistRange = 70; pAssistRange = 70;
hate_list.WipeHateList(); hate_list.WipeHateList();
m_Delta = glm::vec4(); m_Delta = glm::vec4();
@ -1304,7 +1304,7 @@ void Mob::AI_Process() {
if (m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)) if (m_PlayerState & static_cast<uint32>(PlayerState::Aggressive))
SendRemovePlayerState(PlayerState::Aggressive); SendRemovePlayerState(PlayerState::Aggressive);
if(!zone->CanDoCombat() && AI_feign_remember_timer->Check()) { if(zone->CanDoCombat() && AI_feign_remember_timer->Check()) {
// 6/14/06 // 6/14/06
// Improved Feign Death Memory // Improved Feign Death Memory
// check to see if any of our previous feigned targets have gotten up. // check to see if any of our previous feigned targets have gotten up.
@ -1329,18 +1329,13 @@ void Mob::AI_Process() {
{ {
//we processed a spell action, so do nothing else. //we processed a spell action, so do nothing else.
} }
else if (!zone->CanDoCombat() && AI_scan_area_timer->Check()) else if (zone->CanDoCombat() && CastToNPC()->WillAggroNPCs() && AI_scan_area_timer->Check())
{ {
/* /*
* This is where NPCs look around to see if they want to attack anybody. * NPC to NPC aggro checking, npc needs npc_aggro flag
*
* if REVERSE_AGGRO is enabled, then this timer is disabled unless they
* have the npc_aggro flag on them, and aggro against clients is checked
* by the clients.
*
*/ */
Mob* temp_target = entity_list.AICheckCloseAggro(this, GetAggroRange(), GetAssistRange()); Mob* temp_target = entity_list.AICheckNPCtoNPCAggro(this, GetAggroRange(), GetAssistRange());
if (temp_target){ if (temp_target){
AddToHateList(temp_target); AddToHateList(temp_target);
} }
@ -1415,6 +1410,7 @@ void Mob::AI_Process() {
} }
} }
} }
/* Entity has been assigned another entity to follow */
else if (GetFollowID()) else if (GetFollowID())
{ {
Mob* follow = entity_list.GetMob(GetFollowID()); Mob* follow = entity_list.GetMob(GetFollowID());
@ -1458,6 +1454,7 @@ void Mob::AI_Process() {
minLastFightingDelayMoving = 0; minLastFightingDelayMoving = 0;
maxLastFightingDelayMoving = 0; maxLastFightingDelayMoving = 0;
} }
/* All normal NPC pathing */
CastToNPC()->AI_DoMovement(); CastToNPC()->AI_DoMovement();
} }
} }

View File

@ -374,6 +374,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
InitializeBuffSlots(); InitializeBuffSlots();
CalcBonuses(); CalcBonuses();
raid_target = d->raid_target; raid_target = d->raid_target;
ignore_despawn = d->ignore_despawn;
} }
NPC::~NPC() NPC::~NPC()

View File

@ -417,6 +417,8 @@ public:
bool IsRaidTarget() const { return raid_target; }; bool IsRaidTarget() const { return raid_target; };
void ResetHPUpdateTimer() { sendhpupdate_timer.Start(); } void ResetHPUpdateTimer() { sendhpupdate_timer.Start(); }
bool IgnoreDespawn() { return ignore_despawn; }
protected: protected:
const NPCType* NPCTypedata; const NPCType* NPCTypedata;
@ -532,6 +534,7 @@ protected:
bool raid_target; bool raid_target;
uint8 probability; uint8 probability;
bool ignore_despawn; //NPCs with this set to 1 will ignore the despawn value in spawngroup
private: private:
uint32 loottable_id; uint32 loottable_id;

View File

@ -208,11 +208,26 @@ bool Spawn2::Process() {
} }
} }
if(sg->despawn != 0 && condition_id == 0) bool ignore_despawn = false;
zone->Despawn(spawn2_id); if (npcthis)
{
if(IsDespawned) ignore_despawn = npcthis->IgnoreDespawn();
}
if (ignore_despawn)
{
return true; return true;
}
if (sg->despawn != 0 && condition_id == 0 && !ignore_despawn)
{
zone->Despawn(spawn2_id);
}
if (IsDespawned)
{
return true;
}
currentnpcid = npcid; currentnpcid = npcid;
NPC* npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), FlyMode3); NPC* npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), FlyMode3);
@ -295,6 +310,9 @@ void Spawn2::ForceDespawn()
if(npcthis != nullptr) if(npcthis != nullptr)
{ {
if (npcthis->IgnoreDespawn())
return;
if(!npcthis->IsEngaged()) if(!npcthis->IsEngaged())
{ {
if(sg->despawn == 3 || sg->despawn == 4) if(sg->despawn == 3 || sg->despawn == 4)

View File

@ -478,23 +478,8 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
// now tell the people in the area -- we ALWAYS want to send this, even instant cast spells. // now tell the people in the area -- we ALWAYS want to send this, even instant cast spells.
// The only time this is skipped is for NPC innate procs and weapon procs. Procs from buffs // The only time this is skipped is for NPC innate procs and weapon procs. Procs from buffs
// oddly still send this. Since those cases don't reach here, we don't need to check them // oddly still send this. Since those cases don't reach here, we don't need to check them
if (slot != CastingSlot::Discipline) { if (slot != CastingSlot::Discipline)
auto outapp = new EQApplicationPacket(OP_BeginCast,sizeof(BeginCast_Struct)); SendBeginCast(spell_id, orgcasttime);
BeginCast_Struct* begincast = (BeginCast_Struct*)outapp->pBuffer;
begincast->caster_id = GetID();
begincast->spell_id = spell_id;
begincast->cast_time = orgcasttime; // client calculates reduced time by itself
outapp->priority = 3;
entity_list.QueueCloseClients(
this, /* Sender */
outapp, /* Packet */
false, /* Ignore Sender */
RuleI(Range, BeginCast),
0, /* Skip this Mob */
true /* Packet ACK */
); //IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS);
safe_delete(outapp);
}
// cast time is 0, just finish it right now and be done with it // cast time is 0, just finish it right now and be done with it
if(cast_time == 0) { if(cast_time == 0) {
@ -537,6 +522,29 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
return(true); return(true);
} }
void Mob::SendBeginCast(uint16 spell_id, uint32 casttime)
{
auto outapp = new EQApplicationPacket(OP_BeginCast, sizeof(BeginCast_Struct));
auto begincast = (BeginCast_Struct *)outapp->pBuffer;
begincast->caster_id = GetID();
begincast->spell_id = spell_id;
begincast->cast_time = casttime; // client calculates reduced time by itself
outapp->priority = 3;
entity_list.QueueCloseClients(
this, /* Sender */
outapp, /* Packet */
false, /* Ignore Sender */
RuleI(Range, BeginCast),
0, /* Skip this Mob */
true /* Packet ACK */
); //IsClient() ? FILTER_PCSPELLS : FILTER_NPCSPELLS);
safe_delete(outapp);
}
/* /*
* Some failures should be caught before the spell finishes casting * Some failures should be caught before the spell finishes casting
* This is especially helpful to clients when they cast really long things * This is especially helpful to clients when they cast really long things
@ -3782,11 +3790,13 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
default: default:
break; break;
} }
if(reflect_chance) { if (reflect_chance) {
Message_StringID(MT_Spells, SPELL_REFLECT, GetCleanName(), spelltar->GetCleanName()); entity_list.MessageClose_StringID(this, false, RuleI(Range, SpellMessages), MT_Spells,
SPELL_REFLECT, GetCleanName(), spelltar->GetCleanName());
CheckNumHitsRemaining(NumHit::ReflectSpell); CheckNumHitsRemaining(NumHit::ReflectSpell);
// caster actually appears to change // caster actually appears to change
// ex. During OMM fight you click your reflect mask and you get the recourse from the reflected spell // ex. During OMM fight you click your reflect mask and you get the recourse from the reflected
// spell
spelltar->SpellOnTarget(spell_id, this, true, use_resist_adjust, resist_adjust); spelltar->SpellOnTarget(spell_id, this, true, use_resist_adjust, resist_adjust);
safe_delete(action_packet); safe_delete(action_packet);
return false; return false;

View File

@ -1966,7 +1966,8 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
"npc_types.bracertexture, " "npc_types.bracertexture, "
"npc_types.handtexture, " "npc_types.handtexture, "
"npc_types.legtexture, " "npc_types.legtexture, "
"npc_types.feettexture " "npc_types.feettexture, "
"npc_types.ignore_despawn "
"FROM npc_types %s", "FROM npc_types %s",
where_condition.c_str() where_condition.c_str()
); );
@ -2141,6 +2142,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
temp_npctype_data->handtexture = atoi(row[94]); temp_npctype_data->handtexture = atoi(row[94]);
temp_npctype_data->legtexture = atoi(row[95]); temp_npctype_data->legtexture = atoi(row[95]);
temp_npctype_data->feettexture = atoi(row[96]); temp_npctype_data->feettexture = atoi(row[96]);
temp_npctype_data->ignore_despawn = atoi(row[97]) == 1 ? true : false;
// If NPC with duplicate NPC id already in table, // If NPC with duplicate NPC id already in table,
// free item we attempted to add. // free item we attempted to add.
@ -2987,16 +2989,16 @@ void ZoneDatabase::QGlobalPurge()
database.QueryDatabase(query); database.QueryDatabase(query);
} }
void ZoneDatabase::InsertDoor(uint32 ddoordbid, uint16 ddoorid, const char* ddoor_name, const glm::vec4& position, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize){ void ZoneDatabase::InsertDoor(uint32 ddoordbid, uint16 ddoorid, const char* ddoor_name, const glm::vec4& position, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize, bool ddisabletimer){
std::string query = StringFormat("REPLACE INTO doors (id, doorid, zone, version, name, " std::string query = StringFormat("REPLACE INTO doors (id, doorid, zone, version, name, "
"pos_x, pos_y, pos_z, heading, opentype, guild, lockpick, " "pos_x, pos_y, pos_z, heading, opentype, guild, lockpick, "
"keyitem, door_param, invert_state, incline, size) " "keyitem, disable_timer, door_param, invert_state, incline, size) "
"VALUES('%i', '%i', '%s', '%i', '%s', '%f', '%f', " "VALUES('%i', '%i', '%s', '%i', '%s', '%f', '%f', "
"'%f', '%f', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i')", "'%f', '%f', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i', '%i')",
ddoordbid, ddoorid, zone->GetShortName(), zone->GetInstanceVersion(), ddoordbid, ddoorid, zone->GetShortName(), zone->GetInstanceVersion(),
ddoor_name, position.x, position.y, position.z, position.w, ddoor_name, position.x, position.y, position.z, position.w,
dopentype, dguildid, dlockpick, dkeyitem, ddoor_param, dinvert, dincline, dsize); dopentype, dguildid, dlockpick, dkeyitem, (ddisabletimer ? 1 : 0), ddoor_param, dinvert, dincline, dsize);
QueryDatabase(query); QueryDatabase(query);
} }

View File

@ -454,7 +454,7 @@ public:
int32 GetDoorsCount(uint32* oMaxID, const char *zone_name, int16 version); int32 GetDoorsCount(uint32* oMaxID, const char *zone_name, int16 version);
int32 GetDoorsCountPlusOne(const char *zone_name, int16 version); int32 GetDoorsCountPlusOne(const char *zone_name, int16 version);
int32 GetDoorsDBCountPlusOne(const char *zone_name, int16 version); int32 GetDoorsDBCountPlusOne(const char *zone_name, int16 version);
void InsertDoor(uint32 did, uint16 ddoorid, const char* ddoor_name, const glm::vec4& position, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize); void InsertDoor(uint32 did, uint16 ddoorid, const char* ddoor_name, const glm::vec4& position, uint8 dopentype, uint16 dguildid, uint32 dlockpick, uint32 dkeyitem, uint8 ddoor_param, uint8 dinvert, int dincline, uint16 dsize, bool ddisabletimer = false);
/* Blocked Spells */ /* Blocked Spells */
int32 GetBlockedSpellsCount(uint32 zoneid); int32 GetBlockedSpellsCount(uint32 zoneid);

View File

@ -132,6 +132,7 @@ struct NPCType
uint8 handtexture; uint8 handtexture;
uint8 legtexture; uint8 legtexture;
uint8 feettexture; uint8 feettexture;
bool ignore_despawn;
}; };
namespace player_lootitem { namespace player_lootitem {
@ -201,6 +202,7 @@ struct Door {
uint8 nokeyring; uint8 nokeyring;
uint8 trigger_door; uint8 trigger_door;
uint8 trigger_type; uint8 trigger_type;
uint8 disable_timer;
uint32 door_param; uint32 door_param;
int invert_state; int invert_state;
uint16 size; uint16 size;