From d1facd9368ebeb24051376dabfefdbbbd8449c55 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 27 Mar 2016 10:57:29 -0400 Subject: [PATCH 1/2] Kayen: delete bad test files accidently merged --- common/shareddbx.cpp | 2162 -------- .../required/2014_02_12_spells_new_update.sql | 13 - ...014_04_10_No_Target_With_Hotkey - Copy.sql | 3 - .../git/required/2014_06_22_MetabolismAAs.sql | 6 - zone/AA_base.cpp | 1990 ------- zone/AA_v1.cpp | 2032 ------- zone/MobAI_M.cpp | 2760 ---------- zone/attackx.cpp | 4663 ----------------- zone/bonusesxx.cpp | 4337 --------------- zone/groupsx.cpp | 2194 -------- zone/npcx.cpp | 2492 --------- zone/perl_npcX.cpp | 2487 --------- 12 files changed, 25139 deletions(-) delete mode 100644 common/shareddbx.cpp delete mode 100644 utils/sql/git/required/2014_02_12_spells_new_update.sql delete mode 100644 utils/sql/git/required/2014_04_10_No_Target_With_Hotkey - Copy.sql delete mode 100644 utils/sql/git/required/2014_06_22_MetabolismAAs.sql delete mode 100644 zone/AA_base.cpp delete mode 100644 zone/AA_v1.cpp delete mode 100644 zone/MobAI_M.cpp delete mode 100644 zone/attackx.cpp delete mode 100644 zone/bonusesxx.cpp delete mode 100644 zone/groupsx.cpp delete mode 100644 zone/npcx.cpp delete mode 100644 zone/perl_npcX.cpp diff --git a/common/shareddbx.cpp b/common/shareddbx.cpp deleted file mode 100644 index 60e29cccb..000000000 --- a/common/shareddbx.cpp +++ /dev/null @@ -1,2162 +0,0 @@ -#include -#include -#include - -#include "shareddb.h" -#include "mysql.h" -#include "Item.h" -#include "classes.h" -#include "rulesys.h" -#include "seperator.h" -#include "StringUtil.h" -#include "eq_packet_structs.h" -#include "guilds.h" -#include "extprofile.h" -#include "memory_mapped_file.h" -#include "ipc_mutex.h" -#include "eqemu_exception.h" -#include "loottable.h" -#include "faction.h" -#include "features.h" - -SharedDatabase::SharedDatabase() -: Database(), skill_caps_mmf(nullptr), items_mmf(nullptr), items_hash(nullptr), faction_mmf(nullptr), faction_hash(nullptr), - loot_table_mmf(nullptr), loot_table_hash(nullptr), loot_drop_mmf(nullptr), loot_drop_hash(nullptr), base_data_mmf(nullptr) -{ -} - -SharedDatabase::SharedDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port) -: Database(host, user, passwd, database, port), skill_caps_mmf(nullptr), items_mmf(nullptr), items_hash(nullptr), - faction_mmf(nullptr), faction_hash(nullptr), loot_table_mmf(nullptr), loot_table_hash(nullptr), loot_drop_mmf(nullptr), - loot_drop_hash(nullptr), base_data_mmf(nullptr) -{ -} - -SharedDatabase::~SharedDatabase() { - safe_delete(skill_caps_mmf); - safe_delete(items_mmf); - safe_delete(items_hash); - safe_delete(faction_mmf); - safe_delete(faction_hash); - safe_delete(loot_table_mmf); - safe_delete(loot_drop_mmf); - safe_delete(loot_table_hash); - safe_delete(loot_drop_hash); - safe_delete(base_data_mmf); -} - -bool SharedDatabase::SetHideMe(uint32 account_id, uint8 hideme) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET hideme = %i where id = %i", hideme, account_id), errbuf)) { - std::cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - safe_delete_array(query); - return true; - -} - -uint8 SharedDatabase::GetGMSpeed(uint32 account_id) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT gmspeed FROM account where id='%i'", account_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint8 gmspeed = atoi(row[0]); - mysql_free_result(result); - return gmspeed; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - - std::cerr << "Error in GetGMSpeed query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - return 0; - - -} - -bool SharedDatabase::SetGMSpeed(uint32 account_id, uint8 gmspeed) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET gmspeed = %i where id = %i", gmspeed, account_id), errbuf)) { - std::cerr << "Error in SetGMSpeed query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - safe_delete_array(query); - return true; - -} - -uint32 SharedDatabase::GetTotalTimeEntitledOnAccount(uint32 AccountID) { - - uint32 EntitledTime = 0; - - const char *EntitledQuery = "select sum(ascii(substring(profile, 237, 1)) + (ascii(substring(profile, 238, 1)) * 256) +" - "(ascii(substring(profile, 239, 1)) * 65536) + (ascii(substring(profile, 240, 1)) * 16777216))" - "from character_ where account_id = %i"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, EntitledQuery, AccountID), errbuf, &result)) { - - if (mysql_num_rows(result) == 1) { - - row = mysql_fetch_row(result); - - EntitledTime = atoi(row[0]); - } - - mysql_free_result(result); - } - - safe_delete_array(query); - - return EntitledTime; -} - -bool SharedDatabase::SaveCursor(uint32 char_id, std::list::const_iterator &start, std::list::const_iterator &end) -{ -iter_queue it; -int i; -bool ret=true; - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - // Delete cursor items - if ((ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND ( (slotid >=8000 and slotid<=8999) or slotid=30 or (slotid>=331 and slotid<=340))", char_id), errbuf))) { - for(it=start,i=8000;it!=end;++it,i++) { - ItemInst *inst=*it; - if (!(ret=SaveInventory(char_id,inst,(i==8000) ? 30 : i))) - break; - } - } else { - std::cout << "Clearing cursor failed: " << errbuf << std::endl; - } - safe_delete_array(query); - - return ret; -} - -bool SharedDatabase::VerifyInventory(uint32 account_id, int16 slot_id, const ItemInst* inst) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - // Delete cursor items - if (!RunQuery(query, MakeAnyLenString(&query, - "SELECT itemid,charges FROM sharedbank " - "WHERE acctid=%d AND slotid=%d", - account_id, slot_id), errbuf, &result)) { - LogFile->write(EQEMuLog::Error, "Error runing inventory verification query '%s': %s", query, errbuf); - safe_delete_array(query); - //returning true is less harmful in the face of a query error - return(true); - } - safe_delete_array(query); - - row = mysql_fetch_row(result); - bool found = false; - if(row) { - uint32 id = atoi(row[0]); - uint16 charges = atoi(row[1]); - - uint16 expect_charges = 0; - if(inst->GetCharges() >= 0) - expect_charges = inst->GetCharges(); - else - expect_charges = 0x7FFF; - - if(id == inst->GetItem()->ID && charges == expect_charges) - found = true; - } - mysql_free_result(result); - return(found); -} - -bool SharedDatabase::SaveInventory(uint32 char_id, const ItemInst* inst, int16 slot_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - bool ret = false; - uint32 augslot[5] = { 0, 0, 0, 0, 0 }; - - //never save tribute slots: - if(slot_id >= 400 && slot_id <= 404) - return(true); - - if (inst && inst->IsType(ItemClassCommon)) { - for(int i=0;i<5;i++) { - ItemInst *auginst=inst->GetItem(i); - augslot[i]=(auginst && auginst->GetItem()) ? auginst->GetItem()->ID : 0; - } - } - - if (slot_id>=2500 && slot_id<=2600) { // Shared bank inventory - if (!inst) { - // Delete item - uint32 account_id = GetAccountIDByChar(char_id); - uint32 len_query = MakeAnyLenString(&query, "DELETE FROM sharedbank WHERE acctid=%i AND slotid=%i", - account_id, slot_id); - - ret = RunQuery(query, len_query, errbuf); - - // Delete bag slots, if need be - if (ret && Inventory::SupportsContainers(slot_id)) { - safe_delete_array(query); - int16 base_slot_id = Inventory::CalcSlotId(slot_id, 0); - ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM sharedbank WHERE acctid=%i AND slotid>=%i AND slotid<%i", - account_id, base_slot_id, (base_slot_id+10)), errbuf); - } - - // @merth: need to delete augments here - } - else { - // Update/Insert item - uint32 account_id = GetAccountIDByChar(char_id); - uint16 charges = 0; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - - uint32 len_query = MakeAnyLenString(&query, - "REPLACE INTO sharedbank " - " (acctid,slotid,itemid,charges,custom_data," - " augslot1,augslot2,augslot3,augslot4,augslot5)" - " VALUES(%lu,%lu,%lu,%lu,'%s'," - " %lu,%lu,%lu,%lu,%lu)", - (unsigned long)account_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, (unsigned long)charges, - inst->GetCustomDataString().c_str(), - (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4]); - - - ret = RunQuery(query, len_query, errbuf); - } - } - else { // All other inventory - if (!inst) { - // Delete item - ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND slotid=%i", - char_id, slot_id), errbuf); - - // Delete bag slots, if need be - if (ret && Inventory::SupportsContainers(slot_id)) { - safe_delete_array(query); - int16 base_slot_id = Inventory::CalcSlotId(slot_id, 0); - ret = RunQuery(query, MakeAnyLenString(&query, "DELETE FROM inventory WHERE charid=%i AND slotid>=%i AND slotid<%i", - char_id, base_slot_id, (base_slot_id+10)), errbuf); - } - - // @merth: need to delete augments here - } - else { - uint16 charges = 0; - if(inst->GetCharges() >= 0) - charges = inst->GetCharges(); - else - charges = 0x7FFF; - // Update/Insert item - uint32 len_query = MakeAnyLenString(&query, - "REPLACE INTO inventory " - " (charid,slotid,itemid,charges,instnodrop,custom_data,color," - " augslot1,augslot2,augslot3,augslot4,augslot5)" - " VALUES(%lu,%lu,%lu,%lu,%lu,'%s',%lu," - " %lu,%lu,%lu,%lu,%lu)", - (unsigned long)char_id, (unsigned long)slot_id, (unsigned long)inst->GetItem()->ID, (unsigned long)charges, - (unsigned long)(inst->IsInstNoDrop() ? 1:0),inst->GetCustomDataString().c_str(),(unsigned long)inst->GetColor(), - (unsigned long)augslot[0],(unsigned long)augslot[1],(unsigned long)augslot[2],(unsigned long)augslot[3],(unsigned long)augslot[4] ); - - ret = RunQuery(query, len_query, errbuf); - } - } - - if (!ret) - LogFile->write(EQEMuLog::Error, "SaveInventory query '%s': %s", query, errbuf); - safe_delete_array(query); - - // Save bag contents, if slot supports bag contents - if (inst && inst->IsType(ItemClassContainer) && Inventory::SupportsContainers(slot_id)) { - for (uint8 idx=0; idx<10; idx++) { - const ItemInst* baginst = inst->GetItem(idx); - SaveInventory(char_id, baginst, Inventory::CalcSlotId(slot_id, idx)); - } - } - - // @merth: need to save augments here - - return ret; -} - -int32 SharedDatabase::GetSharedPlatinum(uint32 account_id) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT sharedplat FROM account WHERE id='%i'", account_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) - { - row = mysql_fetch_row(result); - uint32 shared_platinum = atoi(row[0]); - mysql_free_result(result); - return shared_platinum; - } - else - { - mysql_free_result(result); - return 0; - } - mysql_free_result(result); - } - else - { - std::cerr << "Error in GetSharedPlatinum query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - return 0; -} - -bool SharedDatabase::SetSharedPlatinum(uint32 account_id, int32 amount_to_add) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE account SET sharedplat = sharedplat + %i WHERE id = %i", amount_to_add, account_id), errbuf)) { - std::cerr << "Error in SetSharedPlatinum query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - safe_delete_array(query); - return true; -} - -bool SharedDatabase::SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin_level) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - const Item_Struct* myitem; - - RunQuery - ( - query, - MakeAnyLenString - ( - &query, - "SELECT itemid, item_charges, slot FROM starting_items " - "WHERE (race = %i or race = 0) AND (class = %i or class = 0) AND " - "(deityid = %i or deityid=0) AND (zoneid = %i or zoneid = 0) AND " - "gm <= %i ORDER BY id", - si_race, si_class, si_deity, si_current_zone, admin_level - ), - errbuf, - &result - ); - safe_delete_array(query); - - while((row = mysql_fetch_row(result))) { - int itemid = atoi(row[0]); - int charges = atoi(row[1]); - int slot = atoi(row[2]); - myitem = GetItem(itemid); - if(!myitem) - continue; - ItemInst* myinst = CreateBaseItem(myitem, charges); - if(slot < 0) - slot = inv->FindFreeSlot(0,0); - inv->PutItem(slot, *myinst); - safe_delete(myinst); - } - - if(result) mysql_free_result(result); - - return true; -} - - -// Retrieve shared bank inventory based on either account or character -bool SharedDatabase::GetSharedBank(uint32 id, Inventory* inv, bool is_charid) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 len_query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - bool ret = false; - - if (is_charid) { - len_query = MakeAnyLenString(&query, - "SELECT sb.slotid,sb.itemid,sb.charges,sb.augslot1,sb.augslot2,sb.augslot3,sb.augslot4,sb.augslot5,sb.custom_data from sharedbank sb " - "INNER JOIN character_ ch ON ch.account_id=sb.acctid " - "WHERE ch.id=%i", id); - } - else { - len_query = MakeAnyLenString(&query, - "SELECT slotid,itemid,charges,augslot1,augslot2,augslot3,augslot4,augslot5,custom_data from sharedbank WHERE acctid=%i", id); - } - - if (RunQuery(query, len_query, errbuf, &result)) { - while ((row = mysql_fetch_row(result))) { - int16 slot_id = (int16)atoi(row[0]); - uint32 item_id = (uint32)atoi(row[1]); - int8 charges = (int8)atoi(row[2]); - uint32 aug[5]; - aug[0] = (uint32)atoi(row[3]); - aug[1] = (uint32)atoi(row[4]); - aug[2] = (uint32)atoi(row[5]); - aug[3] = (uint32)atoi(row[6]); - aug[4] = (uint32)atoi(row[7]); - const Item_Struct* item = GetItem(item_id); - - if (item) { - int16 put_slot_id = INVALID_INDEX; - - ItemInst* inst = CreateBaseItem(item, charges); - if (item->ItemClass == ItemClassCommon) { - for(int i=0;i<5;i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); - } - } - } - if(row[8]) { - std::string data_str(row[8]); - std::string id; - std::string value; - bool use_id = true; - - for(int i = 0; i < data_str.length(); ++i) { - if(data_str[i] == '^') { - if(!use_id) { - inst->SetCustomData(id, value); - id.clear(); - value.clear(); - } - use_id = !use_id; - } - else { - char v = data_str[i]; - if(use_id) { - id.push_back(v); - } else { - value.push_back(v); - } - } - } - } - - put_slot_id = inv->PutItem(slot_id, *inst); - safe_delete(inst); - - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in shared bank inventory: %s=%i, item_id=%i, slot_id=%i", - ((is_charid==true) ? "charid" : "acctid"), id, item_id, slot_id); - - if(is_charid) - SaveInventory(id,nullptr,slot_id); - } - } - else { - LogFile->write(EQEMuLog::Error, - "Warning: %s %i has an invalid item_id %i in inventory slot %i", - ((is_charid==true) ? "charid" : "acctid"), id, item_id, slot_id); - } - } - - mysql_free_result(result); - ret = true; - } - else { - LogFile->write(EQEMuLog::Error, "Database::GetSharedBank(uint32 account_id): %s", errbuf); - } - - safe_delete_array(query); - return ret; -} - - -// Overloaded: Retrieve character inventory based on character id -bool SharedDatabase::GetInventory(uint32 char_id, Inventory* inv) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - bool ret = false; - - // Retrieve character inventory - if (RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5," - "instnodrop,custom_data FROM inventory WHERE charid=%i ORDER BY slotid", char_id), errbuf, &result)) { - - while ((row = mysql_fetch_row(result))) { - int16 slot_id = atoi(row[0]); - uint32 item_id = atoi(row[1]); - uint16 charges = atoi(row[2]); - uint32 color = atoul(row[3]); - uint32 aug[5]; - aug[0] = (uint32)atoul(row[4]); - aug[1] = (uint32)atoul(row[5]); - aug[2] = (uint32)atoul(row[6]); - aug[3] = (uint32)atoul(row[7]); - aug[4] = (uint32)atoul(row[8]); - bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; - - const Item_Struct* item = GetItem(item_id); - - if (item) { - int16 put_slot_id = INVALID_INDEX; - - ItemInst* inst = CreateBaseItem(item, charges); - - if(row[10]) { - std::string data_str(row[10]); - std::string id; - std::string value; - bool use_id = true; - - for(int i = 0; i < data_str.length(); ++i) { - if(data_str[i] == '^') { - if(!use_id) { - inst->SetCustomData(id, value); - id.clear(); - value.clear(); - } - use_id = !use_id; - } - else { - char v = data_str[i]; - if(use_id) { - id.push_back(v); - } else { - value.push_back(v); - } - } - } - } - - if (instnodrop || (slot_id >= 0 && slot_id <= 21 && inst->GetItem()->Attuneable)) - inst->SetInstNoDrop(true); - if (color > 0) - inst->SetColor(color); - if(charges==0x7FFF) - inst->SetCharges(-1); - else - inst->SetCharges(charges); - - if (item->ItemClass == ItemClassCommon) { - for(int i=0;i<5;i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); - } - } - } - - if (slot_id>=8000 && slot_id <= 8999) - put_slot_id = inv->PushCursor(*inst); - else - put_slot_id = inv->PutItem(slot_id, *inst); - safe_delete(inst); - - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in inventory: charid=%i, item_id=%i, slot_id=%i", - char_id, item_id, slot_id); - } - } - else { - LogFile->write(EQEMuLog::Error, - "Warning: charid %i has an invalid item_id %i in inventory slot %i", - char_id, item_id, slot_id); - } - } - mysql_free_result(result); - - // Retrieve shared inventory - ret = GetSharedBank(char_id, inv, true); - } - else { - LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query, errbuf); - LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); - } - - safe_delete_array(query); - return ret; -} - -// Overloaded: Retrieve character inventory based on account_id and character name -bool SharedDatabase::GetInventory(uint32 account_id, char* name, Inventory* inv) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - bool ret = false; - - // Retrieve character inventory - if (RunQuery(query, MakeAnyLenString(&query, "SELECT slotid,itemid,charges,color,augslot1,augslot2,augslot3,augslot4,augslot5," - "instnodrop,custom_data FROM inventory INNER JOIN character_ ch ON ch.id=charid WHERE ch.name='%s' AND ch.account_id=%i ORDER BY slotid", - name, account_id), errbuf, &result)) - { - while ((row = mysql_fetch_row(result))) { - int16 slot_id = atoi(row[0]); - uint32 item_id = atoi(row[1]); - int8 charges = atoi(row[2]); - uint32 color = atoul(row[3]); - uint32 aug[5]; - aug[0] = (uint32)atoi(row[4]); - aug[1] = (uint32)atoi(row[5]); - aug[2] = (uint32)atoi(row[6]); - aug[3] = (uint32)atoi(row[7]); - aug[4] = (uint32)atoi(row[8]); - bool instnodrop = (row[9] && (uint16)atoi(row[9])) ? true : false; - const Item_Struct* item = GetItem(item_id); - int16 put_slot_id = INVALID_INDEX; - if(!item) - continue; - - ItemInst* inst = CreateBaseItem(item, charges); - inst->SetInstNoDrop(instnodrop); - - if(row[10]) { - std::string data_str(row[10]); - std::string id; - std::string value; - bool use_id = true; - - for(int i = 0; i < data_str.length(); ++i) { - if(data_str[i] == '^') { - if(!use_id) { - inst->SetCustomData(id, value); - id.clear(); - value.clear(); - } - use_id = !use_id; - } - else { - char v = data_str[i]; - if(use_id) { - id.push_back(v); - } else { - value.push_back(v); - } - } - } - } - - if (color > 0) - inst->SetColor(color); - inst->SetCharges(charges); - - if (item->ItemClass == ItemClassCommon) { - for(int i=0;i<5;i++) { - if (aug[i]) { - inst->PutAugment(this, i, aug[i]); - } - } - } - if (slot_id>=8000 && slot_id <= 8999) - put_slot_id = inv->PushCursor(*inst); - else - put_slot_id = inv->PutItem(slot_id, *inst); - safe_delete(inst); - - // Save ptr to item in inventory - if (put_slot_id == INVALID_INDEX) { - LogFile->write(EQEMuLog::Error, - "Warning: Invalid slot_id for item in inventory: name=%s, acctid=%i, item_id=%i, slot_id=%i", - name, account_id, item_id, slot_id); - } - } - mysql_free_result(result); - - // Retrieve shared inventory - ret = GetSharedBank(account_id, inv, false); - } - else { - LogFile->write(EQEMuLog::Error, "GetInventory query '%s' %s", query, errbuf); - LogFile->write(EQEMuLog::Error, "If you got an error related to the 'instnodrop' field, run the following SQL Queries:\nalter table inventory add instnodrop tinyint(1) unsigned default 0 not null;\n"); - } - - safe_delete_array(query); - return ret; -} - - -void SharedDatabase::GetItemsCount(int32 &item_count, uint32 &max_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - item_count = -1; - max_id = 0; - - char query[] = "SELECT MAX(id), count(*) FROM items"; - if (RunQuery(query, static_cast(strlen(query)), errbuf, &result)) { - row = mysql_fetch_row(result); - if (row != nullptr && row[1] != 0) { - item_count = atoi(row[1]); - if(row[0]) - max_id = atoi(row[0]); - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in GetItemsCount '%s': '%s'", query, errbuf); - } -} - -bool SharedDatabase::LoadItems() { - if(items_mmf) { - return true; - } - - try { - EQEmu::IPCMutex mutex("items"); - mutex.Lock(); - items_mmf = new EQEmu::MemoryMappedFile("shared/items"); - - int32 items = -1; - uint32 max_item = 0; - GetItemsCount(items, max_item); - if(items == -1) { - EQ_EXCEPT("SharedDatabase", "Database returned no result"); - } - uint32 size = static_cast(EQEmu::FixedMemoryHashSet::estimated_size(items, max_item)); - if(items_mmf->Size() != size) { - EQ_EXCEPT("SharedDatabase", "Couldn't load items because items_mmf->Size() != size"); - } - - items_hash = new EQEmu::FixedMemoryHashSet(reinterpret_cast(items_mmf->Get()), size); - mutex.Unlock(); - } catch(std::exception& ex) { - LogFile->write(EQEMuLog::Error, "Error Loading Items: %s", ex.what()); - return false; - } - - return true; -} - -void SharedDatabase::LoadItems(void *data, uint32 size, int32 items, uint32 max_item_id) { - EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, items, max_item_id); - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - char ndbuffer[4]; - bool disableNoRent = false; - if(GetVariable("disablenorent", ndbuffer, 4)) { - if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { - disableNoRent = true; - } - } - bool disableNoDrop = false; - if(GetVariable("disablenodrop", ndbuffer, 4)) { - if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { - disableNoDrop = true; - } - } - bool disableLoreGroup = false; - if(GetVariable("disablelore", ndbuffer, 4)) { - if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { - disableLoreGroup = true; - } - } - bool disableNoTransfer = false; - if(GetVariable("disablenotransfer", ndbuffer, 4)) { - if(ndbuffer[0] == '1' && ndbuffer[1] == '\0') { - disableNoTransfer = true; - } - } - - char query[] = "select source," -#define F(x) "`"#x"`," -#include "item_fieldlist.h" -#undef F - "updated" - " from items order by id"; - Item_Struct item; - if(RunQuery(query, sizeof(query), errbuf, &result)) { - while((row = mysql_fetch_row(result))) { - memset(&item, 0, sizeof(Item_Struct)); - - item.ItemClass = (uint8)atoi(row[ItemField::itemclass]); - strcpy(item.Name,row[ItemField::name]); - strcpy(item.Lore,row[ItemField::lore]); - strcpy(item.IDFile,row[ItemField::idfile]); - item.ID = (uint32)atoul(row[ItemField::id]); - item.Weight = (uint8)atoi(row[ItemField::weight]); - item.NoRent = disableNoRent ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::norent]); - item.NoDrop = disableNoDrop ? (uint8)atoi("255") : (uint8)atoi(row[ItemField::nodrop]); - item.Size = (uint8)atoi(row[ItemField::size]); - item.Slots = (uint32)atoul(row[ItemField::slots]); - item.Price = (uint32)atoul(row[ItemField::price]); - item.Icon = (uint32)atoul(row[ItemField::icon]); - item.BenefitFlag = (atoul(row[ItemField::benefitflag]) != 0); - item.Tradeskills = (atoi(row[ItemField::tradeskills])==0) ? false : true; - item.CR = (int8)atoi(row[ItemField::cr]); - item.DR = (int8)atoi(row[ItemField::dr]); - item.PR = (int8)atoi(row[ItemField::pr]); - item.MR = (int8)atoi(row[ItemField::mr]); - item.FR = (int8)atoi(row[ItemField::fr]); - item.AStr = (int8)atoi(row[ItemField::astr]); - item.ASta = (int8)atoi(row[ItemField::asta]); - item.AAgi = (int8)atoi(row[ItemField::aagi]); - item.ADex = (int8)atoi(row[ItemField::adex]); - item.ACha = (int8)atoi(row[ItemField::acha]); - item.AInt = (int8)atoi(row[ItemField::aint]); - item.AWis = (int8)atoi(row[ItemField::awis]); - item.HP = (int32)atoul(row[ItemField::hp]); - item.Mana = (int32)atoul(row[ItemField::mana]); - item.AC = (int32)atoul(row[ItemField::ac]); - item.Deity = (uint32)atoul(row[ItemField::deity]); - item.SkillModValue = (int32)atoul(row[ItemField::skillmodvalue]); - //item.Unk033 = (int32)atoul(row[ItemField::UNK033]); - item.SkillModType = (uint32)atoul(row[ItemField::skillmodtype]); - item.BaneDmgRace = (uint32)atoul(row[ItemField::banedmgrace]); - item.BaneDmgAmt = (int8)atoi(row[ItemField::banedmgamt]); - item.BaneDmgBody = (uint32)atoul(row[ItemField::banedmgbody]); - item.Magic = (atoi(row[ItemField::magic])==0) ? false : true; - item.CastTime_ = (int32)atoul(row[ItemField::casttime_]); - item.ReqLevel = (uint8)atoi(row[ItemField::reqlevel]); - item.BardType = (uint32)atoul(row[ItemField::bardtype]); - item.BardValue = (int32)atoul(row[ItemField::bardvalue]); - item.Light = (int8)atoi(row[ItemField::light]); - item.Delay = (uint8)atoi(row[ItemField::delay]); - item.RecLevel = (uint8)atoi(row[ItemField::reclevel]); - item.RecSkill = (uint8)atoi(row[ItemField::recskill]); - item.ElemDmgType = (uint8)atoi(row[ItemField::elemdmgtype]); - item.ElemDmgAmt = (uint8)atoi(row[ItemField::elemdmgamt]); - item.Range = (uint8)atoi(row[ItemField::range]); - item.Damage = (uint32)atoi(row[ItemField::damage]); - item.Color = (uint32)atoul(row[ItemField::color]); - item.Classes = (uint32)atoul(row[ItemField::classes]); - item.Races = (uint32)atoul(row[ItemField::races]); - //item.Unk054 = (uint32)atoul(row[ItemField::UNK054]); - item.MaxCharges = (int16)atoi(row[ItemField::maxcharges]); - item.ItemType = (uint8)atoi(row[ItemField::itemtype]); - item.Material = (uint8)atoi(row[ItemField::material]); - item.SellRate = (float)atof(row[ItemField::sellrate]); - //item.Unk059 = (uint32)atoul(row[ItemField::UNK059]); - item.CastTime = (uint32)atoul(row[ItemField::casttime]); - item.EliteMaterial = (uint32)atoul(row[ItemField::elitematerial]); - item.ProcRate = (int32)atoi(row[ItemField::procrate]); - item.CombatEffects = (int8)atoi(row[ItemField::combateffects]); - item.Shielding = (int8)atoi(row[ItemField::shielding]); - item.StunResist = (int8)atoi(row[ItemField::stunresist]); - item.StrikeThrough = (int8)atoi(row[ItemField::strikethrough]); - item.ExtraDmgSkill = (uint32)atoul(row[ItemField::extradmgskill]); - item.ExtraDmgAmt = (uint32)atoul(row[ItemField::extradmgamt]); - item.SpellShield = (int8)atoi(row[ItemField::spellshield]); - item.Avoidance = (int8)atoi(row[ItemField::avoidance]); - item.Accuracy = (int8)atoi(row[ItemField::accuracy]); - item.CharmFileID = (uint32)atoul(row[ItemField::charmfileid]); - item.FactionMod1 = (int32)atoul(row[ItemField::factionmod1]); - item.FactionMod2 = (int32)atoul(row[ItemField::factionmod2]); - item.FactionMod3 = (int32)atoul(row[ItemField::factionmod3]); - item.FactionMod4 = (int32)atoul(row[ItemField::factionmod4]); - item.FactionAmt1 = (int32)atoul(row[ItemField::factionamt1]); - item.FactionAmt2 = (int32)atoul(row[ItemField::factionamt2]); - item.FactionAmt3 = (int32)atoul(row[ItemField::factionamt3]); - item.FactionAmt4 = (int32)atoul(row[ItemField::factionamt4]); - strcpy(item.CharmFile,row[ItemField::charmfile]); - item.AugType = (uint32)atoul(row[ItemField::augtype]); - item.AugSlotType[0] = (uint8)atoi(row[ItemField::augslot1type]); - item.AugSlotVisible[0] = (uint8)atoi(row[ItemField::augslot1visible]); - item.AugSlotUnk2[0] = 0; - item.AugSlotType[1] = (uint8)atoi(row[ItemField::augslot2type]); - item.AugSlotVisible[1] = (uint8)atoi(row[ItemField::augslot2visible]); - item.AugSlotUnk2[1] = 0; - item.AugSlotType[2] = (uint8)atoi(row[ItemField::augslot3type]); - item.AugSlotVisible[2] = (uint8)atoi(row[ItemField::augslot3visible]); - item.AugSlotUnk2[2] = 0; - item.AugSlotType[3] = (uint8)atoi(row[ItemField::augslot4type]); - item.AugSlotVisible[3] = (uint8)atoi(row[ItemField::augslot4visible]); - item.AugSlotUnk2[3] = 0; - item.AugSlotType[4] = (uint8)atoi(row[ItemField::augslot5type]); - item.AugSlotVisible[4] = (uint8)atoi(row[ItemField::augslot5visible]); - item.AugSlotUnk2[4] = 0; - item.LDoNTheme = (uint32)atoul(row[ItemField::ldontheme]); - item.LDoNPrice = (uint32)atoul(row[ItemField::ldonprice]); - item.LDoNSold = (uint32)atoul(row[ItemField::ldonsold]); - item.BagType = (uint8)atoi(row[ItemField::bagtype]); - item.BagSlots = (uint8)atoi(row[ItemField::bagslots]); - item.BagSize = (uint8)atoi(row[ItemField::bagsize]); - item.BagWR = (uint8)atoi(row[ItemField::bagwr]); - item.Book = (uint8)atoi(row[ItemField::book]); - item.BookType = (uint32)atoul(row[ItemField::booktype]); - strcpy(item.Filename,row[ItemField::filename]); - item.BaneDmgRaceAmt = (uint32)atoul(row[ItemField::banedmgraceamt]); - item.AugRestrict = (uint32)atoul(row[ItemField::augrestrict]); - item.LoreGroup = disableLoreGroup ? (uint8)atoi("0") : atoi(row[ItemField::loregroup]); - item.LoreFlag = item.LoreGroup!=0; - item.PendingLoreFlag = (atoi(row[ItemField::pendingloreflag])==0) ? false : true; - item.ArtifactFlag = (atoi(row[ItemField::artifactflag])==0) ? false : true; - item.SummonedFlag = (atoi(row[ItemField::summonedflag])==0) ? false : true; - item.Favor = (uint32)atoul(row[ItemField::favor]); - item.FVNoDrop = (atoi(row[ItemField::fvnodrop])==0) ? false : true; - item.Endur = (uint32)atoul(row[ItemField::endur]); - item.DotShielding = (uint32)atoul(row[ItemField::dotshielding]); - item.Attack = (uint32)atoul(row[ItemField::attack]); - item.Regen = (uint32)atoul(row[ItemField::regen]); - item.ManaRegen = (uint32)atoul(row[ItemField::manaregen]); - item.EnduranceRegen = (uint32)atoul(row[ItemField::enduranceregen]); - item.Haste = (uint32)atoul(row[ItemField::haste]); - item.DamageShield = (uint32)atoul(row[ItemField::damageshield]); - item.RecastDelay = (uint32)atoul(row[ItemField::recastdelay]); - item.RecastType = (uint32)atoul(row[ItemField::recasttype]); - item.GuildFavor = (uint32)atoul(row[ItemField::guildfavor]); - item.AugDistiller = (uint32)atoul(row[ItemField::augdistiller]); - item.Attuneable = (atoi(row[ItemField::attuneable])==0) ? false : true; - item.NoPet = (atoi(row[ItemField::nopet])==0) ? false : true; - item.PointType = (uint32)atoul(row[ItemField::pointtype]); - item.PotionBelt = (atoi(row[ItemField::potionbelt])==0) ? false : true; - item.PotionBeltSlots = (atoi(row[ItemField::potionbeltslots])==0) ? false : true; - item.StackSize = (uint16)atoi(row[ItemField::stacksize]); - item.NoTransfer = disableNoTransfer ? false : (atoi(row[ItemField::notransfer])==0) ? false : true; - item.Stackable = (atoi(row[ItemField::stackable])==0) ? false : true; - item.Click.Effect = (uint32)atoul(row[ItemField::clickeffect]); - item.Click.Type = (uint8)atoul(row[ItemField::clicktype]); - item.Click.Level = (uint8)atoul(row[ItemField::clicklevel]); - item.Click.Level2 = (uint8)atoul(row[ItemField::clicklevel2]); - strcpy(item.CharmFile,row[ItemField::charmfile]); - item.Proc.Effect = (uint16)atoul(row[ItemField::proceffect]); - item.Proc.Type = (uint8)atoul(row[ItemField::proctype]); - item.Proc.Level = (uint8)atoul(row[ItemField::proclevel]); - item.Proc.Level2 = (uint8)atoul(row[ItemField::proclevel2]); - item.Worn.Effect = (uint16)atoul(row[ItemField::worneffect]); - item.Worn.Type = (uint8)atoul(row[ItemField::worntype]); - item.Worn.Level = (uint8)atoul(row[ItemField::wornlevel]); - item.Worn.Level2 = (uint8)atoul(row[ItemField::wornlevel2]); - item.Focus.Effect = (uint16)atoul(row[ItemField::focuseffect]); - item.Focus.Type = (uint8)atoul(row[ItemField::focustype]); - item.Focus.Level = (uint8)atoul(row[ItemField::focuslevel]); - item.Focus.Level2 = (uint8)atoul(row[ItemField::focuslevel2]); - item.Scroll.Effect = (uint16)atoul(row[ItemField::scrolleffect]); - item.Scroll.Type = (uint8)atoul(row[ItemField::scrolltype]); - item.Scroll.Level = (uint8)atoul(row[ItemField::scrolllevel]); - item.Scroll.Level2 = (uint8)atoul(row[ItemField::scrolllevel2]); - item.Bard.Effect = (uint16)atoul(row[ItemField::bardeffect]); - item.Bard.Type = (uint8)atoul(row[ItemField::bardtype]); - item.Bard.Level = (uint8)atoul(row[ItemField::bardlevel]); - item.Bard.Level2 = (uint8)atoul(row[ItemField::bardlevel2]); - item.QuestItemFlag = (atoi(row[ItemField::questitemflag])==0) ? false : true; - item.SVCorruption = (int32)atoi(row[ItemField::svcorruption]); - item.Purity = (uint32)atoul(row[ItemField::purity]); - item.BackstabDmg = (uint32)atoul(row[ItemField::backstabdmg]); - item.DSMitigation = (uint32)atoul(row[ItemField::dsmitigation]); - item.HeroicStr = (int32)atoi(row[ItemField::heroic_str]); - item.HeroicInt = (int32)atoi(row[ItemField::heroic_int]); - item.HeroicWis = (int32)atoi(row[ItemField::heroic_wis]); - item.HeroicAgi = (int32)atoi(row[ItemField::heroic_agi]); - item.HeroicDex = (int32)atoi(row[ItemField::heroic_dex]); - item.HeroicSta = (int32)atoi(row[ItemField::heroic_sta]); - item.HeroicCha = (int32)atoi(row[ItemField::heroic_cha]); - item.HeroicMR = (int32)atoi(row[ItemField::heroic_mr]); - item.HeroicFR = (int32)atoi(row[ItemField::heroic_fr]); - item.HeroicCR = (int32)atoi(row[ItemField::heroic_cr]); - item.HeroicDR = (int32)atoi(row[ItemField::heroic_dr]); - item.HeroicPR = (int32)atoi(row[ItemField::heroic_pr]); - item.HeroicSVCorrup = (int32)atoi(row[ItemField::heroic_svcorrup]); - item.HealAmt = (int32)atoi(row[ItemField::healamt]); - item.SpellDmg = (int32)atoi(row[ItemField::spelldmg]); - item.LDoNSellBackRate = (uint32)atoul(row[ItemField::ldonsellbackrate]); - item.ScriptFileID = (uint32)atoul(row[ItemField::scriptfileid]); - item.ExpendableArrow = (uint16)atoul(row[ItemField::expendablearrow]); - item.Clairvoyance = (uint32)atoul(row[ItemField::clairvoyance]); - strcpy(item.ClickName,row[ItemField::clickname]); - strcpy(item.ProcName,row[ItemField::procname]); - strcpy(item.WornName,row[ItemField::wornname]); - strcpy(item.FocusName,row[ItemField::focusname]); - strcpy(item.ScrollName,row[ItemField::scrollname]); - - try { - hash.insert(item.ID, item); - } catch(std::exception &ex) { - LogFile->write(EQEMuLog::Error, "Database::LoadItems: %s", ex.what()); - break; - } - } - - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "LoadItems '%s', %s", query, errbuf); - } -} - -const Item_Struct* SharedDatabase::GetItem(uint32 id) { - if(!items_hash || id > items_hash->max_key()) { - return nullptr; - } - - if(items_hash->exists(id)) { - return &(items_hash->at(id)); - } - - return nullptr; -} - -const Item_Struct* SharedDatabase::IterateItems(uint32* id) { - if(!items_hash || !id) { - return nullptr; - } - - for(;;) { - if(*id > items_hash->max_key()) { - break; - } - - if(items_hash->exists(*id)) { - return &(items_hash->at((*id)++)); - } else { - ++(*id); - } - } - - return nullptr; -} - -std::string SharedDatabase::GetBook(const char *txtfile) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - char txtfile2[20]; - std::string txtout; - strcpy(txtfile2,txtfile); - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT txtfile FROM books where name='%s'", txtfile2), errbuf, &result)) { - std::cerr << "Error in GetBook query '" << query << "' " << errbuf << std::endl; - if (query != 0) - safe_delete_array(query); - txtout.assign(" ",1); - return txtout; - } - else { - safe_delete_array(query); - if (mysql_num_rows(result) == 0) { - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "No book to send, (%s)", txtfile); - txtout.assign(" ",1); - return txtout; - } - else { - row = mysql_fetch_row(result); - txtout.assign(row[0],strlen(row[0])); - mysql_free_result(result); - return txtout; - } - } -} - -void SharedDatabase::GetFactionListInfo(uint32 &list_count, uint32 &max_lists) { - list_count = 0; - max_lists = 0; - const char *query = "SELECT COUNT(*), MAX(id) FROM npc_faction"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - if(RunQuery(query, strlen(query), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - list_count = static_cast(atoul(row[0])); - max_lists = static_cast(atoul(row[1])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query, errbuf); - } -} - -const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) { - if(!faction_hash) { - return nullptr; - } - - if(faction_hash->exists(id)) { - return &(faction_hash->at(id)); - } - - return nullptr; -} - -void SharedDatabase::LoadNPCFactionLists(void *data, uint32 size, uint32 list_count, uint32 max_lists) { - EQEmu::FixedMemoryHashSet hash(reinterpret_cast(data), size, list_count, max_lists); - const char *query = "SELECT npc_faction.id, npc_faction.primaryfaction, npc_faction.ignore_primary_assist, " - "npc_faction_entries.faction_id, npc_faction_entries.value, npc_faction_entries.npc_value, npc_faction_entries.temp " - "FROM npc_faction LEFT JOIN npc_faction_entries ON npc_faction.id = npc_faction_entries.npc_faction_id ORDER BY " - "npc_faction.id;"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - NPCFactionList faction; - - if(RunQuery(query, strlen(query), errbuf, &result)) { - uint32 current_id = 0; - uint32 current_entry = 0; - while(row = mysql_fetch_row(result)) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, faction); - } - - memset(&faction, 0, sizeof(faction)); - current_entry = 0; - current_id = id; - faction.id = id; - faction.primaryfaction = static_cast(atoul(row[1])); - faction.assistprimaryfaction = (atoi(row[2]) == 0); - } - - if(!row[3]) { - continue; - } - - if(current_entry >= MAX_NPC_FACTIONS) { - continue; - } - - faction.factionid[current_entry] = static_cast(atoul(row[3])); - faction.factionvalue[current_entry] = static_cast(atoi(row[4])); - faction.factionnpcvalue[current_entry] = static_cast(atoi(row[5])); - faction.factiontemp[current_entry] = static_cast(atoi(row[6])); - ++current_entry; - } - - if(current_id != 0) { - hash.insert(current_id, faction); - } - - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting npc faction info from database: %s, %s", query, errbuf); -} -} - -bool SharedDatabase::LoadNPCFactionLists() { - if(faction_hash) { - return true; - } - - try { - EQEmu::IPCMutex mutex("faction"); - mutex.Lock(); - faction_mmf = new EQEmu::MemoryMappedFile("shared/faction"); - - uint32 list_count = 0; - uint32 max_lists = 0; - GetFactionListInfo(list_count, max_lists); - if(list_count == 0) { - EQ_EXCEPT("SharedDatabase", "Database returned no result"); - } - uint32 size = static_cast(EQEmu::FixedMemoryHashSet::estimated_size( - list_count, max_lists)); - - if(faction_mmf->Size() != size) { - EQ_EXCEPT("SharedDatabase", "Couldn't load npc factions because faction_mmf->Size() != size"); - } - - faction_hash = new EQEmu::FixedMemoryHashSet(reinterpret_cast(faction_mmf->Get()), size); - mutex.Unlock(); - } catch(std::exception& ex) { - LogFile->write(EQEMuLog::Error, "Error Loading npc factions: %s", ex.what()); - return false; - } - - return true; -} - -// Get the player profile and inventory for the given account "account_id" and -// character name "name". Return true if the character was found, otherwise false. -// False will also be returned if there is a database error. -bool SharedDatabase::GetPlayerProfile(uint32 account_id, char* name, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, char* current_zone, uint32 *current_instance) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES* result; - MYSQL_ROW row; - bool ret = false; - - unsigned long* lengths; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT profile,zonename,x,y,z,extprofile,instanceid FROM character_ WHERE account_id=%i AND name='%s'", account_id, name), errbuf, &result)) { - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - lengths = mysql_fetch_lengths(result); - if (lengths[0] == sizeof(PlayerProfile_Struct)) { - memcpy(pp, row[0], sizeof(PlayerProfile_Struct)); - - if (current_zone) - strcpy(current_zone, row[1]); - pp->zone_id = GetZoneID(row[1]); - pp->x = atof(row[2]); - pp->y = atof(row[3]); - pp->z = atof(row[4]); - pp->zoneInstance = atoi(row[6]); - if (pp->x == -1 && pp->y == -1 && pp->z == -1) - GetSafePoints(pp->zone_id, GetInstanceVersion(pp->zoneInstance), &pp->x, &pp->y, &pp->z); - - if(current_instance) - *current_instance = pp->zoneInstance; - - if(ext) { - //SetExtendedProfile handles any conversion - SetExtendedProfile(ext, row[5], lengths[5]); - } - - // Retrieve character inventory - ret = GetInventory(account_id, name, inv); - } - else { - LogFile->write(EQEMuLog::Error, "Player profile length mismatch in GetPlayerProfile. Found: %i, Expected: %i", - lengths[0], sizeof(PlayerProfile_Struct)); - } - } - - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "GetPlayerProfile query '%s' %s", query, errbuf); - } - - safe_delete_array(query); - return ret; -} - -bool SharedDatabase::SetPlayerProfile(uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - uint32 affected_rows = 0; - bool ret = false; - - if (RunQuery(query, SetPlayerProfile_MQ(&query, account_id, charid, pp, inv, ext, current_zone, current_instance, MaxXTargets), errbuf, 0, &affected_rows)) { - ret = (affected_rows != 0); - } - - if (!ret) { - LogFile->write(EQEMuLog::Error, "SetPlayerProfile query '%s' %s", query, errbuf); - } - - safe_delete_array(query); - return ret; -} - -// Generate SQL for updating player profile -uint32 SharedDatabase::SetPlayerProfile_MQ(char** query, uint32 account_id, uint32 charid, PlayerProfile_Struct* pp, Inventory* inv, ExtendedProfile_Struct *ext, uint32 current_zone, uint32 current_instance, uint8 MaxXTargets) { - *query = new char[396 + sizeof(PlayerProfile_Struct)*2 + sizeof(ExtendedProfile_Struct)*2 + 4]; - char* end = *query; - if (!current_zone) - current_zone = pp->zone_id; - - if (!current_instance) - current_instance = pp->zoneInstance; - - if(strlen(pp->name) == 0) // Sanity check in case pp never loaded - return false; - - end += sprintf(end, "UPDATE character_ SET timelaston=unix_timestamp(now()),name=\'%s\', zonename=\'%s\', zoneid=%u, instanceid=%u, x = %f, y = %f, z = %f, profile=\'", pp->name, GetZoneName(current_zone), current_zone, current_instance, pp->x, pp->y, pp->z); - end += DoEscapeString(end, (char*)pp, sizeof(PlayerProfile_Struct)); - end += sprintf(end,"\', extprofile=\'"); - end += DoEscapeString(end, (char*)ext, sizeof(ExtendedProfile_Struct)); - end += sprintf(end,"\',class=%d,level=%d,xtargets=%u WHERE id=%u", pp->class_, pp->level, MaxXTargets, charid); - - return (uint32) (end - (*query)); -} - - - -// Create appropriate ItemInst class -ItemInst* SharedDatabase::CreateItem(uint32 item_id, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5) -{ - const Item_Struct* item = nullptr; - ItemInst* inst = nullptr; - item = GetItem(item_id); - if (item) { - inst = CreateBaseItem(item, charges); - inst->PutAugment(this, 0, aug1); - inst->PutAugment(this, 1, aug2); - inst->PutAugment(this, 2, aug3); - inst->PutAugment(this, 3, aug4); - inst->PutAugment(this, 4, aug5); - } - - return inst; -} - - -// Create appropriate ItemInst class -ItemInst* SharedDatabase::CreateItem(const Item_Struct* item, int16 charges, uint32 aug1, uint32 aug2, uint32 aug3, uint32 aug4, uint32 aug5) -{ - ItemInst* inst = nullptr; - if (item) { - inst = CreateBaseItem(item, charges); - inst->PutAugment(this, 0, aug1); - inst->PutAugment(this, 1, aug2); - inst->PutAugment(this, 2, aug3); - inst->PutAugment(this, 3, aug4); - inst->PutAugment(this, 4, aug5); - } - - return inst; -} - -ItemInst* SharedDatabase::CreateBaseItem(const Item_Struct* item, int16 charges) { - ItemInst* inst = nullptr; - if (item) { - // if maxcharges is -1 that means it is an unlimited use item. - // set it to 1 charge so that it is usable on creation - if (charges == 0 && item->MaxCharges == -1) - charges = 1; - - inst = new ItemInst(item, charges); - - if(item->CharmFileID != 0 || (item->LoreGroup >= 1000 && item->LoreGroup != -1)) { - inst->Initialize(this); - } - } - return inst; -} - -int32 SharedDatabase::DeleteStalePlayerCorpses() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - if(RuleB(Zone, EnableShadowrest)) - { - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE player_corpses SET IsBurried = 1 WHERE IsBurried=0 and " - "(UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > %d and not timeofdeath=0", - (RuleI(Character, CorpseDecayTimeMS) / 1000)), errbuf, 0, &affected_rows)) - { - safe_delete_array(query); - return -1; - } - } - else - { - if (!RunQuery(query, MakeAnyLenString(&query, "Delete from player_corpses where (UNIX_TIMESTAMP() - " - "UNIX_TIMESTAMP(timeofdeath)) > %d and not timeofdeath=0", (RuleI(Character, CorpseDecayTimeMS) / 1000)), - errbuf, 0, &affected_rows)) - { - safe_delete_array(query); - return -1; - } - } - - safe_delete_array(query); - return affected_rows; -} - -int32 SharedDatabase::DeleteStalePlayerBackups() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - uint32 affected_rows = 0; - - // 1209600 seconds = 2 weeks - if (!RunQuery(query, MakeAnyLenString(&query, "Delete from player_corpses_backup where (UNIX_TIMESTAMP() - UNIX_TIMESTAMP(timeofdeath)) > 1209600"), errbuf, 0, &affected_rows)) { - safe_delete_array(query); - return -1; - } - safe_delete_array(query); - - return affected_rows; -} - -bool SharedDatabase::GetCommandSettings(std::map &commands) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - query = new char[256]; - strcpy(query, "SELECT command,access from commands"); - commands.clear(); - if (RunQuery(query, strlen(query), errbuf, &result)) { - safe_delete_array(query); - while((row = mysql_fetch_row(result))) { - commands[row[0]]=atoi(row[1]); - } - mysql_free_result(result); - return true; - } else { - std::cerr << "Error in GetCommands query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return false; - } - - return false; -} - -bool SharedDatabase::LoadSkillCaps() { - if(skill_caps_mmf) - return true; - - uint32 class_count = PLAYER_CLASS_COUNT; - uint32 skill_count = HIGHEST_SKILL + 1; - uint32 level_count = HARD_LEVEL_CAP + 1; - uint32 size = (class_count * skill_count * level_count * sizeof(uint16)); - - try { - EQEmu::IPCMutex mutex("skill_caps"); - mutex.Lock(); - skill_caps_mmf = new EQEmu::MemoryMappedFile("shared/skill_caps"); - if(skill_caps_mmf->Size() != size) { - EQ_EXCEPT("SharedDatabase", "Unable to load skill caps: skill_caps_mmf->Size() != size"); - } - - mutex.Unlock(); - } catch(std::exception &ex) { - LogFile->write(EQEMuLog::Error, "Error loading skill caps: %s", ex.what()); - return false; - } - - return true; -} - -void SharedDatabase::LoadSkillCaps(void *data) { - uint32 class_count = PLAYER_CLASS_COUNT; - uint32 skill_count = HIGHEST_SKILL + 1; - uint32 level_count = HARD_LEVEL_CAP + 1; - uint16 *skill_caps_table = reinterpret_cast(data); - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if(RunQuery(query, MakeAnyLenString(&query, - "SELECT skillID, class, level, cap FROM skill_caps ORDER BY skillID, class, level"), - errbuf, &result)) { - safe_delete_array(query); - - while((row = mysql_fetch_row(result))) { - uint8 skillID = atoi(row[0]); - uint8 class_ = atoi(row[1]) - 1; - uint8 level = atoi(row[2]); - uint16 cap = atoi(row[3]); - if(skillID >= skill_count || class_ >= class_count || level >= level_count) - continue; - - uint32 index = (((class_ * skill_count) + skillID) * level_count) + level; - skill_caps_table[index] = cap; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error loading skill caps from database: %s", errbuf); - safe_delete_array(query); - } -} - -uint16 SharedDatabase::GetSkillCap(uint8 Class_, SkillUseTypes Skill, uint8 Level) { - if(!skill_caps_mmf) { - return 0; - } - - if(Class_ == 0) - return 0; - - int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel); - if(SkillMaxLevel < 1) { - SkillMaxLevel = RuleI(Character, MaxLevel); - } - - uint32 class_count = PLAYER_CLASS_COUNT; - uint32 skill_count = HIGHEST_SKILL + 1; - uint32 level_count = HARD_LEVEL_CAP + 1; - if(Class_ > class_count || static_cast(Skill) > skill_count || Level > level_count) { - return 0; - } - - if(Level > static_cast(SkillMaxLevel)){ - Level = static_cast(SkillMaxLevel); - } - - uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count) + Level; - uint16 *skill_caps_table = reinterpret_cast(skill_caps_mmf->Get()); - return skill_caps_table[index]; -} - -uint8 SharedDatabase::GetTrainLevel(uint8 Class_, SkillUseTypes Skill, uint8 Level) { - if(!skill_caps_mmf) { - return 0; - } - - if(Class_ == 0) - return 0; - - int SkillMaxLevel = RuleI(Character, SkillCapMaxLevel); - if (SkillMaxLevel < 1) { - SkillMaxLevel = RuleI(Character, MaxLevel); - } - - uint32 class_count = PLAYER_CLASS_COUNT; - uint32 skill_count = HIGHEST_SKILL + 1; - uint32 level_count = HARD_LEVEL_CAP + 1; - if(Class_ > class_count || static_cast(Skill) > skill_count || Level > level_count) { - return 0; - } - - uint8 ret = 0; - if(Level > static_cast(SkillMaxLevel)) { - uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count); - uint16 *skill_caps_table = reinterpret_cast(skill_caps_mmf->Get()); - for(uint8 x = 0; x < Level; x++){ - if(skill_caps_table[index + x]){ - ret = x; - break; - } - } - } - else - { - uint32 index = ((((Class_ - 1) * skill_count) + Skill) * level_count); - uint16 *skill_caps_table = reinterpret_cast(skill_caps_mmf->Get()); - for(int x = 0; x < SkillMaxLevel; x++){ - if(skill_caps_table[index + x]){ - ret = x; - break; - } - } - } - - if(ret > GetSkillCap(Class_, Skill, Level)) - ret = static_cast(GetSkillCap(Class_, Skill, Level)); - - return ret; -} - -void SharedDatabase::LoadDamageShieldTypes(SPDat_Spell_Struct* sp, int32 iMaxSpellID) { - - const char *DSQuery = "SELECT `spellid`, `type` from `damageshieldtypes` WHERE `spellid` > 0 " - "AND `spellid` <= %i"; - - const char *ERR_MYSQLERROR = "Error in LoadDamageShieldTypes: %s %s"; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(RunQuery(query,MakeAnyLenString(&query,DSQuery,iMaxSpellID),errbuf,&result)) { - - while((row = mysql_fetch_row(result))) { - - int SpellID = atoi(row[0]); - if((SpellID > 0) && (SpellID <= iMaxSpellID)) { - sp[SpellID].DamageShieldType = atoi(row[1]); - } - } - mysql_free_result(result); - safe_delete_array(query); - } - else { - LogFile->write(EQEMuLog::Error, ERR_MYSQLERROR, query, errbuf); - safe_delete_array(query); - } -} - -const EvolveInfo* SharedDatabase::GetEvolveInfo(uint32 loregroup) { - return nullptr; // nothing here for now... database and/or sharemem pulls later -} - -int SharedDatabase::GetMaxSpellID() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = nullptr; - MYSQL_RES *result; - MYSQL_ROW row; - int32 ret = 0; - if(RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM spells_new"), - errbuf, &result)) { - safe_delete_array(query); - row = mysql_fetch_row(result); - ret = atoi(row[0]); - mysql_free_result(result); - } else { - _log(SPELLS__LOAD_ERR, "Error in GetMaxSpellID query '%s' %s", query, errbuf); - safe_delete_array(query); - ret = -1; - } - return ret; -} - -void SharedDatabase::LoadSpells(void *data, int max_spells) { - SPDat_Spell_Struct *sp = reinterpret_cast(data); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if(RunQuery(query, MakeAnyLenString(&query, - "SELECT * FROM spells_new ORDER BY id ASC"), - errbuf, &result)) { - safe_delete_array(query); - - int tempid = 0; - int counter = 0; - while (row = mysql_fetch_row(result)) { - tempid = atoi(row[0]); - if(tempid >= max_spells) { - _log(SPELLS__LOAD_ERR, "Non fatal error: spell.id >= max_spells, ignoring."); - continue; - } - - ++counter; - sp[tempid].id = tempid; - strn0cpy(sp[tempid].name, row[1], sizeof(sp[tempid].name)); - strn0cpy(sp[tempid].player_1, row[2], sizeof(sp[tempid].player_1)); - strn0cpy(sp[tempid].teleport_zone, row[3], sizeof(sp[tempid].teleport_zone)); - strn0cpy(sp[tempid].you_cast, row[4], sizeof(sp[tempid].you_cast)); - strn0cpy(sp[tempid].other_casts, row[5], sizeof(sp[tempid].other_casts)); - strn0cpy(sp[tempid].cast_on_you, row[6], sizeof(sp[tempid].cast_on_you)); - strn0cpy(sp[tempid].cast_on_other, row[7], sizeof(sp[tempid].cast_on_other)); - strn0cpy(sp[tempid].spell_fades, row[8], sizeof(sp[tempid].spell_fades)); - - sp[tempid].range=static_cast(atof(row[9])); - sp[tempid].aoerange=static_cast(atof(row[10])); - sp[tempid].pushback=static_cast(atof(row[11])); - sp[tempid].pushup=static_cast(atof(row[12])); - sp[tempid].cast_time=atoi(row[13]); - sp[tempid].recovery_time=atoi(row[14]); - sp[tempid].recast_time=atoi(row[15]); - sp[tempid].buffdurationformula=atoi(row[16]); - sp[tempid].buffduration=atoi(row[17]); - sp[tempid].AEDuration=atoi(row[18]); - sp[tempid].mana=atoi(row[19]); - - int y=0; - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].base[y]=atoi(row[20+y]); // effect_base_value - for(y=0; y < EFFECT_COUNT; y++) - sp[tempid].base2[y]=atoi(row[32+y]); // effect_limit_value - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].max[y]=atoi(row[44+y]); - - for(y=0; y< 4;y++) - sp[tempid].components[y]=atoi(row[58+y]); - - for(y=0; y< 4;y++) - sp[tempid].component_counts[y]=atoi(row[62+y]); - - for(y=0; y< 4;y++) - sp[tempid].NoexpendReagent[y]=atoi(row[66+y]); - - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].formula[y]=atoi(row[70+y]); - - sp[tempid].goodEffect=atoi(row[83]); - sp[tempid].Activated=atoi(row[84]); - sp[tempid].resisttype=atoi(row[85]); - - for(y=0; y< EFFECT_COUNT;y++) - sp[tempid].effectid[y]=atoi(row[86+y]); - - sp[tempid].targettype = (SpellTargetType) atoi(row[98]); - sp[tempid].basediff=atoi(row[99]); - int tmp_skill = atoi(row[100]);; - if(tmp_skill < 0 || tmp_skill > HIGHEST_SKILL) - sp[tempid].skill = SkillBegging; /* not much better we can do. */ // can probably be changed to client-based 'SkillNone' once activated - else - sp[tempid].skill = (SkillUseTypes) tmp_skill; - sp[tempid].zonetype=atoi(row[101]); - sp[tempid].EnvironmentType=atoi(row[102]); - sp[tempid].TimeOfDay=atoi(row[103]); - - for(y=0; y < PLAYER_CLASS_COUNT;y++) - sp[tempid].classes[y]=atoi(row[104+y]); - - sp[tempid].CastingAnim=atoi(row[120]); - sp[tempid].SpellAffectIndex=atoi(row[123]); - sp[tempid].disallow_sit=atoi(row[124]); - - for (y = 0; y < 16; y++) - sp[tempid].deities[y]=atoi(row[126+y]); - - sp[tempid].uninterruptable=atoi(row[146]); - sp[tempid].ResistDiff=atoi(row[147]); - sp[tempid].dot_stacking_exempt=atoi(row[148]); - sp[tempid].RecourseLink = atoi(row[150]); - sp[tempid].no_partial_resist = atoi(row[151]) != 0; - - sp[tempid].short_buff_box = atoi(row[154]); - sp[tempid].descnum = atoi(row[155]); - sp[tempid].effectdescnum = atoi(row[157]); - - sp[tempid].reflectable = atoi(row[161]) != 0; - sp[tempid].bonushate=atoi(row[162]); - - sp[tempid].EndurCost=atoi(row[166]); - sp[tempid].EndurTimerIndex=atoi(row[167]); - sp[tempid].HateAdded=atoi(row[173]); - sp[tempid].EndurUpkeep=atoi(row[174]); - sp[tempid].numhitstype = atoi(row[175]); - sp[tempid].numhits = atoi(row[176]); - sp[tempid].pvpresistbase=atoi(row[177]); - sp[tempid].pvpresistcalc=atoi(row[178]); - sp[tempid].pvpresistcap=atoi(row[179]); - sp[tempid].spell_category=atoi(row[180]); - sp[tempid].can_mgb=atoi(row[185]); - sp[tempid].dispel_flag = atoi(row[186]); - sp[tempid].MinResist = atoi(row[189]); - sp[tempid].MaxResist = atoi(row[190]); - sp[tempid].viral_targets = atoi(row[191]); - sp[tempid].viral_timer = atoi(row[192]); - sp[tempid].NimbusEffect = atoi(row[193]); - sp[tempid].directional_start = (float)atoi(row[194]); - sp[tempid].directional_end = (float)atoi(row[195]); - sp[tempid].not_extendable = atoi(row[197]) != 0; - sp[tempid].suspendable = atoi(row[200]) != 0; - sp[tempid].spellgroup=atoi(row[207]); - sp[tempid].powerful_flag=atoi(row[209]); - sp[tempid].CastRestriction = atoi(row[211]); - sp[tempid].AllowRest = atoi(row[212]) != 0; - sp[tempid].NotOutofCombat = atoi(row[213]) != 0; - sp[tempid].NotInCombat = atoi(row[214]) != 0; - sp[tempid].aemaxtargets = atoi(row[218]); - sp[tempid].maxtargets = atoi(row[219]); - sp[tempid].persistdeath = atoi(row[224]) != 0; - sp[tempid].DamageShieldType = 0; - } - mysql_free_result(result); - - LoadDamageShieldTypes(sp, max_spells); - } else { - _log(SPELLS__LOAD_ERR, "Error in LoadSpells query '%s' %s", query, errbuf); - safe_delete_array(query); - } -} - -int SharedDatabase::GetMaxBaseDataLevel() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT MAX(level) FROM base_data"; - MYSQL_RES *result; - MYSQL_ROW row; - int32 ret = 0; - if(RunQuery(query, strlen(query), errbuf, &result)) { - row = mysql_fetch_row(result); - if(row) { - ret = atoi(row[0]); - mysql_free_result(result); - } else { - ret = -1; - mysql_free_result(result); - } - } else { - LogFile->write(EQEMuLog::Error, "Error in GetMaxBaseDataLevel query '%s' %s", query, errbuf); - ret = -1; - } - return ret; -} - -bool SharedDatabase::LoadBaseData() { - if(base_data_mmf) { - return true; - } - - try { - EQEmu::IPCMutex mutex("base_data"); - mutex.Lock(); - base_data_mmf = new EQEmu::MemoryMappedFile("shared/base_data"); - - int size = 16 * (GetMaxBaseDataLevel() + 1) * sizeof(BaseDataStruct); - if(size == 0) { - EQ_EXCEPT("SharedDatabase", "Base Data size is zero"); - } - - if(base_data_mmf->Size() != size) { - EQ_EXCEPT("SharedDatabase", "Couldn't load base data because base_data_mmf->Size() != size"); - } - - mutex.Unlock(); - } catch(std::exception& ex) { - LogFile->write(EQEMuLog::Error, "Error Loading Base Data: %s", ex.what()); - return false; - } - - return true; -} - -void SharedDatabase::LoadBaseData(void *data, int max_level) { - char *base_ptr = reinterpret_cast(data); - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = "SELECT * FROM base_data ORDER BY level, class ASC"; - MYSQL_RES *result; - MYSQL_ROW row; - - if(RunQuery(query, strlen(query), errbuf, &result)) { - - int lvl = 0; - int cl = 0; - while (row = mysql_fetch_row(result)) { - lvl = atoi(row[0]); - cl = atoi(row[1]); - if(lvl <= 0) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level <= 0, ignoring."); - continue; - } - - if(lvl >= max_level) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.level >= max_level, ignoring."); - continue; - } - - if(cl <= 0) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.cl <= 0, ignoring."); - continue; - } - - if(cl > 16) { - LogFile->write(EQEMuLog::Error, "Non fatal error: base_data.class > 16, ignoring."); - continue; - } - - BaseDataStruct *bd = reinterpret_cast(base_ptr + (((16 * (lvl - 1)) + (cl - 1)) * sizeof(BaseDataStruct))); - bd->base_hp = atof(row[2]); - bd->base_mana = atof(row[3]); - bd->base_end = atof(row[4]); - bd->unk1 = atof(row[5]); - bd->unk2 = atof(row[6]); - bd->hp_factor = atof(row[7]); - bd->mana_factor = atof(row[8]); - bd->endurance_factor = atof(row[9]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in LoadBaseData query '%s' %s", query, errbuf); - safe_delete_array(query); - } -} - -const BaseDataStruct* SharedDatabase::GetBaseData(int lvl, int cl) { - if(!base_data_mmf) { - return nullptr; - } - - if(lvl <= 0) { - return nullptr; - } - - if(cl <= 0) { - return nullptr; - } - - if(cl > 16) { - return nullptr; - } - - char *base_ptr = reinterpret_cast(base_data_mmf->Get()); - - uint32 offset = ((16 * (lvl - 1)) + (cl - 1)) * sizeof(BaseDataStruct); - - if(offset >= base_data_mmf->Size()) { - return nullptr; - } - - BaseDataStruct *bd = reinterpret_cast(base_ptr + offset); - return bd; -} - -void SharedDatabase::GetLootTableInfo(uint32 &loot_table_count, uint32 &max_loot_table, uint32 &loot_table_entries) { - loot_table_count = 0; - max_loot_table = 0; - loot_table_entries = 0; - const char *query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM loottable_entries) FROM loottable"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - if(RunQuery(query, strlen(query), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - loot_table_count = static_cast(atoul(row[0])); - max_loot_table = static_cast(atoul(row[1])); - loot_table_entries = static_cast(atoul(row[2])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); - } -} - -void SharedDatabase::GetLootDropInfo(uint32 &loot_drop_count, uint32 &max_loot_drop, uint32 &loot_drop_entries) { - loot_drop_count = 0; - max_loot_drop = 0; - loot_drop_entries = 0; - const char *query = "SELECT COUNT(*), MAX(id), (SELECT COUNT(*) FROM lootdrop_entries) FROM lootdrop"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - if(RunQuery(query, strlen(query), errbuf, &result)) { - if(row = mysql_fetch_row(result)) { - loot_drop_count = static_cast(atoul(row[0])); - max_loot_drop = static_cast(atoul(row[1])); - loot_drop_entries = static_cast(atoul(row[2])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); - } -} - -void SharedDatabase::LoadLootTables(void *data, uint32 size) { - EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); - const char *query = "SELECT loottable.id, loottable.mincash, loottable.maxcash, loottable.avgcoin," - " loottable_entries.lootdrop_id, loottable_entries.multiplier, loottable_entries.droplimit, " - "loottable_entries.mindrop, loottable_entries.probability FROM loottable LEFT JOIN loottable_entries" - " ON loottable.id = loottable_entries.loottable_id ORDER BY id"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - uint8 loot_table[sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)]; - LootTable_Struct *lt = reinterpret_cast(loot_table); - - if(RunQuery(query, strlen(query), errbuf, &result)) { - uint32 current_id = 0; - uint32 current_entry = 0; - while(row = mysql_fetch_row(result)) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + - (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - } - - memset(loot_table, 0, sizeof(LootTable_Struct) + (sizeof(LootTableEntries_Struct) * 128)); - current_entry = 0; - current_id = id; - lt->mincash = static_cast(atoul(row[1])); - lt->maxcash = static_cast(atoul(row[2])); - lt->avgcoin = static_cast(atoul(row[3])); - } - - if(current_entry > 128) { - continue; - } - - if(!row[4]) { - continue; - } - - lt->Entries[current_entry].lootdrop_id = static_cast(atoul(row[4])); - lt->Entries[current_entry].multiplier = static_cast(atoi(row[5])); - lt->Entries[current_entry].droplimit = static_cast(atoi(row[6])); - lt->Entries[current_entry].mindrop = static_cast(atoi(row[7])); - lt->Entries[current_entry].probability = static_cast(atof(row[8])); - - ++(lt->NumEntries); - ++current_entry; - } - if(current_id != 0) { - hash.insert(current_id, loot_table, (sizeof(LootTable_Struct) + - (sizeof(LootTableEntries_Struct) * lt->NumEntries))); - } - - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot table info from database: %s, %s", query, errbuf); - } -} - -void SharedDatabase::LoadLootDrops(void *data, uint32 size) { - EQEmu::FixedMemoryVariableHashSet hash(reinterpret_cast(data), size); - const char *query = "SELECT lootdrop.id, lootdrop_entries.item_id, lootdrop_entries.item_charges, " - "lootdrop_entries.equip_item, lootdrop_entries.chance, lootdrop_entries.minlevel, " - "lootdrop_entries.maxlevel, lootdrop_entries.multiplier FROM lootdrop JOIN lootdrop_entries " - "ON lootdrop.id = lootdrop_entries.lootdrop_id ORDER BY lootdrop_id"; - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - uint8 loot_drop[sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)]; - LootDrop_Struct *ld = reinterpret_cast(loot_drop); - if(RunQuery(query, strlen(query), errbuf, &result)) { - uint32 current_id = 0; - uint32 current_entry = 0; - while(row = mysql_fetch_row(result)) { - uint32 id = static_cast(atoul(row[0])); - if(id != current_id) { - if(current_id != 0) { - hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + - (sizeof(LootDropEntries_Struct) * ld->NumEntries))); - } - - memset(loot_drop, 0, sizeof(LootDrop_Struct) + (sizeof(LootDropEntries_Struct) * 1260)); - current_entry = 0; - current_id = id; - } - - if(current_entry >= 1260) { - continue; - } - - ld->Entries[current_entry].item_id = static_cast(atoul(row[1])); - ld->Entries[current_entry].item_charges = static_cast(atoi(row[2])); - ld->Entries[current_entry].equip_item = static_cast(atoi(row[3])); - ld->Entries[current_entry].chance = static_cast(atof(row[4])); - ld->Entries[current_entry].minlevel = static_cast(atoi(row[5])); - ld->Entries[current_entry].maxlevel = static_cast(atoi(row[6])); - ld->Entries[current_entry].multiplier = static_cast(atoi(row[7])); - - ++(ld->NumEntries); - ++current_entry; - } - if(current_id != 0) { - hash.insert(current_id, loot_drop, (sizeof(LootDrop_Struct) + - (sizeof(LootDropEntries_Struct) * ld->NumEntries))); - } - - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error getting loot drop info from database: %s, %s", query, errbuf); - } -} - -bool SharedDatabase::LoadLoot() { - if(loot_table_mmf || loot_drop_mmf) - return true; - - try { - EQEmu::IPCMutex mutex("loot"); - mutex.Lock(); - loot_table_mmf = new EQEmu::MemoryMappedFile("shared/loot_table"); - loot_table_hash = new EQEmu::FixedMemoryVariableHashSet( - reinterpret_cast(loot_table_mmf->Get()), - loot_table_mmf->Size()); - loot_drop_mmf = new EQEmu::MemoryMappedFile("shared/loot_drop"); - loot_drop_hash = new EQEmu::FixedMemoryVariableHashSet( - reinterpret_cast(loot_drop_mmf->Get()), - loot_drop_mmf->Size()); - mutex.Unlock(); - } catch(std::exception &ex) { - LogFile->write(EQEMuLog::Error, "Error loading loot: %s", ex.what()); - return false; - } - - return true; -} - -const LootTable_Struct* SharedDatabase::GetLootTable(uint32 loottable_id) { - if(!loot_table_hash) - return nullptr; - - try { - if(loot_table_hash->exists(loottable_id)) { - return &loot_table_hash->at(loottable_id); - } - } catch(std::exception &ex) { - LogFile->write(EQEMuLog::Error, "Could not get loot table: %s", ex.what()); - } - return nullptr; -} - -const LootDrop_Struct* SharedDatabase::GetLootDrop(uint32 lootdrop_id) { - if(!loot_drop_hash) - return nullptr; - - try { - if(loot_drop_hash->exists(lootdrop_id)) { - return &loot_drop_hash->at(lootdrop_id); - } - } catch(std::exception &ex) { - LogFile->write(EQEMuLog::Error, "Could not get loot drop: %s", ex.what()); - } - return nullptr; -} - -void SharedDatabase::GetPlayerInspectMessage(char* playername, InspectMessage_Struct* message) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT inspectmessage FROM character_ WHERE name='%s'", playername), errbuf, &result)) { - safe_delete_array(query); - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - memcpy(message, row[0], sizeof(InspectMessage_Struct)); - } - - mysql_free_result(result); - } - else { - std::cerr << "Error in GetPlayerInspectMessage query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } -} - -void SharedDatabase::SetPlayerInspectMessage(char* playername, const InspectMessage_Struct* message) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE character_ SET inspectmessage='%s' WHERE name='%s'", message->text, playername), errbuf)) { - std::cerr << "Error in SetPlayerInspectMessage query '" << query << "' " << errbuf << std::endl; - } - - safe_delete_array(query); -} - -void SharedDatabase::GetBotInspectMessage(uint32 botid, InspectMessage_Struct* message) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT BotInspectMessage FROM bots WHERE BotID=%i", botid), errbuf, &result)) { - safe_delete_array(query); - - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - memcpy(message, row[0], sizeof(InspectMessage_Struct)); - } - - mysql_free_result(result); - } - else { - std::cerr << "Error in GetBotInspectMessage query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - } -} - -void SharedDatabase::SetBotInspectMessage(uint32 botid, const InspectMessage_Struct* message) { - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE bots SET BotInspectMessage='%s' WHERE BotID=%i", message->text, botid), errbuf)) { - std::cerr << "Error in SetBotInspectMessage query '" << query << "' " << errbuf << std::endl; - } - - safe_delete_array(query); -} diff --git a/utils/sql/git/required/2014_02_12_spells_new_update.sql b/utils/sql/git/required/2014_02_12_spells_new_update.sql deleted file mode 100644 index ba552e1ed..000000000 --- a/utils/sql/git/required/2014_02_12_spells_new_update.sql +++ /dev/null @@ -1,13 +0,0 @@ -ALTER TABLE `spells_new` CHANGE `field161` `not_reflectable` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field151` `no_partial_resist` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field189` `MinResist` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field190` `MaxResist` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field194` `ConeStartAngle` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field195` `ConeStopAngle` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field208` `rank` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field159` `npc_no_los` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field213` `NotOutofCombat` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field214` `NotInCombat` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field168` `IsDiscipline` INT(11) NOT NULL DEFAULT '0'; -ALTER TABLE `spells_new` CHANGE `field211` `CastRestrict` INT(11) NOT NULL DEFAULT '0'; - diff --git a/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey - Copy.sql b/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey - Copy.sql deleted file mode 100644 index 12e9963a5..000000000 --- a/utils/sql/git/required/2014_04_10_No_Target_With_Hotkey - Copy.sql +++ /dev/null @@ -1,3 +0,0 @@ -ALTER TABLE `npc_types` ADD `no_target_hotkey` tinyint( 1 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `healscale`; - - diff --git a/utils/sql/git/required/2014_06_22_MetabolismAAs.sql b/utils/sql/git/required/2014_06_22_MetabolismAAs.sql deleted file mode 100644 index 3a957edb2..000000000 --- a/utils/sql/git/required/2014_06_22_MetabolismAAs.sql +++ /dev/null @@ -1,6 +0,0 @@ --- Innate Metabolism -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('68', '1', '233', '110', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('69', '1', '233', '125', '0'); -INSERT INTO `aa_effects` (`aaid`, `slot`, `effectid`, `base1`, `base2`) VALUES ('70', '1', '233', '150', '0'); - - diff --git a/zone/AA_base.cpp b/zone/AA_base.cpp deleted file mode 100644 index 8566c09b5..000000000 --- a/zone/AA_base.cpp +++ /dev/null @@ -1,1990 +0,0 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -// Test 1 - -#include "../common/debug.h" -#include "AA.h" -#include "mob.h" -#include "client.h" -#include "groups.h" -#include "raids.h" -#include "../common/spdat.h" -#include "object.h" -#include "doors.h" -#include "beacon.h" -#include "corpse.h" -#include "titles.h" -#include "../common/races.h" -#include "../common/classes.h" -#include "../common/eq_packet_structs.h" -#include "../common/packet_dump.h" -#include "../common/StringUtil.h" -#include "../common/logsys.h" -#include "zonedb.h" -#include "StringIDs.h" - -//static data arrays, really not big enough to warrant shared mem. -AA_DBAction AA_Actions[aaHighestID][MAX_AA_ACTION_RANKS]; //[aaid][rank] -std::mapaas_send; -std::map > aa_effects; //stores the effects from the aa_effects table in memory -std::map AARequiredLevelAndCost; - -/* - - -Schema: - -spell_id is spell to cast, SPELL_UNKNOWN == no spell -nonspell_action is action to preform on activation which is not a spell, 0=none -nonspell_mana is mana that the nonspell action consumes -nonspell_duration is a duration which may be used by the nonspell action -redux_aa is the aa which reduces the reuse timer of the skill -redux_rate is the multiplier of redux_aa, as a percentage of total rate (10 == 10% faster) - -CREATE TABLE aa_actions ( - aaid mediumint unsigned not null, - rank tinyint unsigned not null, - reuse_time mediumint unsigned not null, - spell_id mediumint unsigned not null, - target tinyint unsigned not null, - nonspell_action tinyint unsigned not null, - nonspell_mana mediumint unsigned not null, - nonspell_duration mediumint unsigned not null, - redux_aa mediumint unsigned not null, - redux_rate tinyint not null, - - PRIMARY KEY(aaid, rank) -); - -CREATE TABLE aa_swarmpets ( - spell_id mediumint unsigned not null, - count tinyint unsigned not null, - npc_id int not null, - duration mediumint unsigned not null, - PRIMARY KEY(spell_id) -); -*/ - -/* - -Credits for this function: - -FatherNitwit: Structure and mechanism - -Wiz: Initial set of AAs, original function contents - -Branks: Much updated info and a bunch of higher-numbered AAs - -*/ -int Client::GetAATimerID(aaID activate) -{ - SendAA_Struct* aa2 = zone->FindAA(activate); - - if(!aa2) - { - for(int i = 1;i < MAX_AA_ACTION_RANKS; ++i) - { - int a = activate - i; - - if(a <= 0) - break; - - aa2 = zone->FindAA(a); - - if(aa2 != nullptr) - break; - } - } - - if(aa2) - return aa2->spell_type; - - return 0; -} - -int Client::CalcAAReuseTimer(const AA_DBAction *caa) { - - if(!caa) - return 0; - - int ReuseTime = caa->reuse_time; - - if(ReuseTime > 0) - { - int ReductionPercentage; - - if(caa->redux_aa > 0 && caa->redux_aa < aaHighestID) - { - ReductionPercentage = GetAA(caa->redux_aa) * caa->redux_rate; - - if(caa->redux_aa2 > 0 && caa->redux_aa2 < aaHighestID) - ReductionPercentage += (GetAA(caa->redux_aa2) * caa->redux_rate2); - - ReuseTime = caa->reuse_time * (100 - ReductionPercentage) / 100; - } - - } - return ReuseTime; -} - -void Client::ActivateAA(aaID activate){ - if(activate < 0 || activate >= aaHighestID) - return; - if(IsStunned() || IsFeared() || IsMezzed() || IsSilenced() || IsPet() || IsSitting() || GetFeigned()) - return; - - int AATimerID = GetAATimerID(activate); - - SendAA_Struct* aa2 = nullptr; - aaID aaid = activate; - uint8 activate_val = GetAA(activate); - //this wasn't taking into acct multi tiered act talents before... - if(activate_val == 0){ - aa2 = zone->FindAA(activate); - if(!aa2){ - int i; - int a; - for(i=1;iFindAA(a); - if(aa2 != nullptr) - break; - } - } - if(aa2){ - aaid = (aaID) aa2->id; - activate_val = GetAA(aa2->id); - } - } - - if (activate_val == 0){ - return; - } - - if(aa2) - { - if(aa2->account_time_required) - { - if((Timer::GetTimeSeconds() + account_creation) < aa2->account_time_required) - { - return; - } - } - } - - if(!p_timers.Expired(&database, AATimerID + pTimerAAStart)) - { - uint32 aaremain = p_timers.GetRemainingTime(AATimerID + pTimerAAStart); - uint32 aaremain_hr = aaremain / (60 * 60); - uint32 aaremain_min = (aaremain / 60) % 60; - uint32 aaremain_sec = aaremain % 60; - - if(aa2) { - if (aaremain_hr >= 1) //1 hour or more - Message(13, "You can use the ability %s again in %u hour(s) %u minute(s) %u seconds", - aa2->name, aaremain_hr, aaremain_min, aaremain_sec); - else //less than an hour - Message(13, "You can use the ability %s again in %u minute(s) %u seconds", - aa2->name, aaremain_min, aaremain_sec); - } else { - if (aaremain_hr >= 1) //1 hour or more - Message(13, "You can use this ability again in %u hour(s) %u minute(s) %u seconds", - aaremain_hr, aaremain_min, aaremain_sec); - else //less than an hour - Message(13, "You can use this ability again in %u minute(s) %u seconds", - aaremain_min, aaremain_sec); - } - return; - } - - if(activate_val > MAX_AA_ACTION_RANKS) - activate_val = MAX_AA_ACTION_RANKS; - activate_val--; //to get array index. - - //get our current node, now that the indices are well bounded - const AA_DBAction *caa = &AA_Actions[aaid][activate_val]; - - if((aaid == aaImprovedHarmTouch || aaid == aaLeechTouch) && !p_timers.Expired(&database, pTimerHarmTouch)){ - Message(13,"Ability recovery time not yet met."); - return; - } - - //everything should be configured out now - - uint16 target_id = 0; - - //figure out our target - switch(caa->target) { - case aaTargetUser: - case aaTargetGroup: - target_id = GetID(); - break; - case aaTargetCurrent: - case aaTargetCurrentGroup: - if(GetTarget() == nullptr) { - Message_StringID(MT_DefaultText, AA_NO_TARGET); //You must first select a target for this ability! - p_timers.Clear(&database, AATimerID + pTimerAAStart); - return; - } - target_id = GetTarget()->GetID(); - break; - case aaTargetPet: - if(GetPet() == nullptr) { - Message(0, "A pet is required for this skill."); - return; - } - target_id = GetPetID(); - break; - } - - //handle non-spell action - if(caa->action != aaActionNone) { - if(caa->mana_cost > 0) { - if(GetMana() < caa->mana_cost) { - Message_StringID(13, INSUFFICIENT_MANA); - return; - } - SetMana(GetMana() - caa->mana_cost); - } - if(caa->reuse_time > 0) - { - uint32 timer_base = CalcAAReuseTimer(caa); - if(activate == aaImprovedHarmTouch || activate == aaLeechTouch) - { - p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); - } - p_timers.Start(AATimerID + pTimerAAStart, timer_base); - SendAATimer(AATimerID, 0, 0); - } - HandleAAAction(aaid); - } - - //cast the spell, if we have one - if(caa->spell_id > 0 && caa->spell_id < SPDAT_RECORDS) { - - if(caa->reuse_time > 0) - { - uint32 timer_base = CalcAAReuseTimer(caa); - SendAATimer(AATimerID, 0, 0); - p_timers.Start(AATimerID + pTimerAAStart, timer_base); - if(activate == aaImprovedHarmTouch || activate == aaLeechTouch) - { - p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); - } - // Bards can cast instant cast AAs while they are casting another song - if (spells[caa->spell_id].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) { - if(!SpellFinished(caa->spell_id, entity_list.GetMob(target_id), 10, -1, -1, spells[caa->spell_id].ResistDiff, false)) { - //Reset on failed cast - SendAATimer(AATimerID, 0, 0xFFFFFF); - Message_StringID(15,ABILITY_FAILED); - p_timers.Clear(&database, AATimerID + pTimerAAStart); - return; - } - } else { - if(!CastSpell(caa->spell_id, target_id, 10, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) { - //Reset on failed cast - SendAATimer(AATimerID, 0, 0xFFFFFF); - Message_StringID(15,ABILITY_FAILED); - p_timers.Clear(&database, AATimerID + pTimerAAStart); - return; - } - } - } - else - { - if(!CastSpell(caa->spell_id, target_id)) - return; - } - } - // Check if AA is expendable - if (aas_send[activate - activate_val]->special_category == 7) - { - // Add the AA cost to the extended profile to track overall total - m_epp.expended_aa += aas_send[activate]->cost; - SetAA(activate, 0); - - Save(); - SendAA(activate); - SendAATable(); - } -} - -void Client::HandleAAAction(aaID activate) { - if(activate < 0 || activate >= aaHighestID) - return; - - uint8 activate_val = GetAA(activate); - - if (activate_val == 0) - return; - - if(activate_val > MAX_AA_ACTION_RANKS) - activate_val = MAX_AA_ACTION_RANKS; - activate_val--; //to get array index. - - //get our current node, now that the indices are well bounded - const AA_DBAction *caa = &AA_Actions[activate][activate_val]; - - uint16 timer_id = 0; - uint16 timer_duration = caa->duration; - aaTargetType target = aaTargetUser; - - uint16 spell_id = SPELL_UNKNOWN; //gets cast at the end if not still unknown - - switch(caa->action) { - case aaActionAETaunt: - entity_list.AETaunt(this); - break; - - case aaActionMassBuff: - EnableAAEffect(aaEffectMassGroupBuff, 3600); - Message_StringID(MT_Disciplines, MGB_STRING); //The next group buff you cast will hit all targets in range. - break; - - case aaActionFlamingArrows: - //toggle it - if(CheckAAEffect(aaEffectFlamingArrows)) - EnableAAEffect(aaEffectFlamingArrows); - else - DisableAAEffect(aaEffectFlamingArrows); - break; - - case aaActionFrostArrows: - if(CheckAAEffect(aaEffectFrostArrows)) - EnableAAEffect(aaEffectFrostArrows); - else - DisableAAEffect(aaEffectFrostArrows); - break; - - case aaActionRampage: - EnableAAEffect(aaEffectRampage, 10); - break; - - case aaActionSharedHealth: - if(CheckAAEffect(aaEffectSharedHealth)) - EnableAAEffect(aaEffectSharedHealth); - else - DisableAAEffect(aaEffectSharedHealth); - break; - - case aaActionCelestialRegen: { - //special because spell_id depends on a different AA - switch (GetAA(aaCelestialRenewal)) { - case 1: - spell_id = 3250; - break; - case 2: - spell_id = 3251; - break; - default: - spell_id = 2740; - break; - } - target = aaTargetCurrent; - break; - } - - case aaActionDireCharm: { - //special because spell_id depends on class - switch (GetClass()) - { - case DRUID: - spell_id = 2760; //2644? - break; - case NECROMANCER: - spell_id = 2759; //2643? - break; - case ENCHANTER: - spell_id = 2761; //2642? - break; - } - target = aaTargetCurrent; - break; - } - - case aaActionImprovedFamiliar: { - //Spell IDs might be wrong... - if (GetAA(aaAllegiantFamiliar)) - spell_id = 3264; //1994? - else - spell_id = 2758; //2155? - break; - } - - case aaActionActOfValor: - if(GetTarget() != nullptr) { - int curhp = GetTarget()->GetHP(); - target = aaTargetCurrent; - GetTarget()->HealDamage(curhp, this); - Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand); - } - break; - - case aaActionSuspendedMinion: - if (GetPet()) { - target = aaTargetPet; - switch (GetAA(aaSuspendedMinion)) { - case 1: - spell_id = 3248; - break; - case 2: - spell_id = 3249; - break; - } - //do we really need to cast a spell? - - Message(0,"You call your pet to your side."); - GetPet()->WipeHateList(); - GetPet()->GMMove(GetX(),GetY(),GetZ()); - if (activate_val > 1) - entity_list.ClearFeignAggro(GetPet()); - } else { - Message(0,"You have no pet to call."); - } - break; - - case aaActionProjectIllusion: - EnableAAEffect(aaEffectProjectIllusion, 3600); - Message(10, "The power of your next illusion spell will flow to your grouped target in your place."); - break; - - - case aaActionEscape: - Escape(); - break; - - // Don't think this code is used any longer for Bestial Alignment as the AA has a spell_id and no nonspell_action. - case aaActionBeastialAlignment: - switch(GetBaseRace()) { - case BARBARIAN: - spell_id = AA_Choose3(activate_val, 4521, 4522, 4523); - break; - case TROLL: - spell_id = AA_Choose3(activate_val, 4524, 4525, 4526); - break; - case OGRE: - spell_id = AA_Choose3(activate_val, 4527, 4527, 4529); - break; - case IKSAR: - spell_id = AA_Choose3(activate_val, 4530, 4531, 4532); - break; - case VAHSHIR: - spell_id = AA_Choose3(activate_val, 4533, 4534, 4535); - break; - } - - case aaActionLeechTouch: - target = aaTargetCurrent; - spell_id = SPELL_HARM_TOUCH2; - EnableAAEffect(aaEffectLeechTouch, 1000); - break; - - case aaActionFadingMemories: - // Do nothing since spell effect works correctly, but mana isn't used. - break; - - default: - LogFile->write(EQEMuLog::Error, "Unknown AA nonspell action type %d", caa->action); - return; - } - - - uint16 target_id = 0; - //figure out our target - switch(target) { - case aaTargetUser: - case aaTargetGroup: - target_id = GetID(); - break; - case aaTargetCurrent: - case aaTargetCurrentGroup: - if(GetTarget() == nullptr) { - Message_StringID(MT_DefaultText, AA_NO_TARGET); //You must first select a target for this ability! - p_timers.Clear(&database, timer_id + pTimerAAEffectStart); - return; - } - target_id = GetTarget()->GetID(); - break; - case aaTargetPet: - if(GetPet() == nullptr) { - Message(0, "A pet is required for this skill."); - return; - } - target_id = GetPetID(); - break; - } - - //cast the spell, if we have one - if(IsValidSpell(spell_id)) { - int aatid = GetAATimerID(activate); - if(!CastSpell(spell_id, target_id , 10, -1, -1, 0, -1, pTimerAAStart + aatid , CalcAAReuseTimer(caa), 1)) { - SendAATimer(aatid, 0, 0xFFFFFF); - Message_StringID(15,ABILITY_FAILED); - p_timers.Clear(&database, pTimerAAStart + aatid); - return; - } - } - - //handle the duration timer if we have one. - if(timer_id > 0 && timer_duration > 0) { - p_timers.Start(pTimerAAEffectStart + timer_id, timer_duration); - } -} - - -//Originally written by Branks -//functionality rewritten by Father Nitwit -void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override) { - - //It might not be a bad idea to put these into the database, eventually.. - - //Dook- swarms and wards - - PetRecord record; - if(!database.GetPetEntry(spells[spell_id].teleport_zone, &record)) - { - LogFile->write(EQEMuLog::Error, "Unknown swarm pet spell id: %d, check pets table", spell_id); - Message(13, "Unable to find data for pet %s", spells[spell_id].teleport_zone); - return; - } - - AA_SwarmPet pet; - pet.count = 1; - pet.duration = 1; - - for(int x = 0; x < 12; x++) - { - if(spells[spell_id].effectid[x] == SE_TemporaryPets) - { - pet.count = spells[spell_id].base[x]; - pet.duration = spells[spell_id].max[x]; - } - } - - if(IsClient()) - pet.duration += (CastToClient()->GetFocusEffect(focusSwarmPetDuration, spell_id) / 1000); - - pet.npc_id = record.npc_type; - - NPCType *made_npc = nullptr; - - const NPCType *npc_type = database.GetNPCType(pet.npc_id); - if(npc_type == nullptr) { - //log write - LogFile->write(EQEMuLog::Error, "Unknown npc type for swarm pet spell id: %d", spell_id); - Message(0,"Unable to find pet!"); - return; - } - - if(name_override != nullptr) { - //we have to make a custom NPC type for this name change - made_npc = new NPCType; - memcpy(made_npc, npc_type, sizeof(NPCType)); - strcpy(made_npc->name, name_override); - npc_type = made_npc; - } - - int summon_count = 0; - summon_count = pet.count; - - if(summon_count > MAX_SWARM_PETS) - summon_count = MAX_SWARM_PETS; - - static const float swarm_pet_x[MAX_SWARM_PETS] = { 5, -5, 5, -5, - 10, -10, 10, -10, - 8, -8, 8, -8 }; - static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5, - 10, 10, -10, -10, - 8, 8, -8, -8 }; - TempPets(true); - - while(summon_count > 0) { - int pet_duration = pet.duration; - if(duration_override > 0) - pet_duration = duration_override; - - //this is a little messy, but the only way to do it right - //it would be possible to optimize out this copy for the last pet, but oh well - NPCType *npc_dup = nullptr; - if(made_npc != nullptr) { - npc_dup = new NPCType; - memcpy(npc_dup, made_npc, sizeof(NPCType)); - } - - NPC* npca = new NPC( - (npc_dup!=nullptr)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer - 0, - GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count], - GetZ(), GetHeading(), FlyMode3); - - if((spell_id == 6882) || (spell_id == 6884)) - npca->SetFollowID(GetID()); - - if(!npca->GetSwarmInfo()){ - AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; - npca->SetSwarmInfo(nSI); - npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); - } - else{ - npca->GetSwarmInfo()->duration->Start(pet_duration*1000); - } - - //removing this prevents the pet from attacking - npca->GetSwarmInfo()->owner_id = GetID(); - - //give the pets somebody to "love" - if(targ != nullptr){ - npca->AddToHateList(targ, 1000, 1000); - npca->GetSwarmInfo()->target = targ->GetID(); - } - - //we allocated a new NPC type object, give the NPC ownership of that memory - if(npc_dup != nullptr) - npca->GiveNPCTypeData(npc_dup); - - entity_list.AddNPC(npca, true, true); - summon_count--; - } - - //the target of these swarm pets will take offense to being cast on... - if(targ != nullptr) - targ->AddToHateList(this, 1, 0); -} - -void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_override, uint32 duration_override, bool followme) { - - AA_SwarmPet pet; - pet.count = 1; - pet.duration = 1; - - pet.npc_id = typesid; - - NPCType *made_npc = nullptr; - - const NPCType *npc_type = database.GetNPCType(typesid); - if(npc_type == nullptr) { - //log write - LogFile->write(EQEMuLog::Error, "Unknown npc type for swarm pet type id: %d", typesid); - Message(0,"Unable to find pet!"); - return; - } - - if(name_override != nullptr) { - //we have to make a custom NPC type for this name change - made_npc = new NPCType; - memcpy(made_npc, npc_type, sizeof(NPCType)); - strcpy(made_npc->name, name_override); - npc_type = made_npc; - } - - int summon_count = 0; - summon_count = pet.count; - - if(summon_count > MAX_SWARM_PETS) - summon_count = MAX_SWARM_PETS; - - static const float swarm_pet_x[MAX_SWARM_PETS] = { 5, -5, 5, -5, - 10, -10, 10, -10, - 8, -8, 8, -8 }; - static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5, - 10, 10, -10, -10, - 8, 8, -8, -8 }; - TempPets(true); - - while(summon_count > 0) { - int pet_duration = pet.duration; - if(duration_override > 0) - pet_duration = duration_override; - - //this is a little messy, but the only way to do it right - //it would be possible to optimize out this copy for the last pet, but oh well - NPCType *npc_dup = nullptr; - if(made_npc != nullptr) { - npc_dup = new NPCType; - memcpy(npc_dup, made_npc, sizeof(NPCType)); - } - - NPC* npca = new NPC( - (npc_dup!=nullptr)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer - 0, - GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count], - GetZ(), GetHeading(), FlyMode3); - - if(!npca->GetSwarmInfo()){ - AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; - npca->SetSwarmInfo(nSI); - npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); - } - else{ - npca->GetSwarmInfo()->duration->Start(pet_duration*1000); - } - - //removing this prevents the pet from attacking - npca->GetSwarmInfo()->owner_id = GetID(); - - //give the pets somebody to "love" - if(targ != nullptr){ - npca->AddToHateList(targ, 1000, 1000); - npca->GetSwarmInfo()->target = targ->GetID(); - } - - //we allocated a new NPC type object, give the NPC ownership of that memory - if(npc_dup != nullptr) - npca->GiveNPCTypeData(npc_dup); - - entity_list.AddNPC(npca, true, true); - summon_count--; - } -} - -void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration) -{ - Corpse *CorpseToUse = nullptr; - CorpseToUse = entity_list.GetClosestCorpse(this, nullptr); - - if(!CorpseToUse) - return; - - //assuming we have pets in our table; we take the first pet as a base type. - const NPCType *base_type = database.GetNPCType(500); - NPCType *make_npc = new NPCType; - memcpy(make_npc, base_type, sizeof(NPCType)); - - //combat stats - make_npc->AC = ((GetLevel() * 7) + 550); - make_npc->ATK = GetLevel(); - make_npc->max_dmg = (GetLevel() * 4) + 2; - make_npc->min_dmg = 1; - - //base stats - make_npc->cur_hp = (GetLevel() * 55); - make_npc->max_hp = (GetLevel() * 55); - make_npc->STR = 85 + (GetLevel() * 3); - make_npc->STA = 85 + (GetLevel() * 3); - make_npc->DEX = 85 + (GetLevel() * 3); - make_npc->AGI = 85 + (GetLevel() * 3); - make_npc->INT = 85 + (GetLevel() * 3); - make_npc->WIS = 85 + (GetLevel() * 3); - make_npc->CHA = 85 + (GetLevel() * 3); - make_npc->MR = 25; - make_npc->FR = 25; - make_npc->CR = 25; - make_npc->DR = 25; - make_npc->PR = 25; - - //level class and gender - make_npc->level = GetLevel(); - make_npc->class_ = CorpseToUse->class_; - make_npc->race = CorpseToUse->race; - make_npc->gender = CorpseToUse->gender; - make_npc->loottable_id = 0; - //name - char NewName[64]; - sprintf(NewName, "%s`s Animated Corpse", GetCleanName()); - strcpy(make_npc->name, NewName); - - //appearance - make_npc->beard = CorpseToUse->beard; - make_npc->beardcolor = CorpseToUse->beardcolor; - make_npc->eyecolor1 = CorpseToUse->eyecolor1; - make_npc->eyecolor2 = CorpseToUse->eyecolor2; - make_npc->haircolor = CorpseToUse->haircolor; - make_npc->hairstyle = CorpseToUse->hairstyle; - make_npc->helmtexture = CorpseToUse->helmtexture; - make_npc->luclinface = CorpseToUse->luclinface; - make_npc->size = CorpseToUse->size; - make_npc->texture = CorpseToUse->texture; - - //cast stuff.. based off of PEQ's if you want to change - //it you'll have to mod this code, but most likely - //most people will be using PEQ style for the first - //part of their spell list; can't think of any smooth - //way to do this - //some basic combat mods here too since it's convienent - switch(CorpseToUse->class_) - { - case CLERIC: - make_npc->npc_spells_id = 1; - break; - case WIZARD: - make_npc->npc_spells_id = 2; - break; - case NECROMANCER: - make_npc->npc_spells_id = 3; - break; - case MAGICIAN: - make_npc->npc_spells_id = 4; - break; - case ENCHANTER: - make_npc->npc_spells_id = 5; - break; - case SHAMAN: - make_npc->npc_spells_id = 6; - break; - case DRUID: - make_npc->npc_spells_id = 7; - break; - case PALADIN: - //SPECATK_TRIPLE - strcpy(make_npc->special_abilities, "6,1"); - make_npc->cur_hp = make_npc->cur_hp * 150 / 100; - make_npc->max_hp = make_npc->max_hp * 150 / 100; - make_npc->npc_spells_id = 8; - break; - case SHADOWKNIGHT: - strcpy(make_npc->special_abilities, "6,1"); - make_npc->cur_hp = make_npc->cur_hp * 150 / 100; - make_npc->max_hp = make_npc->max_hp * 150 / 100; - make_npc->npc_spells_id = 9; - break; - case RANGER: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->cur_hp = make_npc->cur_hp * 135 / 100; - make_npc->max_hp = make_npc->max_hp * 135 / 100; - make_npc->npc_spells_id = 10; - break; - case BARD: - strcpy(make_npc->special_abilities, "6,1"); - make_npc->cur_hp = make_npc->cur_hp * 110 / 100; - make_npc->max_hp = make_npc->max_hp * 110 / 100; - make_npc->npc_spells_id = 11; - break; - case BEASTLORD: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->cur_hp = make_npc->cur_hp * 110 / 100; - make_npc->max_hp = make_npc->max_hp * 110 / 100; - make_npc->npc_spells_id = 12; - break; - case ROGUE: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->max_dmg = make_npc->max_dmg * 150 /100; - make_npc->cur_hp = make_npc->cur_hp * 110 / 100; - make_npc->max_hp = make_npc->max_hp * 110 / 100; - break; - case MONK: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->max_dmg = make_npc->max_dmg * 150 /100; - make_npc->cur_hp = make_npc->cur_hp * 135 / 100; - make_npc->max_hp = make_npc->max_hp * 135 / 100; - break; - case WARRIOR: - case BERSERKER: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->max_dmg = make_npc->max_dmg * 150 /100; - make_npc->cur_hp = make_npc->cur_hp * 175 / 100; - make_npc->max_hp = make_npc->max_hp * 175 / 100; - break; - default: - make_npc->npc_spells_id = 0; - break; - } - - make_npc->loottable_id = 0; - make_npc->merchanttype = 0; - make_npc->d_meele_texture1 = 0; - make_npc->d_meele_texture2 = 0; - - TempPets(true); - - NPC* npca = new NPC(make_npc, 0, GetX(), GetY(), GetZ(), GetHeading(), FlyMode3); - - if(!npca->GetSwarmInfo()){ - AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; - npca->SetSwarmInfo(nSI); - npca->GetSwarmInfo()->duration = new Timer(duration*1000); - } - else{ - npca->GetSwarmInfo()->duration->Start(duration*1000); - } - - npca->GetSwarmInfo()->owner_id = GetID(); - - //give the pet somebody to "love" - if(target != nullptr){ - npca->AddToHateList(target, 100000); - npca->GetSwarmInfo()->target = target->GetID(); - } - - //gear stuff, need to make sure there's - //no situation where this stuff can be duped - for(int x = 0; x < 21; x++) - { - uint32 sitem = 0; - sitem = CorpseToUse->GetWornItem(x); - if(sitem){ - const Item_Struct * itm = database.GetItem(sitem); - npca->AddLootDrop(itm, &npca->itemlist, 1, 1, 127, true, true); - } - } - - //we allocated a new NPC type object, give the NPC ownership of that memory - if(make_npc != nullptr) - npca->GiveNPCTypeData(make_npc); - - entity_list.AddNPC(npca, true, true); - - //the target of these swarm pets will take offense to being cast on... - if(target != nullptr) - target->AddToHateList(this, 1, 0); -} - -//turn on an AA effect -//duration == 0 means no time limit, used for one-shot deals, etc.. -void Client::EnableAAEffect(aaEffectType type, uint32 duration) { - if(type > 32) - return; //for now, special logic needed. - m_epp.aa_effects |= 1 << (type-1); - - if(duration > 0) { - p_timers.Start(pTimerAAEffectStart + type, duration); - } else { - p_timers.Clear(&database, pTimerAAEffectStart + type); - } -} - -void Client::DisableAAEffect(aaEffectType type) { - if(type > 32) - return; //for now, special logic needed. - uint32 bit = 1 << (type-1); - if(m_epp.aa_effects & bit) { - m_epp.aa_effects ^= bit; - } - p_timers.Clear(&database, pTimerAAEffectStart + type); -} - -/* -By default an AA effect is a one shot deal, unless -a duration timer is set. -*/ -bool Client::CheckAAEffect(aaEffectType type) { - if(type > 32) - return(false); //for now, special logic needed. - if(m_epp.aa_effects & (1 << (type-1))) { //is effect enabled? - //has our timer expired? - if(p_timers.Expired(&database, pTimerAAEffectStart + type)) { - DisableAAEffect(type); - return(false); - } - return(true); - } - return(false); -} - -void Client::SendAAStats() { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAExpUpdate, sizeof(AltAdvStats_Struct)); - AltAdvStats_Struct *aps = (AltAdvStats_Struct *)outapp->pBuffer; - aps->experience = m_pp.expAA; - aps->experience = (uint32)(((float)330.0f * (float)m_pp.expAA) / (float)max_AAXP); - aps->unspent = m_pp.aapoints; - aps->percentage = m_epp.perAA; - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::BuyAA(AA_Action* action) -{ - mlog(AA__MESSAGE, "Starting to buy AA %d", action->ability); - - //find the AA information from the database - SendAA_Struct* aa2 = zone->FindAA(action->ability); - if(!aa2) { - //hunt for a lower level... - int i; - int a; - for(i=1;iability - i; - if(a <= 0) - break; - mlog(AA__MESSAGE, "Could not find AA %d, trying potential parent %d", action->ability, a); - aa2 = zone->FindAA(a); - if(aa2 != nullptr) - break; - } - } - if(aa2 == nullptr) - return; //invalid ability... - - if(aa2->special_category == 1 || aa2->special_category == 2) - return; // Not purchasable progression style AAs - - if(aa2->special_category == 8 && aa2->cost == 0) - return; // Not purchasable racial AAs(set a cost to make them purchasable) - - uint32 cur_level = GetAA(aa2->id); - if((aa2->id + cur_level) != action->ability) { //got invalid AA - mlog(AA__ERROR, "Unable to find or match AA %d (found %d + lvl %d)", action->ability, aa2->id, cur_level); - return; - } - - if(aa2->account_time_required) - { - if((Timer::GetTimeSeconds() - account_creation) < aa2->account_time_required) - { - return; - } - } - - uint32 real_cost; - std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(action->ability); - - if(RequiredLevel != AARequiredLevelAndCost.end()) - { - real_cost = RequiredLevel->second.Cost; - } - else - real_cost = aa2->cost + (aa2->cost_inc * cur_level); - - if(m_pp.aapoints >= real_cost && cur_level < aa2->max_level) { - SetAA(aa2->id, cur_level+1); - - mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level+1); - - m_pp.aapoints -= real_cost; - - Save(); - if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (aa2->hotkey_sid == 4294967295u)) - && ((aa2->max_level == (cur_level+1)) && aa2->sof_next_id)){ - SendAA(aa2->id); - SendAA(aa2->sof_next_id); - } - else - SendAA(aa2->id); - - SendAATable(); - - //we are building these messages ourself instead of using the stringID to work around patch discrepencies - //these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2 - if(cur_level<1) - Message(15,"You have gained the ability \"%s\" at a cost of %d ability %s.", aa2->name, real_cost, (real_cost>1)?"points":"point"); - else - Message(15,"You have improved %s %d at a cost of %d ability %s.", aa2->name, cur_level+1, real_cost, (real_cost>1)?"points":"point"); - - - SendAAStats(); - - CalcBonuses(); - if(title_manager.IsNewAATitleAvailable(m_pp.aapoints_spent, GetBaseClass())) - NotifyNewTitlesAvailable(); - } -} - -void Client::SendAATimer(uint32 ability, uint32 begin, uint32 end) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction,sizeof(UseAA_Struct)); - UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; - uaaout->ability = ability; - uaaout->begin = begin; - uaaout->end = end; - QueuePacket(outapp); - safe_delete(outapp); -} - -//sends all AA timers. -void Client::SendAATimers() { - //we dont use SendAATimer because theres no reason to allocate the EQApplicationPacket every time - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction,sizeof(UseAA_Struct)); - UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; - - PTimerList::iterator c,e; - c = p_timers.begin(); - e = p_timers.end(); - for(; c != e; ++c) { - PersistentTimer *cur = c->second; - if(cur->GetType() < pTimerAAStart || cur->GetType() > pTimerAAEnd) - continue; //not an AA timer - //send timer - uaaout->begin = cur->GetStartTime(); - uaaout->end = static_cast(time(nullptr)); - uaaout->ability = cur->GetType() - pTimerAAStart; // uuaaout->ability is really a shared timer number - QueuePacket(outapp); - } - - safe_delete(outapp); -} - -void Client::SendAATable() { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RespondAA, sizeof(AATable_Struct)); - - AATable_Struct* aa2 = (AATable_Struct *)outapp->pBuffer; - aa2->aa_spent = GetAAPointsSpent(); - - uint32 i; - for(i=0;i < MAX_PP_AA_ARRAY;i++){ - aa2->aa_list[i].aa_skill = aa[i]->AA; - aa2->aa_list[i].aa_value = aa[i]->value; - aa2->aa_list[i].unknown08 = 0; - } - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SendPreviousAA(uint32 id, int seq){ - uint32 value=0; - SendAA_Struct* saa2 = nullptr; - if(id==0) - saa2 = zone->GetAABySequence(seq); - else - saa2 = zone->FindAA(id); - if(!saa2) - return; - int size=sizeof(SendAA_Struct)+sizeof(AA_Ability)*saa2->total_abilities; - uchar* buffer = new uchar[size]; - SendAA_Struct* saa=(SendAA_Struct*)buffer; - value = GetAA(saa2->id); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAATable); - outapp->size=size; - outapp->pBuffer=(uchar*)saa; - value--; - memcpy(saa,saa2,size); - - if(value>0){ - if(saa->spellid==0) - saa->spellid=0xFFFFFFFF; - saa->id+=value; - saa->next_id=saa->id+1; - if(value==1) - saa->last_id=saa2->id; - else - saa->last_id=saa->id-1; - saa->current_level=value+1; - saa->cost2 = 0; //cost 2 is what the client uses to calc how many points we've spent, so we have to add up the points in order - for(uint32 i = 0; i < (value+1); i++) { - saa->cost2 += saa->cost + (saa->cost_inc * i); - } - } - - database.FillAAEffects(saa); - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SendAA(uint32 id, int seq) { - - uint32 value=0; - SendAA_Struct* saa2 = nullptr; - SendAA_Struct* qaa = nullptr; - SendAA_Struct* saa_pp = nullptr; - bool IsBaseLevel = true; - bool aa_stack = false; - - if(id==0) - saa2 = zone->GetAABySequence(seq); - else - saa2 = zone->FindAA(id); - if(!saa2) - return; - - uint16 classes = saa2->classes; - if(!(classes & (1 << GetClass())) && (GetClass()!=BERSERKER || saa2->berserker==0)){ - return; - } - - if(saa2->account_time_required) - { - if((Timer::GetTimeSeconds() - account_creation) < saa2->account_time_required) - { - return; - } - } - - // Hide Quest/Progression AAs unless player has been granted the first level using $client->IncrementAA(skill_id). - if (saa2->special_category == 1 || saa2->special_category == 2 ) { - if(GetAA(saa2->id) == 0) - return; - // For Quest line AA(demiplane AEs) where only 1 is visible at a time, check to make sure only the highest level obtained is shown - if(saa2->aa_expansion > 0) { - qaa = zone->FindAA(saa2->id+1); - if(qaa && (saa2->aa_expansion == qaa->aa_expansion) && GetAA(qaa->id) > 0) - return; - } - } - -/* Beginning of Shroud AAs, these categories are for Passive and Active Shroud AAs - Eventually with a toggle we could have it show player list or shroud list - if (saa2->special_category == 3 || saa2->special_category == 4) - return; -*/ - // Check for racial/Drakkin blood line AAs - if (saa2->special_category == 8) - { - uint32 client_race = this->GetBaseRace(); - - // Drakkin Bloodlines - if (saa2->aa_expansion > 522) - { - if (client_race != 522) - return; // Check for Drakkin Race - - int heritage = this->GetDrakkinHeritage() + 523; // 523 = Drakkin Race(522) + Bloodline - - if (heritage != saa2->aa_expansion) - return; - } - // Racial AAs - else if (client_race != saa2->aa_expansion) - { - return; - } - } - - /* - AA stacking on SoF+ clients. - - Note: There were many ways to achieve this effect - The method used proved to be the most straight forward and consistent. - Stacking does not currently work ideally for AA's that use hotkeys, therefore they will be excluded at this time. - - TODO: Problem with AA hotkeys - When you reach max rank of an AA tier (ie 5/5), it automatically displays the next AA in - the series and you can not transfer the hotkey to the next AA series. To the best of the my ability and through many - different variations of coding I could not find an ideal solution to this issue. - - How stacking works: - Utilizes two new fields: sof_next_id (which is the next id in the series), sof_current_level (ranks the AA's as the current level) - 1) If no AA's purchased only display the base levels of each AA series. - 2) When you purchase an AA and its rank is maxed it sends the packet for the completed AA, and the packet - for the next aa in the series. The previous tier is removed from your window, and the new AA is displayed. - 3) When you zone/buy your player profile will be checked and determine what AA can be displayed base on what you have already. - */ - - if (RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (saa2->hotkey_sid == 4294967295u)) - aa_stack = true; - - if (aa_stack){ - uint32 aa_AA = 0; - uint32 aa_value = 0; - for (int i = 0; i < MAX_PP_AA_ARRAY; i++) { - if (aa[i]) { - aa_AA = aa[i]->AA; - aa_value = aa[i]->value; - - if (aa_AA){ - - if (aa_value > 0) - aa_AA -= aa_value-1; - - saa_pp = zone->FindAA(aa_AA); - - if (saa_pp){ - - if (saa_pp->sof_next_skill == saa2->sof_next_skill){ - - if (saa_pp->id == saa2->id) - break; //You already have this in the player profile. - else if ((saa_pp->sof_current_level < saa2->sof_current_level) && (aa_value < saa_pp->max_level)) - return; //DISABLE DISPLAY HIGHER - You have not reached max level yet of your current AA. - else if ((saa_pp->sof_current_level < saa2->sof_current_level) && (aa_value == saa_pp->max_level) && (saa_pp->sof_next_id == saa2->id)) - IsBaseLevel = false; //ALLOW DISPLAY HIGHER - } - } - } - } - } - } - - //Hide higher tiers of multi tiered AA's if the base level is not fully purchased. - if (aa_stack && IsBaseLevel && saa2->sof_current_level > 0) - return; - - int size=sizeof(SendAA_Struct)+sizeof(AA_Ability)*saa2->total_abilities; - - if(size == 0) - return; - - uchar* buffer = new uchar[size]; - SendAA_Struct* saa=(SendAA_Struct*)buffer; - memcpy(saa,saa2,size); - - if(saa->spellid==0) - saa->spellid=0xFFFFFFFF; - - value=GetAA(saa->id); - uint32 orig_val = value; - - if(value && saa->id){ - - if(value < saa->max_level){ - saa->id+=value; - saa->next_id=saa->id+1; - value++; - } - - else if (aa_stack && saa->sof_next_id){ - saa->id+=value-1; - saa->next_id=saa->sof_next_id; - - //Prevent removal of previous AA from window if next AA belongs to a higher client version. - SendAA_Struct* saa_next = nullptr; - saa_next = zone->FindAA(saa->sof_next_id); - if (saa_next && - (((GetClientVersionBit() == 4) && (saa_next->clientver > 4)) - || ((GetClientVersionBit() == 8) && (saa_next->clientver > 5)) - || ((GetClientVersionBit() == 16) && (saa_next->clientver > 6)))){ - saa->next_id=0xFFFFFFFF; - } - } - - else{ - saa->id+=value-1; - saa->next_id=0xFFFFFFFF; - } - - uint32 current_level_mod = 0; - if (aa_stack) - current_level_mod = saa->sof_current_level; - - saa->last_id=saa->id-1; - saa->current_level=value+(current_level_mod); - saa->cost = saa2->cost + (saa2->cost_inc*(value-1)); - saa->cost2 = 0; - for(uint32 i = 0; i < value; i++) { - saa->cost2 += saa2->cost + (saa2->cost_inc * i); - } - saa->class_type = saa2->class_type + (saa2->level_inc*(value-1)); - } - - if (aa_stack){ - - if (saa->sof_current_level >= 1 && value == 0) - saa->current_level = saa->sof_current_level+1; - - saa->max_level = saa->sof_max_level; - } - - database.FillAAEffects(saa); - - if(value > 0) - { - // AA_Action stores the base ID - const AA_DBAction *caa = &AA_Actions[saa->id - value + 1][value - 1]; - - if(caa && caa->reuse_time > 0) - saa->spell_refresh = CalcAAReuseTimer(caa); - } - - //You can now use the level_inc field in the altadv_vars table to accomplish this, though still needed - //for special cases like LOH/HT due to inability to implement correct stacking of AA's that use hotkeys. - std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(saa->id); - - if(RequiredLevel != AARequiredLevelAndCost.end()) - { - saa->class_type = RequiredLevel->second.Level; - saa->cost = RequiredLevel->second.Cost; - } - - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAATable); - outapp->size=size; - outapp->pBuffer=(uchar*)saa; - if(id==0 && value && (orig_val < saa->max_level)) //send previous AA only on zone in - SendPreviousAA(id, seq); - - QueuePacket(outapp); - safe_delete(outapp); - //will outapp delete the buffer for us even though it didnt make it? --- Yes, it should -} - -void Client::SendAAList(){ - int total = zone->GetTotalAAs(); - for(int i=0;i < total;i++){ - SendAA(0,i); - } -} - -uint32 Client::GetAA(uint32 aa_id) const { - std::map::const_iterator res; - res = aa_points.find(aa_id); - if(res != aa_points.end()) { - return(res->second); - } - return(0); -} - -bool Client::SetAA(uint32 aa_id, uint32 new_value) { - aa_points[aa_id] = new_value; - uint32 cur; - for(cur=0;cur < MAX_PP_AA_ARRAY;cur++){ - if((aa[cur]->value > 1) && ((aa[cur]->AA - aa[cur]->value + 1)== aa_id)){ - aa[cur]->value = new_value; - if(new_value > 0) - aa[cur]->AA++; - else - aa[cur]->AA = 0; - return true; - } - else if((aa[cur]->value == 1) && (aa[cur]->AA == aa_id)){ - aa[cur]->value = new_value; - if(new_value > 0) - aa[cur]->AA++; - else - aa[cur]->AA = 0; - return true; - } - else if(aa[cur]->AA==0){ //end of list - aa[cur]->AA = aa_id; - aa[cur]->value = new_value; - return true; - } - } - return false; -} - -SendAA_Struct* Zone::FindAA(uint32 id) { - return aas_send[id]; -} - -void Zone::LoadAAs() { - LogFile->write(EQEMuLog::Status, "Loading AA information..."); - totalAAs = database.CountAAs(); - if(totalAAs == 0) { - LogFile->write(EQEMuLog::Error, "Failed to load AAs!"); - aas = nullptr; - return; - } - aas = new SendAA_Struct *[totalAAs]; - - database.LoadAAs(aas); - - int i; - for(i=0; i < totalAAs; i++){ - SendAA_Struct* aa = aas[i]; - aas_send[aa->id] = aa; - } - - //load AA Effects into aa_effects - LogFile->write(EQEMuLog::Status, "Loading AA Effects..."); - if (database.LoadAAEffects2()) - LogFile->write(EQEMuLog::Status, "Loaded %d AA Effects.", aa_effects.size()); - else - LogFile->write(EQEMuLog::Error, "Failed to load AA Effects!"); -} - -bool ZoneDatabase::LoadAAEffects2() { - aa_effects.clear(); //start fresh - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT aaid, slot, effectid, base1, base2 FROM aa_effects ORDER BY aaid ASC, slot ASC"), errbuf, &result)) { - int count = 0; - while((row = mysql_fetch_row(result))!= nullptr) { - int aaid = atoi(row[0]); - int slot = atoi(row[1]); - int effectid = atoi(row[2]); - int base1 = atoi(row[3]); - int base2 = atoi(row[4]); - aa_effects[aaid][slot].skill_id = effectid; - aa_effects[aaid][slot].base1 = base1; - aa_effects[aaid][slot].base2 = base2; - aa_effects[aaid][slot].slot = slot; //not really needed, but we'll populate it just in case - count++; - } - mysql_free_result(result); - if (count < 1) //no results - LogFile->write(EQEMuLog::Error, "Error loading AA Effects, none found in the database."); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAEffects2 query: '%s': %s", query, errbuf); - return false; - } - safe_delete_array(query); - return true; -} -void Client::ResetAA(){ - uint32 i; - for(i=0;iAA = 0; - aa[i]->value = 0; - } - std::map::iterator itr; - for(itr=aa_points.begin();itr!=aa_points.end();++itr) - aa_points[itr->first] = 0; - - for(int i = 0; i < _maxLeaderAA; ++i) - m_pp.leader_abilities.ranks[i] = 0; - - m_pp.group_leadership_points = 0; - m_pp.raid_leadership_points = 0; - m_pp.group_leadership_exp = 0; - m_pp.raid_leadership_exp = 0; -} - -int Client::GroupLeadershipAAHealthEnhancement() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAHealthEnhancement)) - { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; - } - - return 0; -} - -int Client::GroupLeadershipAAManaEnhancement() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAManaEnhancement)) - { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; - } - - return 0; -} - -int Client::GroupLeadershipAAHealthRegeneration() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAHealthRegeneration)) - { - case 0: - return 0; - case 1: - return 4; - case 2: - return 6; - case 3: - return 8; - } - - return 0; -} - -int Client::GroupLeadershipAAOffenseEnhancement() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAOffenseEnhancement)) - { - case 0: - return 0; - case 1: - return 10; - case 2: - return 19; - case 3: - return 28; - case 4: - return 34; - case 5: - return 40; - } - return 0; -} - -void Client::InspectBuffs(Client* Inspector, int Rank) -{ - if(!Inspector || (Rank == 0)) return; - - Inspector->Message_StringID(0, CURRENT_SPELL_EFFECTS, GetName()); - uint32 buff_count = GetMaxTotalSlots(); - for (uint32 i = 0; i < buff_count; ++i) - { - if (buffs[i].spellid != SPELL_UNKNOWN) - { - if(Rank == 1) - Inspector->Message(0, "%s", spells[buffs[i].spellid].name); - else - { - if (spells[buffs[i].spellid].buffdurationformula == DF_Permanent) - Inspector->Message(0, "%s (Permanent)", spells[buffs[i].spellid].name); - else { - char *TempString = nullptr; - MakeAnyLenString(&TempString, "%.1f", static_cast(buffs[i].ticsremaining) / 10.0f); - Inspector->Message_StringID(0, BUFF_MINUTES_REMAINING, spells[buffs[i].spellid].name, TempString); - safe_delete_array(TempString); - } - } - } - } -} - -//this really need to be renamed to LoadAAActions() -bool ZoneDatabase::LoadAAEffects() { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - memset(AA_Actions, 0, sizeof(AA_Actions)); //I hope the compiler is smart about this size... - - const char *query = "SELECT aaid,rank,reuse_time,spell_id,target,nonspell_action,nonspell_mana,nonspell_duration," - "redux_aa,redux_rate,redux_aa2,redux_rate2 FROM aa_actions"; - - if(RunQuery(query, static_cast(strlen(query)), errbuf, &result)) { - //safe_delete_array(query); - int r; - while ((row = mysql_fetch_row(result))) { - r = 0; - int aaid = atoi(row[r++]); - int rank = atoi(row[r++]); - if(aaid < 0 || aaid >= aaHighestID || rank < 0 || rank >= MAX_AA_ACTION_RANKS) - continue; - AA_DBAction *caction = &AA_Actions[aaid][rank]; - - caction->reuse_time = atoi(row[r++]); - caction->spell_id = atoi(row[r++]); - caction->target = (aaTargetType) atoi(row[r++]); - caction->action = (aaNonspellAction) atoi(row[r++]); - caction->mana_cost = atoi(row[r++]); - caction->duration = atoi(row[r++]); - caction->redux_aa = (aaID) atoi(row[r++]); - caction->redux_rate = atoi(row[r++]); - caction->redux_aa2 = (aaID) atoi(row[r++]); - caction->redux_rate2 = atoi(row[r++]); - - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadAAEffects query '%s': %s", query, errbuf);; - //safe_delete_array(query); - return false; - } - - return true; -} - -//Returns the number effects an AA has when we send them to the client -//For the purposes of sizing a packet because every skill does not -//have the same number effects, they can range from none to a few depending on AA. -//counts the # of effects by counting the different slots of an AAID in the DB. - -//AndMetal: this may now be obsolete since we have Zone::GetTotalAALevels() -uint8 ZoneDatabase::GetTotalAALevels(uint32 skill_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int total=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(slot) from aa_effects where aaid=%i", skill_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - total=atoi(row[0]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetTotalAALevels '%s: %s", query, errbuf); - safe_delete_array(query); - } - return total; -} - -//this will allow us to count the number of effects for an AA by pulling the info from memory instead of the database. hopefully this will same some CPU cycles -uint8 Zone::GetTotalAALevels(uint32 skill_id) { - size_t sz = aa_effects[skill_id].size(); - return sz >= 255 ? 255 : static_cast(sz); -} - -/* -Every AA can send the client effects, which are purely for client side effects. -Essentially it's like being able to attach a very simple version of a spell to -Any given AA, it has 4 fields: -skill_id = spell effect id -slot = ID slot, doesn't appear to have any impact on stacking like real spells, just needs to be unique. -base1 = the base field of a spell -base2 = base field 2 of a spell, most AAs do not utilize this -example: - skill_id = SE_STA - slot = 1 - base1 = 15 - This would if you filled the abilities struct with this make the client show if it had - that AA an additional 15 stamina on the client's stats -*/ -void ZoneDatabase::FillAAEffects(SendAA_Struct* aa_struct){ - if(!aa_struct) - return; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT effectid, base1, base2, slot from aa_effects where aaid=%i order by slot asc", aa_struct->id), errbuf, &result)) { - int ndx=0; - while((row = mysql_fetch_row(result))!=nullptr) { - aa_struct->abilities[ndx].skill_id=atoi(row[0]); - aa_struct->abilities[ndx].base1=atoi(row[1]); - aa_struct->abilities[ndx].base2=atoi(row[2]); - aa_struct->abilities[ndx].slot=atoi(row[3]); - ndx++; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in Client::FillAAEffects query: '%s': %s", query, errbuf); - } - safe_delete_array(query); -} - -uint32 ZoneDatabase::CountAAs(){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(title_sid) from altadv_vars"), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr) - count = atoi(row[0]); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAAs query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return count; -} - -uint32 ZoneDatabase::CountAAEffects(){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(id) from aa_effects"), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr){ - count = atoi(row[0]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAALevels query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return count; -} - -uint32 ZoneDatabase::GetSizeAA(){ - int size=CountAAs()*sizeof(SendAA_Struct); - if(size>0) - size+=CountAAEffects()*sizeof(AA_Ability); - return size; -} - -void ZoneDatabase::LoadAAs(SendAA_Struct **load){ - if(!load) - return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT skill_id from altadv_vars order by skill_id"), errbuf, &result)) { - int skill=0,ndx=0; - while((row = mysql_fetch_row(result))!=nullptr) { - skill=atoi(row[0]); - load[ndx] = GetAASkillVars(skill); - load[ndx]->seq = ndx+1; - ndx++; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query, errbuf); - } - safe_delete_array(query); - - AARequiredLevelAndCost.clear(); - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT skill_id, level, cost from aa_required_level_cost order by skill_id"), errbuf, &result)) - { - AALevelCost_Struct aalcs; - while((row = mysql_fetch_row(result))!=nullptr) - { - aalcs.Level = atoi(row[1]); - aalcs.Cost = atoi(row[2]); - AARequiredLevelAndCost[atoi(row[0])] = aalcs; - } - mysql_free_result(result); - } - else - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query, errbuf); - - safe_delete_array(query); -} - -SendAA_Struct* ZoneDatabase::GetAASkillVars(uint32 skill_id) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - SendAA_Struct* sendaa = nullptr; - uchar* buffer; - if (RunQuery(query, MakeAnyLenString(&query, "SET @row = 0"), errbuf)) { //initialize "row" variable in database for next query - safe_delete_array(query); - MYSQL_RES *result; //we don't really need these unless we get to this point, so why bother? - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT " - "a.cost, " - "a.max_level, " - "a.hotkey_sid, " - "a.hotkey_sid2, " - "a.title_sid, " - "a.desc_sid, " - "a.type, " - "COALESCE(" //so we can return 0 if it's null - "(" //this is our derived table that has the row # that we can SELECT from, because the client is stupid - "SELECT " - "p.prereq_index_num " - "FROM " - "(" - "SELECT " - "a2.skill_id, " - "@row := @row + 1 AS prereq_index_num " - "FROM " - "altadv_vars a2" - ") AS p " - "WHERE " - "p.skill_id = a.prereq_skill" - "), " - "0) AS prereq_skill_index, " - "a.prereq_minpoints, " - "a.spell_type, " - "a.spell_refresh, " - "a.classes, " - "a.berserker, " - "a.spellid, " - "a.class_type, " - "a.name, " - "a.cost_inc, " - "a.aa_expansion, " - "a.special_category, " - "a.sof_type, " - "a.sof_cost_inc, " - "a.sof_max_level, " - "a.sof_next_skill, " - "a.clientver, " // Client Version 0 = None, 1 = All, 2 = Titanium/6.2, 4 = SoF 5 = SOD 6 = UF - "a.account_time_required, " - "a.sof_current_level," - "a.sof_next_id, " - "a.level_inc " - " FROM altadv_vars a WHERE skill_id=%i", skill_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - int total_abilities = GetTotalAALevels(skill_id); //eventually we'll want to use zone->GetTotalAALevels(skill_id) since it should save queries to the DB - int totalsize = total_abilities * sizeof(AA_Ability) + sizeof(SendAA_Struct); - - buffer = new uchar[totalsize]; - memset(buffer,0,totalsize); - sendaa = (SendAA_Struct*)buffer; - - row = mysql_fetch_row(result); - - //ATOI IS NOT UNISGNED LONG-SAFE!!! - - sendaa->cost = atoul(row[0]); - sendaa->cost2 = sendaa->cost; - sendaa->max_level = atoul(row[1]); - sendaa->hotkey_sid = atoul(row[2]); - sendaa->id = skill_id; - sendaa->hotkey_sid2 = atoul(row[3]); - sendaa->title_sid = atoul(row[4]); - sendaa->desc_sid = atoul(row[5]); - sendaa->type = atoul(row[6]); - sendaa->prereq_skill = atoul(row[7]); - sendaa->prereq_minpoints = atoul(row[8]); - sendaa->spell_type = atoul(row[9]); - sendaa->spell_refresh = atoul(row[10]); - sendaa->classes = static_cast(atoul(row[11])); - sendaa->berserker = static_cast(atoul(row[12])); - sendaa->last_id = 0xFFFFFFFF; - sendaa->current_level=1; - sendaa->spellid = atoul(row[13]); - sendaa->class_type = atoul(row[14]); - strcpy(sendaa->name,row[15]); - - sendaa->total_abilities=total_abilities; - if(sendaa->max_level > 1) - sendaa->next_id=skill_id+1; - else - sendaa->next_id=0xFFFFFFFF; - - sendaa->cost_inc = atoi(row[16]); - // Begin SoF Specific/Adjusted AA Fields - sendaa->aa_expansion = atoul(row[17]); - sendaa->special_category = atoul(row[18]); - sendaa->sof_type = atoul(row[19]); - sendaa->sof_cost_inc = atoi(row[20]); - sendaa->sof_max_level = atoul(row[21]); - sendaa->sof_next_skill = atoul(row[22]); - sendaa->clientver = atoul(row[23]); - sendaa->account_time_required = atoul(row[24]); - //Internal use only - not sent to client - sendaa->sof_current_level = atoul(row[25]); - sendaa->sof_next_id = atoul(row[26]); - sendaa->level_inc = static_cast(atoul(row[27])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query, errbuf); - safe_delete_array(query); - } - } else { - LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query, errbuf); - safe_delete_array(query); - } - return sendaa; -} - -void Client::DurationRampage(uint32 duration) -{ - if(duration) { - m_epp.aa_effects |= 1 << (aaEffectRampage-1); - p_timers.Start(pTimerAAEffectStart + aaEffectRampage, duration); - } -} - -AA_SwarmPetInfo::AA_SwarmPetInfo() -{ - target = 0; - owner_id = 0; - duration = nullptr; -} - -AA_SwarmPetInfo::~AA_SwarmPetInfo() -{ - target = 0; - owner_id = 0; - safe_delete(duration); -} - -Mob *AA_SwarmPetInfo::GetOwner() -{ - return entity_list.GetMobID(owner_id); -} diff --git a/zone/AA_v1.cpp b/zone/AA_v1.cpp deleted file mode 100644 index fb1413927..000000000 --- a/zone/AA_v1.cpp +++ /dev/null @@ -1,2032 +0,0 @@ -/* EQEMu: Everquest Server Emulator -Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -// Test 1 - -#include "../common/debug.h" -#include "AA.h" -#include "mob.h" -#include "client.h" -#include "groups.h" -#include "raids.h" -#include "../common/spdat.h" -#include "object.h" -#include "doors.h" -#include "beacon.h" -#include "corpse.h" -#include "titles.h" -#include "../common/races.h" -#include "../common/classes.h" -#include "../common/eq_packet_structs.h" -#include "../common/packet_dump.h" -#include "../common/StringUtil.h" -#include "../common/logsys.h" -#include "zonedb.h" -#include "StringIDs.h" - -//static data arrays, really not big enough to warrant shared mem. -AA_DBAction AA_Actions[aaHighestID][MAX_AA_ACTION_RANKS]; //[aaid][rank] -std::mapaas_send; -std::map > aa_effects; //stores the effects from the aa_effects table in memory -std::map AARequiredLevelAndCost; - -/* - - -Schema: - -spell_id is spell to cast, SPELL_UNKNOWN == no spell -nonspell_action is action to preform on activation which is not a spell, 0=none -nonspell_mana is mana that the nonspell action consumes -nonspell_duration is a duration which may be used by the nonspell action -redux_aa is the aa which reduces the reuse timer of the skill -redux_rate is the multiplier of redux_aa, as a percentage of total rate (10 == 10% faster) - -CREATE TABLE aa_actions ( - aaid mediumint unsigned not null, - rank tinyint unsigned not null, - reuse_time mediumint unsigned not null, - spell_id mediumint unsigned not null, - target tinyint unsigned not null, - nonspell_action tinyint unsigned not null, - nonspell_mana mediumint unsigned not null, - nonspell_duration mediumint unsigned not null, - redux_aa mediumint unsigned not null, - redux_rate tinyint not null, - - PRIMARY KEY(aaid, rank) -); - -CREATE TABLE aa_swarmpets ( - spell_id mediumint unsigned not null, - count tinyint unsigned not null, - npc_id int not null, - duration mediumint unsigned not null, - PRIMARY KEY(spell_id) -); -*/ - -/* - -Credits for this function: - -FatherNitwit: Structure and mechanism - -Wiz: Initial set of AAs, original function contents - -Branks: Much updated info and a bunch of higher-numbered AAs - -*/ -int Client::GetAATimerID(aaID activate) -{ - SendAA_Struct* aa2 = zone->FindAA(activate); - - if(!aa2) - { - for(int i = 1;i < MAX_AA_ACTION_RANKS; ++i) - { - int a = activate - i; - - if(a <= 0) - break; - - aa2 = zone->FindAA(a); - - if(aa2 != nullptr) - break; - } - } - - if(aa2) - return aa2->spell_type; - - return 0; -} - -int Client::CalcAAReuseTimer(const AA_DBAction *caa) { - - if(!caa) - return 0; - - int ReuseTime = caa->reuse_time; - - if(ReuseTime > 0) - { - int ReductionPercentage; - - if(caa->redux_aa > 0 && caa->redux_aa < aaHighestID) - { - ReductionPercentage = GetAA(caa->redux_aa) * caa->redux_rate; - - if(caa->redux_aa2 > 0 && caa->redux_aa2 < aaHighestID) - ReductionPercentage += (GetAA(caa->redux_aa2) * caa->redux_rate2); - - ReuseTime = caa->reuse_time * (100 - ReductionPercentage) / 100; - } - - } - return ReuseTime; -} - -void Client::ActivateAA(aaID activate){ - if(activate < 0 || activate >= aaHighestID) - return; - if(IsStunned() || IsFeared() || IsMezzed() || IsSilenced() || IsPet() || IsSitting() || GetFeigned()) - return; - - int AATimerID = GetAATimerID(activate); - - SendAA_Struct* aa2 = nullptr; - aaID aaid = activate; - uint8 activate_val = GetAA(activate); - //this wasn't taking into acct multi tiered act talents before... - if(activate_val == 0){ - aa2 = zone->FindAA(activate); - if(!aa2){ - int i; - int a; - for(i=1;iFindAA(a); - if(aa2 != nullptr) - break; - } - } - if(aa2){ - aaid = (aaID) aa2->id; - activate_val = GetAA(aa2->id); - } - } - - if (activate_val == 0){ - return; - } - - if(aa2) - { - if(aa2->account_time_required) - { - if((Timer::GetTimeSeconds() + account_creation) < aa2->account_time_required) - { - return; - } - } - } - - if(!p_timers.Expired(&database, AATimerID + pTimerAAStart)) - { - uint32 aaremain = p_timers.GetRemainingTime(AATimerID + pTimerAAStart); - uint32 aaremain_hr = aaremain / (60 * 60); - uint32 aaremain_min = (aaremain / 60) % 60; - uint32 aaremain_sec = aaremain % 60; - - if(aa2) { - if (aaremain_hr >= 1) //1 hour or more - Message(13, "You can use the ability %s again in %u hour(s) %u minute(s) %u seconds", - aa2->name, aaremain_hr, aaremain_min, aaremain_sec); - else //less than an hour - Message(13, "You can use the ability %s again in %u minute(s) %u seconds", - aa2->name, aaremain_min, aaremain_sec); - } else { - if (aaremain_hr >= 1) //1 hour or more - Message(13, "You can use this ability again in %u hour(s) %u minute(s) %u seconds", - aaremain_hr, aaremain_min, aaremain_sec); - else //less than an hour - Message(13, "You can use this ability again in %u minute(s) %u seconds", - aaremain_min, aaremain_sec); - } - return; - } - - if(activate_val > MAX_AA_ACTION_RANKS) - activate_val = MAX_AA_ACTION_RANKS; - activate_val--; //to get array index. - - //get our current node, now that the indices are well bounded - const AA_DBAction *caa = &AA_Actions[aaid][activate_val]; - - if((aaid == aaImprovedHarmTouch || aaid == aaLeechTouch) && !p_timers.Expired(&database, pTimerHarmTouch)){ - Message(13,"Ability recovery time not yet met."); - return; - } - Shout("spell id %i", caa->spell_id); - //everything should be configured out now - - uint16 target_id = 0; - - //figure out our target - switch(caa->target) { - case aaTargetUser: - case aaTargetGroup: - target_id = GetID(); - break; - case aaTargetCurrent: - case aaTargetCurrentGroup: - if(GetTarget() == nullptr) { - Message_StringID(MT_DefaultText, AA_NO_TARGET); //You must first select a target for this ability! - p_timers.Clear(&database, AATimerID + pTimerAAStart); - return; - } - target_id = GetTarget()->GetID(); - break; - case aaTargetPet: - if(GetPet() == nullptr) { - Message(0, "A pet is required for this skill."); - return; - } - target_id = GetPetID(); - break; - } - - //handle non-spell action - if(caa->action != aaActionNone) { - if(caa->mana_cost > 0) { - if(GetMana() < caa->mana_cost) { - Message_StringID(13, INSUFFICIENT_MANA); - return; - } - SetMana(GetMana() - caa->mana_cost); - } - if(caa->reuse_time > 0) - { - uint32 timer_base = CalcAAReuseTimer(caa); - if(activate == aaImprovedHarmTouch || activate == aaLeechTouch) - { - p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); - } - p_timers.Start(AATimerID + pTimerAAStart, timer_base); - SendAATimer(AATimerID, 0, 0); - } - HandleAAAction(aaid); - } - Shout("1 spell id %i", caa->spell_id); - //cast the spell, if we have one - if(caa->spell_id > 0 && caa->spell_id < SPDAT_RECORDS) { - Shout("2 spell id %i", caa->spell_id); - if(caa->reuse_time > 0) - { - uint32 timer_base = CalcAAReuseTimer(caa); - SendAATimer(AATimerID, 0, 0); - p_timers.Start(AATimerID + pTimerAAStart, timer_base); - if(activate == aaImprovedHarmTouch || activate == aaLeechTouch) - { - p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime); - } - // Bards can cast instant cast AAs while they are casting another song - if (spells[caa->spell_id].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) { - if(!SpellFinished(caa->spell_id, entity_list.GetMob(target_id), 10, -1, -1, spells[caa->spell_id].ResistDiff, false)) { - //Reset on failed cast - SendAATimer(AATimerID, 0, 0xFFFFFF); - Message_StringID(15,ABILITY_FAILED); - p_timers.Clear(&database, AATimerID + pTimerAAStart); - return; - } - } else { - if(!CastSpell(caa->spell_id, target_id, 10, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) { - //Reset on failed cast - SendAATimer(AATimerID, 0, 0xFFFFFF); - Message_StringID(15,ABILITY_FAILED); - p_timers.Clear(&database, AATimerID + pTimerAAStart); - return; - } - } - } - else - { - if(!CastSpell(caa->spell_id, target_id)) - return; - } - } - // Check if AA is expendable - if (aas_send[activate - activate_val]->special_category == 7) - { - // Add the AA cost to the extended profile to track overall total - m_epp.expended_aa += aas_send[activate]->cost; - SetAA(activate, 0); - - Save(); - SendAA(activate); - SendAATable(); - } -} - -void Client::HandleAAAction(aaID activate) { - if(activate < 0 || activate >= aaHighestID) - return; - - uint8 activate_val = GetAA(activate); - - if (activate_val == 0) - return; - - if(activate_val > MAX_AA_ACTION_RANKS) - activate_val = MAX_AA_ACTION_RANKS; - activate_val--; //to get array index. - - //get our current node, now that the indices are well bounded - const AA_DBAction *caa = &AA_Actions[activate][activate_val]; - - uint16 timer_id = 0; - uint16 timer_duration = caa->duration; - aaTargetType target = aaTargetUser; - - uint16 spell_id = SPELL_UNKNOWN; //gets cast at the end if not still unknown - - switch(caa->action) { - case aaActionAETaunt: - entity_list.AETaunt(this); - break; - - case aaActionMassBuff: - EnableAAEffect(aaEffectMassGroupBuff, 3600); - Message_StringID(MT_Disciplines, MGB_STRING); //The next group buff you cast will hit all targets in range. - break; - - case aaActionFlamingArrows: - //toggle it - if(CheckAAEffect(aaEffectFlamingArrows)) - EnableAAEffect(aaEffectFlamingArrows); - else - DisableAAEffect(aaEffectFlamingArrows); - break; - - case aaActionFrostArrows: - if(CheckAAEffect(aaEffectFrostArrows)) - EnableAAEffect(aaEffectFrostArrows); - else - DisableAAEffect(aaEffectFrostArrows); - break; - - case aaActionRampage: - EnableAAEffect(aaEffectRampage, 10); - break; - - case aaActionSharedHealth: - if(CheckAAEffect(aaEffectSharedHealth)) - EnableAAEffect(aaEffectSharedHealth); - else - DisableAAEffect(aaEffectSharedHealth); - break; - - case aaActionCelestialRegen: { - //special because spell_id depends on a different AA - switch (GetAA(aaCelestialRenewal)) { - case 1: - spell_id = 3250; - break; - case 2: - spell_id = 3251; - break; - default: - spell_id = 2740; - break; - } - target = aaTargetCurrent; - break; - } - - case aaActionDireCharm: { - //special because spell_id depends on class - switch (GetClass()) - { - case DRUID: - spell_id = 2760; //2644? - break; - case NECROMANCER: - spell_id = 2759; //2643? - break; - case ENCHANTER: - spell_id = 2761; //2642? - break; - } - target = aaTargetCurrent; - break; - } - - case aaActionImprovedFamiliar: { - //Spell IDs might be wrong... - if (GetAA(aaAllegiantFamiliar)) - spell_id = 3264; //1994? - else - spell_id = 2758; //2155? - break; - } - - case aaActionActOfValor: - if(GetTarget() != nullptr) { - int curhp = GetTarget()->GetHP(); - target = aaTargetCurrent; - GetTarget()->HealDamage(curhp, this); - Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand); - } - break; - - case aaActionSuspendedMinion: - if (GetPet()) { - target = aaTargetPet; - switch (GetAA(aaSuspendedMinion)) { - case 1: - spell_id = 3248; - break; - case 2: - spell_id = 3249; - break; - } - //do we really need to cast a spell? - - Message(0,"You call your pet to your side."); - GetPet()->WipeHateList(); - GetPet()->GMMove(GetX(),GetY(),GetZ()); - if (activate_val > 1) - entity_list.ClearFeignAggro(GetPet()); - } else { - Message(0,"You have no pet to call."); - } - break; - - case aaActionProjectIllusion: - EnableAAEffect(aaEffectProjectIllusion, 3600); - Message(10, "The power of your next illusion spell will flow to your grouped target in your place."); - break; - - - case aaActionEscape: - Escape(); - break; - - // Don't think this code is used any longer for Bestial Alignment as the AA has a spell_id and no nonspell_action. - case aaActionBeastialAlignment: - switch(GetBaseRace()) { - case BARBARIAN: - spell_id = AA_Choose3(activate_val, 4521, 4522, 4523); - break; - case TROLL: - spell_id = AA_Choose3(activate_val, 4524, 4525, 4526); - break; - case OGRE: - spell_id = AA_Choose3(activate_val, 4527, 4527, 4529); - break; - case IKSAR: - spell_id = AA_Choose3(activate_val, 4530, 4531, 4532); - break; - case VAHSHIR: - spell_id = AA_Choose3(activate_val, 4533, 4534, 4535); - break; - } - - case aaActionLeechTouch: - target = aaTargetCurrent; - spell_id = SPELL_HARM_TOUCH2; - EnableAAEffect(aaEffectLeechTouch, 1000); - break; - - case aaActionFadingMemories: - // Do nothing since spell effect works correctly, but mana isn't used. - break; - - default: - LogFile->write(EQEMuLog::Error, "Unknown AA nonspell action type %d", caa->action); - return; - } - - - uint16 target_id = 0; - //figure out our target - switch(target) { - case aaTargetUser: - case aaTargetGroup: - target_id = GetID(); - break; - case aaTargetCurrent: - case aaTargetCurrentGroup: - if(GetTarget() == nullptr) { - Message_StringID(MT_DefaultText, AA_NO_TARGET); //You must first select a target for this ability! - p_timers.Clear(&database, timer_id + pTimerAAEffectStart); - return; - } - target_id = GetTarget()->GetID(); - break; - case aaTargetPet: - if(GetPet() == nullptr) { - Message(0, "A pet is required for this skill."); - return; - } - target_id = GetPetID(); - break; - } - - //cast the spell, if we have one - if(IsValidSpell(spell_id)) { - int aatid = GetAATimerID(activate); - if(!CastSpell(spell_id, target_id , 10, -1, -1, 0, -1, pTimerAAStart + aatid , CalcAAReuseTimer(caa), 1)) { - SendAATimer(aatid, 0, 0xFFFFFF); - Message_StringID(15,ABILITY_FAILED); - p_timers.Clear(&database, pTimerAAStart + aatid); - return; - } - } - - //handle the duration timer if we have one. - if(timer_id > 0 && timer_duration > 0) { - p_timers.Start(pTimerAAEffectStart + timer_id, timer_duration); - } -} - - -//Originally written by Branks -//functionality rewritten by Father Nitwit -void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, uint32 duration_override) { - - //It might not be a bad idea to put these into the database, eventually.. - - //Dook- swarms and wards - - PetRecord record; - if(!database.GetPetEntry(spells[spell_id].teleport_zone, &record)) - { - LogFile->write(EQEMuLog::Error, "Unknown swarm pet spell id: %d, check pets table", spell_id); - Message(13, "Unable to find data for pet %s", spells[spell_id].teleport_zone); - return; - } - - AA_SwarmPet pet; - pet.count = 1; - pet.duration = 1; - - for(int x = 0; x < 12; x++) - { - if(spells[spell_id].effectid[x] == SE_TemporaryPets) - { - pet.count = spells[spell_id].base[x]; - pet.duration = spells[spell_id].max[x]; - } - } - - if(IsClient()) - pet.duration += (CastToClient()->GetFocusEffect(focusSwarmPetDuration, spell_id) / 1000); - - pet.npc_id = record.npc_type; - - NPCType *made_npc = nullptr; - - const NPCType *npc_type = database.GetNPCType(pet.npc_id); - if(npc_type == nullptr) { - //log write - LogFile->write(EQEMuLog::Error, "Unknown npc type for swarm pet spell id: %d", spell_id); - Message(0,"Unable to find pet!"); - return; - } - - if(name_override != nullptr) { - //we have to make a custom NPC type for this name change - made_npc = new NPCType; - memcpy(made_npc, npc_type, sizeof(NPCType)); - strcpy(made_npc->name, name_override); - npc_type = made_npc; - } - - int summon_count = 0; - summon_count = pet.count; - - if(summon_count > MAX_SWARM_PETS) - summon_count = MAX_SWARM_PETS; - - static const float swarm_pet_x[MAX_SWARM_PETS] = { 5, -5, 5, -5, - 10, -10, 10, -10, - 8, -8, 8, -8 }; - static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5, - 10, 10, -10, -10, - 8, 8, -8, -8 }; - TempPets(true); - - while(summon_count > 0) { - int pet_duration = pet.duration; - if(duration_override > 0) - pet_duration = duration_override; - - //this is a little messy, but the only way to do it right - //it would be possible to optimize out this copy for the last pet, but oh well - NPCType *npc_dup = nullptr; - if(made_npc != nullptr) { - npc_dup = new NPCType; - memcpy(npc_dup, made_npc, sizeof(NPCType)); - } - - NPC* npca = new NPC( - (npc_dup!=nullptr)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer - 0, - GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count], - GetZ(), GetHeading(), FlyMode3); - - if((spell_id == 6882) || (spell_id == 6884)) - npca->SetFollowID(GetID()); - - if(!npca->GetSwarmInfo()){ - AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; - npca->SetSwarmInfo(nSI); - npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); - } - else{ - npca->GetSwarmInfo()->duration->Start(pet_duration*1000); - } - - //removing this prevents the pet from attacking - npca->GetSwarmInfo()->owner_id = GetID(); - - //give the pets somebody to "love" - if(targ != nullptr){ - npca->AddToHateList(targ, 1000, 1000); - npca->GetSwarmInfo()->target = targ->GetID(); - } - - //we allocated a new NPC type object, give the NPC ownership of that memory - if(npc_dup != nullptr) - npca->GiveNPCTypeData(npc_dup); - - entity_list.AddNPC(npca, true, true); - summon_count--; - } - - //the target of these swarm pets will take offense to being cast on... - if(targ != nullptr) - targ->AddToHateList(this, 1, 0); -} - -void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_override, uint32 duration_override, bool followme) { - - AA_SwarmPet pet; - pet.count = 1; - pet.duration = 1; - - pet.npc_id = typesid; - - NPCType *made_npc = nullptr; - - const NPCType *npc_type = database.GetNPCType(typesid); - if(npc_type == nullptr) { - //log write - LogFile->write(EQEMuLog::Error, "Unknown npc type for swarm pet type id: %d", typesid); - Message(0,"Unable to find pet!"); - return; - } - - if(name_override != nullptr) { - //we have to make a custom NPC type for this name change - made_npc = new NPCType; - memcpy(made_npc, npc_type, sizeof(NPCType)); - strcpy(made_npc->name, name_override); - npc_type = made_npc; - } - - int summon_count = 0; - summon_count = pet.count; - - if(summon_count > MAX_SWARM_PETS) - summon_count = MAX_SWARM_PETS; - - static const float swarm_pet_x[MAX_SWARM_PETS] = { 5, -5, 5, -5, - 10, -10, 10, -10, - 8, -8, 8, -8 }; - static const float swarm_pet_y[MAX_SWARM_PETS] = { 5, 5, -5, -5, - 10, 10, -10, -10, - 8, 8, -8, -8 }; - TempPets(true); - - while(summon_count > 0) { - int pet_duration = pet.duration; - if(duration_override > 0) - pet_duration = duration_override; - - //this is a little messy, but the only way to do it right - //it would be possible to optimize out this copy for the last pet, but oh well - NPCType *npc_dup = nullptr; - if(made_npc != nullptr) { - npc_dup = new NPCType; - memcpy(npc_dup, made_npc, sizeof(NPCType)); - } - - NPC* npca = new NPC( - (npc_dup!=nullptr)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer - 0, - GetX()+swarm_pet_x[summon_count], GetY()+swarm_pet_y[summon_count], - GetZ(), GetHeading(), FlyMode3); - - if(!npca->GetSwarmInfo()){ - AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; - npca->SetSwarmInfo(nSI); - npca->GetSwarmInfo()->duration = new Timer(pet_duration*1000); - } - else{ - npca->GetSwarmInfo()->duration->Start(pet_duration*1000); - } - - //removing this prevents the pet from attacking - npca->GetSwarmInfo()->owner_id = GetID(); - - //give the pets somebody to "love" - if(targ != nullptr){ - npca->AddToHateList(targ, 1000, 1000); - npca->GetSwarmInfo()->target = targ->GetID(); - } - - //we allocated a new NPC type object, give the NPC ownership of that memory - if(npc_dup != nullptr) - npca->GiveNPCTypeData(npc_dup); - - entity_list.AddNPC(npca, true, true); - summon_count--; - } -} - -void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration) -{ - Corpse *CorpseToUse = nullptr; - CorpseToUse = entity_list.GetClosestCorpse(this, nullptr); - - if(!CorpseToUse) - return; - - //assuming we have pets in our table; we take the first pet as a base type. - const NPCType *base_type = database.GetNPCType(500); - NPCType *make_npc = new NPCType; - memcpy(make_npc, base_type, sizeof(NPCType)); - - //combat stats - make_npc->AC = ((GetLevel() * 7) + 550); - make_npc->ATK = GetLevel(); - make_npc->max_dmg = (GetLevel() * 4) + 2; - make_npc->min_dmg = 1; - - //base stats - make_npc->cur_hp = (GetLevel() * 55); - make_npc->max_hp = (GetLevel() * 55); - make_npc->STR = 85 + (GetLevel() * 3); - make_npc->STA = 85 + (GetLevel() * 3); - make_npc->DEX = 85 + (GetLevel() * 3); - make_npc->AGI = 85 + (GetLevel() * 3); - make_npc->INT = 85 + (GetLevel() * 3); - make_npc->WIS = 85 + (GetLevel() * 3); - make_npc->CHA = 85 + (GetLevel() * 3); - make_npc->MR = 25; - make_npc->FR = 25; - make_npc->CR = 25; - make_npc->DR = 25; - make_npc->PR = 25; - - //level class and gender - make_npc->level = GetLevel(); - make_npc->class_ = CorpseToUse->class_; - make_npc->race = CorpseToUse->race; - make_npc->gender = CorpseToUse->gender; - make_npc->loottable_id = 0; - //name - char NewName[64]; - sprintf(NewName, "%s`s Animated Corpse", GetCleanName()); - strcpy(make_npc->name, NewName); - - //appearance - make_npc->beard = CorpseToUse->beard; - make_npc->beardcolor = CorpseToUse->beardcolor; - make_npc->eyecolor1 = CorpseToUse->eyecolor1; - make_npc->eyecolor2 = CorpseToUse->eyecolor2; - make_npc->haircolor = CorpseToUse->haircolor; - make_npc->hairstyle = CorpseToUse->hairstyle; - make_npc->helmtexture = CorpseToUse->helmtexture; - make_npc->luclinface = CorpseToUse->luclinface; - make_npc->size = CorpseToUse->size; - make_npc->texture = CorpseToUse->texture; - - //cast stuff.. based off of PEQ's if you want to change - //it you'll have to mod this code, but most likely - //most people will be using PEQ style for the first - //part of their spell list; can't think of any smooth - //way to do this - //some basic combat mods here too since it's convienent - switch(CorpseToUse->class_) - { - case CLERIC: - make_npc->npc_spells_id = 1; - break; - case WIZARD: - make_npc->npc_spells_id = 2; - break; - case NECROMANCER: - make_npc->npc_spells_id = 3; - break; - case MAGICIAN: - make_npc->npc_spells_id = 4; - break; - case ENCHANTER: - make_npc->npc_spells_id = 5; - break; - case SHAMAN: - make_npc->npc_spells_id = 6; - break; - case DRUID: - make_npc->npc_spells_id = 7; - break; - case PALADIN: - //SPECATK_TRIPLE - strcpy(make_npc->special_abilities, "6,1"); - make_npc->cur_hp = make_npc->cur_hp * 150 / 100; - make_npc->max_hp = make_npc->max_hp * 150 / 100; - make_npc->npc_spells_id = 8; - break; - case SHADOWKNIGHT: - strcpy(make_npc->special_abilities, "6,1"); - make_npc->cur_hp = make_npc->cur_hp * 150 / 100; - make_npc->max_hp = make_npc->max_hp * 150 / 100; - make_npc->npc_spells_id = 9; - break; - case RANGER: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->cur_hp = make_npc->cur_hp * 135 / 100; - make_npc->max_hp = make_npc->max_hp * 135 / 100; - make_npc->npc_spells_id = 10; - break; - case BARD: - strcpy(make_npc->special_abilities, "6,1"); - make_npc->cur_hp = make_npc->cur_hp * 110 / 100; - make_npc->max_hp = make_npc->max_hp * 110 / 100; - make_npc->npc_spells_id = 11; - break; - case BEASTLORD: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->cur_hp = make_npc->cur_hp * 110 / 100; - make_npc->max_hp = make_npc->max_hp * 110 / 100; - make_npc->npc_spells_id = 12; - break; - case ROGUE: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->max_dmg = make_npc->max_dmg * 150 /100; - make_npc->cur_hp = make_npc->cur_hp * 110 / 100; - make_npc->max_hp = make_npc->max_hp * 110 / 100; - break; - case MONK: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->max_dmg = make_npc->max_dmg * 150 /100; - make_npc->cur_hp = make_npc->cur_hp * 135 / 100; - make_npc->max_hp = make_npc->max_hp * 135 / 100; - break; - case WARRIOR: - case BERSERKER: - strcpy(make_npc->special_abilities, "7,1"); - make_npc->max_dmg = make_npc->max_dmg * 150 /100; - make_npc->cur_hp = make_npc->cur_hp * 175 / 100; - make_npc->max_hp = make_npc->max_hp * 175 / 100; - break; - default: - make_npc->npc_spells_id = 0; - break; - } - - make_npc->loottable_id = 0; - make_npc->merchanttype = 0; - make_npc->d_meele_texture1 = 0; - make_npc->d_meele_texture2 = 0; - - TempPets(true); - - NPC* npca = new NPC(make_npc, 0, GetX(), GetY(), GetZ(), GetHeading(), FlyMode3); - - if(!npca->GetSwarmInfo()){ - AA_SwarmPetInfo* nSI = new AA_SwarmPetInfo; - npca->SetSwarmInfo(nSI); - npca->GetSwarmInfo()->duration = new Timer(duration*1000); - } - else{ - npca->GetSwarmInfo()->duration->Start(duration*1000); - } - - npca->GetSwarmInfo()->owner_id = GetID(); - - //give the pet somebody to "love" - if(target != nullptr){ - npca->AddToHateList(target, 100000); - npca->GetSwarmInfo()->target = target->GetID(); - } - - //gear stuff, need to make sure there's - //no situation where this stuff can be duped - for(int x = 0; x < 21; x++) - { - uint32 sitem = 0; - sitem = CorpseToUse->GetWornItem(x); - if(sitem){ - const Item_Struct * itm = database.GetItem(sitem); - npca->AddLootDrop(itm, &npca->itemlist, 1, 1, 127, true, true); - } - } - - //we allocated a new NPC type object, give the NPC ownership of that memory - if(make_npc != nullptr) - npca->GiveNPCTypeData(make_npc); - - entity_list.AddNPC(npca, true, true); - - //the target of these swarm pets will take offense to being cast on... - if(target != nullptr) - target->AddToHateList(this, 1, 0); -} - -//turn on an AA effect -//duration == 0 means no time limit, used for one-shot deals, etc.. -void Client::EnableAAEffect(aaEffectType type, uint32 duration) { - if(type > 32) - return; //for now, special logic needed. - m_epp.aa_effects |= 1 << (type-1); - - if(duration > 0) { - p_timers.Start(pTimerAAEffectStart + type, duration); - } else { - p_timers.Clear(&database, pTimerAAEffectStart + type); - } -} - -void Client::DisableAAEffect(aaEffectType type) { - if(type > 32) - return; //for now, special logic needed. - uint32 bit = 1 << (type-1); - if(m_epp.aa_effects & bit) { - m_epp.aa_effects ^= bit; - } - p_timers.Clear(&database, pTimerAAEffectStart + type); -} - -/* -By default an AA effect is a one shot deal, unless -a duration timer is set. -*/ -bool Client::CheckAAEffect(aaEffectType type) { - if(type > 32) - return(false); //for now, special logic needed. - if(m_epp.aa_effects & (1 << (type-1))) { //is effect enabled? - //has our timer expired? - if(p_timers.Expired(&database, pTimerAAEffectStart + type)) { - DisableAAEffect(type); - return(false); - } - return(true); - } - return(false); -} - -void Client::SendAAStats() { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAExpUpdate, sizeof(AltAdvStats_Struct)); - AltAdvStats_Struct *aps = (AltAdvStats_Struct *)outapp->pBuffer; - aps->experience = m_pp.expAA; - aps->experience = (uint32)(((float)330.0f * (float)m_pp.expAA) / (float)max_AAXP); - aps->unspent = m_pp.aapoints; - aps->percentage = m_epp.perAA; - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::BuyAA(AA_Action* action) -{ - mlog(AA__MESSAGE, "Starting to buy AA %d", action->ability); - - //find the AA information from the database - SendAA_Struct* aa2 = zone->FindAA(action->ability); - if(!aa2) { - //hunt for a lower level... - int i; - int a; - for(i=1;iability - i; - if(a <= 0) - break; - mlog(AA__MESSAGE, "Could not find AA %d, trying potential parent %d", action->ability, a); - aa2 = zone->FindAA(a); - if(aa2 != nullptr) - break; - } - } - if(aa2 == nullptr) - return; //invalid ability... - - if(aa2->special_category == 1 || aa2->special_category == 2) - return; // Not purchasable progression style AAs - - if(aa2->special_category == 8 && aa2->cost == 0) - return; // Not purchasable racial AAs(set a cost to make them purchasable) - - uint32 cur_level = GetAA(aa2->id); - if((aa2->id + cur_level) != action->ability) { //got invalid AA - mlog(AA__ERROR, "Unable to find or match AA %d (found %d + lvl %d)", action->ability, aa2->id, cur_level); - return; - } - - if(aa2->account_time_required) - { - if((Timer::GetTimeSeconds() - account_creation) < aa2->account_time_required) - { - return; - } - } - - uint32 real_cost; - std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(action->ability); - - if(RequiredLevel != AARequiredLevelAndCost.end()) - { - real_cost = RequiredLevel->second.Cost; - } - else - real_cost = aa2->cost + (aa2->cost_inc * cur_level); - - if(m_pp.aapoints >= real_cost && cur_level < aa2->max_level) { - SetAA(aa2->id, cur_level+1); - - mlog(AA__MESSAGE, "Set AA %d to level %d", aa2->id, cur_level+1); - - m_pp.aapoints -= real_cost; - - Save(); - //if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (aa2->hotkey_sid == 4294967295u)) - if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4)) - && ((aa2->max_level == (cur_level+1)) && aa2->sof_next_id)){ - SendAA(aa2->id); - SendAA(aa2->sof_next_id); - } - //else if ((RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && cur_level == 0 && aa2->hotkey_sid != 4294967295u)) - //{ - //Shout("Current lv 0 for AA hot key %i %i", cur_level, aa2->hotkey_sid); - //} - - else - SendAA(aa2->id); - - SendAATable(); - - //we are building these messages ourself instead of using the stringID to work around patch discrepencies - //these are AA_GAIN_ABILITY (410) & AA_IMPROVE (411), respectively, in both Titanium & SoF. not sure about 6.2 - if(cur_level<1) - Message(15,"You have gained the ability \"%s\" at a cost of %d ability %s.", aa2->name, real_cost, (real_cost>1)?"points":"point"); - else - Message(15,"You have improved %s %d at a cost of %d ability %s.", aa2->name, cur_level+1, real_cost, (real_cost>1)?"points":"point"); - - - SendAAStats(); - - //SendAAList(true); - //SendAATable(); - CalcBonuses(); - if(title_manager.IsNewAATitleAvailable(m_pp.aapoints_spent, GetBaseClass())) - NotifyNewTitlesAvailable(); - } -} - -void Client::SendAATimer(uint32 ability, uint32 begin, uint32 end) { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction,sizeof(UseAA_Struct)); - UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; - uaaout->ability = ability; - uaaout->begin = begin; - uaaout->end = end; - QueuePacket(outapp); - safe_delete(outapp); -} - -//sends all AA timers. -void Client::SendAATimers() { - //we dont use SendAATimer because theres no reason to allocate the EQApplicationPacket every time - EQApplicationPacket* outapp = new EQApplicationPacket(OP_AAAction,sizeof(UseAA_Struct)); - UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer; - - PTimerList::iterator c,e; - c = p_timers.begin(); - e = p_timers.end(); - for(; c != e; ++c) { - PersistentTimer *cur = c->second; - if(cur->GetType() < pTimerAAStart || cur->GetType() > pTimerAAEnd) - continue; //not an AA timer - //send timer - uaaout->begin = cur->GetStartTime(); - uaaout->end = static_cast(time(nullptr)); - uaaout->ability = cur->GetType() - pTimerAAStart; // uuaaout->ability is really a shared timer number - QueuePacket(outapp); - } - - safe_delete(outapp); -} - -void Client::SendAATable() { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_RespondAA, sizeof(AATable_Struct)); - - AATable_Struct* aa2 = (AATable_Struct *)outapp->pBuffer; - aa2->aa_spent = GetAAPointsSpent(); - - uint32 i; - for(i=0;i < MAX_PP_AA_ARRAY;i++){ - aa2->aa_list[i].aa_skill = aa[i]->AA; - aa2->aa_list[i].aa_value = aa[i]->value; - aa2->aa_list[i].unknown08 = 0; - } - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SendPreviousAA(uint32 id, int seq){ - uint32 value=0; - SendAA_Struct* saa2 = nullptr; - if(id==0) - saa2 = zone->GetAABySequence(seq); - else - saa2 = zone->FindAA(id); - if(!saa2) - return; - int size=sizeof(SendAA_Struct)+sizeof(AA_Ability)*saa2->total_abilities; - uchar* buffer = new uchar[size]; - SendAA_Struct* saa=(SendAA_Struct*)buffer; - value = GetAA(saa2->id); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAATable); - outapp->size=size; - outapp->pBuffer=(uchar*)saa; - value--; - memcpy(saa,saa2,size); - - if(value>0){ - if(saa->spellid==0) - saa->spellid=0xFFFFFFFF; - saa->id+=value; - saa->next_id=saa->id+1; - if(value==1) - saa->last_id=saa2->id; - else - saa->last_id=saa->id-1; - saa->current_level=value+1; - saa->cost2 = 0; //cost 2 is what the client uses to calc how many points we've spent, so we have to add up the points in order - for(uint32 i = 0; i < (value+1); i++) { - saa->cost2 += saa->cost + (saa->cost_inc * i); - } - } - - database.FillAAEffects(saa); - QueuePacket(outapp); - safe_delete(outapp); -} - -void Client::SendAA(uint32 id, int seq, bool seqrest) { - - uint32 value=0; - SendAA_Struct* saa2 = nullptr; - SendAA_Struct* qaa = nullptr; - SendAA_Struct* saa_pp = nullptr; - bool IsBaseLevel = true; - bool aa_stack = false; - - Shout("Reset: %i", seqrest); - - if(id==0){ - saa2 = zone->GetAABySequence(seq); - //Shout("SAA2 %i x seq %i", GetAA(saa2->id), seq); - } - else - saa2 = zone->FindAA(id); - if(!saa2) - return; - - uint16 classes = saa2->classes; - if(!(classes & (1 << GetClass())) && (GetClass()!=BERSERKER || saa2->berserker==0)){ - return; - } - - if(saa2->account_time_required) - { - if((Timer::GetTimeSeconds() - account_creation) < saa2->account_time_required) - { - return; - } - } - - // Hide Quest/Progression AAs unless player has been granted the first level using $client->IncrementAA(skill_id). - if (saa2->special_category == 1 || saa2->special_category == 2 ) { - if(GetAA(saa2->id) == 0) - return; - // For Quest line AA(demiplane AEs) where only 1 is visible at a time, check to make sure only the highest level obtained is shown - if(saa2->aa_expansion > 0) { - qaa = zone->FindAA(saa2->id+1); - if(qaa && (saa2->aa_expansion == qaa->aa_expansion) && GetAA(qaa->id) > 0) - return; - } - } - -/* Beginning of Shroud AAs, these categories are for Passive and Active Shroud AAs - Eventually with a toggle we could have it show player list or shroud list - if (saa2->special_category == 3 || saa2->special_category == 4) - return; -*/ - // Check for racial/Drakkin blood line AAs - if (saa2->special_category == 8) - { - uint32 client_race = this->GetBaseRace(); - - // Drakkin Bloodlines - if (saa2->aa_expansion > 522) - { - if (client_race != 522) - return; // Check for Drakkin Race - - int heritage = this->GetDrakkinHeritage() + 523; // 523 = Drakkin Race(522) + Bloodline - - if (heritage != saa2->aa_expansion) - return; - } - // Racial AAs - else if (client_race != saa2->aa_expansion) - { - return; - } - } - - /* - AA stacking on SoF+ clients. - - Note: There were many ways to achieve this effect - The method used proved to be the most straight forward and consistent. - Stacking does not currently work ideally for AA's that use hotkeys, therefore they will be excluded at this time. - - TODO: Problem with AA hotkeys - When you reach max rank of an AA tier (ie 5/5), it automatically displays the next AA in - the series and you can not transfer the hotkey to the next AA series. To the best of the my ability and through many - different variations of coding I could not find an ideal solution to this issue. - - How stacking works: - Utilizes two new fields: sof_next_id (which is the next id in the series), sof_current_level (ranks the AA's as the current level) - 1) If no AA's purchased only display the base levels of each AA series. - 2) When you purchase an AA and its rank is maxed it sends the packet for the completed AA, and the packet - for the next aa in the series. The previous tier is removed from your window, and the new AA is displayed. - 3) When you zone/buy your player profile will be checked and determine what AA can be displayed base on what you have already. - */ - - //if (RuleB(AA, Stacking) && (GetClientVersionBit() >= 4) && (saa2->hotkey_sid == 4294967295u)) - if (RuleB(AA, Stacking) && (GetClientVersionBit() >= 4)) - aa_stack = true; - - if (aa_stack){ - uint32 aa_AA = 0; - uint32 aa_value = 0; - for (int i = 0; i < MAX_PP_AA_ARRAY; i++) { - if (aa[i]) { - aa_AA = aa[i]->AA; - aa_value = aa[i]->value; - - if (aa_AA){ - - if (aa_value > 0) - aa_AA -= aa_value-1; - - saa_pp = zone->FindAA(aa_AA); - - if (saa_pp){ - - if (saa_pp->sof_next_skill == saa2->sof_next_skill){ - - if (saa_pp->id == saa2->id) - break; //You already have this in the player profile. - else if ((saa_pp->sof_current_level < saa2->sof_current_level) && (aa_value < saa_pp->max_level)) - return; //DISABLE DISPLAY HIGHER - You have not reached max level yet of your current AA. - else if ((saa_pp->sof_current_level < saa2->sof_current_level) && (aa_value == saa_pp->max_level) && (saa_pp->sof_next_id == saa2->id)) - IsBaseLevel = false; //ALLOW DISPLAY HIGHER - } - } - } - } - } - } - - //Hide higher tiers of multi tiered AA's if the base level is not fully purchased. - if (aa_stack && IsBaseLevel && saa2->sof_current_level > 0) - return; - - int size=sizeof(SendAA_Struct)+sizeof(AA_Ability)*saa2->total_abilities; - - if(size == 0) - return; - - uchar* buffer = new uchar[size]; - SendAA_Struct* saa=(SendAA_Struct*)buffer; - memcpy(saa,saa2,size); - Shout("[AA ID %i] SEQ %i SEQLIVE %i",saa->id, saa->seq, saa->seqlive); - if(saa->spellid==0) - saa->spellid=0xFFFFFFFF; - - value=GetAA(saa->id); - uint32 orig_val = value; - - if(value && saa->id){ - - if(value < saa->max_level){ - saa->id+=value; - saa->next_id=saa->id+1; - value++; - } - - else if (aa_stack && saa->sof_next_id){ - //Max value of current tier reached. - saa->id+=value-1; - saa->next_id=saa->sof_next_id; - - //Prevent removal of previous AA from window if next AA belongs to a higher client version. - SendAA_Struct* saa_next = nullptr; - saa_next = zone->FindAA(saa->sof_next_id); - if (saa_next && - (((GetClientVersionBit() == 4) && (saa_next->clientver > 4)) - || ((GetClientVersionBit() == 8) && (saa_next->clientver > 5)) - || ((GetClientVersionBit() == 16) && (saa_next->clientver > 6)))){ - saa->next_id=0xFFFFFFFF; - } - - //if (saa->id == 131) { - // saa->seq = 1; - // Shout("SET SEQ 1"); - //} - Shout("AA Completed: Next"); - } - - else{ - saa->id+=value-1; - saa->next_id=0xFFFFFFFF; - Shout("AA Completed: Final"); - } - - uint32 current_level_mod = 0; - if (aa_stack) - current_level_mod = saa->sof_current_level; - - saa->last_id=saa->id-1; - Shout("1 Current level %i + Value %i",saa->sof_current_level, value); - saa->current_level=value+(current_level_mod); - saa->cost = saa2->cost + (saa2->cost_inc*(value-1)); - saa->cost2 = 0; - for(uint32 i = 0; i < value; i++) { - saa->cost2 += saa2->cost + (saa2->cost_inc * i); - } - saa->class_type = saa2->class_type + (saa2->level_inc*(value-1)); - - } - - if (aa_stack){ - Shout("2 Current level %i VALUE %i",saa->sof_current_level, value); - //After finishing an AA tier transfer over the current level to the next tier to display. - if (saa->sof_current_level >= 1 && value == 0){ - saa->current_level = saa->sof_current_level+1; - - Shout("value = 0 SET LAST AND SEQ"); - saa->last_id = 131; - //saa->seq = 38; - if (saa->seqlive) - saa->seq = saa->seqlive; - } - - saa->max_level = saa->sof_max_level; - } - - - if(seqrest) { - //saa->seq = 0; - saa->id+= saa->max_level-1; - saa->next_id=1; - saa->seq = 0; - Shout("reset"); - } - - database.FillAAEffects(saa); - - if(value > 0) - { - // AA_Action stores the base ID - const AA_DBAction *caa = &AA_Actions[saa->id - value + 1][value - 1]; - - if(caa && caa->reuse_time > 0) - saa->spell_refresh = CalcAAReuseTimer(caa); - } - - //You can now use the level_inc field in the altadv_vars table to accomplish this, though still needed - //for special cases like LOH/HT due to inability to implement correct stacking of AA's that use hotkeys. - std::map::iterator RequiredLevel = AARequiredLevelAndCost.find(saa->id); - - if(RequiredLevel != AARequiredLevelAndCost.end()) - { - saa->class_type = RequiredLevel->second.Level; - saa->cost = RequiredLevel->second.Cost; - } - - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendAATable); - outapp->size=size; - outapp->pBuffer=(uchar*)saa; - if(id==0 && value && (orig_val < saa->max_level)) //send previous AA only on zone in - SendPreviousAA(id, seq); - - QueuePacket(outapp); - safe_delete(outapp); - //will outapp delete the buffer for us even though it didnt make it? --- Yes, it should -} - -void Client::SendAAList(bool seqrest){ - int total = zone->GetTotalAAs(); - for(int i=0;i < total;i++){ - SendAA(0,i, seqrest); - } -} - -uint32 Client::GetAA(uint32 aa_id) const { - std::map::const_iterator res; - res = aa_points.find(aa_id); - if(res != aa_points.end()) { - return(res->second); - } - return(0); -} - -bool Client::SetAA(uint32 aa_id, uint32 new_value) { - aa_points[aa_id] = new_value; - uint32 cur; - for(cur=0;cur < MAX_PP_AA_ARRAY;cur++){ - if((aa[cur]->value > 1) && ((aa[cur]->AA - aa[cur]->value + 1)== aa_id)){ - aa[cur]->value = new_value; - if(new_value > 0) - aa[cur]->AA++; - else - aa[cur]->AA = 0; - return true; - } - else if((aa[cur]->value == 1) && (aa[cur]->AA == aa_id)){ - aa[cur]->value = new_value; - if(new_value > 0) - aa[cur]->AA++; - else - aa[cur]->AA = 0; - return true; - } - else if(aa[cur]->AA==0){ //end of list - aa[cur]->AA = aa_id; - aa[cur]->value = new_value; - return true; - } - } - return false; -} - -SendAA_Struct* Zone::FindAA(uint32 id) { - return aas_send[id]; -} - -void Zone::LoadAAs() { - LogFile->write(EQEMuLog::Status, "Loading AA information..."); - totalAAs = database.CountAAs(); - if(totalAAs == 0) { - LogFile->write(EQEMuLog::Error, "Failed to load AAs!"); - aas = nullptr; - return; - } - aas = new SendAA_Struct *[totalAAs]; - - database.LoadAAs(aas); - - int i; - for(i=0; i < totalAAs; i++){ - SendAA_Struct* aa = aas[i]; - aas_send[aa->id] = aa; - } - - //load AA Effects into aa_effects - LogFile->write(EQEMuLog::Status, "Loading AA Effects..."); - if (database.LoadAAEffects2()) - LogFile->write(EQEMuLog::Status, "Loaded %d AA Effects.", aa_effects.size()); - else - LogFile->write(EQEMuLog::Error, "Failed to load AA Effects!"); -} - -bool ZoneDatabase::LoadAAEffects2() { - aa_effects.clear(); //start fresh - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT aaid, slot, effectid, base1, base2 FROM aa_effects ORDER BY aaid ASC, slot ASC"), errbuf, &result)) { - int count = 0; - while((row = mysql_fetch_row(result))!= nullptr) { - int aaid = atoi(row[0]); - int slot = atoi(row[1]); - int effectid = atoi(row[2]); - int base1 = atoi(row[3]); - int base2 = atoi(row[4]); - aa_effects[aaid][slot].skill_id = effectid; - aa_effects[aaid][slot].base1 = base1; - aa_effects[aaid][slot].base2 = base2; - aa_effects[aaid][slot].slot = slot; //not really needed, but we'll populate it just in case - count++; - } - mysql_free_result(result); - if (count < 1) //no results - LogFile->write(EQEMuLog::Error, "Error loading AA Effects, none found in the database."); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAEffects2 query: '%s': %s", query, errbuf); - return false; - } - safe_delete_array(query); - return true; -} -void Client::ResetAA(){ - uint32 i; - for(i=0;iAA = 0; - aa[i]->value = 0; - } - std::map::iterator itr; - for(itr=aa_points.begin();itr!=aa_points.end();++itr) - aa_points[itr->first] = 0; - - for(int i = 0; i < _maxLeaderAA; ++i) - m_pp.leader_abilities.ranks[i] = 0; - - m_pp.group_leadership_points = 0; - m_pp.raid_leadership_points = 0; - m_pp.group_leadership_exp = 0; - m_pp.raid_leadership_exp = 0; -} - -int Client::GroupLeadershipAAHealthEnhancement() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAHealthEnhancement)) - { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; - } - - return 0; -} - -int Client::GroupLeadershipAAManaEnhancement() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAManaEnhancement)) - { - case 0: - return 0; - case 1: - return 30; - case 2: - return 60; - case 3: - return 100; - } - - return 0; -} - -int Client::GroupLeadershipAAHealthRegeneration() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAHealthRegeneration)) - { - case 0: - return 0; - case 1: - return 4; - case 2: - return 6; - case 3: - return 8; - } - - return 0; -} - -int Client::GroupLeadershipAAOffenseEnhancement() -{ - Group *g = GetGroup(); - - if(!g || (g->GroupCount() < 3)) - return 0; - - switch(g->GetLeadershipAA(groupAAOffenseEnhancement)) - { - case 0: - return 0; - case 1: - return 10; - case 2: - return 19; - case 3: - return 28; - case 4: - return 34; - case 5: - return 40; - } - return 0; -} - -void Client::InspectBuffs(Client* Inspector, int Rank) -{ - if(!Inspector || (Rank == 0)) return; - - Inspector->Message_StringID(0, CURRENT_SPELL_EFFECTS, GetName()); - uint32 buff_count = GetMaxTotalSlots(); - for (uint32 i = 0; i < buff_count; ++i) - { - if (buffs[i].spellid != SPELL_UNKNOWN) - { - if(Rank == 1) - Inspector->Message(0, "%s", spells[buffs[i].spellid].name); - else - { - if (spells[buffs[i].spellid].buffdurationformula == DF_Permanent) - Inspector->Message(0, "%s (Permanent)", spells[buffs[i].spellid].name); - else { - char *TempString = nullptr; - MakeAnyLenString(&TempString, "%.1f", static_cast(buffs[i].ticsremaining) / 10.0f); - Inspector->Message_StringID(0, BUFF_MINUTES_REMAINING, spells[buffs[i].spellid].name, TempString); - safe_delete_array(TempString); - } - } - } - } -} - -//this really need to be renamed to LoadAAActions() -bool ZoneDatabase::LoadAAEffects() { - char errbuf[MYSQL_ERRMSG_SIZE]; - MYSQL_RES *result; - MYSQL_ROW row; - - memset(AA_Actions, 0, sizeof(AA_Actions)); //I hope the compiler is smart about this size... - - const char *query = "SELECT aaid,rank,reuse_time,spell_id,target,nonspell_action,nonspell_mana,nonspell_duration," - "redux_aa,redux_rate,redux_aa2,redux_rate2 FROM aa_actions"; - - if(RunQuery(query, static_cast(strlen(query)), errbuf, &result)) { - //safe_delete_array(query); - int r; - while ((row = mysql_fetch_row(result))) { - r = 0; - int aaid = atoi(row[r++]); - int rank = atoi(row[r++]); - if(aaid < 0 || aaid >= aaHighestID || rank < 0 || rank >= MAX_AA_ACTION_RANKS) - continue; - AA_DBAction *caction = &AA_Actions[aaid][rank]; - - caction->reuse_time = atoi(row[r++]); - caction->spell_id = atoi(row[r++]); - caction->target = (aaTargetType) atoi(row[r++]); - caction->action = (aaNonspellAction) atoi(row[r++]); - caction->mana_cost = atoi(row[r++]); - caction->duration = atoi(row[r++]); - caction->redux_aa = (aaID) atoi(row[r++]); - caction->redux_rate = atoi(row[r++]); - caction->redux_aa2 = (aaID) atoi(row[r++]); - caction->redux_rate2 = atoi(row[r++]); - - } - mysql_free_result(result); - } - else { - LogFile->write(EQEMuLog::Error, "Error in LoadAAEffects query '%s': %s", query, errbuf);; - //safe_delete_array(query); - return false; - } - - return true; -} - -//Returns the number effects an AA has when we send them to the client -//For the purposes of sizing a packet because every skill does not -//have the same number effects, they can range from none to a few depending on AA. -//counts the # of effects by counting the different slots of an AAID in the DB. - -//AndMetal: this may now be obsolete since we have Zone::GetTotalAALevels() -uint8 ZoneDatabase::GetTotalAALevels(uint32 skill_id) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int total=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(slot) from aa_effects where aaid=%i", skill_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - total=atoi(row[0]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetTotalAALevels '%s: %s", query, errbuf); - safe_delete_array(query); - } - return total; -} - -//this will allow us to count the number of effects for an AA by pulling the info from memory instead of the database. hopefully this will same some CPU cycles -uint8 Zone::GetTotalAALevels(uint32 skill_id) { - size_t sz = aa_effects[skill_id].size(); - return sz >= 255 ? 255 : static_cast(sz); -} - -/* -Every AA can send the client effects, which are purely for client side effects. -Essentially it's like being able to attach a very simple version of a spell to -Any given AA, it has 4 fields: -skill_id = spell effect id -slot = ID slot, doesn't appear to have any impact on stacking like real spells, just needs to be unique. -base1 = the base field of a spell -base2 = base field 2 of a spell, most AAs do not utilize this -example: - skill_id = SE_STA - slot = 1 - base1 = 15 - This would if you filled the abilities struct with this make the client show if it had - that AA an additional 15 stamina on the client's stats -*/ -void ZoneDatabase::FillAAEffects(SendAA_Struct* aa_struct){ - if(!aa_struct) - return; - - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT effectid, base1, base2, slot from aa_effects where aaid=%i order by slot asc", aa_struct->id), errbuf, &result)) { - int ndx=0; - while((row = mysql_fetch_row(result))!=nullptr) { - aa_struct->abilities[ndx].skill_id=atoi(row[0]); - aa_struct->abilities[ndx].base1=atoi(row[1]); - aa_struct->abilities[ndx].base2=atoi(row[2]); - aa_struct->abilities[ndx].slot=atoi(row[3]); - ndx++; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in Client::FillAAEffects query: '%s': %s", query, errbuf); - } - safe_delete_array(query); -} - -uint32 ZoneDatabase::CountAAs(){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(title_sid) from altadv_vars"), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr) - count = atoi(row[0]); - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAAs query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return count; -} - -uint32 ZoneDatabase::CountAAEffects(){ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - int count=0; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT count(id) from aa_effects"), errbuf, &result)) { - if((row = mysql_fetch_row(result))!=nullptr){ - count = atoi(row[0]); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::CountAALevels query '%s': %s", query, errbuf); - } - safe_delete_array(query); - return count; -} - -uint32 ZoneDatabase::GetSizeAA(){ - int size=CountAAs()*sizeof(SendAA_Struct); - if(size>0) - size+=CountAAEffects()*sizeof(AA_Ability); - return size; -} - -void ZoneDatabase::LoadAAs(SendAA_Struct **load){ - if(!load) - return; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT skill_id from altadv_vars order by skill_id"), errbuf, &result)) { - int skill=0,ndx=0; - while((row = mysql_fetch_row(result))!=nullptr) { - skill=atoi(row[0]); - load[ndx] = GetAASkillVars(skill); - load[ndx]->seq = ndx+1; - ndx++; - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query, errbuf); - } - safe_delete_array(query); - - AARequiredLevelAndCost.clear(); - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT skill_id, level, cost from aa_required_level_cost order by skill_id"), errbuf, &result)) - { - AALevelCost_Struct aalcs; - while((row = mysql_fetch_row(result))!=nullptr) - { - aalcs.Level = atoi(row[1]); - aalcs.Cost = atoi(row[2]); - AARequiredLevelAndCost[atoi(row[0])] = aalcs; - } - mysql_free_result(result); - } - else - LogFile->write(EQEMuLog::Error, "Error in ZoneDatabase::LoadAAs query '%s': %s", query, errbuf); - - safe_delete_array(query); -} - -SendAA_Struct* ZoneDatabase::GetAASkillVars(uint32 skill_id) -{ - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - SendAA_Struct* sendaa = nullptr; - uchar* buffer; - if (RunQuery(query, MakeAnyLenString(&query, "SET @row = 0"), errbuf)) { //initialize "row" variable in database for next query - safe_delete_array(query); - MYSQL_RES *result; //we don't really need these unless we get to this point, so why bother? - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, - "SELECT " - "a.cost, " - "a.max_level, " - "a.hotkey_sid, " - "a.hotkey_sid2, " - "a.title_sid, " - "a.desc_sid, " - "a.type, " - "COALESCE(" //so we can return 0 if it's null - "(" //this is our derived table that has the row # that we can SELECT from, because the client is stupid - "SELECT " - "p.prereq_index_num " - "FROM " - "(" - "SELECT " - "a2.skill_id, " - "@row := @row + 1 AS prereq_index_num " - "FROM " - "altadv_vars a2" - ") AS p " - "WHERE " - "p.skill_id = a.prereq_skill" - "), " - "0) AS prereq_skill_index, " - "a.prereq_minpoints, " - "a.spell_type, " - "a.spell_refresh, " - "a.classes, " - "a.berserker, " - "a.spellid, " - "a.class_type, " - "a.name, " - "a.cost_inc, " - "a.aa_expansion, " - "a.special_category, " - "a.sof_type, " - "a.sof_cost_inc, " - "a.sof_max_level, " - "a.sof_next_skill, " - "a.clientver, " // Client Version 0 = None, 1 = All, 2 = Titanium/6.2, 4 = SoF 5 = SOD 6 = UF - "a.account_time_required, " - "a.sof_current_level," - "a.sof_next_id, " - "a.level_inc, " - "a.seqlive " - " FROM altadv_vars a WHERE skill_id=%i", skill_id), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - int total_abilities = GetTotalAALevels(skill_id); //eventually we'll want to use zone->GetTotalAALevels(skill_id) since it should save queries to the DB - int totalsize = total_abilities * sizeof(AA_Ability) + sizeof(SendAA_Struct); - - buffer = new uchar[totalsize]; - memset(buffer,0,totalsize); - sendaa = (SendAA_Struct*)buffer; - - row = mysql_fetch_row(result); - - //ATOI IS NOT UNISGNED LONG-SAFE!!! - - sendaa->cost = atoul(row[0]); - sendaa->cost2 = sendaa->cost; - sendaa->max_level = atoul(row[1]); - sendaa->hotkey_sid = atoul(row[2]); - sendaa->id = skill_id; - sendaa->hotkey_sid2 = atoul(row[3]); - sendaa->title_sid = atoul(row[4]); - sendaa->desc_sid = atoul(row[5]); - sendaa->type = atoul(row[6]); - sendaa->prereq_skill = atoul(row[7]); - sendaa->prereq_minpoints = atoul(row[8]); - sendaa->spell_type = atoul(row[9]); - sendaa->spell_refresh = atoul(row[10]); - sendaa->classes = static_cast(atoul(row[11])); - sendaa->berserker = static_cast(atoul(row[12])); - sendaa->last_id = 0xFFFFFFFF; - sendaa->current_level=1; - sendaa->spellid = atoul(row[13]); - sendaa->class_type = atoul(row[14]); - strcpy(sendaa->name,row[15]); - - sendaa->total_abilities=total_abilities; - if(sendaa->max_level > 1) - sendaa->next_id=skill_id+1; - else - sendaa->next_id=0xFFFFFFFF; - - sendaa->cost_inc = atoi(row[16]); - // Begin SoF Specific/Adjusted AA Fields - sendaa->aa_expansion = atoul(row[17]); - sendaa->special_category = atoul(row[18]); - sendaa->sof_type = atoul(row[19]); - sendaa->sof_cost_inc = atoi(row[20]); - sendaa->sof_max_level = atoul(row[21]); - sendaa->sof_next_skill = atoul(row[22]); - sendaa->clientver = atoul(row[23]); - sendaa->account_time_required = atoul(row[24]); - //Internal use only - not sent to client - sendaa->sof_current_level = atoul(row[25]); - sendaa->sof_next_id = atoul(row[26]); - sendaa->level_inc = static_cast(atoul(row[27])); - sendaa->seqlive = (atoul(row[28])); - } - mysql_free_result(result); - } else { - LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query, errbuf); - safe_delete_array(query); - } - } else { - LogFile->write(EQEMuLog::Error, "Error in GetAASkillVars '%s': %s", query, errbuf); - safe_delete_array(query); - } - return sendaa; -} - -void Client::DurationRampage(uint32 duration) -{ - if(duration) { - m_epp.aa_effects |= 1 << (aaEffectRampage-1); - p_timers.Start(pTimerAAEffectStart + aaEffectRampage, duration); - } -} - -AA_SwarmPetInfo::AA_SwarmPetInfo() -{ - target = 0; - owner_id = 0; - duration = nullptr; -} - -AA_SwarmPetInfo::~AA_SwarmPetInfo() -{ - target = 0; - owner_id = 0; - safe_delete(duration); -} - -Mob *AA_SwarmPetInfo::GetOwner() -{ - return entity_list.GetMobID(owner_id); -} diff --git a/zone/MobAI_M.cpp b/zone/MobAI_M.cpp deleted file mode 100644 index 7574f19ad..000000000 --- a/zone/MobAI_M.cpp +++ /dev/null @@ -1,2760 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 EQEMu Development Team (http://eqemu.org) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include "../common/debug.h" -#include -#include -#include -#include -#include -#include "npc.h" -#include "masterentity.h" -#include "NpcAI.h" -#include "map.h" -#include "../common/moremath.h" -#include "StringIDs.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" -#include "../common/rulesys.h" -#include "../common/features.h" -#include "QuestParserCollection.h" -#include "watermap.h" - -extern EntityList entity_list; - -extern Zone *zone; - -#ifdef _EQDEBUG - #define MobAI_DEBUG_Spells -1 -#else - #define MobAI_DEBUG_Spells -1 -#endif -#define ABS(x) ((x)<0?-(x):(x)) - -//NOTE: do NOT pass in beneficial and detrimental spell types into the same call here! -bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) { - if (!tar) - return false; - - if (IsNoCast()) - return false; - - if(AI_HasSpells() == false) - return false; - - if (iChance < 100) { - if (MakeRandomInt(0, 100) >= iChance) - return false; - } - - float dist2; - - if (iSpellTypes & SpellType_Escape) { - dist2 = 0; //DistNoRoot(*this); //WTF was up with this... - } - else - dist2 = DistNoRoot(*tar); - - bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once. - - float manaR = GetManaRatio(); - for (int i = static_cast(AIspells.size()) - 1; i >= 0; i--) { - if (AIspells[i].spellid <= 0 || AIspells[i].spellid >= SPDAT_RECORDS) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - //return false; - continue; - } - if (iSpellTypes & AIspells[i].type) { - // manacost has special values, -1 is no mana cost, -2 is instant cast (no mana) - int32 mana_cost = AIspells[i].manacost; - if (mana_cost == -1) - mana_cost = spells[AIspells[i].spellid].mana; - else if (mana_cost == -2) - mana_cost = 0; - if ( - (( - (spells[AIspells[i].spellid].targettype==ST_AECaster || spells[AIspells[i].spellid].targettype==ST_AEBard) - && dist2 <= spells[AIspells[i].spellid].aoerange*spells[AIspells[i].spellid].aoerange - ) || - dist2 <= spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range - ) - && (mana_cost <= GetMana() || GetMana() == GetMaxMana()) - && (AIspells[i].time_cancast + (MakeRandomInt(0, 4) * 1000)) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time. - ) { - -#if MobAI_DEBUG_Spells >= 21 - std::cout << "Mob::AICastSpell: Casting: spellid=" << AIspells[i].spellid - << ", tar=" << tar->GetName() - << ", dist2[" << dist2 << "]<=" << spells[AIspells[i].spellid].range *spells[AIspells[i].spellid].range - << ", mana_cost[" << mana_cost << "]<=" << GetMana() - << ", cancast[" << AIspells[i].time_cancast << "]<=" << Timer::GetCurrentTime() - << ", type=" << AIspells[i].type << std::endl; -#endif - - switch (AIspells[i].type) { - case SpellType_Heal: { - if ( - (spells[AIspells[i].spellid].targettype == ST_Target || tar == this) - && tar->DontHealMeBefore() < Timer::GetCurrentTime() - && !(tar->IsPet() && tar->GetOwner()->IsClient()) //no buffing PC's pets - ) { - uint8 hpr = (uint8)tar->GetHPRatio(); - - if(hpr <= 35 || (!IsEngaged() && hpr <= 50) || (tar->IsClient() && hpr <= 99)) { - uint32 tempTime = 0; - AIDoSpellCast(i, tar, mana_cost, &tempTime); - tar->SetDontHealMeBefore(tempTime); - return true; - } - } - break; - } - case SpellType_Root: { - Mob *rootee = GetHateRandom(); - if (rootee && !rootee->IsRooted() && MakeRandomInt(0, 99) < 50 - && rootee->DontRootMeBefore() < Timer::GetCurrentTime() - && rootee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 - ) { - if(!checked_los) { - if(!CheckLosFN(rootee)) - return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call - checked_los = true; - } - uint32 tempTime = 0; - AIDoSpellCast(i, rootee, mana_cost, &tempTime); - rootee->SetDontRootMeBefore(tempTime); - return true; - } - break; - } - case SpellType_Buff: { - if ( - (spells[AIspells[i].spellid].targettype == ST_Target || tar == this) - && tar->DontBuffMeBefore() < Timer::GetCurrentTime() - && !tar->IsImmuneToSpell(AIspells[i].spellid, this) - && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 - && !(tar->IsPet() && tar->GetOwner()->IsClient() && this != tar) //no buffing PC's pets, but they can buff themself - ) - { - if(!checked_los) { - if(!CheckLosFN(tar)) - return(false); - checked_los = true; - } - uint32 tempTime = 0; - AIDoSpellCast(i, tar, mana_cost, &tempTime); - tar->SetDontBuffMeBefore(tempTime); - return true; - } - break; - } - - case SpellType_InCombatBuff: { - if(MakeRandomInt(0, 99) < 50) - { - AIDoSpellCast(i, tar, mana_cost); - return true; - } - break; - } - - case SpellType_Escape: { - if (GetHPRatio() <= 5 ) - { - AIDoSpellCast(i, tar, mana_cost); - return true; - } - break; - } - case SpellType_Slow: - case SpellType_Debuff: { - Mob * debuffee = GetHateRandom(); - if (debuffee && manaR >= 10 && MakeRandomInt(0, 99 < 70) && - debuffee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0) { - if (!checked_los) { - if (!CheckLosFN(debuffee)) - return false; - checked_los = true; - } - AIDoSpellCast(i, debuffee, mana_cost); - return true; - } - break; - } - case SpellType_Nuke: { - if ( - manaR >= 10 && MakeRandomInt(0, 99) < 70 - && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 - ) { - if(!checked_los) { - if(!CheckLosFN(tar)) - return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call - checked_los = true; - } - AIDoSpellCast(i, tar, mana_cost); - return true; - } - break; - } - case SpellType_Dispel: { - if(MakeRandomInt(0, 99) < 15) - { - if(!checked_los) { - if(!CheckLosFN(tar)) - return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call - checked_los = true; - } - if(tar->CountDispellableBuffs() > 0) - { - AIDoSpellCast(i, tar, mana_cost); - return true; - } - } - break; - } - case SpellType_Mez: { - if(MakeRandomInt(0, 99) < 20) - { - Mob * mezTar = nullptr; - mezTar = entity_list.GetTargetForMez(this); - - if(mezTar && mezTar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0) - { - AIDoSpellCast(i, mezTar, mana_cost); - return true; - } - } - break; - } - - case SpellType_Charm: - { - if(!IsPet() && MakeRandomInt(0, 99) < 20) - { - Mob * chrmTar = GetHateRandom(); - if(chrmTar && chrmTar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0) - { - AIDoSpellCast(i, chrmTar, mana_cost); - return true; - } - } - break; - } - - case SpellType_Pet: { - //keep mobs from recasting pets when they have them. - if (!IsPet() && !GetPetID() && MakeRandomInt(0, 99) < 25) { - AIDoSpellCast(i, tar, mana_cost); - return true; - } - break; - } - case SpellType_Lifetap: { - if (GetHPRatio() <= 95 - && MakeRandomInt(0, 99) < 50 - && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 - ) { - if(!checked_los) { - if(!CheckLosFN(tar)) - return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call - checked_los = true; - } - AIDoSpellCast(i, tar, mana_cost); - return true; - } - break; - } - case SpellType_Snare: { - if ( - !tar->IsRooted() - && MakeRandomInt(0, 99) < 50 - && tar->DontSnareMeBefore() < Timer::GetCurrentTime() - && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 - ) { - if(!checked_los) { - if(!CheckLosFN(tar)) - return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call - checked_los = true; - } - uint32 tempTime = 0; - AIDoSpellCast(i, tar, mana_cost, &tempTime); - tar->SetDontSnareMeBefore(tempTime); - return true; - } - break; - } - case SpellType_DOT: { - if ( - MakeRandomInt(0, 99) < 60 - && tar->DontDotMeBefore() < Timer::GetCurrentTime() - && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 - ) { - if(!checked_los) { - if(!CheckLosFN(tar)) - return(false); //cannot see target... we assume that no spell is going to work since we will only be casting detrimental spells in this call - checked_los = true; - } - uint32 tempTime = 0; - AIDoSpellCast(i, tar, mana_cost, &tempTime); - tar->SetDontDotMeBefore(tempTime); - return true; - } - break; - } - default: { - std::cout << "Error: Unknown spell type in AICastSpell. caster:" << this->GetName() << " type:" << AIspells[i].type << " slot:" << i << std::endl; - break; - } - } - } -#if MobAI_DEBUG_Spells >= 21 - else { - std::cout << "Mob::AICastSpell: NotCasting: spellid=" << AIspells[i].spellid << ", tar=" << tar->GetName() << ", dist2[" << dist2 << "]<=" << spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range << ", mana_cost[" << mana_cost << "]<=" << GetMana() << ", cancast[" << AIspells[i].time_cancast << "]<=" << Timer::GetCurrentTime() << std::endl; - } -#endif - } - } - return false; -} - -bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) { -#if MobAI_DEBUG_Spells >= 1 - std::cout << "Mob::AIDoSpellCast: spellid=" << AIspells[i].spellid << ", tar=" << tar->GetName() << ", mana=" << mana_cost << ", Name: " << spells[AIspells[i].spellid].name << std::endl; -#endif - casting_spell_AIindex = i; - - //stop moving if were casting a spell and were not a bard... - if(!IsBardSong(AIspells[i].spellid)) { - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - } - - return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0, &(AIspells[i].resist_adjust)); -} - -bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes) { - if((iSpellTypes&SpellTypes_Detrimental) != 0) { - //according to live, you can buff and heal through walls... - //now with PCs, this only applies if you can TARGET the target, but - // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. - // - // This check was put in to address an idle-mob CPU issue - _log(AI__ERROR, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!"); - return(false); - } - - if(!caster) - return false; - - if(caster->AI_HasSpells() == false) - return false; - - if(caster->GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS)) - return false; - - if (iChance < 100) { - uint8 tmp = MakeRandomInt(0, 99); - if (tmp >= iChance) - return false; - } - if (caster->GetPrimaryFaction() == 0 ) - return(false); // well, if we dont have a faction set, we're gonna be indiff to everybody - - float iRange2 = iRange*iRange; - - float t1, t2, t3; - - - //Only iterate through NPCs - for (auto it = npc_list.begin(); it != npc_list.end(); ++it) { - NPC* mob = it->second; - - //Since >90% of mobs will always be out of range, try to - //catch them with simple bounding box checks first. These - //checks are about 6X faster than DistNoRoot on my athlon 1Ghz - t1 = mob->GetX() - caster->GetX(); - t2 = mob->GetY() - caster->GetY(); - t3 = mob->GetZ() - caster->GetZ(); - //cheap ABS() - if(t1 < 0) - t1 = 0 - t1; - if(t2 < 0) - t2 = 0 - t2; - if(t3 < 0) - t3 = 0 - t3; - if (t1 > iRange - || t2 > iRange - || t3 > iRange - || mob->DistNoRoot(*caster) > iRange2 - //this call should seem backwards: - || !mob->CheckLosFN(caster) - || mob->GetReverseFactionCon(caster) >= FACTION_KINDLY - ) { - continue; - } - - //since we assume these are beneficial spells, which do not - //require LOS, we just go for it. - // we have a winner! - if((iSpellTypes & SpellType_Buff) && !RuleB(NPC, BuffFriends)){ - if (mob != caster) - iSpellTypes = SpellType_Heal; - } - - if (caster->AICastSpell(mob, 100, iSpellTypes)) - return true; - } - return false; -} - -void Mob::AI_Init() { - pAIControlled = false; - AIthink_timer = 0; - AIwalking_timer = 0; - AImovement_timer = 0; - AItarget_check_timer = 0; - AIfeignremember_timer = nullptr; - AIscanarea_timer = 0; - minLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMin); - maxLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMax); - - pDontHealMeBefore = 0; - pDontBuffMeBefore = 0; - pDontDotMeBefore = 0; - pDontRootMeBefore = 0; - pDontSnareMeBefore = 0; - pDontCureMeBefore = 0; -} - -void NPC::AI_Init() { - Mob::AI_Init(); - - AIautocastspell_timer = 0; - casting_spell_AIindex = static_cast(AIspells.size()); - - roambox_max_x = 0; - roambox_max_y = 0; - roambox_min_x = 0; - roambox_min_y = 0; - roambox_distance = 0; - roambox_movingto_x = 0; - roambox_movingto_y = 0; - roambox_min_delay = 2500; - roambox_delay = 2500; -} - -void Client::AI_Init() { - Mob::AI_Init(); - minLastFightingDelayMoving = CLIENT_LD_TIMEOUT; - maxLastFightingDelayMoving = CLIENT_LD_TIMEOUT; -} - -void Mob::AI_Start(uint32 iMoveDelay) { - if (pAIControlled) - return; - - if (iMoveDelay) - pLastFightingDelayMoving = Timer::GetCurrentTime() + iMoveDelay; - else - pLastFightingDelayMoving = 0; - - pAIControlled = true; - AIthink_timer = new Timer(AIthink_duration); - AIthink_timer->Trigger(); - AIwalking_timer = new Timer(0); - AImovement_timer = new Timer(AImovement_duration); - AItarget_check_timer = new Timer(AItarget_check_duration); - AIfeignremember_timer = new Timer(AIfeignremember_delay); - AIscanarea_timer = new Timer(AIscanarea_delay); -#ifdef REVERSE_AGGRO - if(IsNPC() && !CastToNPC()->WillAggroNPCs()) - AIscanarea_timer->Disable(); -#endif - - if (GetAggroRange() == 0) - pAggroRange = 70; - if (GetAssistRange() == 0) - pAssistRange = 70; - hate_list.Wipe(); - - delta_heading = 0; - delta_x = 0; - delta_y = 0; - delta_z = 0; - pRunAnimSpeed = 0; - pLastChange = Timer::GetCurrentTime(); -} - -void Client::AI_Start(uint32 iMoveDelay) { - Mob::AI_Start(iMoveDelay); - - if (!pAIControlled) - return; - - pClientSideTarget = GetTarget() ? GetTarget()->GetID() : 0; - SendAppearancePacket(AT_Anim, ANIM_FREEZE); // this freezes the client - SendAppearancePacket(AT_Linkdead, 1); // Sending LD packet so *LD* appears by the player name when charmed/feared -Kasai - SetAttackTimer(); - SetFeigned(false); -} - -void NPC::AI_Start(uint32 iMoveDelay) { - Mob::AI_Start(iMoveDelay); - if (!pAIControlled) - return; - - if (AIspells.size() == 0) { - AIautocastspell_timer = new Timer(1000); - AIautocastspell_timer->Disable(); - } else { - AIautocastspell_timer = new Timer(750); - AIautocastspell_timer->Start(RandomTimer(0, 15000), false); - } - - if (NPCTypedata) { - AI_AddNPCSpells(NPCTypedata->npc_spells_id); - ProcessSpecialAbilities(NPCTypedata->special_abilities); - AI_AddNPCSpellsEffects(NPCTypedata->npc_spells_effects_id); - } - - SendTo(GetX(), GetY(), GetZ()); - SetChanged(); - SaveGuardSpot(); -} - -void Mob::AI_Stop() { - if (!IsAIControlled()) - return; - pAIControlled = false; - safe_delete(AIthink_timer); - safe_delete(AIwalking_timer); - safe_delete(AImovement_timer); - safe_delete(AItarget_check_timer) - safe_delete(AIscanarea_timer); - safe_delete(AIfeignremember_timer); - hate_list.Wipe(); -} - -void NPC::AI_Stop() { - Waypoints.clear(); - safe_delete(AIautocastspell_timer); -} - -void Client::AI_Stop() { - Mob::AI_Stop(); - this->Message_StringID(13,PLAYER_REGAIN); - - EQApplicationPacket *app = new EQApplicationPacket(OP_Charm, sizeof(Charm_Struct)); - Charm_Struct *ps = (Charm_Struct*)app->pBuffer; - ps->owner_id = 0; - ps->pet_id = this->GetID(); - ps->command = 0; - entity_list.QueueClients(this, app); - safe_delete(app); - - SetTarget(entity_list.GetMob(pClientSideTarget)); - SendAppearancePacket(AT_Anim, GetAppearanceValue(GetAppearance())); - SendAppearancePacket(AT_Linkdead, 0); // Removing LD packet so *LD* no longer appears by the player name when charmed/feared -Kasai - if (!auto_attack) { - attack_timer.Disable(); - attack_dw_timer.Disable(); - } - if (IsLD()) - { - Save(); - Disconnect(); - } -} - -//todo: expand the logic here to cover: -//redundant debuffs -//buffing owner -//certain types of det spells that need special behavior. -void Client::AI_SpellCast() -{ - if(!charm_cast_timer.Check()) - return; - - Mob *targ = GetTarget(); - if(!targ) - return; - - float dist = DistNoRootNoZ(*targ); - - std::vector valid_spells; - std::vector slots; - - for(uint32 x = 0; x < 9; ++x) - { - uint32 current_spell = m_pp.mem_spells[x]; - if(!IsValidSpell(current_spell)) - continue; - - if(IsBeneficialSpell(current_spell)) - { - continue; - } - - if(dist > spells[current_spell].range*spells[current_spell].range) - { - continue; - } - - if(GetMana() < spells[current_spell].mana) - { - continue; - } - - if(IsEffectInSpell(current_spell, SE_Charm)) - { - continue; - } - - if(!GetPTimers().Expired(&database, pTimerSpellStart + current_spell, false)) - { - continue; - } - - if(targ->CanBuffStack(current_spell, GetLevel(), true) < 0) - { - continue; - } - - //bard songs cause trouble atm - if(IsBardSong(current_spell)) - continue; - - valid_spells.push_back(current_spell); - slots.push_back(x); - } - - uint32 spell_to_cast = 0xFFFFFFFF; - uint32 slot_to_use = 10; - if(valid_spells.size() == 1) - { - spell_to_cast = valid_spells[0]; - slot_to_use = slots[0]; - } - else if(valid_spells.size() == 0) - { - return; - } - else - { - uint32 idx = MakeRandomInt(0, (valid_spells.size()-1)); - spell_to_cast = valid_spells[idx]; - slot_to_use = slots[idx]; - } - - if(IsMezSpell(spell_to_cast) || IsFearSpell(spell_to_cast)) - { - Mob *tar = entity_list.GetTargetForMez(this); - if(!tar) - { - tar = GetTarget(); - if(tar && IsFearSpell(spell_to_cast)) - { - if(!IsBardSong(spell_to_cast)) - { - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - } - CastSpell(spell_to_cast, tar->GetID(), slot_to_use); - return; - } - } - } - else - { - Mob *tar = GetTarget(); - if(tar) - { - if(!IsBardSong(spell_to_cast)) - { - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - } - CastSpell(spell_to_cast, tar->GetID(), slot_to_use); - return; - } - } - - -} - -void Client::AI_Process() -{ - if (!IsAIControlled()) - return; - - if (!(AIthink_timer->Check() || attack_timer.Check(false))) - return; - - if (IsCasting()) - return; - - bool engaged = IsEngaged(); - - Mob *ow = GetOwner(); - if(!engaged) - { - if(ow) - { - if(ow->IsEngaged()) - { - Mob *tar = ow->GetTarget(); - if(tar) - { - AddToHateList(tar, 1, 0, false); - } - } - } - } - - if(!ow) - { - if(!IsFeared() && !IsLD()) - { - BuffFadeByEffect(SE_Charm); - return; - } - } - - if(RuleB(Combat, EnableFearPathing)){ - if(curfp) { - if(IsRooted()) { - //make sure everybody knows were not moving, for appearance sake - if(IsMoving()) - { - if(GetTarget()) - SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - moved=false; - } - //continue on to attack code, ensuring that we execute the engaged code - engaged = true; - } else { - if(AImovement_timer->Check()) { - animation = GetRunspeed() * 21; - // Check if we have reached the last fear point - if((ABS(GetX()-fear_walkto_x) < 0.1) && (ABS(GetY()-fear_walkto_y) <0.1)) { - // Calculate a new point to run to - CalculateNewFearpoint(); - } - if(!RuleB(Pathing, Fear) || !zone->pathing) - CalculateNewPosition2(fear_walkto_x, fear_walkto_y, fear_walkto_z, GetFearSpeed(), true); - else - { - bool WaypointChanged, NodeReached; - - VERTEX Goal = UpdatePath(fear_walkto_x, fear_walkto_y, fear_walkto_z, - GetFearSpeed(), WaypointChanged, NodeReached); - - if(WaypointChanged) - tar_ndx = 20; - - CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetFearSpeed()); - } - } - return; - } - } - } - - if (engaged) - { - if (IsRooted()) - SetTarget(hate_list.GetClosest(this)); - else - { - if(AItarget_check_timer->Check()) - { - SetTarget(hate_list.GetTop(this)); - } - } - - if (!GetTarget()) - return; - - if (GetTarget()->IsCorpse()) { - RemoveFromHateList(this); - return; - } - - if(DivineAura()) - return; - - bool is_combat_range = CombatRange(GetTarget()); - - if(is_combat_range) { - if(charm_class_attacks_timer.Check()) { - DoClassAttacks(GetTarget()); - } - - if (AImovement_timer->Check()) { - SetRunAnimSpeed(0); - } - if(IsMoving()) { - SetMoving(false); - moved=false; - SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SendPosition(); - tar_ndx =0; - } - - if(GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) { - if(attack_timer.Check()) { - Attack(GetTarget(), 13); - if(GetTarget()) { - if(CheckDoubleAttack()) { - Attack(GetTarget(), 13); - if(GetTarget()) { - bool triple_attack_success = false; - if((((GetClass() == MONK || GetClass() == WARRIOR || GetClass() == RANGER || GetClass() == BERSERKER) - && GetLevel() >= 60) || GetSpecialAbility(SPECATK_TRIPLE)) - && CheckDoubleAttack(true)) - { - Attack(GetTarget(), 13, true); - triple_attack_success = true; - } - - if(GetTarget()) - { - //Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). - int16 flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; - - if (flurrychance) - { - if(MakeRandomInt(0, 100) < flurrychance) - { - Message_StringID(MT_NPCFlurry, YOU_FLURRY); - Attack(GetTarget(), 13, false); - Attack(GetTarget(), 13, false); - } - } - - int16 ExtraAttackChanceBonus = spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance + aabonuses.ExtraAttackChance; - - if (ExtraAttackChanceBonus && GetTarget()) { - ItemInst *wpn = GetInv().GetItem(SLOT_PRIMARY); - if(wpn){ - if(wpn->GetItem()->ItemType == ItemType2HSlash || - wpn->GetItem()->ItemType == ItemType2HBlunt || - wpn->GetItem()->ItemType == ItemType2HPiercing ) - { - if(MakeRandomInt(0, 100) < ExtraAttackChanceBonus) - { - Attack(GetTarget(), 13, false); - } - } - } - } - - if (GetClass() == WARRIOR || GetClass() == BERSERKER) - { - if(!dead && !berserk && this->GetHPRatio() < 30) - { - entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_START, GetName()); - berserk = true; - } - else if (berserk && this->GetHPRatio() > 30) - { - entity_list.MessageClose_StringID(this, false, 200, 0, BERSERK_END, GetName()); - berserk = false; - } - } - } - } - } - } - } - } - - if(CanThisClassDualWield() && attack_dw_timer.Check()) - { - if(GetTarget()) - { - float DualWieldProbability = 0.0f; - - int16 Ambidexterity = aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity; - DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f; // 78.0 max - int16 DWBonus = spellbonuses.DualWieldChance + itembonuses.DualWieldChance; - DualWieldProbability += DualWieldProbability*float(DWBonus)/ 100.0f; - - if(MakeRandomFloat(0.0, 1.0) < DualWieldProbability) - { - Attack(GetTarget(), 14); - if(CheckDoubleAttack()) - { - Attack(GetTarget(), 14); - } - - } - } - } - } - else - { - if(!IsRooted()) - { - animation = 21 * GetRunspeed(); - if(!RuleB(Pathing, Aggro) || !zone->pathing) - CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed()); - else - { - bool WaypointChanged, NodeReached; - VERTEX Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), - GetRunspeed(), WaypointChanged, NodeReached); - - if(WaypointChanged) - tar_ndx = 20; - - CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); - } - } - else if(IsMoving()) - { - SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - moved=false; - } - } - AI_SpellCast(); - } - else - { - if(AIfeignremember_timer->Check()) { - std::set::iterator RememberedCharID; - RememberedCharID = feign_memory_list.begin(); - while (RememberedCharID != feign_memory_list.end()) { - Client* remember_client = entity_list.GetClientByCharID(*RememberedCharID); - if (remember_client == nullptr) { - //they are gone now... - RememberedCharID = feign_memory_list.erase(RememberedCharID); - } else if (!remember_client->GetFeigned()) { - AddToHateList(remember_client->CastToMob(),1); - RememberedCharID = feign_memory_list.erase(RememberedCharID); - break; - } else { - //they are still feigned, carry on... - ++RememberedCharID; - } - } - } - - if(IsPet()) - { - Mob* owner = GetOwner(); - if(owner == nullptr) - return; - - float dist = DistNoRoot(*owner); - if (dist >= 100) - { - float speed = dist >= 225 ? GetRunspeed() : GetWalkspeed(); - animation = 21 * speed; - CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed); - } - else - { - SetHeading(owner->GetHeading()); - if(moved) - { - moved=false; - SetMoving(false); - SendPosition(); - SetRunAnimSpeed(0); - } - } - } - } -} - -void Mob::AI_Process() { - if (!IsAIControlled()) - return; - - if (!(AIthink_timer->Check() || attack_timer.Check(false))) - return; - - if (IsCasting()) - return; - - bool engaged = IsEngaged(); - bool doranged = false; - - // Begin: Additions for Wiz Fear Code - // - if(RuleB(Combat, EnableFearPathing)){ - if(curfp) { - if(IsRooted()) { - //make sure everybody knows were not moving, for appearance sake - if(IsMoving()) - { - if(target) - SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - moved=false; - } - //continue on to attack code, ensuring that we execute the engaged code - engaged = true; - } else { - if(AImovement_timer->Check()) { - // Check if we have reached the last fear point - if((ABS(GetX()-fear_walkto_x) < 0.1) && (ABS(GetY()-fear_walkto_y) <0.1)) { - // Calculate a new point to run to - CalculateNewFearpoint(); - } - if(!RuleB(Pathing, Fear) || !zone->pathing) - CalculateNewPosition2(fear_walkto_x, fear_walkto_y, fear_walkto_z, GetFearSpeed(), true); - else - { - bool WaypointChanged, NodeReached; - - VERTEX Goal = UpdatePath(fear_walkto_x, fear_walkto_y, fear_walkto_z, - GetFearSpeed(), WaypointChanged, NodeReached); - - if(WaypointChanged) - tar_ndx = 20; - - CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetFearSpeed()); - } - } - return; - } - } - } - - // trigger EVENT_SIGNAL if required - if(IsNPC()) { - CastToNPC()->CheckSignal(); - } - - if (engaged) - { - if (IsRooted()) - SetTarget(hate_list.GetClosest(this)); - else - { - if(AItarget_check_timer->Check()) - { - if (IsFocused()) { - if (!target) { - SetTarget(hate_list.GetTop(this)); - } - } else { - if (!ImprovedTaunt()) - SetTarget(hate_list.GetTop(this)); - } - - } - } - - if (!target) - return; - - if (target->IsCorpse()) - { - RemoveFromHateList(this); - return; - } - -#ifdef BOTS - if (IsPet() && GetOwner()->IsBot() && target == GetOwner()) - { - // this blocks all pet attacks against owner..bot pet test (copied above check) - RemoveFromHateList(this); - return; - } -#endif //BOTS - - if(DivineAura()) - return; - - if(GetSpecialAbility(TETHER)) { - float tether_range = static_cast(GetSpecialAbilityParam(TETHER, 0)); - tether_range = tether_range > 0.0f ? tether_range * tether_range : pAggroRange * pAggroRange; - - if(DistNoRootNoZ(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY()) > tether_range) { - GMMove(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY(), CastToNPC()->GetSpawnPointZ(), CastToNPC()->GetSpawnPointH()); - } - } else if(GetSpecialAbility(LEASH)) { - float leash_range = static_cast(GetSpecialAbilityParam(LEASH, 0)); - leash_range = leash_range > 0.0f ? leash_range * leash_range : pAggroRange * pAggroRange; - - if(DistNoRootNoZ(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY()) > leash_range) { - GMMove(CastToNPC()->GetSpawnPointX(), CastToNPC()->GetSpawnPointY(), CastToNPC()->GetSpawnPointZ(), CastToNPC()->GetSpawnPointH()); - SetHP(GetMaxHP()); - BuffFadeAll(); - WipeHateList(); - return; - } - } - - StartEnrage(); - - bool is_combat_range = CombatRange(target); - - if (is_combat_range) - { - if (AImovement_timer->Check()) - { - SetRunAnimSpeed(0); - } - if(IsMoving()) - { - SetMoving(false); - moved=false; - SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); - SendPosition(); - tar_ndx =0; - } - - //casting checked above... - if(target && !IsStunned() && !IsMezzed() && GetAppearance() != eaDead && !IsMeleeDisabled()) { - - //we should check to see if they die mid-attacks, previous - //crap of checking target for null was not gunna cut it - - //try main hand first - if(attack_timer.Check()) { - if(IsNPC()) { - int16 n_atk = CastToNPC()->GetNumberOfAttacks(); - if(n_atk <= 1) { - Attack(target, 13); - } else { - for(int i = 0; i < n_atk; ++i) { - Attack(target, 13); - } - } - } else { - Attack(target, 13); - } - - if (target) { - //we use this random value in three comparisons with different - //thresholds, and if its truely random, then this should work - //out reasonably and will save us compute resources. - int32 RandRoll = MakeRandomInt(0, 99); - if ((CanThisClassDoubleAttack() || GetSpecialAbility(SPECATK_TRIPLE) - || GetSpecialAbility(SPECATK_QUAD)) - //check double attack, this is NOT the same rules that clients use... - && RandRoll < (GetLevel() + NPCDualAttackModifier)) { - Attack(target, 13); - // lets see if we can do a triple attack with the main hand - //pets are excluded from triple and quads... - if ((GetSpecialAbility(SPECATK_TRIPLE) || GetSpecialAbility(SPECATK_QUAD)) - && !IsPet() && RandRoll < (GetLevel() + NPCTripleAttackModifier)) { - Attack(target, 13); - // now lets check the quad attack - if (GetSpecialAbility(SPECATK_QUAD) - && RandRoll < (GetLevel() + NPCQuadAttackModifier)) { - Attack(target, 13); - } - } - } - } - - if (GetSpecialAbility(SPECATK_FLURRY)) { - int flurry_chance = GetSpecialAbilityParam(SPECATK_FLURRY, 0); - flurry_chance = flurry_chance > 0 ? flurry_chance : RuleI(Combat, NPCFlurryChance); - - if (MakeRandomInt(0, 99) < flurry_chance) { - ExtraAttackOptions opts; - int cur = GetSpecialAbilityParam(SPECATK_FLURRY, 2); - if (cur > 0) - opts.damage_percent = cur / 100.0f; - - cur = GetSpecialAbilityParam(SPECATK_FLURRY, 3); - if (cur > 0) - opts.damage_flat = cur; - - cur = GetSpecialAbilityParam(SPECATK_FLURRY, 4); - if (cur > 0) - opts.armor_pen_percent = cur / 100.0f; - - cur = GetSpecialAbilityParam(SPECATK_FLURRY, 5); - if (cur > 0) - opts.armor_pen_flat = cur; - - cur = GetSpecialAbilityParam(SPECATK_FLURRY, 6); - if (cur > 0) - opts.crit_percent = cur / 100.0f; - - cur = GetSpecialAbilityParam(SPECATK_FLURRY, 7); - if (cur > 0) - opts.crit_flat = cur; - - Flurry(&opts); - } - } - - if (IsPet()) { - Mob *owner = GetOwner(); - if (owner) { - int16 flurry_chance = owner->aabonuses.PetFlurry + - owner->spellbonuses.PetFlurry + owner->itembonuses.PetFlurry; - if (flurry_chance && (MakeRandomInt(0, 99) < flurry_chance)) - Flurry(nullptr); - } - } - - if (GetSpecialAbility(SPECATK_RAMPAGE)) - { - int rampage_chance = GetSpecialAbilityParam(SPECATK_RAMPAGE, 0); - rampage_chance = rampage_chance > 0 ? rampage_chance : 20; - if(MakeRandomInt(0, 99) < rampage_chance) { - ExtraAttackOptions opts; - int cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 2); - if(cur > 0) { - opts.damage_percent = cur / 100.0f; - } - - cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 3); - if(cur > 0) { - opts.damage_flat = cur; - } - - cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 4); - if(cur > 0) { - opts.armor_pen_percent = cur / 100.0f; - } - - cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 5); - if(cur > 0) { - opts.armor_pen_flat = cur; - } - - cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 6); - if(cur > 0) { - opts.crit_percent = cur / 100.0f; - } - - cur = GetSpecialAbilityParam(SPECATK_RAMPAGE, 7); - if(cur > 0) { - opts.crit_flat = cur; - } - Rampage(&opts); - } - } - - if (GetSpecialAbility(SPECATK_AREA_RAMPAGE)) - { - int rampage_chance = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 0); - rampage_chance = rampage_chance > 0 ? rampage_chance : 20; - if(MakeRandomInt(0, 99) < rampage_chance) { - ExtraAttackOptions opts; - int cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 2); - if(cur > 0) { - opts.damage_percent = cur / 100.0f; - } - - cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 3); - if(cur > 0) { - opts.damage_flat = cur; - } - - cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 4); - if(cur > 0) { - opts.armor_pen_percent = cur / 100.0f; - } - - cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 5); - if(cur > 0) { - opts.armor_pen_flat = cur; - } - - cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 6); - if(cur > 0) { - opts.crit_percent = cur / 100.0f; - } - - cur = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 7); - if(cur > 0) { - opts.crit_flat = cur; - } - - AreaRampage(&opts); - } - } - } - - //now off hand - if (attack_dw_timer.Check() && CanThisClassDualWield()) - { - int myclass = GetClass(); - //can only dual wield without a weapon if your a monk - if(GetSpecialAbility(SPECATK_INNATE_DW) || (GetEquipment(MaterialSecondary) != 0 && GetLevel() > 29) || myclass == MONK || myclass == MONKGM) { - float DualWieldProbability = (GetSkill(SkillDualWield) + GetLevel()) / 400.0f; - if(MakeRandomFloat(0.0, 1.0) < DualWieldProbability) - { - Attack(target, 14); - if (CanThisClassDoubleAttack()) - { - int32 RandRoll = MakeRandomInt(0, 99); - if (RandRoll < (GetLevel() + 20)) - { - Attack(target, 14); - } - } - } - } - } - - //now special attacks (kick, etc) - if(IsNPC()) - CastToNPC()->DoClassAttacks(target); - } - AI_EngagedCastCheck(); - } //end is within combat range - else { - //we cannot reach our target... - //underwater stuff only works with water maps in the zone! - if(IsNPC() && CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { - if(!zone->watermap->InLiquid(target->GetX(), target->GetY(), target->GetZ())) { - Mob *tar = hate_list.GetTop(this); - if(tar == target) { - WipeHateList(); - Heal(); - BuffFadeAll(); - AIwalking_timer->Start(100); - pLastFightingDelayMoving = Timer::GetCurrentTime(); - return; - } else if(tar != nullptr) { - SetTarget(tar); - return; - } - } - } - - // See if we can summon the mob to us - if (!HateSummon()) - { - //could not summon them, check ranged... - if(GetSpecialAbility(SPECATK_RANGED_ATK)) - doranged = true; - - // Now pursue - // TODO: Check here for another person on hate list with close hate value - if(AI_PursueCastCheck()){ - //we did something, so do not process movement. - } - else if (AImovement_timer->Check()) - { - if(!IsRooted()) { - mlog(AI__WAYPOINTS, "Pursuing %s while engaged.", target->GetName()); - if(!RuleB(Pathing, Aggro) || !zone->pathing) - CalculateNewPosition2(target->GetX(), target->GetY(), target->GetZ(), GetRunspeed()); - else - { - bool WaypointChanged, NodeReached; - - VERTEX Goal = UpdatePath(target->GetX(), target->GetY(), target->GetZ(), - GetRunspeed(), WaypointChanged, NodeReached); - - if(WaypointChanged) - tar_ndx = 20; - - CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); - } - - } - else if(IsMoving()) { - SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); - SetRunAnimSpeed(0); - SendPosition(); - SetMoving(false); - moved=false; - - } - } - } - } - } - else - { - if(AIfeignremember_timer->Check()) { - // EverHood - 6/14/06 - // Improved Feign Death Memory - // check to see if any of our previous feigned targets have gotten up. - std::set::iterator RememberedCharID; - RememberedCharID = feign_memory_list.begin(); - while (RememberedCharID != feign_memory_list.end()) { - Client* remember_client = entity_list.GetClientByCharID(*RememberedCharID); - if (remember_client == nullptr) { - //they are gone now... - RememberedCharID = feign_memory_list.erase(RememberedCharID); - } else if (!remember_client->GetFeigned()) { - AddToHateList(remember_client->CastToMob(),1); - RememberedCharID = feign_memory_list.erase(RememberedCharID); - break; - } else { - //they are still feigned, carry on... - ++RememberedCharID; - } - } - } - if (AI_IdleCastCheck()) - { - //we processed a spell action, so do nothing else. - } - else if (AIscanarea_timer->Check()) - { - /* - * This is where NPCs look around to see if they want to attack anybody. - * - * 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* tmptar = entity_list.AICheckCloseAggro(this, GetAggroRange(), GetAssistRange()); - if (tmptar) - AddToHateList(tmptar); - } - else if (AImovement_timer->Check() && !IsRooted()) - { - SetRunAnimSpeed(0); - if (IsPet()) - { - // we're a pet, do as we're told - switch (pStandingPetOrder) - { - case SPO_Follow: - { - - Mob* owner = GetOwner(); - if(owner == nullptr) - break; - - //if(owner->IsClient()) - // printf("Pet start pos: (%f, %f, %f)\n", GetX(), GetY(), GetZ()); - - float dist = DistNoRoot(*owner); - if (dist >= 400) - { - float speed = GetWalkspeed(); - if (dist >= 5625) - speed = GetRunspeed(); - CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed); - } - else - { - if(moved) - { - moved=false; - SetMoving(false); - SendPosition(); - } - } - - /* - //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; - } - case SPO_Sit: - { - SetAppearance(eaSitting, false); - break; - } - case SPO_Guard: - { - //only NPCs can guard stuff. (forced by where the guard movement code is in the AI) - if(IsNPC()) { - CastToNPC()->NextGuardPosition(); - } - break; - } - } - } - else if (GetFollowID()) - { - Mob* follow = entity_list.GetMob(GetFollowID()); - if (!follow) SetFollowID(0); - else - { - float dist2 = DistNoRoot(*follow); - int followdist = GetFollowDistance(); - - if (dist2 >= followdist) // Default follow distance is 100 - { - float speed = GetWalkspeed(); - if (dist2 >= followdist + 150) - speed = GetRunspeed(); - CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); - } - else - { - if(moved) - { - SendPosition(); - moved=false; - SetMoving(false); - } - } - } - } - else //not a pet, and not following somebody... - { - // dont move till a bit after you last fought - if (pLastFightingDelayMoving < Timer::GetCurrentTime()) - { - if (this->IsClient()) - { - // LD timer expired, drop out of world - if (this->CastToClient()->IsLD()) - this->CastToClient()->Disconnect(); - return; - } - - if(IsNPC()) - { - if(RuleB(NPC, SmartLastFightingDelayMoving) && !feign_memory_list.empty()) - { - minLastFightingDelayMoving = 0; - maxLastFightingDelayMoving = 0; - } - CastToNPC()->AI_DoMovement(); - } - } - - } - } // else if (AImovement_timer->Check()) - } - - //Do Ranged attack here - if(doranged) - { - int attacks = GetSpecialAbilityParam(SPECATK_RANGED_ATK, 0); - attacks = attacks > 0 ? attacks : 1; - for(int i = 0; i < attacks; ++i) { - RangedAttack(target); - } - } -} - -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. - - if (roambox_distance > 0) { - if ( - roambox_movingto_x > roambox_max_x - || roambox_movingto_x < roambox_min_x - || roambox_movingto_y > roambox_max_y - || roambox_movingto_y < roambox_min_y - ) - { - float movedist = roambox_distance*roambox_distance; - float movex = MakeRandomFloat(0, movedist); - float movey = movedist - movex; - movex = sqrtf(movex); - movey = sqrtf(movey); - movex *= MakeRandomInt(0, 1) ? 1 : -1; - movey *= MakeRandomInt(0, 1) ? 1 : -1; - roambox_movingto_x = GetX() + movex; - roambox_movingto_y = GetY() + movey; - //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 = MakeRandomFloat(roambox_min_x+1,roambox_max_x-1); - if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y) - roambox_movingto_y = MakeRandomFloat(roambox_min_y+1,roambox_max_y-1); - } - - mlog(AI__WAYPOINTS, "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); - if (!CalculateNewPosition2(roambox_movingto_x, roambox_movingto_y, GetZ(), walksp, true)) - { - roambox_movingto_x = roambox_max_x + 1; // force update - pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay); - SetMoving(false); - SendPosition(); // makes mobs stop clientside - } - } - else if (roamer) - { - if (AIwalking_timer->Check()) - { - movetimercompleted=true; - AIwalking_timer->Disable(); - } - - - int16 gridno = CastToNPC()->GetGrid(); - - if (gridno > 0 || cur_wp==-2) { - if (movetimercompleted==true) { // time to pause at wp is over - - int32 spawn_id = this->GetSpawnPointID(); - LinkedListIterator iterator(zone->spawn2_list); - iterator.Reset(); - Spawn2 *found_spawn = nullptr; - - while(iterator.MoreElements()) - { - Spawn2* cur = iterator.GetData(); - iterator.Advance(); - if(cur->GetID() == spawn_id) - { - found_spawn = cur; - break; - } - } - - if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(true); //depop and resart spawn timer - if(found_spawn) - found_spawn->SetNPCPointerNull(); - } - else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { - CastToNPC()->Depop(false);//depop without spawn timer - if(found_spawn) - found_spawn->SetNPCPointerNull(); - } - else { - movetimercompleted=false; - - mlog(QUESTS__PATHING, "We are departing waypoint %d.", cur_wp); - - //if we were under quest control (with no grid), we are done now.. - if(cur_wp == -2) { - mlog(QUESTS__PATHING, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode."); - roamer = false; - cur_wp = 0; - } - - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); - - entity_list.OpenDoorsNear(CastToNPC()); - - if(!DistractedFromGrid) { - //kick off event_waypoint depart - char temp[16]; - sprintf(temp, "%d", cur_wp); - parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0); - - //setup our next waypoint, if we are still on our normal grid - //remember that the quest event above could have done anything it wanted with our grid - if(gridno > 0) { - CastToNPC()->CalculateNewWaypoint(); - } - } - else { - DistractedFromGrid = false; - } - } - } // endif (movetimercompleted==true) - else if (!(AIwalking_timer->Enabled())) - { // currently moving - if (cur_wp_x == GetX() && cur_wp_y == GetY()) - { // are we there yet? then stop - mlog(AI__WAYPOINTS, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); - SetWaypointPause(); - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); - SetMoving(false); - if (cur_wp_heading >= 0.0) { - SetHeading(cur_wp_heading); - } - SendPosition(); - - //kick off event_waypoint arrive - char temp[16]; - sprintf(temp, "%d", cur_wp); - parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0); - - // wipe feign memory since we reached our first waypoint - if(cur_wp == 1) - ClearFeignMemory(); - } - else - { // not at waypoint yet, so keep moving - if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0)) - CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, walksp, true); - else - { - bool WaypointChanged; - bool NodeReached; - VERTEX Goal = UpdatePath(cur_wp_x, cur_wp_y, cur_wp_z, walksp, WaypointChanged, NodeReached); - if(WaypointChanged) - tar_ndx = 20; - - if(NodeReached) - entity_list.OpenDoorsNear(CastToNPC()); - - CalculateNewPosition2(Goal.x, Goal.y, Goal.z, walksp, true); - } - - } - } - } // endif (gridno > 0) -// handle new quest grid command processing - else if (gridno < 0) - { // this mob is under quest control - if (movetimercompleted==true) - { // time to pause has ended - SetGrid( 0 - GetGrid()); // revert to AI control - mlog(QUESTS__PATHING, "Quest pathing is finished. Resuming on grid %d", GetGrid()); - - if(GetAppearance() != eaStanding) - SetAppearance(eaStanding, false); - - CalculateNewWaypoint(); - } - } - - } - else if (IsGuarding()) - { - bool CP2Moved; - if(!RuleB(Pathing, Guard) || !zone->pathing) - CP2Moved = CalculateNewPosition2(guard_x, guard_y, guard_z, walksp); - else - { - if(!((x_pos == guard_x) && (y_pos == guard_y) && (z_pos == guard_z))) - { - bool WaypointChanged, NodeReached; - VERTEX Goal = UpdatePath(guard_x, guard_y, guard_z, walksp, WaypointChanged, NodeReached); - if(WaypointChanged) - tar_ndx = 20; - - if(NodeReached) - entity_list.OpenDoorsNear(CastToNPC()); - - CP2Moved = CalculateNewPosition2(Goal.x, Goal.y, Goal.z, walksp); - } - else - CP2Moved = false; - - } - if (!CP2Moved) - { - if(moved) { - mlog(AI__WAYPOINTS, "Reached guard point (%.3f,%.3f,%.3f)", guard_x, guard_y, guard_z); - ClearFeignMemory(); - moved=false; - SetMoving(false); - if (GetTarget() == nullptr || DistNoRoot(*GetTarget()) >= 5*5 ) - { - SetHeading(guard_heading); - } else { - FaceTarget(GetTarget()); - } - SendPosition(); - SetAppearance(GetGuardPointAnim()); - } - } - } -} - -// Note: Mob that caused this may not get added to the hate list until after this function call completes -void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { - if (!IsAIControlled()) - return; - - if(GetAppearance() != eaStanding) - { - SetAppearance(eaStanding); - } - - if (iYellForHelp) { - if(IsPet()) { - GetOwner()->AI_Event_Engaged(attacker, iYellForHelp); - } else { - entity_list.AIYellForHelp(this, attacker); - } - } - - if(IsNPC()) - { - if(CastToNPC()->GetGrid() > 0) - { - DistractedFromGrid = true; - } - if(attacker && !attacker->IsCorpse()) - { - //Because sometimes the AIYellForHelp triggers another engaged and then immediately a not engaged - //if the target dies before it goes off - if(attacker->GetHP() > 0) - { - if(!CastToNPC()->GetCombatEvent() && GetHP() > 0) - { - parse->EventNPC(EVENT_COMBAT, CastToNPC(), attacker, "1", 0); - uint16 emoteid = GetEmoteID(); - if(emoteid != 0) - CastToNPC()->DoNPCEmote(ENTERCOMBAT,emoteid); - CastToNPC()->SetCombatEvent(true); - } - } - } - } -} - -// Note: Hate list may not be actually clear until after this function call completes -void Mob::AI_Event_NoLongerEngaged() { - if (!IsAIControlled()) - return; - this->AIwalking_timer->Start(RandomTimer(3000,20000)); - pLastFightingDelayMoving = Timer::GetCurrentTime(); - if (minLastFightingDelayMoving == maxLastFightingDelayMoving) - pLastFightingDelayMoving += minLastFightingDelayMoving; - else - pLastFightingDelayMoving += MakeRandomInt(minLastFightingDelayMoving, maxLastFightingDelayMoving); - // EverHood - So mobs don't keep running as a ghost until AIwalking_timer fires - // if they were moving prior to losing all hate - if(IsMoving()){ - SetRunAnimSpeed(0); - SetMoving(false); - SendPosition(); - } - ClearRampage(); - - if(IsNPC()) - { - if(CastToNPC()->GetCombatEvent() && GetHP() > 0) - { - if(entity_list.GetNPCByID(this->GetID())) - { - uint16 emoteid = CastToNPC()->GetEmoteID(); - parse->EventNPC(EVENT_COMBAT, CastToNPC(), nullptr, "0", 0); - if(emoteid != 0) - CastToNPC()->DoNPCEmote(LEAVECOMBAT,emoteid); - CastToNPC()->SetCombatEvent(false); - } - } - } -} - -//this gets called from InterruptSpell() for failure or SpellFinished() for success -void NPC::AI_Event_SpellCastFinished(bool iCastSucceeded, uint8 slot) { - if (slot == 1) { - uint32 recovery_time = 0; - if (iCastSucceeded) { - if (casting_spell_AIindex < AIspells.size()) { - recovery_time += spells[AIspells[casting_spell_AIindex].spellid].recovery_time; - if (AIspells[casting_spell_AIindex].recast_delay >= 0) - { - if (AIspells[casting_spell_AIindex].recast_delay < 10000) - AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + (AIspells[casting_spell_AIindex].recast_delay*1000); - } - else - AIspells[casting_spell_AIindex].time_cancast = Timer::GetCurrentTime() + spells[AIspells[casting_spell_AIindex].spellid].recast_time; - } - if (recovery_time < AIautocastspell_timer->GetSetAtTrigger()) - recovery_time = AIautocastspell_timer->GetSetAtTrigger(); - AIautocastspell_timer->Start(recovery_time, false); - } - else - AIautocastspell_timer->Start(800, false); - casting_spell_AIindex = AIspells.size(); - } -} - - -bool NPC::AI_EngagedCastCheck() { - if (AIautocastspell_timer->Check(false)) { - AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - - mlog(AI__SPELLS, "Engaged autocast check triggered. Trying to cast healing spells then maybe offensive spells."); - Shout("KAYEN"); - // try casting a heal or gate - if (!AICastSpell(this, 100, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) { - // try casting a heal on nearby - if (!entity_list.AICheckCloseBeneficialSpells(this, 25, MobAISpellRange, SpellType_Heal)) { - //nobody to heal, try some detrimental spells. - if(!AICastSpell(GetTarget(), 20, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) { - //no spell to cast, try again soon. - AIautocastspell_timer->Start(RandomTimer(500, 1000), false); - } - } - } - return(true); - } - - return(false); -} - -bool NPC::AI_PursueCastCheck() { - if (AIautocastspell_timer->Check(false)) { - AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - - mlog(AI__SPELLS, "Engaged (pursuing) autocast check triggered. Trying to cast offensive spells."); - if(!AICastSpell(GetTarget(), 90, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff)) { - //no spell cast, try again soon. - AIautocastspell_timer->Start(RandomTimer(500, 2000), false); - } //else, spell casting finishing will reset the timer. - return(true); - } - return(false); -} - -bool NPC::AI_IdleCastCheck() { - if (AIautocastspell_timer->Check(false)) { -#if MobAI_DEBUG_Spells >= 25 - std::cout << "Non-Engaged autocast check triggered: " << this->GetName() << std::endl; -#endif - AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - if (!AICastSpell(this, 100, SpellType_Heal | SpellType_Buff | SpellType_Pet)) { - if(!entity_list.AICheckCloseBeneficialSpells(this, 33, MobAISpellRange, SpellType_Heal | SpellType_Buff)) { - //if we didnt cast any spells, our autocast timer just resets to the - //last duration it was set to... try to put up a more reasonable timer... - AIautocastspell_timer->Start(RandomTimer(1000, 5000), false); - } //else, spell casting finishing will reset the timer. - } //else, spell casting finishing will reset the timer. - return(true); - } - return(false); -} - -void Mob::StartEnrage() -{ - // dont continue if already enraged - if (bEnraged) - return; - - if(!GetSpecialAbility(SPECATK_ENRAGE)) - return; - - int hp_ratio = GetSpecialAbilityParam(SPECATK_ENRAGE, 0); - hp_ratio = hp_ratio > 0 ? hp_ratio : RuleI(NPC, StartEnrageValue); - if(GetHPRatio() > static_cast(hp_ratio)) { - return; - } - - if(RuleB(NPC, LiveLikeEnrage) && !((IsPet() && !IsCharmed() && GetOwner() && GetOwner()->IsClient()) || - (CastToNPC()->GetSwarmOwner() && entity_list.GetMob(CastToNPC()->GetSwarmOwner())->IsClient()))) { - return; - } - - Timer *timer = GetSpecialAbilityTimer(SPECATK_ENRAGE); - if (timer && !timer->Check()) - return; - - int enraged_duration = GetSpecialAbilityParam(SPECATK_ENRAGE, 1); - enraged_duration = enraged_duration > 0 ? enraged_duration : EnragedDurationTimer; - StartSpecialAbilityTimer(SPECATK_ENRAGE, enraged_duration); - - // start the timer. need to call IsEnraged frequently since we dont have callback timers :-/ - bEnraged = true; - entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_START, GetCleanName()); -} - -void Mob::ProcessEnrage(){ - if(IsEnraged()){ - Timer *timer = GetSpecialAbilityTimer(SPECATK_ENRAGE); - if(timer && timer->Check()){ - entity_list.MessageClose_StringID(this, true, 200, MT_NPCEnrage, NPC_ENRAGE_END, GetCleanName()); - - int enraged_cooldown = GetSpecialAbilityParam(SPECATK_ENRAGE, 2); - enraged_cooldown = enraged_cooldown > 0 ? enraged_cooldown : EnragedTimer; - StartSpecialAbilityTimer(SPECATK_ENRAGE, enraged_cooldown); - bEnraged = false; - } - } -} - -bool Mob::IsEnraged() -{ - return bEnraged; -} - -bool Mob::Flurry(ExtraAttackOptions *opts) -{ - // this is wrong, flurry is extra attacks on the current target - Mob *target = GetTarget(); - if (target) { - if (!IsPet()) { - entity_list.MessageClose_StringID(this, true, 200, MT_NPCFlurry, NPC_FLURRY, GetCleanName(), target->GetCleanName()); - } else { - entity_list.MessageClose_StringID(this, true, 200, MT_PetFlurry, NPC_FLURRY, GetCleanName(), target->GetCleanName()); - } - - int num_attacks = GetSpecialAbilityParam(SPECATK_FLURRY, 1); - num_attacks = num_attacks > 0 ? num_attacks : RuleI(Combat, MaxFlurryHits); - for (int i = 0; i < num_attacks; i++) - Attack(target, 13, false, false, false, opts); - } - return true; -} - -bool Mob::AddRampage(Mob *mob) -{ - if (!mob) - return false; - - if (!GetSpecialAbility(SPECATK_RAMPAGE)) - return false; - - for (int i = 0; i < RampageArray.size(); i++) { - // if Entity ID is already on the list don't add it again - if (mob->GetID() == RampageArray[i]) - return false; - } - RampageArray.push_back(mob->GetID()); - return true; -} - -void Mob::ClearRampage() -{ - RampageArray.clear(); -} - -bool Mob::Rampage(ExtraAttackOptions *opts) -{ - int index_hit = 0; - if (!IsPet()) - entity_list.MessageClose_StringID(this, true, 200, MT_NPCRampage, NPC_RAMPAGE, GetCleanName()); - else - entity_list.MessageClose_StringID(this, true, 200, MT_PetFlurry, NPC_RAMPAGE, GetCleanName()); - - int rampage_targets = GetSpecialAbilityParam(SPECATK_RAMPAGE, 1); - if (rampage_targets == 0) // if set to 0 or not set in the DB - rampage_targets = RuleI(Combat, DefaultRampageTargets); - if (rampage_targets > RuleI(Combat, MaxRampageTargets)) - rampage_targets = RuleI(Combat, MaxRampageTargets); - for (int i = 0; i < RampageArray.size(); i++) { - if (index_hit >= rampage_targets) - break; - // range is important - Mob *m_target = entity_list.GetMob(RampageArray[i]); - if (m_target) { - if (m_target == GetTarget()) - continue; - if (CombatRange(m_target)) { - Attack(m_target, 13, false, false, false, opts); - index_hit++; - } - } - } - - if (RuleB(Combat, RampageHitsTarget) && index_hit < rampage_targets) - Attack(GetTarget(), 13, false, false, false, opts); - - return true; -} - -void Mob::AreaRampage(ExtraAttackOptions *opts) -{ - int index_hit = 0; - if (!IsPet()) { // do not know every pet AA so thought it safer to add this - entity_list.MessageClose_StringID(this, true, 200, MT_NPCRampage, AE_RAMPAGE, GetCleanName()); - } else { - entity_list.MessageClose_StringID(this, true, 200, MT_PetFlurry, AE_RAMPAGE, GetCleanName()); - } - - int rampage_targets = GetSpecialAbilityParam(SPECATK_AREA_RAMPAGE, 1); - rampage_targets = rampage_targets > 0 ? rampage_targets : 1; - index_hit = hate_list.AreaRampage(this, GetTarget(), rampage_targets, opts); - - if(index_hit == 0) { - Attack(GetTarget(), 13, false, false, false, opts); - } -} - -uint32 Mob::GetLevelCon(uint8 mylevel, uint8 iOtherLevel) { - int16 diff = iOtherLevel - mylevel; - uint32 conlevel=0; - - if (diff == 0) - return CON_WHITE; - else if (diff >= 1 && diff <= 2) - return CON_YELLOW; - else if (diff >= 3) - return CON_RED; - - if (mylevel <= 8) - { - if (diff <= -4) - conlevel = CON_GREEN; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 9) - { - if (diff <= -6) - conlevel = CON_GREEN; - else if (diff <= -4) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 13) - { - if (diff <= -7) - conlevel = CON_GREEN; - else if (diff <= -5) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 15) - { - if (diff <= -7) - conlevel = CON_GREEN; - else if (diff <= -5) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 17) - { - if (diff <= -8) - conlevel = CON_GREEN; - else if (diff <= -6) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 21) - { - if (diff <= -9) - conlevel = CON_GREEN; - else if (diff <= -7) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 25) - { - if (diff <= -10) - conlevel = CON_GREEN; - else if (diff <= -8) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 29) - { - if (diff <= -11) - conlevel = CON_GREEN; - else if (diff <= -9) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 31) - { - if (diff <= -12) - conlevel = CON_GREEN; - else if (diff <= -9) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 33) - { - if (diff <= -13) - conlevel = CON_GREEN; - else if (diff <= -10) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 37) - { - if (diff <= -14) - conlevel = CON_GREEN; - else if (diff <= -11) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 41) - { - if (diff <= -16) - conlevel = CON_GREEN; - else if (diff <= -12) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 45) - { - if (diff <= -17) - conlevel = CON_GREEN; - else if (diff <= -13) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 49) - { - if (diff <= -18) - conlevel = CON_GREEN; - else if (diff <= -14) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 53) - { - if (diff <= -19) - conlevel = CON_GREEN; - else if (diff <= -15) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else if (mylevel <= 55) - { - if (diff <= -20) - conlevel = CON_GREEN; - else if (diff <= -15) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - else - { - if (diff <= -21) - conlevel = CON_GREEN; - else if (diff <= -16) - conlevel = CON_LIGHTBLUE; - else - conlevel = CON_BLUE; - } - return conlevel; -} - -void NPC::CheckSignal() { - if (!signal_q.empty()) { - int signal_id = signal_q.front(); - signal_q.pop_front(); - char buf[32]; - snprintf(buf, 31, "%d", signal_id); - buf[31] = '\0'; - parse->EventNPC(EVENT_SIGNAL, this, nullptr, buf, 0); - } -} - - - -/* -alter table npc_types drop column usedspells; -alter table npc_types add column npc_spells_id int(11) unsigned not null default 0 after merchant_id; -Create Table npc_spells ( - id int(11) unsigned not null auto_increment primary key, - name tinytext, - parent_list int(11) unsigned not null default 0, - attack_proc smallint(5) not null default -1, - proc_chance tinyint(3) not null default 3 - ); -create table npc_spells_entries ( - id int(11) unsigned not null auto_increment primary key, - npc_spells_id int(11) not null, - spellid smallint(5) not null default 0, - type smallint(5) unsigned not null default 0, - minlevel tinyint(3) unsigned not null default 0, - maxlevel tinyint(3) unsigned not null default 255, - manacost smallint(5) not null default '-1', - recast_delay int(11) not null default '-1', - priority smallint(5) not null default 0, - index npc_spells_id (npc_spells_id) - ); -*/ - -bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID); -bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base, int32 limit, int32 max); -bool Compare_AI_Spells(AISpells_Struct i, AISpells_Struct j); - -bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) { - // ok, this function should load the list, and the parent list then shove them into the struct and sort - npc_spells_id = iDBSpellsID; - AIspells.clear(); - if (iDBSpellsID == 0) { - AIautocastspell_timer->Disable(); - return false; - } - DBnpcspells_Struct* spell_list = database.GetNPCSpells(iDBSpellsID); - if (!spell_list) { - AIautocastspell_timer->Disable(); - return false; - } - DBnpcspells_Struct* parentlist = database.GetNPCSpells(spell_list->parent_list); - uint32 i; -#if MobAI_DEBUG_Spells >= 10 - std::cout << "Loading NPCSpells onto " << this->GetName() << ": dbspellsid=" << iDBSpellsID; - if (spell_list) { - std::cout << " (found, " << spell_list->numentries << "), parentlist=" << spell_list->parent_list; - if (spell_list->parent_list) { - if (parentlist) { - std::cout << " (found, " << parentlist->numentries << ")"; - } - else - std::cout << " (not found)"; - } - } - else - std::cout << " (not found)"; - std::cout << std::endl; -#endif - int16 attack_proc_spell = -1; - int8 proc_chance = 3; - if (parentlist) { - attack_proc_spell = parentlist->attack_proc; - proc_chance = parentlist->proc_chance; - for (i=0; inumentries; i++) { - if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spellid > 0) { - if (!IsSpellInList(spell_list, parentlist->entries[i].spellid)) - { - AddSpellToNPCList(parentlist->entries[i].priority, - parentlist->entries[i].spellid, parentlist->entries[i].type, - parentlist->entries[i].manacost, parentlist->entries[i].recast_delay, - parentlist->entries[i].resist_adjust); - } - } - } - } - if (spell_list->attack_proc >= 0) { - attack_proc_spell = spell_list->attack_proc; - proc_chance = spell_list->proc_chance; - } - for (i=0; inumentries; i++) { - if (GetLevel() >= spell_list->entries[i].minlevel && GetLevel() <= spell_list->entries[i].maxlevel && spell_list->entries[i].spellid > 0) { - AddSpellToNPCList(spell_list->entries[i].priority, - spell_list->entries[i].spellid, spell_list->entries[i].type, - spell_list->entries[i].manacost, spell_list->entries[i].recast_delay, - spell_list->entries[i].resist_adjust); - } - } - std::sort(AIspells.begin(), AIspells.end(), Compare_AI_Spells); - - if (attack_proc_spell > 0) - AddProcToWeapon(attack_proc_spell, true, proc_chance); - - if (AIspells.size() == 0) - AIautocastspell_timer->Disable(); - else - AIautocastspell_timer->Trigger(); - return true; -} - -bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) { - - npc_spells_effects_id = iDBSpellsEffectsID; - AIspellsEffects.clear(); - - if (iDBSpellsEffectsID == 0) - return false; - - DBnpcspellseffects_Struct* spell_effects_list = database.GetNPCSpellsEffects(iDBSpellsEffectsID); - - if (!spell_effects_list) { - return false; - } - - DBnpcspellseffects_Struct* parentlist = database.GetNPCSpellsEffects(spell_effects_list->parent_list); - - uint32 i; -#if MobAI_DEBUG_Spells >= 10 - std::cout << "Loading NPCSpellsEffects onto " << this->GetName() << ": dbspellseffectsid=" << iDBSpellsEffectsID; - if (spell_effects_list) { - std::cout << " (found, " << spell_effects_list->numentries << "), parentlist=" << spell_effects)list->parent_list; - if (spell_effects_list->parent_list) { - if (parentlist) { - std::cout << " (found, " << parentlist->numentries << ")"; - } - else - std::cout << " (not found)"; - } - } - else - std::cout << " (not found)"; - std::cout << std::endl; -#endif - - if (parentlist) { - for (i=0; inumentries; i++) { - if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spelleffectid > 0) { - if (!IsSpellEffectInList(spell_effects_list, parentlist->entries[i].spelleffectid, parentlist->entries[i].base, - parentlist->entries[i].limit, parentlist->entries[i].max)) - { - AddSpellEffectToNPCList(parentlist->entries[i].spelleffectid, - parentlist->entries[i].base, parentlist->entries[i].limit, - parentlist->entries[i].max); - } - } - } - } - - for (i=0; inumentries; i++) { - if (GetLevel() >= spell_effects_list->entries[i].minlevel && GetLevel() <= spell_effects_list->entries[i].maxlevel && spell_effects_list->entries[i].spelleffectid > 0) { - AddSpellEffectToNPCList(spell_effects_list->entries[i].spelleffectid, - spell_effects_list->entries[i].base, spell_effects_list->entries[i].limit, - spell_effects_list->entries[i].max); - } - } - - return true; -} - -void NPC::ApplyAISpellEffects(StatBonuses* newbon) -{ - if (!AI_HasSpellsEffects()) - return; - - - - for(int i=0; i < AIspellsEffects.size(); i++) - { - Shout("ApplyAISpellEffects %i %i %i %i", AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max); - ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1, - true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max); - } - - return; -} - -// adds a spell to the list, taking into account priority and resorting list as needed. -void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max) -{ - - if(!iSpellEffectID) - return; - - - HasAISpellEffects = true; - AISpellsEffects_Struct t; - - t.spelleffectid = iSpellEffectID; - t.base = base; - t.limit = limit; - t.max = max; - Shout("AddSpellEffectToNPCList %i %i %i %i", iSpellEffectID,base, limit, max ); - AIspellsEffects.push_back(t); -} - -bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base, int32 limit, int32 max) { - for (uint32 i=0; i < spelleffect_list->numentries; i++) { - if (spelleffect_list->entries[i].spelleffectid == iSpellEffectID && spelleffect_list->entries[i].base == base - && spelleffect_list->entries[i].limit == limit && spelleffect_list->entries[i].max == max) - return true; - } - return false; -} - -bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID) { - for (uint32 i=0; i < spell_list->numentries; i++) { - if (spell_list->entries[i].spellid == iSpellID) - return true; - } - return false; -} - -bool Compare_AI_Spells(AISpells_Struct i, AISpells_Struct j) -{ - return(i.priority > j.priority); -} - -// adds a spell to the list, taking into account priority and resorting list as needed. -void NPC::AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint16 iType, - int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust) -{ - - if(!IsValidSpell(iSpellID)) - return; - - HasAISpell = true; - AISpells_Struct t; - - t.priority = iPriority; - t.spellid = iSpellID; - t.type = iType; - t.manacost = iManaCost; - t.recast_delay = iRecastDelay; - t.time_cancast = 0; - t.resist_adjust = iResistAdjust; - - AIspells.push_back(t); -} - -void NPC::RemoveSpellFromNPCList(int16 spell_id) -{ - std::vector::iterator iter = AIspells.begin(); - while(iter != AIspells.end()) - { - if((*iter).spellid == spell_id) - { - iter = AIspells.erase(iter); - continue; - } - ++iter; - } -} - -void NPC::AISpellsList(Client *c) -{ - if (!c) - return; - - for (std::vector::iterator it = AIspells.begin(); it != AIspells.end(); ++it) - c->Message(0, "%s (%d): Type %d, Priority %d", - spells[it->spellid].name, it->spellid, it->type, it->priority); - - return; -} - -DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) { - if (iDBSpellsID == 0) - return 0; - - if (!npc_spells_cache) { - npc_spells_maxid = GetMaxNPCSpellsID(); - npc_spells_cache = new DBnpcspells_Struct*[npc_spells_maxid+1]; - npc_spells_loadtried = new bool[npc_spells_maxid+1]; - for (uint32 i=0; i<=npc_spells_maxid; i++) { - npc_spells_cache[i] = 0; - npc_spells_loadtried[i] = false; - } - } - - if (iDBSpellsID > npc_spells_maxid) - return 0; - if (npc_spells_cache[iDBSpellsID]) { // it's in the cache, easy =) - return npc_spells_cache[iDBSpellsID]; - } - - else if (!npc_spells_loadtried[iDBSpellsID]) { // no reason to ask the DB again if we have failed once already - npc_spells_loadtried[iDBSpellsID] = true; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list, attack_proc, proc_chance from npc_spells where id=%d", iDBSpellsID), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 tmpparent_list = atoi(row[1]); - int16 tmpattack_proc = atoi(row[2]); - uint8 tmpproc_chance = atoi(row[3]); - mysql_free_result(result); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT spellid, type, minlevel, maxlevel, manacost, recast_delay, priority, resist_adjust from npc_spells_entries where npc_spells_id=%d ORDER BY minlevel", iDBSpellsID), errbuf, &result)) { - safe_delete_array(query); - uint32 tmpSize = sizeof(DBnpcspells_Struct) + (sizeof(DBnpcspells_entries_Struct) * mysql_num_rows(result)); - npc_spells_cache[iDBSpellsID] = (DBnpcspells_Struct*) new uchar[tmpSize]; - memset(npc_spells_cache[iDBSpellsID], 0, tmpSize); - npc_spells_cache[iDBSpellsID]->parent_list = tmpparent_list; - npc_spells_cache[iDBSpellsID]->attack_proc = tmpattack_proc; - npc_spells_cache[iDBSpellsID]->proc_chance = tmpproc_chance; - npc_spells_cache[iDBSpellsID]->numentries = mysql_num_rows(result); - int j = 0; - while ((row = mysql_fetch_row(result))) { - int spell_id = atoi(row[0]); - npc_spells_cache[iDBSpellsID]->entries[j].spellid = spell_id; - npc_spells_cache[iDBSpellsID]->entries[j].type = atoi(row[1]); - npc_spells_cache[iDBSpellsID]->entries[j].minlevel = atoi(row[2]); - npc_spells_cache[iDBSpellsID]->entries[j].maxlevel = atoi(row[3]); - npc_spells_cache[iDBSpellsID]->entries[j].manacost = atoi(row[4]); - npc_spells_cache[iDBSpellsID]->entries[j].recast_delay = atoi(row[5]); - npc_spells_cache[iDBSpellsID]->entries[j].priority = atoi(row[6]); - if(row[7]) - { - npc_spells_cache[iDBSpellsID]->entries[j].resist_adjust = atoi(row[7]); - } - else - { - if(IsValidSpell(spell_id)) - { - npc_spells_cache[iDBSpellsID]->entries[j].resist_adjust = spells[spell_id].ResistDiff; - } - } - j++; - } - mysql_free_result(result); - return npc_spells_cache[iDBSpellsID]; - } - else { - std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - } - else { - mysql_free_result(result); - } - } - else { - std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - - return 0; - } - return 0; -} - -uint32 ZoneDatabase::GetMaxNPCSpellsID() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id) from npc_spells"), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 ret = 0; - if (row[0]) - ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetMaxNPCSpellsID query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - - return 0; -} - -DBnpcspellseffects_Struct* ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEffectsID) { - if (iDBSpellsEffectsID == 0) - return 0; - - if (!npc_spellseffects_cache) { - npc_spellseffects_maxid = GetMaxNPCSpellsEffectsID(); - npc_spellseffects_cache = new DBnpcspellseffects_Struct*[npc_spellseffects_maxid+1]; - npc_spellseffects_loadtried = new bool[npc_spellseffects_maxid+1]; - for (uint32 i=0; i<=npc_spellseffects_maxid; i++) { - npc_spellseffects_cache[i] = 0; - npc_spellseffects_loadtried[i] = false; - } - } - - if (iDBSpellsEffectsID > npc_spellseffects_maxid) - return 0; - if (npc_spellseffects_cache[iDBSpellsEffectsID]) { // it's in the cache, easy =) - return npc_spellseffects_cache[iDBSpellsEffectsID]; - } - - else if (!npc_spellseffects_loadtried[iDBSpellsEffectsID]) { // no reason to ask the DB again if we have failed once already - npc_spellseffects_loadtried[iDBSpellsEffectsID] = true; - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list from npc_spells_effects where id=%d", iDBSpellsEffectsID), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 tmpparent_list = atoi(row[1]); - mysql_free_result(result); - if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_effect_id, minlevel, maxlevel,se_base, se_limit, se_max from npc_spells_effects_entries where npc_spells_effects_id=%d ORDER BY minlevel", iDBSpellsEffectsID), errbuf, &result)) { - safe_delete_array(query); - uint32 tmpSize = sizeof(DBnpcspellseffects_Struct) + (sizeof(DBnpcspellseffects_Struct) * mysql_num_rows(result)); - npc_spellseffects_cache[iDBSpellsEffectsID] = (DBnpcspellseffects_Struct*) new uchar[tmpSize]; - memset(npc_spellseffects_cache[iDBSpellsEffectsID], 0, tmpSize); - npc_spellseffects_cache[iDBSpellsEffectsID]->parent_list = tmpparent_list; - npc_spellseffects_cache[iDBSpellsEffectsID]->numentries = mysql_num_rows(result); - int j = 0; - while ((row = mysql_fetch_row(result))) { - int spell_effect_id = atoi(row[0]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].spelleffectid = spell_effect_id; - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].minlevel = atoi(row[1]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].maxlevel = atoi(row[2]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].base = atoi(row[3]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].limit = atoi(row[4]); - npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].max = atoi(row[5]); - j++; - } - mysql_free_result(result); - return npc_spellseffects_cache[iDBSpellsEffectsID]; - } - else { - std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - } - else { - mysql_free_result(result); - } - } - else { - std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - return 0; - } - return 0; -} - -uint32 ZoneDatabase::GetMaxNPCSpellsEffectsID() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - - if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id) from npc_spells_effects"), errbuf, &result)) { - safe_delete_array(query); - if (mysql_num_rows(result) == 1) { - row = mysql_fetch_row(result); - uint32 ret = 0; - if (row[0]) - ret = atoi(row[0]); - mysql_free_result(result); - return ret; - } - mysql_free_result(result); - } - else { - std::cerr << "Error in GetMaxNPCSpellsEffectsID query '" << query << "' " << errbuf << std::endl; - safe_delete_array(query); - return 0; - } - - return 0; -} - diff --git a/zone/attackx.cpp b/zone/attackx.cpp deleted file mode 100644 index 3f96399be..000000000 --- a/zone/attackx.cpp +++ /dev/null @@ -1,4663 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemulator.net) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#if EQDEBUG >= 5 -//#define ATTACK_DEBUG 20 -#endif - -#include "../common/debug.h" -#include -#include -#include -#include -#include -#include - -#include "masterentity.h" -#include "NpcAI.h" -#include "../common/packet_dump.h" -#include "../common/eq_packet_structs.h" -#include "../common/eq_constants.h" -#include "../common/skills.h" -#include "../common/spdat.h" -#include "zone.h" -#include "StringIDs.h" -#include "../common/StringUtil.h" -#include "../common/rulesys.h" -#include "QuestParserCollection.h" -#include "watermap.h" -#include "worldserver.h" -extern WorldServer worldserver; - -#ifdef _WINDOWS -#define snprintf _snprintf -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#endif - -extern EntityList entity_list; -extern Zone* zone; - -bool Mob::AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* weapon) -{ - // Determine animation - int type = 0; - if (weapon && weapon->IsType(ItemClassCommon)) { - const Item_Struct* item = weapon->GetItem(); -#if EQDEBUG >= 11 - LogFile->write(EQEMuLog::Debug, "Weapon skill:%i", item->ItemType); -#endif - switch (item->ItemType) - { - case ItemType1HSlash: // 1H Slashing - { - skillinuse = Skill1HSlashing; - type = anim1HWeapon; - break; - } - case ItemType2HSlash: // 2H Slashing - { - skillinuse = Skill2HSlashing; - type = anim2HSlashing; - break; - } - case ItemType1HPiercing: // Piercing - { - skillinuse = Skill1HPiercing; - type = animPiercing; - break; - } - case ItemType1HBlunt: // 1H Blunt - { - skillinuse = Skill1HBlunt; - type = anim1HWeapon; - break; - } - case ItemType2HBlunt: // 2H Blunt - { - skillinuse = Skill2HBlunt; - type = anim2HWeapon; - break; - } - case ItemType2HPiercing: // 2H Piercing - { - skillinuse = Skill1HPiercing; // change to Skill2HPiercing once activated - type = anim2HWeapon; - break; - } - case ItemTypeMartial: - { - skillinuse = SkillHandtoHand; - type = animHand2Hand; - break; - } - default: - { - skillinuse = SkillHandtoHand; - type = animHand2Hand; - break; - } - }// switch - } - else if(IsNPC()) { - - switch (skillinuse) - { - case Skill1HSlashing: // 1H Slashing - { - type = anim1HWeapon; - break; - } - case Skill2HSlashing: // 2H Slashing - { - type = anim2HSlashing; - break; - } - case Skill1HPiercing: // Piercing - { - type = animPiercing; - break; - } - case Skill1HBlunt: // 1H Blunt - { - type = anim1HWeapon; - break; - } - case Skill2HBlunt: // 2H Blunt - { - type = anim2HWeapon; - break; - } - case 99: // 2H Piercing // change to Skill2HPiercing once activated - { - type = anim2HWeapon; - break; - } - case SkillHandtoHand: - { - type = animHand2Hand; - break; - } - default: - { - type = animHand2Hand; - break; - } - }// switch - } - else { - skillinuse = SkillHandtoHand; - type = animHand2Hand; - } - - // If we're attacking with the secondary hand, play the dual wield anim - if (Hand == 14) // DW anim - type = animDualWield; - - DoAnim(type); - return true; -} - -// called when a mob is attacked, does the checks to see if it's a hit -// and does other mitigation checks. 'this' is the mob being attacked. -bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 chance_mod) -{ -/*/ - //Reworked a lot of this code to achieve better balance at higher levels. - //The old code basically meant that any in high level (50+) combat, - //both parties always had 95% chance to hit the other one. -/*/ - - Mob *attacker=other; - Mob *defender=this; - float chancetohit = RuleR(Combat, BaseHitChance); - - if(attacker->IsNPC() && !attacker->IsPet()) - chancetohit += RuleR(Combat, NPCBonusHitChance); - -#if ATTACK_DEBUG>=11 - LogFile->write(EQEMuLog::Debug, "CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName()); -#endif - mlog(COMBAT__TOHIT,"CheckHitChance(%s) attacked by %s", defender->GetName(), attacker->GetName()); - - bool pvpmode = false; - if(IsClient() && other->IsClient()) - pvpmode = true; - - if (chance_mod >= 10000) - return true; - - float bonus; - - //////////////////////////////////////////////////////// - // To hit calcs go here - //////////////////////////////////////////////////////// - - uint8 attacker_level = attacker->GetLevel() ? attacker->GetLevel() : 1; - uint8 defender_level = defender->GetLevel() ? defender->GetLevel() : 1; - - //Calculate the level difference - - mlog(COMBAT__TOHIT, "Chance to hit before level diff calc %.2f", chancetohit); - double level_difference = attacker_level - defender_level; - double range = defender->GetLevel(); - range = ((range / 4) + 3); - - if(level_difference < 0) - { - if(level_difference >= -range) - { - chancetohit += (level_difference / range) * RuleR(Combat,HitFalloffMinor); //5 - } - else if (level_difference >= -(range+3.0)) - { - chancetohit -= RuleR(Combat,HitFalloffMinor); - chancetohit += ((level_difference+range) / (3.0)) * RuleR(Combat,HitFalloffModerate); //7 - } - else - { - chancetohit -= (RuleR(Combat,HitFalloffMinor) + RuleR(Combat,HitFalloffModerate)); - chancetohit += ((level_difference+range+3.0)/12.0) * RuleR(Combat,HitFalloffMajor); //50 - } - } - else - { - chancetohit += (RuleR(Combat,HitBonusPerLevel) * level_difference); - } - - mlog(COMBAT__TOHIT, "Chance to hit after level diff calc %.2f", chancetohit); - - chancetohit -= ((float)defender->GetAGI() * RuleR(Combat, AgiHitFactor)); - - mlog(COMBAT__TOHIT, "Chance to hit after agil calc %.2f", chancetohit); - - if(attacker->IsClient()) - { - chancetohit -= (RuleR(Combat,WeaponSkillFalloff) * (attacker->CastToClient()->MaxSkill(skillinuse) - attacker->GetSkill(skillinuse))); - mlog(COMBAT__TOHIT, "Chance to hit after weapon falloff calc (attack) %.2f", chancetohit); - } - - if(defender->IsClient()) - { - chancetohit += (RuleR(Combat,WeaponSkillFalloff) * (defender->CastToClient()->MaxSkill(SkillDefense) - defender->GetSkill(SkillDefense))); - mlog(COMBAT__TOHIT, "Chance to hit after weapon falloff calc (defense) %.2f", chancetohit); - } - - //I dont think this is 100% correct, but at least it does something... - if(attacker->spellbonuses.MeleeSkillCheckSkill == skillinuse || attacker->spellbonuses.MeleeSkillCheckSkill == 255) { - chancetohit += attacker->spellbonuses.MeleeSkillCheck; - mlog(COMBAT__TOHIT, "Applied spell melee skill bonus %d, yeilding %.2f", attacker->spellbonuses.MeleeSkillCheck, chancetohit); - } - if(attacker->itembonuses.MeleeSkillCheckSkill == skillinuse || attacker->itembonuses.MeleeSkillCheckSkill == 255) { - chancetohit += attacker->itembonuses.MeleeSkillCheck; - mlog(COMBAT__TOHIT, "Applied item melee skill bonus %d, yeilding %.2f", attacker->spellbonuses.MeleeSkillCheck, chancetohit); - } - - //subtract off avoidance by the defender. (Live AA - Combat Agility) - bonus = defender->spellbonuses.AvoidMeleeChance + defender->itembonuses.AvoidMeleeChance + (defender->aabonuses.AvoidMeleeChance * 10); - - //AA Live - Elemental Agility - if (IsPet()) { - Mob *owner = defender->GetOwner(); - if (!owner)return false; - bonus += (owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance)*10; - } - - if(bonus > 0) { - chancetohit -= ((bonus * chancetohit) / 1000); - mlog(COMBAT__TOHIT, "Applied avoidance chance %.2f/10, yeilding %.2f", bonus, chancetohit); - } - - if(attacker->IsNPC()) - chancetohit += (chancetohit * attacker->CastToNPC()->GetAccuracyRating() / 1000); - - mlog(COMBAT__TOHIT, "Chance to hit after accuracy rating calc %.2f", chancetohit); - - float hitBonus = 0; - - /* - Kayen: Unknown if the HitChance and Accuracy effect's should modify 'chancetohit' - cumulatively or successively. For now all hitBonuses are cumulative. - */ - - hitBonus += attacker->itembonuses.HitChanceEffect[skillinuse] + - attacker->spellbonuses.HitChanceEffect[skillinuse]+ - attacker->itembonuses.HitChanceEffect[HIGHEST_SKILL+1] + - attacker->spellbonuses.HitChanceEffect[HIGHEST_SKILL+1]; - - //Accuracy = Spell Effect , HitChance = 'Accuracy' from Item Effect - //Only AA derived accuracy can be skill limited. ie (Precision of the Pathfinder, Dead Aim) - hitBonus += (attacker->itembonuses.Accuracy[HIGHEST_SKILL+1] + - attacker->spellbonuses.Accuracy[HIGHEST_SKILL+1] + - attacker->aabonuses.Accuracy[HIGHEST_SKILL+1] + - attacker->aabonuses.Accuracy[skillinuse] + - attacker->itembonuses.HitChance) / 15.0f; - - hitBonus += chance_mod; //Modifier applied from casted/disc skill attacks. - - chancetohit += ((chancetohit * hitBonus) / 100.0f); - - if(skillinuse == SkillArchery) - chancetohit -= (chancetohit * RuleR(Combat, ArcheryHitPenalty)) / 100.0f; - - chancetohit = mod_hit_chance(chancetohit, skillinuse, attacker); - - // Chance to hit; Max 95%, Min 30% - if(chancetohit > 1000) { - //if chance to hit is crazy high, that means a discipline is in use, and let it stay there - } - else if(chancetohit > 95) { - chancetohit = 95; - } - else if(chancetohit < 5) { - chancetohit = 5; - } - - //I dont know the best way to handle a garunteed hit discipline being used - //agains a garunteed riposte (for example) discipline... for now, garunteed hit wins - - - #if EQDEBUG>=11 - LogFile->write(EQEMuLog::Debug, "3 FINAL calculated chance to hit is: %5.2f", chancetohit); - #endif - - // - // Did we hit? - // - - float tohit_roll = MakeRandomFloat(0, 100); - - mlog(COMBAT__TOHIT, "Final hit chance: %.2f%%. Hit roll %.2f", chancetohit, tohit_roll); - - return(tohit_roll <= chancetohit); -} - - -bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte) -{ - /* solar: called when a mob is attacked, does the checks to see if it's a hit - * and does other mitigation checks. 'this' is the mob being attacked. - * - * special return values: - * -1 - block - * -2 - parry - * -3 - riposte - * -4 - dodge - * - */ - float skill; - float bonus; - float RollTable[4] = {0,0,0,0}; - float roll; - Mob *attacker=other; - Mob *defender=this; - - //garunteed hit - bool ghit = false; - if((attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500) - ghit = true; - - ////////////////////////////////////////////////////////// - // make enrage same as riposte - ///////////////////////////////////////////////////////// - if (IsEnraged() && other->InFrontMob(this, other->GetX(), other->GetY())) { - damage = -3; - mlog(COMBAT__DAMAGE, "I am enraged, riposting frontal attack."); - } - - ///////////////////////////////////////////////////////// - // riposte - ///////////////////////////////////////////////////////// - float riposte_chance = 0.0f; - if (CanRiposte && damage > 0 && CanThisClassRiposte() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - riposte_chance = (100.0f + (float)defender->aabonuses.RiposteChance + (float)defender->spellbonuses.RiposteChance + (float)defender->itembonuses.RiposteChance) / 100.0f; - skill = GetSkill(SkillRiposte); - if (IsClient()) { - CastToClient()->CheckIncreaseSkill(SkillRiposte, other, -10); - } - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/60.0 + (GetDEX()/200); - bonus *= riposte_chance; - bonus = mod_riposte_chance(bonus, attacker); - RollTable[0] = bonus + (itembonuses.HeroicDEX / 25); // 25 heroic = 1%, applies to ripo, parry, block - } - } - - /////////////////////////////////////////////////////// - // block - /////////////////////////////////////////////////////// - - bool bBlockFromRear = false; - bool bShieldBlockFromRear = false; - - if (this->IsClient()) { - int aaChance = 0; - - // a successful roll on this does not mean a successful block is forthcoming. only that a chance to block - // from a direction other than the rear is granted. - - //Live AA - HightenedAwareness - int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind; - - if (BlockBehindChance && (BlockBehindChance > MakeRandomInt(1, 100))){ - bBlockFromRear = true; - - if (spellbonuses.BlockBehind || itembonuses.BlockBehind) - bShieldBlockFromRear = true; //This bonus should allow a chance to Shield Block from behind. - } - } - - float block_chance = 0.0f; - if (damage > 0 && CanThisClassBlock() && (other->InFrontMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) { - block_chance = (100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f; - skill = CastToClient()->GetSkill(SkillBlock); - if (IsClient()) { - CastToClient()->CheckIncreaseSkill(SkillBlock, other, -10); - } - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/35.0 + (GetDEX()/200); - bonus = mod_block_chance(bonus, attacker); - RollTable[1] = RollTable[0] + (bonus * block_chance); - } - } - else{ - RollTable[1] = RollTable[0]; - } - - if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) - && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { - - float bonusShieldBlock = 0.0f; - bonusShieldBlock = aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock; - RollTable[1] += bonusShieldBlock; - } - - if(damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) - && (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) { - bool equiped2 = CastToClient()->m_inv.GetItem(13); - if(equiped2) { - uint8 TwoHandBlunt = CastToClient()->m_inv.GetItem(13)->GetItem()->ItemType; - float bonusStaffBlock = 0.0f; - if(TwoHandBlunt == ItemType2HBlunt) { - - bonusStaffBlock = aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock; - RollTable[1] += bonusStaffBlock; - } - } - } - - ////////////////////////////////////////////////////// - // parry - ////////////////////////////////////////////////////// - float parry_chance = 0.0f; - if (damage > 0 && CanThisClassParry() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - parry_chance = (100.0f + (float)defender->spellbonuses.ParryChance + (float)defender->itembonuses.ParryChance) / 100.0f; - skill = CastToClient()->GetSkill(SkillParry); - if (IsClient()) { - CastToClient()->CheckIncreaseSkill(SkillParry, other, -10); - } - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/60.0 + (GetDEX()/200); - bonus *= parry_chance; - bonus = mod_parry_chance(bonus, attacker); - RollTable[2] = RollTable[1] + bonus; - } - } - else{ - RollTable[2] = RollTable[1]; - } - - //////////////////////////////////////////////////////// - // dodge - //////////////////////////////////////////////////////// - float dodge_chance = 0.0f; - if (damage > 0 && CanThisClassDodge() && other->InFrontMob(this, other->GetX(), other->GetY())) - { - dodge_chance = (100.0f + (float)defender->spellbonuses.DodgeChance + (float)defender->itembonuses.DodgeChance) / 100.0f; - skill = CastToClient()->GetSkill(SkillDodge); - if (IsClient()) { - CastToClient()->CheckIncreaseSkill(SkillDodge, other, -10); - } - - if (!ghit) { //if they are not using a garunteed hit discipline - bonus = 2.0 + skill/60.0 + (GetAGI()/200); - bonus *= dodge_chance; - //DCBOOMKAR - bonus = mod_dodge_chance(bonus, attacker); - RollTable[3] = RollTable[2] + bonus - (itembonuses.HeroicDEX / 25) + (itembonuses.HeroicAGI / 25); - } - } - else{ - RollTable[3] = RollTable[2]; - } - - if(damage > 0){ - roll = MakeRandomFloat(0,100); - if(roll <= RollTable[0]){ - damage = -3; - } - else if(roll <= RollTable[1]){ - damage = -1; - } - else if(roll <= RollTable[2]){ - damage = -2; - } - else if(roll <= RollTable[3]){ - damage = -4; - } - } - - mlog(COMBAT__DAMAGE, "Final damage after all avoidances: %d", damage); - - if (damage < 0) - return true; - return false; -} - -void Mob::MeleeMitigation(Mob *attacker, int32 &damage, int32 minhit, ExtraAttackOptions *opts) -{ - if (damage <= 0) - return; - - Mob* defender = this; - float aa_mit = (aabonuses.CombatStability + itembonuses.CombatStability + - spellbonuses.CombatStability) / 100.0f; - - if (RuleB(Combat, UseIntervalAC)) { - float softcap = (GetSkill(SkillDefense) + GetLevel()) * - RuleR(Combat, SoftcapFactor) * (1.0 + aa_mit); - float mitigation_rating = 0.0; - float attack_rating = 0.0; - int shield_ac = 0; - int armor = 0; - float weight = 0.0; - - float monkweight = RuleI(Combat, MonkACBonusWeight); - monkweight = mod_monk_weight(monkweight, attacker); - - if (IsClient()) { - armor = CastToClient()->GetRawACNoShield(shield_ac); - weight = (CastToClient()->CalcCurrentWeight() / 10.0); - } else if (IsNPC()) { - armor = CastToNPC()->GetRawAC(); - - if (!IsPet()) - armor = (armor / RuleR(Combat, NPCACFactor)); - - armor += spellbonuses.AC + itembonuses.AC + 1; - } - - if (opts) { - armor *= (1.0f - opts->armor_pen_percent); - armor -= opts->armor_pen_flat; - } - - if (RuleB(Combat, OldACSoftcapRules)) { - if (GetClass() == WIZARD || GetClass() == MAGICIAN || - GetClass() == NECROMANCER || GetClass() == ENCHANTER) - softcap = RuleI(Combat, ClothACSoftcap); - else if (GetClass() == MONK && weight <= monkweight) - softcap = RuleI(Combat, MonkACSoftcap); - else if(GetClass() == DRUID || GetClass() == BEASTLORD || GetClass() == MONK) - softcap = RuleI(Combat, LeatherACSoftcap); - else if(GetClass() == SHAMAN || GetClass() == ROGUE || - GetClass() == BERSERKER || GetClass() == RANGER) - softcap = RuleI(Combat, ChainACSoftcap); - else - softcap = RuleI(Combat, PlateACSoftcap); - } - - softcap += shield_ac; - armor += shield_ac; - if (RuleB(Combat, OldACSoftcapRules)) - softcap += (softcap * (aa_mit * RuleR(Combat, AAMitigationACFactor))); - if (armor > softcap) { - int softcap_armor = armor - softcap; - if (RuleB(Combat, OldACSoftcapRules)) { - if (GetClass() == WARRIOR) - softcap_armor = softcap_armor * RuleR(Combat, WarriorACSoftcapReturn); - else if (GetClass() == SHADOWKNIGHT || GetClass() == PALADIN || - (GetClass() == MONK && weight <= monkweight)) - softcap_armor = softcap_armor * RuleR(Combat, KnightACSoftcapReturn); - else if (GetClass() == CLERIC || GetClass() == BARD || - GetClass() == BERSERKER || GetClass() == ROGUE || - GetClass() == SHAMAN || GetClass() == MONK) - softcap_armor = softcap_armor * RuleR(Combat, LowPlateChainACSoftcapReturn); - else if (GetClass() == RANGER || GetClass() == BEASTLORD) - softcap_armor = softcap_armor * RuleR(Combat, LowChainLeatherACSoftcapReturn); - else if (GetClass() == WIZARD || GetClass() == MAGICIAN || - GetClass() == NECROMANCER || GetClass() == ENCHANTER || - GetClass() == DRUID) - softcap_armor = softcap_armor * RuleR(Combat, CasterACSoftcapReturn); - else - softcap_armor = softcap_armor * RuleR(Combat, MiscACSoftcapReturn); - } else { - if (GetClass() == WARRIOR) - softcap_armor *= RuleR(Combat, WarACSoftcapReturn); - else if (GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) - softcap_armor *= RuleR(Combat, PalShdACSoftcapReturn); - else if (GetClass() == CLERIC || GetClass() == RANGER || - GetClass() == MONK || GetClass() == BARD) - softcap_armor *= RuleR(Combat, ClrRngMnkBrdACSoftcapReturn); - else if (GetClass() == DRUID || GetClass() == NECROMANCER || - GetClass() == WIZARD || GetClass() == ENCHANTER || - GetClass() == MAGICIAN) - softcap_armor *= RuleR(Combat, DruNecWizEncMagACSoftcapReturn); - else if (GetClass() == ROGUE || GetClass() == SHAMAN || - GetClass() == BEASTLORD || GetClass() == BERSERKER) - softcap_armor *= RuleR(Combat, RogShmBstBerACSoftcapReturn); - else - softcap_armor *= RuleR(Combat, MiscACSoftcapReturn); - } - armor = softcap + softcap_armor; - } - - if (GetClass() == WIZARD || GetClass() == MAGICIAN || - GetClass() == NECROMANCER || GetClass() == ENCHANTER) - mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 4.0) + armor + 1; - else - mitigation_rating = ((GetSkill(SkillDefense) + itembonuses.HeroicAGI/10) / 3.0) + (armor * 1.333333) + 1; - mitigation_rating *= 0.847; - - mitigation_rating = mod_mitigation_rating(mitigation_rating, attacker); - - if (attacker->IsClient()) - attack_rating = (attacker->CastToClient()->CalcATK() + ((attacker->GetSTR()-66) * 0.9) + (attacker->GetSkill(SkillOffense)*1.345)); - else - attack_rating = (attacker->GetATK() + (attacker->GetSkill(SkillOffense)*1.345) + ((attacker->GetSTR()-66) * 0.9)); - - attack_rating = attacker->mod_attack_rating(attack_rating, this); - - damage = GetMeleeMitDmg(attacker, damage, minhit, mitigation_rating, attack_rating); - } else { - //////////////////////////////////////////////////////// - // Scorpious2k: Include AC in the calculation - // use serverop variables to set values - int myac = GetAC(); - if(opts) { - myac *= (1.0f - opts->armor_pen_percent); - myac -= opts->armor_pen_flat; - } - - if (damage > 0 && myac > 0) { - int acfail=1000; - char tmp[10]; - - if (database.GetVariable("ACfail", tmp, 9)) { - acfail = (int) (atof(tmp) * 100); - if (acfail>100) acfail=100; - } - - if (acfail<=0 || MakeRandomInt(0, 100)>acfail) { - float acreduction=1; - int acrandom=300; - if (database.GetVariable("ACreduction", tmp, 9)) - { - acreduction=atof(tmp); - if (acreduction>100) acreduction=100; - } - - if (database.GetVariable("ACrandom", tmp, 9)) - { - acrandom = (int) ((atof(tmp)+1) * 100); - if (acrandom>10100) acrandom=10100; - } - - if (acreduction>0) { - damage -= (int) (GetAC() * acreduction/100.0f); - } - if (acrandom>0) { - damage -= (myac * MakeRandomInt(0, acrandom) / 10000); - } - if (damage<1) damage=1; - mlog(COMBAT__DAMAGE, "AC Damage Reduction: fail chance %d%%. Failed. Reduction %.3f%%, random %d. Resulting damage %d.", acfail, acreduction, acrandom, damage); - } else { - mlog(COMBAT__DAMAGE, "AC Damage Reduction: fail chance %d%%. Did not fail.", acfail); - } - } - - damage -= (aa_mit * damage); - - if(damage != 0 && damage < minhit) - damage = minhit; - //reduce the damage from shielding item and aa based on the min dmg - //spells offer pure mitigation - damage -= (minhit * defender->itembonuses.MeleeMitigation / 100); - damage -= (damage * defender->spellbonuses.MeleeMitigation / 100); - } - - if (damage < 0) - damage = 0; -} - -// This is called when the Mob is the one being hit -int32 Mob::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, - float mit_rating, float atk_rating) -{ - float d = 10.0; - float mit_roll = MakeRandomFloat(0, mit_rating); - float atk_roll = MakeRandomFloat(0, atk_rating); - - if (atk_roll > mit_roll) { - float a_diff = atk_roll - mit_roll; - float thac0 = atk_rating * RuleR(Combat, ACthac0Factor); - float thac0cap = attacker->GetLevel() * 9 + 20; - if (thac0 > thac0cap) - thac0 = thac0cap; - - d -= 10.0 * (a_diff / thac0); - } else if (mit_roll > atk_roll) { - float m_diff = mit_roll - atk_roll; - float thac20 = mit_rating * RuleR(Combat, ACthac20Factor); - float thac20cap = GetLevel() * 9 + 20; - if (thac20 > thac20cap) - thac20 = thac20cap; - - d += 10.0 * (m_diff / thac20); - } - - if (d < 0.0) - d = 0.0; - else if (d > 20.0) - d = 20.0; - - float interval = (damage - minhit) / 20.0; - damage -= ((int)d * interval); - - damage -= (minhit * itembonuses.MeleeMitigation / 100); - damage -= (damage * spellbonuses.MeleeMitigation / 100); - return damage; -} - -// This is called when the Client is the one being hit -int32 Client::GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, - float mit_rating, float atk_rating) -{ - if (!attacker->IsNPC() || RuleB(Combat, UseOldDamageIntervalRules)) - return Mob::GetMeleeMitDmg(attacker, damage, minhit, mit_rating, atk_rating); - int d = 10; - // floats for the rounding issues - float dmg_interval = (damage - minhit) / 19.0; - float dmg_bonus = minhit - dmg_interval; - float spellMeleeMit = spellbonuses.MeleeMitigation / 100.0; - if (GetClass() == WARRIOR) - spellMeleeMit += 0.05; - dmg_bonus -= dmg_bonus * (itembonuses.MeleeMitigation / 100.0); - dmg_interval -= dmg_interval * spellMeleeMit; - - float mit_roll = MakeRandomFloat(0, mit_rating); - float atk_roll = MakeRandomFloat(0, atk_rating); - - if (atk_roll > mit_roll) { - float a_diff = atk_roll - mit_roll; - float thac0 = atk_rating * RuleR(Combat, ACthac0Factor); - float thac0cap = attacker->GetLevel() * 9 + 20; - if (thac0 > thac0cap) - thac0 = thac0cap; - - d += 10 * (a_diff / thac0); - } else if (mit_roll > atk_roll) { - float m_diff = mit_roll - atk_roll; - float thac20 = mit_rating * RuleR(Combat, ACthac20Factor); - float thac20cap = GetLevel() * 9 + 20; - if (thac20 > thac20cap) - thac20 = thac20cap; - - d -= 10 * (m_diff / thac20); - } - - if (d < 1) - d = 1; - else if (d > 20) - d = 20; - - return static_cast((dmg_bonus + dmg_interval * d)); -} - -//Returns the weapon damage against the input mob -//if we cannot hit the mob with the current weapon we will get a value less than or equal to zero -//Else we know we can hit. -//GetWeaponDamage(mob*, const Item_Struct*) is intended to be used for mobs or any other situation where we do not have a client inventory item -//GetWeaponDamage(mob*, const ItemInst*) is intended to be used for situations where we have a client inventory item -int Mob::GetWeaponDamage(Mob *against, const Item_Struct *weapon_item) { - int dmg = 0; - int banedmg = 0; - - //can't hit invulnerable stuff with weapons. - if(against->GetInvul() || against->GetSpecialAbility(IMMUNE_MELEE)){ - return 0; - } - - //check to see if our weapons or fists are magical. - if(against->GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)){ - if(weapon_item){ - if(weapon_item->Magic){ - dmg = weapon_item->Damage; - - //this is more for non weapon items, ex: boots for kick - //they don't have a dmg but we should be able to hit magical - dmg = dmg <= 0 ? 1 : dmg; - } - else - return 0; - } - else{ - if((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30){ - dmg = GetMonkHandToHandDamage(); - } - else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ - //pets wouldn't actually use this but... - //it gives us an idea if we can hit due to the dual nature of this function - dmg = 1; - } - else if(GetSpecialAbility(SPECATK_MAGICAL)) - { - dmg = 1; - } - else - return 0; - } - } - else{ - if(weapon_item){ - dmg = weapon_item->Damage; - - dmg = dmg <= 0 ? 1 : dmg; - } - else{ - if(GetClass() == MONK || GetClass() == BEASTLORD){ - dmg = GetMonkHandToHandDamage(); - } - else{ - dmg = 1; - } - } - } - - int eledmg = 0; - if(!against->GetSpecialAbility(IMMUNE_MAGIC)){ - if(weapon_item && weapon_item->ElemDmgAmt){ - //we don't check resist for npcs here - eledmg = weapon_item->ElemDmgAmt; - dmg += eledmg; - } - } - - if(against->GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE)){ - if(weapon_item){ - if(weapon_item->BaneDmgBody == against->GetBodyType()){ - banedmg += weapon_item->BaneDmgAmt; - } - - if(weapon_item->BaneDmgRace == against->GetRace()){ - banedmg += weapon_item->BaneDmgRaceAmt; - } - } - - if(!eledmg && !banedmg){ - if(!GetSpecialAbility(SPECATK_BANE)) - return 0; - else - return 1; - } - else - dmg += banedmg; - } - else{ - if(weapon_item){ - if(weapon_item->BaneDmgBody == against->GetBodyType()){ - banedmg += weapon_item->BaneDmgAmt; - } - - if(weapon_item->BaneDmgRace == against->GetRace()){ - banedmg += weapon_item->BaneDmgRaceAmt; - } - } - - dmg += (banedmg + eledmg); - } - - if(dmg <= 0){ - return 0; - } - else - return dmg; -} - -int Mob::GetWeaponDamage(Mob *against, const ItemInst *weapon_item, uint32 *hate) -{ - int dmg = 0; - int banedmg = 0; - - if(!against || against->GetInvul() || against->GetSpecialAbility(IMMUNE_MELEE)){ - return 0; - } - - //check for items being illegally attained - if(weapon_item){ - const Item_Struct *mWeaponItem = weapon_item->GetItem(); - if(mWeaponItem){ - if(mWeaponItem->ReqLevel > GetLevel()){ - return 0; - } - - if(!weapon_item->IsEquipable(GetBaseRace(), GetClass())){ - return 0; - } - } - else{ - return 0; - } - } - - if(against->GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)){ - if(weapon_item){ - // check to see if the weapon is magic - bool MagicWeapon = false; - if(weapon_item->GetItem() && weapon_item->GetItem()->Magic) - MagicWeapon = true; - else { - if(spellbonuses.MagicWeapon || itembonuses.MagicWeapon) - MagicWeapon = true; - } - - if(MagicWeapon) { - - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - dmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->Damage); - } - else{ - dmg = weapon_item->GetItem()->Damage; - } - - for(int x = 0; x < MAX_AUGMENT_SLOTS; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - dmg += weapon_item->GetAugment(x)->GetItem()->Damage; - if (hate) *hate += weapon_item->GetAugment(x)->GetItem()->Damage + weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt; - } - } - dmg = dmg <= 0 ? 1 : dmg; - } - else - return 0; - } - else{ - if((GetClass() == MONK || GetClass() == BEASTLORD) && GetLevel() >= 30){ - dmg = GetMonkHandToHandDamage(); - if (hate) *hate += dmg; - } - else if(GetOwner() && GetLevel() >= RuleI(Combat, PetAttackMagicLevel)){ //pets wouldn't actually use this but... - dmg = 1; //it gives us an idea if we can hit - } - else if(GetSpecialAbility(SPECATK_MAGICAL)){ - dmg = 1; - } - else - return 0; - } - } - else{ - if(weapon_item){ - if(weapon_item->GetItem()){ - - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - dmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->Damage); - } - else{ - dmg = weapon_item->GetItem()->Damage; - } - - for(int x = 0; x < MAX_AUGMENT_SLOTS; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - dmg += weapon_item->GetAugment(x)->GetItem()->Damage; - if (hate) *hate += weapon_item->GetAugment(x)->GetItem()->Damage + weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt; - } - } - dmg = dmg <= 0 ? 1 : dmg; - } - } - else{ - if(GetClass() == MONK || GetClass() == BEASTLORD){ - dmg = GetMonkHandToHandDamage(); - if (hate) *hate += dmg; - } - else{ - dmg = 1; - } - } - } - - int eledmg = 0; - if(!against->GetSpecialAbility(IMMUNE_MAGIC)){ - if(weapon_item && weapon_item->GetItem() && weapon_item->GetItem()->ElemDmgAmt){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - eledmg = CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->ElemDmgAmt); - } - else{ - eledmg = weapon_item->GetItem()->ElemDmgAmt; - } - - if(eledmg) - { - eledmg = (eledmg * against->ResistSpell(weapon_item->GetItem()->ElemDmgType, 0, this) / 100); - } - } - - if(weapon_item){ - for(int x = 0; x < MAX_AUGMENT_SLOTS; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - if(weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt) - eledmg += (weapon_item->GetAugment(x)->GetItem()->ElemDmgAmt * against->ResistSpell(weapon_item->GetAugment(x)->GetItem()->ElemDmgType, 0, this) / 100); - } - } - } - } - - if(against->GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE)){ - if(weapon_item && weapon_item->GetItem()){ - if(weapon_item->GetItem()->BaneDmgBody == against->GetBodyType()){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgAmt); - } - else{ - banedmg += weapon_item->GetItem()->BaneDmgAmt; - } - } - - if(weapon_item->GetItem()->BaneDmgRace == against->GetRace()){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgRaceAmt); - } - else{ - banedmg += weapon_item->GetItem()->BaneDmgRaceAmt; - } - } - - for(int x = 0; x < MAX_AUGMENT_SLOTS; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - if(weapon_item->GetAugment(x)->GetItem()->BaneDmgBody == against->GetBodyType()){ - banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgAmt; - } - - if(weapon_item->GetAugment(x)->GetItem()->BaneDmgRace == against->GetRace()){ - banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgRaceAmt; - } - } - } - } - - if(!eledmg && !banedmg) - { - if(!GetSpecialAbility(SPECATK_BANE)) - return 0; - else - return 1; - } - else { - dmg += (banedmg + eledmg); - if (hate) *hate += banedmg; - } - } - else{ - if(weapon_item && weapon_item->GetItem()){ - if(weapon_item->GetItem()->BaneDmgBody == against->GetBodyType()){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgAmt); - } - else{ - banedmg += weapon_item->GetItem()->BaneDmgAmt; - } - } - - if(weapon_item->GetItem()->BaneDmgRace == against->GetRace()){ - if(IsClient() && GetLevel() < weapon_item->GetItem()->RecLevel){ - banedmg += CastToClient()->CalcRecommendedLevelBonus(GetLevel(), weapon_item->GetItem()->RecLevel, weapon_item->GetItem()->BaneDmgRaceAmt); - } - else{ - banedmg += weapon_item->GetItem()->BaneDmgRaceAmt; - } - } - - for(int x = 0; x < MAX_AUGMENT_SLOTS; x++){ - if(weapon_item->GetAugment(x) && weapon_item->GetAugment(x)->GetItem()){ - if(weapon_item->GetAugment(x)->GetItem()->BaneDmgBody == against->GetBodyType()){ - banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgAmt; - } - - if(weapon_item->GetAugment(x)->GetItem()->BaneDmgRace == against->GetRace()){ - banedmg += weapon_item->GetAugment(x)->GetItem()->BaneDmgRaceAmt; - } - } - } - } - dmg += (banedmg + eledmg); - if (hate) *hate += banedmg; - } - - if(dmg <= 0){ - return 0; - } - else - return dmg; -} - -//note: throughout this method, setting `damage` to a negative is a way to -//stop the attack calculations -// IsFromSpell added to allow spell effects to use Attack. (Mainly for the Rampage AA right now.) -bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) -{ - if (!other) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Client::Attack() for evaluation!"); - return false; - } - - if(!GetTarget()) - SetTarget(other); - - mlog(COMBAT__ATTACKS, "Attacking %s with hand %d %s", other?other->GetName():"(nullptr)", Hand, bRiposte?"(this is a riposte)":""); - - //SetAttackTimer(); - if ( - (IsCasting() && GetClass() != BARD && !IsFromSpell) - || other == nullptr - || ((IsClient() && CastToClient()->dead) || (other->IsClient() && other->CastToClient()->dead)) - || (GetHP() < 0) - || (!IsAttackAllowed(other)) - ) { - mlog(COMBAT__ATTACKS, "Attack canceled, invalid circumstances."); - return false; // Only bards can attack while casting - } - - if(DivineAura() && !GetGM()) {//cant attack while invulnerable unless your a gm - mlog(COMBAT__ATTACKS, "Attack canceled, Divine Aura is in effect."); - Message_StringID(MT_DefaultText, DIVINE_AURA_NO_ATK); //You can't attack while invulnerable! - return false; - } - - if (GetFeigned()) - return false; // Rogean: How can you attack while feigned? Moved up from Aggro Code. - - - ItemInst* weapon; - if (Hand == 14){ // Kaiyodo - Pick weapon from the attacking hand - weapon = GetInv().GetItem(SLOT_SECONDARY); - OffHandAtk(true); - } - else{ - weapon = GetInv().GetItem(SLOT_PRIMARY); - OffHandAtk(false); - } - - if(weapon != nullptr) { - if (!weapon->IsWeapon()) { - mlog(COMBAT__ATTACKS, "Attack canceled, Item %s (%d) is not a weapon.", weapon->GetItem()->Name, weapon->GetID()); - return(false); - } - mlog(COMBAT__ATTACKS, "Attacking with weapon: %s (%d)", weapon->GetItem()->Name, weapon->GetID()); - } else { - mlog(COMBAT__ATTACKS, "Attacking without a weapon."); - } - - // calculate attack_skill and skillinuse depending on hand and weapon - // also send Packet to near clients - SkillUseTypes skillinuse; - AttackAnimation(skillinuse, Hand, weapon); - mlog(COMBAT__ATTACKS, "Attacking with %s in slot %d using skill %d", weapon?weapon->GetItem()->Name:"Fist", Hand, skillinuse); - - /// Now figure out damage - int damage = 0; - uint8 mylevel = GetLevel() ? GetLevel() : 1; - uint32 hate = 0; - if (weapon) hate = weapon->GetItem()->Damage + weapon->GetItem()->ElemDmgAmt; - int weapon_damage = GetWeaponDamage(other, weapon, &hate); - if (hate == 0 && weapon_damage > 1) hate = weapon_damage; - - //if weapon damage > 0 then we know we can hit the target with this weapon - //otherwise we cannot and we set the damage to -5 later on - if(weapon_damage > 0){ - - //Berserker Berserk damage bonus - if(IsBerserk() && GetClass() == BERSERKER){ - int bonus = 3 + GetLevel()/10; //unverified - weapon_damage = weapon_damage * (100+bonus) / 100; - mlog(COMBAT__DAMAGE, "Berserker damage bonus increases DMG to %d", weapon_damage); - } - - //try a finishing blow.. if successful end the attack - if(TryFinishingBlow(other, skillinuse)) - return (true); - - int min_hit = 1; - int max_hit = (2*weapon_damage*GetDamageTable(skillinuse)) / 100; - - if(GetLevel() < 10 && max_hit > RuleI(Combat, HitCapPre10)) - max_hit = (RuleI(Combat, HitCapPre10)); - else if(GetLevel() < 20 && max_hit > RuleI(Combat, HitCapPre20)) - max_hit = (RuleI(Combat, HitCapPre20)); - - CheckIncreaseSkill(skillinuse, other, -15); - CheckIncreaseSkill(SkillOffense, other, -15); - - - // *************************************************************** - // *** Calculate the damage bonus, if applicable, for this hit *** - // *************************************************************** - -#ifndef EQEMU_NO_WEAPON_DAMAGE_BONUS - - // If you include the preprocessor directive "#define EQEMU_NO_WEAPON_DAMAGE_BONUS", that indicates that you do not - // want damage bonuses added to weapon damage at all. This feature was requested by ChaosSlayer on the EQEmu Forums. - // - // This is not recommended for normal usage, as the damage bonus represents a non-trivial component of the DPS output - // of weapons wielded by higher-level melee characters (especially for two-handed weapons). - - int ucDamageBonus = 0; - - if( Hand == 13 && GetLevel() >= 28 && IsWarriorClass() ) - { - // Damage bonuses apply only to hits from the main hand (Hand == 13) by characters level 28 and above - // who belong to a melee class. If we're here, then all of these conditions apply. - - ucDamageBonus = GetWeaponDamageBonus( weapon ? weapon->GetItem() : (const Item_Struct*) nullptr ); - - min_hit += (int) ucDamageBonus; - max_hit += (int) ucDamageBonus; - hate += ucDamageBonus; - } -#endif - //Live AA - Sinister Strikes *Adds weapon damage bonus to offhand weapon. - if (Hand==14) { - if (aabonuses.SecondaryDmgInc || itembonuses.SecondaryDmgInc || spellbonuses.SecondaryDmgInc){ - - ucDamageBonus = GetWeaponDamageBonus( weapon ? weapon->GetItem() : (const Item_Struct*) nullptr ); - - min_hit += (int) ucDamageBonus; - max_hit += (int) ucDamageBonus; - hate += ucDamageBonus; - } - } - - min_hit += min_hit * GetMeleeMinDamageMod_SE(skillinuse) / 100; - - if(max_hit < min_hit) - max_hit = min_hit; - - if(RuleB(Combat, UseIntervalAC)) - damage = max_hit; - else - damage = MakeRandomInt(min_hit, max_hit); - - damage = mod_client_damage(damage, skillinuse, Hand, weapon, other); - - mlog(COMBAT__DAMAGE, "Damage calculated to %d (min %d, max %d, str %d, skill %d, DMG %d, lv %d)", - damage, min_hit, max_hit, GetSTR(), GetSkill(skillinuse), weapon_damage, mylevel); - - if(opts) { - damage *= opts->damage_percent; - damage += opts->damage_flat; - hate *= opts->hate_percent; - hate += opts->hate_flat; - } - - //check to see if we hit.. - if(!other->CheckHitChance(this, skillinuse, Hand)) { - mlog(COMBAT__ATTACKS, "Attack missed. Damage set to 0."); - damage = 0; - } else { //we hit, try to avoid it - other->AvoidDamage(this, damage); - other->MeleeMitigation(this, damage, min_hit, opts); - if(damage > 0) { - ApplyMeleeDamageBonus(skillinuse, damage); - damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); - TryCriticalHit(other, skillinuse, damage, opts); - } - mlog(COMBAT__DAMAGE, "Final damage after all reductions: %d", damage); - } - - //riposte - bool slippery_attack = false; // Part of hack to allow riposte to become a miss, but still allow a Strikethrough chance (like on Live) - if (damage == -3) { - if (bRiposte) return false; - else { - if (Hand == 14) {// Do we even have it & was attack with mainhand? If not, don't bother with other calculations - //Live AA - SlipperyAttacks - //This spell effect most likely directly modifies the actual riposte chance when using offhand attack. - int16 OffhandRiposteFail = aabonuses.OffhandRiposteFail + itembonuses.OffhandRiposteFail + spellbonuses.OffhandRiposteFail; - OffhandRiposteFail *= -1; //Live uses a negative value for this. - - if (OffhandRiposteFail && - (OffhandRiposteFail > 99 || (MakeRandomInt(0, 100) < OffhandRiposteFail))) { - damage = 0; // Counts as a miss - slippery_attack = true; - } else - DoRiposte(other); - if (IsDead()) return false; - } - else - DoRiposte(other); - if (IsDead()) return false; - } - } - - if (((damage < 0) || slippery_attack) && !bRiposte && !IsStrikethrough) { // Hack to still allow Strikethrough chance w/ Slippery Attacks AA - int16 bonusStrikeThrough = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough; - - if(bonusStrikeThrough && (MakeRandomInt(0, 100) < bonusStrikeThrough)) { - Message_StringID(MT_StrikeThrough, STRIKETHROUGH_STRING); // You strike through your opponents defenses! - Attack(other, Hand, false, true); // Strikethrough only gives another attempted hit - return false; - } - } - } - else{ - damage = -5; - } - - // Hate Generation is on a per swing basis, regardless of a hit, miss, or block, its always the same. - // If we are this far, this means we are atleast making a swing. - - if (!bRiposte) // Ripostes never generate any aggro. - other->AddToHateList(this, hate); - - /////////////////////////////////////////////////////////// - ////// Send Attack Damage - /////////////////////////////////////////////////////////// - other->Damage(this, damage, SPELL_UNKNOWN, skillinuse); - - if (IsDead()) return false; - - MeleeLifeTap(damage); - - if (damage > 0) - CheckNumHitsRemaining(5); - - //break invis when you attack - if(invisible) { - mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); - BuffFadeByEffect(SE_Invisibility); - BuffFadeByEffect(SE_Invisibility2); - invisible = false; - } - if(invisible_undead) { - mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); - BuffFadeByEffect(SE_InvisVsUndead); - BuffFadeByEffect(SE_InvisVsUndead2); - invisible_undead = false; - } - if(invisible_animals){ - mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); - BuffFadeByEffect(SE_InvisVsAnimals); - invisible_animals = false; - } - - if(hidden || improved_hidden){ - hidden = false; - improved_hidden = false; - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } - - if(GetTarget()) - TriggerDefensiveProcs(weapon, other, Hand, damage); - - if (damage > 0) - return true; - - else - return false; -} - -//used by complete heal and #heal -void Mob::Heal() -{ - SetMaxHP(); - SendHPUpdate(); -} - -void Client::Damage(Mob* other, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) -{ - if(dead || IsCorpse()) - return; - - if(spell_id==0) - spell_id = SPELL_UNKNOWN; - - if(spell_id!=0 && spell_id != SPELL_UNKNOWN && other && damage > 0) - { - if(other->IsNPC() && !other->IsPet()) - { - float npcspellscale = other->CastToNPC()->GetSpellScale(); - damage = ((float)damage * npcspellscale) / (float)100; - } - } - - // cut all PVP spell damage to 2/3 -solar - // EverHood - Blasting ourselfs is considered PvP - //Don't do PvP mitigation if the caster is damaging himself - if(other && other->IsClient() && (other != this) && damage > 0) { - int PvPMitigation = 100; - if(attack_skill == SkillArchery) - PvPMitigation = 80; - else - PvPMitigation = 67; - damage = (damage * PvPMitigation) / 100; - } - - if(!ClientFinishedLoading()) - damage = -5; - - //do a majority of the work... - CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); - - if (damage > 0) { - - if (spell_id == SPELL_UNKNOWN) - CheckIncreaseSkill(SkillDefense, other, -15); - } -} - -bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack_skill) -{ - if(!ClientFinishedLoading()) - return false; - - if(dead) - return false; //cant die more than once... - - if(!spell) - spell = SPELL_UNKNOWN; - - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill)); - if(parse->EventPlayer(EVENT_DEATH, this, buffer, 0) != 0) { - if(GetHP() < 0) { - SetHP(0); - } - return false; - } - - if(killerMob && killerMob->IsClient() && (spell != SPELL_UNKNOWN) && damage > 0) { - char val1[20]={0}; - entity_list.MessageClose_StringID(this, false, 100, MT_NonMelee, HIT_NON_MELEE, - killerMob->GetCleanName(), GetCleanName(), ConvertArray(damage, val1)); - } - - int exploss = 0; - mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob ? killerMob->GetName() : "Unknown", damage, spell, attack_skill); - - // - // #1: Send death packet to everyone - // - uint8 killed_level = GetLevel(); - - SendLogoutPackets(); - - //make our become corpse packet, and queue to ourself before OP_Death. - EQApplicationPacket app2(OP_BecomeCorpse, sizeof(BecomeCorpse_Struct)); - BecomeCorpse_Struct* bc = (BecomeCorpse_Struct*)app2.pBuffer; - bc->spawn_id = GetID(); - bc->x = GetX(); - bc->y = GetY(); - bc->z = GetZ(); - QueuePacket(&app2); - - // make death packet - EQApplicationPacket app(OP_Death, sizeof(Death_Struct)); - Death_Struct* d = (Death_Struct*)app.pBuffer; - d->spawn_id = GetID(); - d->killer_id = killerMob ? killerMob->GetID() : 0; - d->corpseid=GetID(); - d->bindzoneid = m_pp.binds[0].zoneId; - d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell; - d->attack_skill = spell != SPELL_UNKNOWN ? 0xe7 : attack_skill; - d->damage = damage; - app.priority = 6; - entity_list.QueueClients(this, &app); - - // - // #2: figure out things that affect the player dying and mark them dead - // - - InterruptSpell(); - SetPet(0); - SetHorseId(0); - dead = true; - - if(GetMerc()) { - GetMerc()->Suspend(); - } - - if (killerMob != nullptr) - { - if (killerMob->IsNPC()) { - parse->EventNPC(EVENT_SLAY, killerMob->CastToNPC(), this, "", 0); - - mod_client_death_npc(killerMob); - - uint16 emoteid = killerMob->GetEmoteID(); - if(emoteid != 0) - killerMob->CastToNPC()->DoNPCEmote(KILLEDPC,emoteid); - killerMob->TrySpellOnKill(killed_level,spell); - } - - if(killerMob->IsClient() && (IsDueling() || killerMob->CastToClient()->IsDueling())) { - SetDueling(false); - SetDuelTarget(0); - if (killerMob->IsClient() && killerMob->CastToClient()->IsDueling() && killerMob->CastToClient()->GetDuelTarget() == GetID()) - { - //if duel opponent killed us... - killerMob->CastToClient()->SetDueling(false); - killerMob->CastToClient()->SetDuelTarget(0); - entity_list.DuelMessage(killerMob,this,false); - - mod_client_death_duel(killerMob); - - } else { - //otherwise, we just died, end the duel. - Mob* who = entity_list.GetMob(GetDuelTarget()); - if(who && who->IsClient()) { - who->CastToClient()->SetDueling(false); - who->CastToClient()->SetDuelTarget(0); - } - } - } - } - - entity_list.RemoveFromTargets(this); - hate_list.RemoveEnt(this); - RemoveAutoXTargets(); - - - //remove ourself from all proximities - ClearAllProximities(); - - // - // #3: exp loss and corpse generation - // - - // figure out if they should lose exp - if(RuleB(Character, UseDeathExpLossMult)){ - float GetNum [] = {0.005f,0.015f,0.025f,0.035f,0.045f,0.055f,0.065f,0.075f,0.085f,0.095f,0.110f }; - int Num = RuleI(Character, DeathExpLossMultiplier); - if((Num < 0) || (Num > 10)) - Num = 3; - float loss = GetNum[Num]; - exploss=(int)((float)GetEXP() * (loss)); //loose % of total XP pending rule (choose 0-10) - } - - if(!RuleB(Character, UseDeathExpLossMult)){ - exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000); - } - - if( (GetLevel() < RuleI(Character, DeathExpLossLevel)) || (GetLevel() > RuleI(Character, DeathExpLossMaxLevel)) || IsBecomeNPC() ) - { - exploss = 0; - } - else if( killerMob ) - { - if( killerMob->IsClient() ) - { - exploss = 0; - } - else if( killerMob->GetOwner() && killerMob->GetOwner()->IsClient() ) - { - exploss = 0; - } - } - - if(spell != SPELL_UNKNOWN) - { - uint32 buff_count = GetMaxTotalSlots(); - for(uint16 buffIt = 0; buffIt < buff_count; buffIt++) - { - if(buffs[buffIt].spellid == spell && buffs[buffIt].client) - { - exploss = 0; // no exp loss for pvp dot - break; - } - } - } - - bool LeftCorpse = false; - - // now we apply the exp loss, unmem their spells, and make a corpse - // unless they're a GM (or less than lvl 10 - if(!GetGM()) - { - if(exploss > 0) { - int32 newexp = GetEXP(); - if(exploss > newexp) { - //lost more than we have... wtf.. - newexp = 1; - } else { - newexp -= exploss; - } - SetEXP(newexp, GetAAXP()); - //m_epp.perAA = 0; //reset to no AA exp on death. - } - - //this generates a lot of 'updates' to the client that the client does not need - BuffFadeNonPersistDeath(); - if((GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover)) - UnmemSpellAll(true); - else - UnmemSpellAll(false); - - if(RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel) || RuleB(Character, LeaveNakedCorpses)) - { - // creating the corpse takes the cash/items off the player too - Corpse *new_corpse = new Corpse(this, exploss); - - char tmp[20]; - database.GetVariable("ServerType", tmp, 9); - if(atoi(tmp)==1 && killerMob != nullptr && killerMob->IsClient()){ - char tmp2[10] = {0}; - database.GetVariable("PvPreward", tmp, 9); - int reward = atoi(tmp); - if(reward==3){ - database.GetVariable("PvPitem", tmp2, 9); - int pvpitem = atoi(tmp2); - if(pvpitem>0 && pvpitem<200000) - new_corpse->SetPKItem(pvpitem); - } - else if(reward==2) - new_corpse->SetPKItem(-1); - else if(reward==1) - new_corpse->SetPKItem(1); - else - new_corpse->SetPKItem(0); - if(killerMob->CastToClient()->isgrouped) { - Group* group = entity_list.GetGroupByClient(killerMob->CastToClient()); - if(group != 0) - { - for(int i=0;i<6;i++) - { - if(group->members[i] != nullptr) - { - new_corpse->AllowMobLoot(group->members[i],i); - } - } - } - } - } - - entity_list.AddCorpse(new_corpse, GetID()); - SetID(0); - - //send the become corpse packet to everybody else in the zone. - entity_list.QueueClients(this, &app2, true); - - LeftCorpse = true; - } - -// if(!IsLD())//Todo: make it so an LDed client leaves corpse if its enabled -// MakeCorpse(exploss); - } else { - BuffFadeDetrimental(); - } - - // - // Finally, send em home - // - - // we change the mob variables, not pp directly, because Save() will copy - // from these and overwrite what we set in pp anyway - // - - if(LeftCorpse && (GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover)) - { - ClearDraggedCorpses(); - - RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000); - - SendRespawnBinds(); - } - else - { - if(isgrouped) - { - Group *g = GetGroup(); - if(g) - g->MemberZoned(this); - } - - Raid* r = entity_list.GetRaidByClient(this); - - if(r) - r->MemberZoned(this); - - dead_timer.Start(5000, true); - - m_pp.zone_id = m_pp.binds[0].zoneId; - m_pp.zoneInstance = 0; - database.MoveCharacterToZone(this->CharacterID(), database.GetZoneName(m_pp.zone_id)); - - Save(); - - GoToDeath(); - } - - parse->EventPlayer(EVENT_DEATH_COMPLETE, this, buffer, 0); - return true; -} - -bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts) -{ - int damage = 0; - - if (!other) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to NPC::Attack() for evaluation!"); - return false; - } - - if(DivineAura()) - return(false); - - if(!GetTarget()) - SetTarget(other); - - //Check that we can attack before we calc heading and face our target - if (!IsAttackAllowed(other)) { - if (this->GetOwnerID()) - this->Say_StringID(NOT_LEGAL_TARGET); - if(other) { - if (other->IsClient()) - other->CastToClient()->RemoveXTarget(this, false); - RemoveFromHateList(other); - mlog(COMBAT__ATTACKS, "I am not allowed to attack %s", other->GetName()); - } - return false; - } - - FaceTarget(GetTarget()); - - SkillUseTypes skillinuse = SkillHandtoHand; - if (Hand == 13) { - skillinuse = static_cast(GetPrimSkill()); - OffHandAtk(false); - } - if (Hand == 14) { - skillinuse = static_cast(GetSecSkill()); - OffHandAtk(true); - } - - //figure out what weapon they are using, if any - const Item_Struct* weapon = nullptr; - if (Hand == 13 && equipment[SLOT_PRIMARY] > 0) - weapon = database.GetItem(equipment[SLOT_PRIMARY]); - else if (equipment[SLOT_SECONDARY]) - weapon = database.GetItem(equipment[SLOT_SECONDARY]); - - //We dont factor much from the weapon into the attack. - //Just the skill type so it doesn't look silly using punching animations and stuff while wielding weapons - if(weapon) { - mlog(COMBAT__ATTACKS, "Attacking with weapon: %s (%d) (too bad im not using it for much)", weapon->Name, weapon->ID); - - if(Hand == 14 && weapon->ItemType == ItemTypeShield){ - mlog(COMBAT__ATTACKS, "Attack with shield canceled."); - return false; - } - - switch(weapon->ItemType){ - case ItemType1HSlash: - skillinuse = Skill1HSlashing; - break; - case ItemType2HSlash: - skillinuse = Skill2HSlashing; - break; - case ItemType1HPiercing: - //skillinuse = Skill1HPiercing; - //break; - case ItemType2HPiercing: - skillinuse = Skill1HPiercing; // change to Skill2HPiercing once activated - break; - case ItemType1HBlunt: - skillinuse = Skill1HBlunt; - break; - case ItemType2HBlunt: - skillinuse = Skill2HBlunt; - break; - case ItemTypeBow: - skillinuse = SkillArchery; - break; - case ItemTypeLargeThrowing: - case ItemTypeSmallThrowing: - skillinuse = SkillThrowing; - break; - default: - skillinuse = SkillHandtoHand; - break; - } - } - - int weapon_damage = GetWeaponDamage(other, weapon); - - //do attack animation regardless of whether or not we can hit below - int16 charges = 0; - ItemInst weapon_inst(weapon, charges); - AttackAnimation(skillinuse, Hand, &weapon_inst); - - // Remove this once Skill2HPiercing is activated - //Work-around for there being no 2HP skill - We use 99 for the 2HB animation and 36 for pierce messages - if(skillinuse == 99) - skillinuse = static_cast(36); - - //basically "if not immune" then do the attack - if((weapon_damage) > 0) { - - //ele and bane dmg too - //NPCs add this differently than PCs - //if NPCs can't inheriently hit the target we don't add bane/magic dmg which isn't exactly the same as PCs - uint16 eleBane = 0; - if(weapon){ - if(weapon->BaneDmgBody == other->GetBodyType()){ - eleBane += weapon->BaneDmgAmt; - } - - if(weapon->BaneDmgRace == other->GetRace()){ - eleBane += weapon->BaneDmgRaceAmt; - } - - if(weapon->ElemDmgAmt){ - eleBane += (weapon->ElemDmgAmt * other->ResistSpell(weapon->ElemDmgType, 0, this) / 100); - } - } - - if(!RuleB(NPC, UseItemBonusesForNonPets)){ - if(!GetOwner()){ - eleBane = 0; - } - } - - uint8 otherlevel = other->GetLevel(); - uint8 mylevel = this->GetLevel(); - - otherlevel = otherlevel ? otherlevel : 1; - mylevel = mylevel ? mylevel : 1; - - //instead of calcing damage in floats lets just go straight to ints - if(RuleB(Combat, UseIntervalAC)) - damage = (max_dmg+eleBane); - else - damage = MakeRandomInt((min_dmg+eleBane),(max_dmg+eleBane)); - - - //check if we're hitting above our max or below it. - if((min_dmg+eleBane) != 0 && damage < (min_dmg+eleBane)) { - mlog(COMBAT__DAMAGE, "Damage (%d) is below min (%d). Setting to min.", damage, (min_dmg+eleBane)); - damage = (min_dmg+eleBane); - } - if((max_dmg+eleBane) != 0 && damage > (max_dmg+eleBane)) { - mlog(COMBAT__DAMAGE, "Damage (%d) is above max (%d). Setting to max.", damage, (max_dmg+eleBane)); - damage = (max_dmg+eleBane); - } - - damage = mod_npc_damage(damage, skillinuse, Hand, weapon, other); - - int32 hate = damage; - if(IsPet()) - { - hate = hate * 100 / GetDamageTable(skillinuse); - } - - if(other->IsClient() && other->CastToClient()->IsSitting()) { - mlog(COMBAT__DAMAGE, "Client %s is sitting. Hitting for max damage (%d).", other->GetName(), (max_dmg+eleBane)); - damage = (max_dmg+eleBane); - damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); - - if(opts) { - damage *= opts->damage_percent; - damage += opts->damage_flat; - hate *= opts->hate_percent; - hate += opts->hate_flat; - } - - mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetName()); - // now add done damage to the hate list - other->AddToHateList(this, hate); - - } else { - if(opts) { - damage *= opts->damage_percent; - damage += opts->damage_flat; - hate *= opts->hate_percent; - hate += opts->hate_flat; - } - - if(!other->CheckHitChance(this, skillinuse, Hand)) { - damage = 0; //miss - } else { //hit, check for damage avoidance - other->AvoidDamage(this, damage); - other->MeleeMitigation(this, damage, min_dmg+eleBane, opts); - if(damage > 0) { - ApplyMeleeDamageBonus(skillinuse, damage); - damage += (itembonuses.HeroicSTR / 10) + (damage * other->GetSkillDmgTaken(skillinuse) / 100) + GetSkillDmgAmt(skillinuse); - TryCriticalHit(other, skillinuse, damage, opts); - } - mlog(COMBAT__HITS, "Generating hate %d towards %s", hate, GetName()); - // now add done damage to the hate list - if(damage > 0) - { - other->AddToHateList(this, hate); - } - else - other->AddToHateList(this, 0); - } - } - - mlog(COMBAT__DAMAGE, "Final damage against %s: %d", other->GetName(), damage); - - if(other->IsClient() && IsPet() && GetOwner()->IsClient()) { - //pets do half damage to clients in pvp - damage=damage/2; - } - } - else - damage = -5; - - //cant riposte a riposte - if (bRiposte && damage == -3) { - mlog(COMBAT__DAMAGE, "Riposte of riposte canceled."); - return false; - } - - int16 DeathHP = 0; - DeathHP = other->GetDelayDeath() * -1; - - if(GetHP() > 0 && other->GetHP() >= DeathHP) { - other->Damage(this, damage, SPELL_UNKNOWN, skillinuse, false); // Not avoidable client already had thier chance to Avoid - } else - return false; - - if (HasDied()) //killed by damage shield ect - return false; - - MeleeLifeTap(damage); - - if (damage > 0) - CheckNumHitsRemaining(5); - - //break invis when you attack - if(invisible) { - mlog(COMBAT__ATTACKS, "Removing invisibility due to melee attack."); - BuffFadeByEffect(SE_Invisibility); - BuffFadeByEffect(SE_Invisibility2); - invisible = false; - } - if(invisible_undead) { - mlog(COMBAT__ATTACKS, "Removing invisibility vs. undead due to melee attack."); - BuffFadeByEffect(SE_InvisVsUndead); - BuffFadeByEffect(SE_InvisVsUndead2); - invisible_undead = false; - } - if(invisible_animals){ - mlog(COMBAT__ATTACKS, "Removing invisibility vs. animals due to melee attack."); - BuffFadeByEffect(SE_InvisVsAnimals); - invisible_animals = false; - } - - if(hidden || improved_hidden) - { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); - } - - - hidden = false; - improved_hidden = false; - - //I doubt this works... - if (!GetTarget()) - return true; //We killed them - - if(!bRiposte && other->GetHP() > 0 ) { - TryWeaponProc(nullptr, weapon, other, Hand); //no weapon - TrySpellProc(nullptr, weapon, other, Hand); - } - - TriggerDefensiveProcs(nullptr, other, Hand, damage); - - // now check ripostes - if (damage == -3) { // riposting - DoRiposte(other); - } - - if (damage > 0) - return true; - - else - return false; -} - -void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, SkillUseTypes attack_skill, bool avoidable, int8 buffslot, bool iBuffTic) { - if(spell_id==0) - spell_id = SPELL_UNKNOWN; - - //handle EVENT_ATTACK. Resets after we have not been attacked for 12 seconds - if(attacked_timer.Check()) - { - mlog(COMBAT__HITS, "Triggering EVENT_ATTACK due to attack by %s", other->GetName()); - parse->EventNPC(EVENT_ATTACK, this, other, "", 0); - } - attacked_timer.Start(CombatEventTimer_expire); - - if (!IsEngaged()) - zone->AddAggroMob(); - - if(GetClass() == LDON_TREASURE) - { - if(IsLDoNLocked() && GetLDoNLockedSkill() != LDoNTypeMechanical) - { - damage = -5; - } - else - { - if(IsLDoNTrapped()) - { - Message_StringID(13, LDON_ACCIDENT_SETOFF2); - SpellFinished(GetLDoNTrapSpellID(), other, 10, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false); - SetLDoNTrapSpellID(0); - SetLDoNTrapped(false); - SetLDoNTrapDetected(false); - } - } - } - - //do a majority of the work... - CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic); - - if(damage > 0) { - //see if we are gunna start fleeing - if(!IsPet()) CheckFlee(); - } -} - -bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack_skill) { - mlog(COMBAT__HITS, "Fatal blow dealt by %s with %d damage, spell %d, skill %d", killerMob->GetName(), damage, spell, attack_skill); - - Mob *oos = nullptr; - if(killerMob) { - oos = killerMob->GetOwnerOrSelf(); - - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill)); - if(parse->EventNPC(EVENT_DEATH, this, oos, buffer, 0) != 0) - { - if(GetHP() < 0) { - SetHP(0); - } - return false; - } - - if(killerMob && killerMob->IsClient() && (spell != SPELL_UNKNOWN) && damage > 0) { - char val1[20]={0}; - entity_list.MessageClose_StringID(this, false, 100, MT_NonMelee, HIT_NON_MELEE, - killerMob->GetCleanName(), GetCleanName(), ConvertArray(damage, val1)); - } - } else { - - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill)); - if(parse->EventNPC(EVENT_DEATH, this, nullptr, buffer, 0) != 0) - { - if(GetHP() < 0) { - SetHP(0); - } - return false; - } - } - - if (IsEngaged()) - { - zone->DelAggroMob(); -#if EQDEBUG >= 11 - LogFile->write(EQEMuLog::Debug,"NPC::Death() Mobs currently Aggro %i", zone->MobsAggroCount()); -#endif - } - SetHP(0); - SetPet(0); - Mob* killer = GetHateDamageTop(this); - - entity_list.RemoveFromTargets(this, p_depop); - - if(p_depop == true) - return false; - - BuffFadeAll(); - uint8 killed_level = GetLevel(); - - EQApplicationPacket* app= new EQApplicationPacket(OP_Death,sizeof(Death_Struct)); - Death_Struct* d = (Death_Struct*)app->pBuffer; - d->spawn_id = GetID(); - d->killer_id = killerMob ? killerMob->GetID() : 0; - d->bindzoneid = 0; - d->spell_id = spell == SPELL_UNKNOWN ? 0xffffffff : spell; - d->attack_skill = SkillDamageTypes[attack_skill]; - d->damage = damage; - app->priority = 6; - entity_list.QueueClients(killerMob, app, false); - - if(respawn2) { - respawn2->DeathReset(1); - } - - if (killerMob) { - if(GetClass() != LDON_TREASURE) - hate_list.Add(killerMob, damage); - } - - safe_delete(app); - - Mob *give_exp = hate_list.GetDamageTop(this); - - if(give_exp == nullptr) - give_exp = killer; - - if(give_exp && give_exp->HasOwner()) { - - bool ownerInGroup = false; - if((give_exp->HasGroup() && give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner())) - || (give_exp->IsPet() && (give_exp->GetOwner()->IsClient() - || ( give_exp->GetOwner()->HasGroup() && give_exp->GetOwner()->GetGroup()->IsGroupMember(give_exp->GetOwner()->GetUltimateOwner()))))) - ownerInGroup = true; - - give_exp = give_exp->GetUltimateOwner(); - -#ifdef BOTS - if(!RuleB(Bots, BotGroupXP) && !ownerInGroup) { - give_exp = nullptr; - } -#endif //BOTS - } - - int PlayerCount = 0; // QueryServ Player Counting - - Client *give_exp_client = nullptr; - if(give_exp && give_exp->IsClient()) - give_exp_client = give_exp->CastToClient(); - - bool IsLdonTreasure = (this->GetClass() == LDON_TREASURE); - if (give_exp_client && !IsCorpse() && MerchantType == 0) - { - Group *kg = entity_list.GetGroupByClient(give_exp_client); - Raid *kr = entity_list.GetRaidByClient(give_exp_client); - - int32 finalxp = EXP_FORMULA; - finalxp = give_exp_client->mod_client_xp(finalxp, this); - - if(kr) - { - if(!IsLdonTreasure) { - kr->SplitExp((finalxp), this); - if(killerMob && (kr->IsRaidMember(killerMob->GetName()) || kr->IsRaidMember(killerMob->GetUltimateOwner()->GetName()))) - killerMob->TrySpellOnKill(killed_level,spell); - } - /* Send the EVENT_KILLED_MERIT event for all raid members */ - for (int i = 0; i < MAX_RAID_MEMBERS; i++) { - if (kr->members[i].member != nullptr) { // If Group Member is Client - parse->EventNPC(EVENT_KILLED_MERIT, this, kr->members[i].member, "killed", 0); - - mod_npc_killed_merit(kr->members[i].member); - - if(RuleB(TaskSystem, EnableTaskSystem)) - kr->members[i].member->UpdateTasksOnKill(GetNPCTypeID()); - PlayerCount++; - } - } - - // QueryServ Logging - Raid Kills - if(RuleB(QueryServ, PlayerLogNPCKills)){ - ServerPacket* pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, sizeof(QSPlayerLogNPCKill_Struct) + (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * PlayerCount)); - PlayerCount = 0; - QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer; - QS->s1.NPCID = this->GetNPCTypeID(); - QS->s1.ZoneID = this->GetZoneID(); - QS->s1.Type = 2; // Raid Fight - for (int i = 0; i < MAX_RAID_MEMBERS; i++) { - if (kr->members[i].member != nullptr) { // If Group Member is Client - Client *c = kr->members[i].member; - QS->Chars[PlayerCount].char_id = c->CharacterID(); - PlayerCount++; - } - } - worldserver.SendPacket(pack); // Send Packet to World - safe_delete(pack); - } - // End QueryServ Logging - - } - else if (give_exp_client->IsGrouped() && kg != nullptr) - { - if(!IsLdonTreasure) { - kg->SplitExp((finalxp), this); - if(killerMob && (kg->IsGroupMember(killerMob->GetName()) || kg->IsGroupMember(killerMob->GetUltimateOwner()->GetName()))) - killerMob->TrySpellOnKill(killed_level,spell); - } - /* Send the EVENT_KILLED_MERIT event and update kill tasks - * for all group members */ - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (kg->members[i] != nullptr && kg->members[i]->IsClient()) { // If Group Member is Client - Client *c = kg->members[i]->CastToClient(); - parse->EventNPC(EVENT_KILLED_MERIT, this, c, "killed", 0); - - mod_npc_killed_merit(c); - - if(RuleB(TaskSystem, EnableTaskSystem)) - c->UpdateTasksOnKill(GetNPCTypeID()); - - PlayerCount++; - } - } - - // QueryServ Logging - Group Kills - if(RuleB(QueryServ, PlayerLogNPCKills)){ - ServerPacket* pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, sizeof(QSPlayerLogNPCKill_Struct) + (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * PlayerCount)); - PlayerCount = 0; - QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer; - QS->s1.NPCID = this->GetNPCTypeID(); - QS->s1.ZoneID = this->GetZoneID(); - QS->s1.Type = 1; // Group Fight - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (kg->members[i] != nullptr && kg->members[i]->IsClient()) { // If Group Member is Client - Client *c = kg->members[i]->CastToClient(); - QS->Chars[PlayerCount].char_id = c->CharacterID(); - PlayerCount++; - } - } - worldserver.SendPacket(pack); // Send Packet to World - safe_delete(pack); - } - // End QueryServ Logging - } - else - { - if(!IsLdonTreasure) { - int conlevel = give_exp->GetLevelCon(GetLevel()); - if (conlevel != CON_GREEN) - { - if(GetOwner() && GetOwner()->IsClient()){ - } - else { - give_exp_client->AddEXP((finalxp), conlevel); // Pyro: Comment this if NPC death crashes zone - if(killerMob && (killerMob->GetID() == give_exp_client->GetID() || killerMob->GetUltimateOwner()->GetID() == give_exp_client->GetID())) - killerMob->TrySpellOnKill(killed_level,spell); - } - } - } - /* Send the EVENT_KILLED_MERIT event */ - parse->EventNPC(EVENT_KILLED_MERIT, this, give_exp_client, "killed", 0); - - mod_npc_killed_merit(give_exp_client); - - if(RuleB(TaskSystem, EnableTaskSystem)) - give_exp_client->UpdateTasksOnKill(GetNPCTypeID()); - - // QueryServ Logging - Solo - if(RuleB(QueryServ, PlayerLogNPCKills)){ - ServerPacket* pack = new ServerPacket(ServerOP_QSPlayerLogNPCKills, sizeof(QSPlayerLogNPCKill_Struct) + (sizeof(QSPlayerLogNPCKillsPlayers_Struct) * 1)); - QSPlayerLogNPCKill_Struct* QS = (QSPlayerLogNPCKill_Struct*) pack->pBuffer; - QS->s1.NPCID = this->GetNPCTypeID(); - QS->s1.ZoneID = this->GetZoneID(); - QS->s1.Type = 0; // Solo Fight - Client *c = give_exp_client; - QS->Chars[0].char_id = c->CharacterID(); - PlayerCount++; - worldserver.SendPacket(pack); // Send Packet to World - safe_delete(pack); - } - // End QueryServ Logging - } - } - - //do faction hits even if we are a merchant, so long as a player killed us - if(give_exp_client) - hate_list.DoFactionHits(GetNPCFactionID()); - - if (!HasOwner() && !IsMerc() && class_ != MERCHANT && class_ != ADVENTUREMERCHANT && !GetSwarmInfo() - && MerchantType == 0 && killer && (killer->IsClient() || (killer->HasOwner() && killer->GetUltimateOwner()->IsClient()) || - (killer->IsNPC() && killer->CastToNPC()->GetSwarmInfo() && killer->CastToNPC()->GetSwarmInfo()->GetOwner() && killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient()))) - { - if(killer != 0) - { - if(killer->GetOwner() != 0 && killer->GetOwner()->IsClient()) - killer = killer->GetOwner(); - - if(!killer->CastToClient()->GetGM() && killer->IsClient()) - this->CheckMinMaxLevel(killer); - } - entity_list.RemoveFromAutoXTargets(this); - uint16 emoteid = this->GetEmoteID(); - Corpse* corpse = new Corpse(this, &itemlist, GetNPCTypeID(), &NPCTypedata,level>54?RuleI(NPC,MajorNPCCorpseDecayTimeMS):RuleI(NPC,MinorNPCCorpseDecayTimeMS)); - entity_list.LimitRemoveNPC(this); - entity_list.AddCorpse(corpse, GetID()); - - entity_list.UnMarkNPC(GetID()); - entity_list.RemoveNPC(GetID()); - this->SetID(0); - if(killer != 0 && emoteid != 0) - corpse->CastToNPC()->DoNPCEmote(AFTERDEATH, emoteid); - if(killer != 0 && killer->IsClient()) { - corpse->AllowMobLoot(killer, 0); - if(killer->IsGrouped()) { - Group* group = entity_list.GetGroupByClient(killer->CastToClient()); - if(group != 0) { - for(int i=0;i<6;i++) { // Doesnt work right, needs work - if(group->members[i] != nullptr) { - corpse->AllowMobLoot(group->members[i],i); - } - } - } - } - else if(killer->IsRaidGrouped()){ - Raid* r = entity_list.GetRaidByClient(killer->CastToClient()); - if(r){ - int i = 0; - for(int x = 0; x < MAX_RAID_MEMBERS; x++) - { - switch(r->GetLootType()) - { - case 0: - case 1: - if(r->members[x].member && r->members[x].IsRaidLeader){ - corpse->AllowMobLoot(r->members[x].member, i); - i++; - } - break; - case 2: - if(r->members[x].member && r->members[x].IsRaidLeader){ - corpse->AllowMobLoot(r->members[x].member, i); - i++; - } - else if(r->members[x].member && r->members[x].IsGroupLeader){ - corpse->AllowMobLoot(r->members[x].member, i); - i++; - } - break; - case 3: - if(r->members[x].member && r->members[x].IsLooter){ - corpse->AllowMobLoot(r->members[x].member, i); - i++; - } - break; - case 4: - if(r->members[x].member) - { - corpse->AllowMobLoot(r->members[x].member, i); - i++; - } - break; - } - } - } - } - } - - if(zone && zone->adv_data) - { - ServerZoneAdventureDataReply_Struct *sr = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; - if(sr->type == Adventure_Kill) - { - zone->DoAdventureCountIncrease(); - } - else if(sr->type == Adventure_Assassinate) - { - if(sr->data_id == GetNPCTypeID()) - { - zone->DoAdventureCountIncrease(); - } - else - { - zone->DoAdventureAssassinationCountIncrease(); - } - } - } - } - else - entity_list.RemoveFromXTargets(this); - - // Parse quests even if we're killed by an NPC - if(oos) { - mod_npc_killed(oos); - - uint16 emoteid = this->GetEmoteID(); - if(emoteid != 0) - this->DoNPCEmote(ONDEATH, emoteid); - if(oos->IsNPC()) - { - parse->EventNPC(EVENT_NPC_SLAY, oos->CastToNPC(), this, "", 0); - uint16 emoteid = oos->GetEmoteID(); - if(emoteid != 0) - oos->CastToNPC()->DoNPCEmote(KILLEDNPC, emoteid); - killerMob->TrySpellOnKill(killed_level, spell); - } - } - - WipeHateList(); - p_depop = true; - if(killerMob && killerMob->GetTarget() == this) //we can kill things without having them targeted - killerMob->SetTarget(nullptr); //via AE effects and such.. - - entity_list.UpdateFindableNPCState(this, true); - - char buffer[48] = { 0 }; - snprintf(buffer, 47, "%d %d %d %d", killerMob ? killerMob->GetID() : 0, damage, spell, static_cast(attack_skill)); - parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, buffer, 0); - return true; -} - -void Mob::AddToHateList(Mob* other, int32 hate, int32 damage, bool iYellForHelp, bool bFrenzy, bool iBuffTic) { - - assert(other != nullptr); - - if (other == this) - return; - - if(damage < 0){ - hate = 1; - } - - bool wasengaged = IsEngaged(); - Mob* owner = other->GetOwner(); - Mob* mypet = this->GetPet(); - Mob* myowner = this->GetOwner(); - Mob* targetmob = this->GetTarget(); - - if(other){ - AddRampage(other); - int hatemod = 100 + other->spellbonuses.hatemod + other->itembonuses.hatemod + other->aabonuses.hatemod; - - int16 shieldhatemod = other->spellbonuses.ShieldEquipHateMod + other->itembonuses.ShieldEquipHateMod + other->aabonuses.ShieldEquipHateMod; - - if (shieldhatemod && other->HasShieldEquiped()) - hatemod += shieldhatemod; - - if(hatemod < 1) - hatemod = 1; - hate = ((hate * (hatemod))/100); - } - - if(IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && !IsFocused()) { //ignore aggro if hold and !focus - return; - } - - if(IsPet() && GetOwner() && GetOwner()->GetAA(aaPetDiscipline) && IsHeld() && GetOwner()->GetAA(aaAdvancedPetDiscipline) >= 1 && IsFocused()) { - if (!targetmob) - return; - } - - if (other->IsNPC() && (other->IsPet() || other->CastToNPC()->GetSwarmOwner() > 0)) - TryTriggerOnValueAmount(false, false, false, true); - - if(IsClient() && !IsAIControlled()) - return; - - if(IsFamiliar() || GetSpecialAbility(IMMUNE_AGGRO)) - return; - - if (other == myowner) - return; - - if(other->GetSpecialAbility(IMMUNE_AGGRO_ON)) - return; - - if(GetSpecialAbility(NPC_TUNNELVISION)) { - int tv_mod = GetSpecialAbilityParam(NPC_TUNNELVISION, 0); - - Mob *top = GetTarget(); - if(top && top != other) { - if(tv_mod) { - float tv = tv_mod / 100.0f; - hate *= tv; - } else { - hate *= RuleR(Aggro, TunnelVisionAggroMod); - } - } - } - - if(IsNPC() && CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) { - if(!zone->watermap->InLiquid(other->GetX(), other->GetY(), other->GetZ())) { - return; - } - } - // first add self - - // The damage on the hate list is used to award XP to the killer. This check is to prevent Killstealing. - // e.g. Mob has 5000 hit points, Player A melees it down to 500 hp, Player B executes a headshot (10000 damage). - // If we add 10000 damage, Player B would get the kill credit, so we only award damage credit to player B of the - // amount of HP the mob had left. - // - if(damage > GetHP()) - damage = GetHP(); - - if (spellbonuses.ImprovedTaunt[1] && (GetLevel() < spellbonuses.ImprovedTaunt[0]) - && other && (buffs[spellbonuses.ImprovedTaunt[2]].casterid != other->GetID())) - hate = (hate*spellbonuses.ImprovedTaunt[1])/100; - - hate_list.Add(other, hate, damage, bFrenzy, !iBuffTic); - - if(other->IsClient()) - other->CastToClient()->AddAutoXTarget(this); - -#ifdef BOTS - // if other is a bot, add the bots client to the hate list - if(other->IsBot()) { - if(other->CastToBot()->GetBotOwner() && other->CastToBot()->GetBotOwner()->CastToClient()->GetFeigned()) { - AddFeignMemory(other->CastToBot()->GetBotOwner()->CastToClient()); - } - else { - if(!hate_list.IsOnHateList(other->CastToBot()->GetBotOwner())) - hate_list.Add(other->CastToBot()->GetBotOwner(), 0, 0, false, true); - } - } -#endif //BOTS - - - // if other is a merc, add the merc client to the hate list - if(other->IsMerc()) { - if(other->CastToMerc()->GetMercOwner() && other->CastToMerc()->GetMercOwner()->CastToClient()->GetFeigned()) { - AddFeignMemory(other->CastToMerc()->GetMercOwner()->CastToClient()); - } - else { - if(!hate_list.IsOnHateList(other->CastToMerc()->GetMercOwner())) - hate_list.Add(other->CastToMerc()->GetMercOwner(), 0, 0, false, true); - } - } //MERC - - // then add pet owner if there's one - if (owner) { // Other is a pet, add him and it - // EverHood 6/12/06 - // Can't add a feigned owner to hate list - if(owner->IsClient() && owner->CastToClient()->GetFeigned()) { - //they avoid hate due to feign death... - } else { - // cb:2007-08-17 - // owner must get on list, but he's not actually gained any hate yet - if(!owner->GetSpecialAbility(IMMUNE_AGGRO)) - { - hate_list.Add(owner, 0, 0, false, !iBuffTic); - if(owner->IsClient()) - owner->CastToClient()->AddAutoXTarget(this); - } - } - } - - if (mypet && (!(GetAA(aaPetDiscipline) && mypet->IsHeld()))) { // I have a pet, add other to it - if(!mypet->IsFamiliar() && !mypet->GetSpecialAbility(IMMUNE_AGGRO)) - mypet->hate_list.Add(other, 0, 0, bFrenzy); - } else if (myowner) { // I am a pet, add other to owner if it's NPC/LD - if (myowner->IsAIControlled() && !myowner->GetSpecialAbility(IMMUNE_AGGRO)) - myowner->hate_list.Add(other, 0, 0, bFrenzy); - } - if (!wasengaged) { - if(IsNPC() && other->IsClient() && other->CastToClient()) - parse->EventNPC(EVENT_AGGRO, this->CastToNPC(), other, "", 0); - AI_Event_Engaged(other, iYellForHelp); - } -} - -// solar: this is called from Damage() when 'this' is attacked by 'other. -// 'this' is the one being attacked -// 'other' is the attacker -// a damage shield causes damage (or healing) to whoever attacks the wearer -// a reverse ds causes damage to the wearer whenever it attack someone -// given this, a reverse ds must be checked each time the wearer is attacking -// and not when they're attacked -//a damage shield on a spell is a negative value but on an item it's a positive value so add the spell value and subtract the item value to get the end ds value -void Mob::DamageShield(Mob* attacker, bool spell_ds) { - - if(!attacker || this == attacker) - return; - - int DS = 0; - int rev_ds = 0; - uint16 spellid = 0; - - if(!spell_ds) - { - DS = spellbonuses.DamageShield; - rev_ds = attacker->spellbonuses.ReverseDamageShield; - - if(spellbonuses.DamageShieldSpellID != 0 && spellbonuses.DamageShieldSpellID != SPELL_UNKNOWN) - spellid = spellbonuses.DamageShieldSpellID; - } - else { - DS = spellbonuses.SpellDamageShield; - rev_ds = 0; - // This ID returns "you are burned", seemed most appropriate for spell DS - spellid = 2166; - } - - if(DS == 0 && rev_ds == 0) - return; - - mlog(COMBAT__HITS, "Applying Damage Shield of value %d to %s", DS, attacker->GetName()); - - //invert DS... spells yield negative values for a true damage shield - if(DS < 0) { - if(!spell_ds) { - - DS += aabonuses.DamageShield; //Live AA - coat of thistles. (negative value) - DS -= itembonuses.DamageShield; //+Damage Shield should only work when you already have a DS spell - - //Spell data for damage shield mitigation shows a negative value for spells for clients and positive - //value for spells that effect pets. Unclear as to why. For now will convert all positive to be consistent. - if (attacker->IsOffHandAtk()){ - int16 mitigation = attacker->itembonuses.DSMitigationOffHand + - attacker->spellbonuses.DSMitigationOffHand + - attacker->aabonuses.DSMitigationOffHand; - DS -= DS*mitigation/100; - } - DS -= DS * attacker->itembonuses.DSMitigation / 100; - } - attacker->Damage(this, -DS, spellid, SkillAbjuration/*hackish*/, false); - //we can assume there is a spell now - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); - CombatDamage_Struct* cds = (CombatDamage_Struct*)outapp->pBuffer; - cds->target = attacker->GetID(); - cds->source = GetID(); - cds->type = spellbonuses.DamageShieldType; - cds->spellid = 0x0; - cds->damage = DS; - entity_list.QueueCloseClients(this, outapp); - safe_delete(outapp); - } else if (DS > 0 && !spell_ds) { - //we are healing the attacker... - attacker->HealDamage(DS); - //TODO: send a packet??? - } - - //Reverse DS - //this is basically a DS, but the spell is on the attacker, not the attackee - //if we've gotten to this point, we know we know "attacker" hit "this" (us) for damage & we aren't invulnerable - uint16 rev_ds_spell_id = SPELL_UNKNOWN; - - if(spellbonuses.ReverseDamageShieldSpellID != 0 && spellbonuses.ReverseDamageShieldSpellID != SPELL_UNKNOWN) - rev_ds_spell_id = spellbonuses.ReverseDamageShieldSpellID; - - if(rev_ds < 0) { - mlog(COMBAT__HITS, "Applying Reverse Damage Shield of value %d to %s", rev_ds, attacker->GetName()); - attacker->Damage(this, -rev_ds, rev_ds_spell_id, SkillAbjuration/*hackish*/, false); //"this" (us) will get the hate, etc. not sure how this works on Live, but it'll works for now, and tanks will love us for this - //do we need to send a damage packet here also? - } -} - -uint8 Mob::GetWeaponDamageBonus( const Item_Struct *Weapon ) -{ - // This function calculates and returns the damage bonus for the weapon identified by the parameter "Weapon". - // Modified 9/21/2008 by Cantus - - - // Assert: This function should only be called for hits by the mainhand, as damage bonuses apply only to the - // weapon in the primary slot. Be sure to check that Hand == 13 before calling. - - // Assert: The caller should ensure that Weapon is actually a weapon before calling this function. - // The ItemInst::IsWeapon() method can be used to quickly determine this. - - // Assert: This function should not be called if the player's level is below 28, as damage bonuses do not begin - // to apply until level 28. - - // Assert: This function should not be called unless the player is a melee class, as casters do not receive a damage bonus. - - - if( Weapon == nullptr || Weapon->ItemType == ItemType1HSlash || Weapon->ItemType == ItemType1HBlunt || Weapon->ItemType == ItemTypeMartial || Weapon->ItemType == ItemType1HPiercing ) - { - // The weapon in the player's main (primary) hand is a one-handed weapon, or there is no item equipped at all. - // - // According to player posts on Allakhazam, 1H damage bonuses apply to bare fists (nothing equipped in the mainhand, - // as indicated by Weapon == nullptr). - // - // The following formula returns the correct damage bonus for all 1H weapons: - - return (uint8) ((GetLevel() - 25) / 3); - } - - // If we've gotten to this point, the weapon in the mainhand is a two-handed weapon. - // Calculating damage bonuses for 2H weapons is more complicated, as it's based on PC level AND the delay of the weapon. - // The formula to calculate 2H bonuses is HIDEOUS. It's a huge conglomeration of ternary operators and multiple operations. - // - // The following is a hybrid approach. In cases where the Level and Delay merit a formula that does not use many operators, - // the formula is used. In other cases, lookup tables are used for speed. - // Though the following code may look bloated and ridiculous, it's actually a very efficient way of calculating these bonuses. - - // Player Level is used several times in the code below, so save it into a variable. - // If GetLevel() were an ordinary function, this would DEFINITELY make sense, as it'd cut back on all of the function calling - // overhead involved with multiple calls to GetLevel(). But in this case, GetLevel() is a simple, inline accessor method. - // So it probably doesn't matter. If anyone knows for certain that there is no overhead involved with calling GetLevel(), - // as I suspect, then please feel free to delete the following line, and replace all occurences of "ucPlayerLevel" with "GetLevel()". - uint8 ucPlayerLevel = (uint8) GetLevel(); - - - // The following may look cleaner, and would certainly be easier to understand, if it was - // a simple 53x150 cell matrix. - // - // However, that would occupy 7,950 Bytes of memory (7.76 KB), and would likely result - // in "thrashing the cache" when performing lookups. - // - // Initially, I thought the best approach would be to reverse-engineer the formula used by - // Sony/Verant to calculate these 2H weapon damage bonuses. But the more than Reno and I - // worked on figuring out this formula, the more we're concluded that the formula itself ugly - // (that is, it contains so many operations and conditionals that it's fairly CPU intensive). - // Because of that, we're decided that, in most cases, a lookup table is the most efficient way - // to calculate these damage bonuses. - // - // The code below is a hybrid between a pure formulaic approach and a pure, brute-force - // lookup table. In cases where a formula is the best bet, I use a formula. In other places - // where a formula would be ugly, I use a lookup table in the interests of speed. - - - if( Weapon->Delay <= 27 ) - { - // Damage Bonuses for all 2H weapons with delays of 27 or less are identical. - // They are the same as the damage bonus would be for a corresponding 1H weapon, plus one. - // This formula applies to all levels 28-80, and will probably continue to apply if - - // the level cap on Live ever is increased beyond 80. - - return (ucPlayerLevel - 22) / 3; - } - - - if( ucPlayerLevel == 65 && Weapon->Delay <= 59 ) - { - // Consider these two facts: - // * Level 65 is the maximum level on many EQ Emu servers. - // * If you listed the levels of all characters logged on to a server, odds are that the number you'll - // see most frequently is level 65. That is, there are more level 65 toons than any other single level. - // - // Therefore, if we can optimize this function for level 65 toons, we're speeding up the server! - // - // With that goal in mind, I create an array of Damage Bonuses for level 65 characters wielding 2H weapons with - // delays between 28 and 59 (inclusive). I suspect that this one small lookup array will therefore handle - // many of the calls to this function. - - static const uint8 ucLevel65DamageBonusesForDelays28to59[] = {35, 35, 36, 36, 37, 37, 38, 38, 39, 39, 40, 40, 42, 42, 42, 45, 45, 47, 48, 49, 49, 51, 51, 52, 53, 54, 54, 56, 56, 57, 58, 59}; - - return ucLevel65DamageBonusesForDelays28to59[Weapon->Delay-28]; - } - - - if( ucPlayerLevel > 65 ) - { - if( ucPlayerLevel > 80 ) - { - // As level 80 is currently the highest achievable level on Live, we only include - // damage bonus information up to this level. - // - // If there is a custom EQEmu server that allows players to level beyond 80, the - // damage bonus for their 2H weapons will simply not increase beyond their damage - // bonus at level 80. - - ucPlayerLevel = 80; - } - - // Lucy does not list a chart of damage bonuses for players levels 66+, - // so my original version of this function just applied the level 65 damage - // bonus for level 66+ toons. That sucked for higher level toons, as their - // 2H weapons stopped ramping up in DPS as they leveled past 65. - // - // Thanks to the efforts of two guys, this is no longer the case: - // - // Janusd (Zetrakyl) ran a nifty query against the PEQ item database to list - // the name of an example 2H weapon that represents each possible unique 2H delay. - // - // Romai then wrote an excellent script to automatically look up each of those - // weapons, open the Lucy item page associated with it, and iterate through all - // levels in the range 66 - 80. He saved the damage bonus for that weapon for - // each level, and that forms the basis of the lookup tables below. - - if( Weapon->Delay <= 59 ) - { - static const uint8 ucDelay28to59Levels66to80[32][15]= - { - /* Level: */ - /* 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 */ - - {36, 37, 38, 39, 41, 42, 43, 44, 45, 47, 49, 49, 49, 50, 53}, /* Delay = 28 */ - {36, 38, 38, 39, 42, 43, 43, 45, 46, 48, 49, 50, 51, 52, 54}, /* Delay = 29 */ - {37, 38, 39, 40, 43, 43, 44, 46, 47, 48, 50, 51, 52, 53, 55}, /* Delay = 30 */ - {37, 39, 40, 40, 43, 44, 45, 46, 47, 49, 51, 52, 52, 52, 54}, /* Delay = 31 */ - {38, 39, 40, 41, 44, 45, 45, 47, 48, 48, 50, 52, 53, 55, 57}, /* Delay = 32 */ - {38, 40, 41, 41, 44, 45, 46, 48, 49, 50, 52, 53, 54, 56, 58}, /* Delay = 33 */ - {39, 40, 41, 42, 45, 46, 47, 48, 49, 51, 53, 54, 55, 57, 58}, /* Delay = 34 */ - {39, 41, 42, 43, 46, 46, 47, 49, 50, 52, 54, 55, 56, 57, 59}, /* Delay = 35 */ - {40, 41, 42, 43, 46, 47, 48, 50, 51, 53, 55, 55, 56, 58, 60}, /* Delay = 36 */ - {40, 42, 43, 44, 47, 48, 49, 50, 51, 53, 55, 56, 57, 59, 61}, /* Delay = 37 */ - {41, 42, 43, 44, 47, 48, 49, 51, 52, 54, 56, 57, 58, 60, 62}, /* Delay = 38 */ - {41, 43, 44, 45, 48, 49, 50, 52, 53, 55, 57, 58, 59, 61, 63}, /* Delay = 39 */ - {43, 45, 46, 47, 50, 51, 52, 54, 55, 57, 59, 60, 61, 63, 65}, /* Delay = 40 */ - {43, 45, 46, 47, 50, 51, 52, 54, 55, 57, 59, 60, 61, 63, 65}, /* Delay = 41 */ - {44, 46, 47, 48, 51, 52, 53, 55, 56, 58, 60, 61, 62, 64, 66}, /* Delay = 42 */ - {46, 48, 49, 50, 53, 54, 55, 58, 59, 61, 63, 64, 65, 67, 69}, /* Delay = 43 */ - {47, 49, 50, 51, 54, 55, 56, 58, 59, 61, 64, 65, 66, 68, 70}, /* Delay = 44 */ - {48, 50, 51, 52, 56, 57, 58, 60, 61, 63, 65, 66, 68, 70, 72}, /* Delay = 45 */ - {50, 52, 53, 54, 57, 58, 59, 62, 63, 65, 67, 68, 69, 71, 74}, /* Delay = 46 */ - {50, 52, 53, 55, 58, 59, 60, 62, 63, 66, 68, 69, 70, 72, 74}, /* Delay = 47 */ - {51, 53, 54, 55, 58, 60, 61, 63, 64, 66, 69, 69, 71, 73, 75}, /* Delay = 48 */ - {52, 54, 55, 57, 60, 61, 62, 65, 66, 68, 70, 71, 73, 75, 77}, /* Delay = 49 */ - {53, 55, 56, 57, 61, 62, 63, 65, 67, 69, 71, 72, 74, 76, 78}, /* Delay = 50 */ - {53, 55, 57, 58, 61, 62, 64, 66, 67, 69, 72, 73, 74, 77, 79}, /* Delay = 51 */ - {55, 57, 58, 59, 63, 64, 65, 68, 69, 71, 74, 75, 76, 78, 81}, /* Delay = 52 */ - {57, 55, 59, 60, 63, 65, 66, 68, 70, 72, 74, 76, 77, 79, 82}, /* Delay = 53 */ - {56, 58, 59, 61, 64, 65, 67, 69, 70, 73, 75, 76, 78, 80, 82}, /* Delay = 54 */ - {57, 59, 61, 62, 66, 67, 68, 71, 72, 74, 77, 78, 80, 82, 84}, /* Delay = 55 */ - {58, 60, 61, 63, 66, 68, 69, 71, 73, 75, 78, 79, 80, 83, 85}, /* Delay = 56 */ - - /* Important Note: Janusd's search for 2H weapons did not find */ - /* any 2H weapon with a delay of 57. Therefore the values below */ - /* are interpolated, not exact! */ - {59, 61, 62, 64, 67, 69, 70, 72, 74, 76, 77, 78, 81, 84, 86}, /* Delay = 57 INTERPOLATED */ - - {60, 62, 63, 65, 68, 70, 71, 74, 75, 78, 80, 81, 83, 85, 88}, /* Delay = 58 */ - - /* Important Note: Janusd's search for 2H weapons did not find */ - /* any 2H weapon with a delay of 59. Therefore the values below */ - /* are interpolated, not exact! */ - {60, 62, 64, 65, 69, 70, 72, 74, 76, 78, 81, 82, 84, 86, 89}, /* Delay = 59 INTERPOLATED */ - }; - - return ucDelay28to59Levels66to80[Weapon->Delay-28][ucPlayerLevel-66]; - } - else - { - // Delay is 60+ - - const static uint8 ucDelayOver59Levels66to80[6][15] = - { - /* Level: */ - /* 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80 */ - - {61, 63, 65, 66, 70, 71, 73, 75, 77, 79, 82, 83, 85, 87, 90}, /* Delay = 60 */ - {65, 68, 69, 71, 75, 76, 78, 80, 82, 85, 87, 89, 91, 93, 96}, /* Delay = 65 */ - - /* Important Note: Currently, the only 2H weapon with a delay */ - /* of 66 is not player equippable (it's None/None). So I'm */ - /* leaving it commented out to keep this table smaller. */ - //{66, 68, 70, 71, 75, 77, 78, 81, 83, 85, 88, 90, 91, 94, 97}, /* Delay = 66 */ - - {70, 72, 74, 76, 80, 81, 83, 86, 88, 88, 90, 95, 97, 99, 102}, /* Delay = 70 */ - {82, 85, 87, 89, 89, 94, 98, 101, 103, 106, 109, 111, 114, 117, 120}, /* Delay = 85 */ - {90, 93, 96, 98, 103, 105, 107, 111, 113, 116, 120, 122, 125, 128, 131}, /* Delay = 95 */ - - /* Important Note: Currently, the only 2H weapons with delay */ - /* 100 are GM-only items purchased from vendors in Sunset Home */ - /* (cshome). Because they are highly unlikely to be used in */ - /* combat, I'm commenting it out to keep the table smaller. */ - //{95, 98, 101, 103, 108, 110, 113, 116, 119, 122, 126, 128, 131, 134, 138},/* Delay = 100 */ - - {136, 140, 144, 148, 154, 157, 161, 166, 170, 174, 179, 183, 187, 191, 196} /* Delay = 150 */ - }; - - if( Weapon->Delay < 65 ) - { - return ucDelayOver59Levels66to80[0][ucPlayerLevel-66]; - } - else if( Weapon->Delay < 70 ) - { - return ucDelayOver59Levels66to80[1][ucPlayerLevel-66]; - } - else if( Weapon->Delay < 85 ) - { - return ucDelayOver59Levels66to80[2][ucPlayerLevel-66]; - } - else if( Weapon->Delay < 95 ) - { - return ucDelayOver59Levels66to80[3][ucPlayerLevel-66]; - } - else if( Weapon->Delay < 150 ) - { - return ucDelayOver59Levels66to80[4][ucPlayerLevel-66]; - } - else - { - return ucDelayOver59Levels66to80[5][ucPlayerLevel-66]; - } - } - } - - - // If we've gotten to this point in the function without hitting a return statement, - // we know that the character's level is between 28 and 65, and that the 2H weapon's - // delay is 28 or higher. - - // The Damage Bonus values returned by this function (in the level 28-65 range) are - // based on a table of 2H Weapon Damage Bonuses provided by Lucy at the following address: - // http://lucy.allakhazam.com/dmgbonus.html - - if( Weapon->Delay <= 39 ) - { - if( ucPlayerLevel <= 53) - { - // The Damage Bonus for all 2H weapons with delays between 28 and 39 (inclusive) is the same for players level 53 and below... - static const uint8 ucDelay28to39LevelUnder54[] = {1, 1, 2, 3, 3, 3, 4, 5, 5, 6, 6, 6, 8, 8, 8, 9, 9, 10, 11, 11, 11, 12, 13, 14, 16, 17}; - - // As a note: The following formula accurately calculates damage bonuses for 2H weapons with delays in the range 28-39 (inclusive) - // for characters levels 28-50 (inclusive): - // return ( (ucPlayerLevel - 22) / 3 ) + ( (ucPlayerLevel - 25) / 5 ); - // - // However, the small lookup array used above is actually much faster. So we'll just use it instead of the formula - // - // (Thanks to Reno for helping figure out the above formula!) - - return ucDelay28to39LevelUnder54[ucPlayerLevel-28]; - } - else - { - // Use a matrix to look up the damage bonus for 2H weapons with delays between 28 and 39 wielded by characters level 54 and above. - static const uint8 ucDelay28to39Level54to64[12][11] = - { - /* Level: */ - /* 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 */ - - {17, 21, 21, 23, 25, 26, 28, 30, 31, 31, 33}, /* Delay = 28 */ - {17, 21, 22, 23, 25, 26, 29, 30, 31, 32, 34}, /* Delay = 29 */ - {18, 21, 22, 23, 25, 27, 29, 31, 32, 32, 34}, /* Delay = 30 */ - {18, 21, 22, 23, 25, 27, 29, 31, 32, 33, 34}, /* Delay = 31 */ - {18, 21, 22, 24, 26, 27, 30, 32, 32, 33, 35}, /* Delay = 32 */ - {18, 21, 22, 24, 26, 27, 30, 32, 33, 34, 35}, /* Delay = 33 */ - {18, 22, 22, 24, 26, 28, 30, 32, 33, 34, 36}, /* Delay = 34 */ - {18, 22, 23, 24, 26, 28, 31, 33, 34, 34, 36}, /* Delay = 35 */ - {18, 22, 23, 25, 27, 28, 31, 33, 34, 35, 37}, /* Delay = 36 */ - {18, 22, 23, 25, 27, 29, 31, 33, 34, 35, 37}, /* Delay = 37 */ - {18, 22, 23, 25, 27, 29, 32, 34, 35, 36, 38}, /* Delay = 38 */ - {18, 22, 23, 25, 27, 29, 32, 34, 35, 36, 38} /* Delay = 39 */ - }; - - return ucDelay28to39Level54to64[Weapon->Delay-28][ucPlayerLevel-54]; - } - } - else if( Weapon->Delay <= 59 ) - { - if( ucPlayerLevel <= 52 ) - { - if( Weapon->Delay <= 45 ) - { - static const uint8 ucDelay40to45Levels28to52[6][25] = - { - /* Level: */ - /* 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52 */ - - {2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 7, 7, 9, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 16, 18}, /* Delay = 40 */ - {2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 7, 7, 9, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 16, 18}, /* Delay = 41 */ - {2, 2, 3, 4, 4, 4, 5, 6, 6, 7, 7, 7, 9, 9, 9, 10, 10, 11, 12, 12, 12, 13, 14, 16, 18}, /* Delay = 42 */ - {4, 4, 5, 6, 6, 6, 7, 8, 8, 9, 9, 9, 11, 11, 11, 12, 12, 13, 14, 14, 14, 15, 16, 18, 20}, /* Delay = 43 */ - {4, 4, 5, 6, 6, 6, 7, 8, 8, 9, 9, 9, 11, 11, 11, 12, 12, 13, 14, 14, 14, 15, 16, 18, 20}, /* Delay = 44 */ - {5, 5, 6, 7, 7, 7, 8, 9, 9, 10, 10, 10, 12, 12, 12, 13, 13, 14, 15, 15, 15, 16, 17, 19, 21} /* Delay = 45 */ - }; - - return ucDelay40to45Levels28to52[Weapon->Delay-40][ucPlayerLevel-28]; - } - else - { - static const uint8 ucDelay46Levels28to52[] = {6, 6, 7, 8, 8, 8, 9, 10, 10, 11, 11, 11, 13, 13, 13, 14, 14, 15, 16, 16, 16, 17, 18, 20, 22}; - - return ucDelay46Levels28to52[ucPlayerLevel-28] + ((Weapon->Delay-46) / 3); - } - } - else - { - // Player is in the level range 53 - 64 - - // Calculating damage bonus for 2H weapons with a delay between 40 and 59 (inclusive) involves, unforunately, a brute-force matrix lookup. - static const uint8 ucDelay40to59Levels53to64[20][37] = - { - /* Level: */ - /* 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64 */ - - {19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40}, /* Delay = 40 */ - {19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40}, /* Delay = 41 */ - {19, 20, 24, 25, 27, 29, 31, 34, 36, 37, 38, 40}, /* Delay = 42 */ - {21, 22, 26, 27, 29, 31, 33, 37, 39, 40, 41, 43}, /* Delay = 43 */ - {21, 22, 26, 27, 29, 32, 34, 37, 39, 40, 41, 43}, /* Delay = 44 */ - {22, 23, 27, 28, 31, 33, 35, 38, 40, 42, 43, 45}, /* Delay = 45 */ - {23, 24, 28, 30, 32, 34, 36, 40, 42, 43, 44, 46}, /* Delay = 46 */ - {23, 24, 29, 30, 32, 34, 37, 40, 42, 43, 44, 47}, /* Delay = 47 */ - {23, 24, 29, 30, 32, 35, 37, 40, 43, 44, 45, 47}, /* Delay = 48 */ - {24, 25, 30, 31, 34, 36, 38, 42, 44, 45, 46, 49}, /* Delay = 49 */ - {24, 26, 30, 31, 34, 36, 39, 42, 44, 46, 47, 49}, /* Delay = 50 */ - {24, 26, 30, 31, 34, 36, 39, 42, 45, 46, 47, 49}, /* Delay = 51 */ - {25, 27, 31, 33, 35, 38, 40, 44, 46, 47, 49, 51}, /* Delay = 52 */ - {25, 27, 31, 33, 35, 38, 40, 44, 46, 48, 49, 51}, /* Delay = 53 */ - {26, 27, 32, 33, 36, 38, 41, 44, 47, 48, 49, 52}, /* Delay = 54 */ - {27, 28, 33, 34, 37, 39, 42, 46, 48, 50, 51, 53}, /* Delay = 55 */ - {27, 28, 33, 34, 37, 40, 42, 46, 49, 50, 51, 54}, /* Delay = 56 */ - {27, 28, 33, 34, 37, 40, 43, 46, 49, 50, 52, 54}, /* Delay = 57 */ - {28, 29, 34, 36, 39, 41, 44, 48, 50, 52, 53, 56}, /* Delay = 58 */ - {28, 29, 34, 36, 39, 41, 44, 48, 51, 52, 54, 56} /* Delay = 59 */ - }; - - return ucDelay40to59Levels53to64[Weapon->Delay-40][ucPlayerLevel-53]; - } - } - else - { - // The following table allows us to look up Damage Bonuses for weapons with delays greater than or equal to 60. - // - // There aren't a lot of 2H weapons with a delay greater than 60. In fact, both a database and Lucy search run by janusd confirm - // that the only unique 2H delays greater than 60 are: 65, 70, 85, 95, and 150. - // - // To be fair, there are also weapons with delays of 66 and 100. But they are either not equippable (None/None), or are - // only available to GMs from merchants in Sunset Home (cshome). In order to keep this table "lean and mean", I will not - // include the values for delays 66 and 100. If they ever are wielded, the 66 delay weapon will use the 65 delay bonuses, - // and the 100 delay weapon will use the 95 delay bonuses. So it's not a big deal. - // - // Still, if someone in the future decides that they do want to include them, here are the tables for these two delays: - // - // {12, 12, 13, 14, 14, 14, 15, 16, 16, 17, 17, 17, 19, 19, 19, 20, 20, 21, 22, 22, 22, 23, 24, 26, 29, 30, 32, 37, 39, 42, 45, 48, 53, 55, 57, 59, 61, 64} /* Delay = 66 */ - // {24, 24, 25, 26, 26, 26, 27, 28, 28, 29, 29, 29, 31, 31, 31, 32, 32, 33, 34, 34, 34, 35, 36, 39, 43, 45, 48, 55, 57, 62, 66, 71, 77, 80, 83, 85, 89, 92} /* Delay = 100 */ - // - // In case there are 2H weapons added in the future with delays other than those listed above (and until the damage bonuses - // associated with that new delay are added to this function), this function is designed to do the following: - // - // For weapons with delays in the range 60-64, use the Damage Bonus that would apply to a 2H weapon with delay 60. - // For weapons with delays in the range 65-69, use the Damage Bonus that would apply to a 2H weapon with delay 65 - // For weapons with delays in the range 70-84, use the Damage Bonus that would apply to a 2H weapon with delay 70. - // For weapons with delays in the range 85-94, use the Damage Bonus that would apply to a 2H weapon with delay 85. - // For weapons with delays in the range 95-149, use the Damage Bonus that would apply to a 2H weapon with delay 95. - // For weapons with delays 150 or higher, use the Damage Bonus that would apply to a 2H weapon with delay 150. - - static const uint8 ucDelayOver59Levels28to65[6][38] = - { - /* Level: */ - /* 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64. 65 */ - - {10, 10, 11, 12, 12, 12, 13, 14, 14, 15, 15, 15, 17, 17, 17, 18, 18, 19, 20, 20, 20, 21, 22, 24, 27, 28, 30, 35, 36, 39, 42, 45, 49, 51, 53, 54, 57, 59}, /* Delay = 60 */ - {12, 12, 13, 14, 14, 14, 15, 16, 16, 17, 17, 17, 19, 19, 19, 20, 20, 21, 22, 22, 22, 23, 24, 26, 29, 30, 32, 37, 39, 42, 45, 48, 52, 55, 57, 58, 61, 63}, /* Delay = 65 */ - {14, 14, 15, 16, 16, 16, 17, 18, 18, 19, 19, 19, 21, 21, 21, 22, 22, 23, 24, 24, 24, 25, 26, 28, 31, 33, 35, 40, 42, 45, 48, 52, 56, 59, 61, 62, 65, 68}, /* Delay = 70 */ - {19, 19, 20, 21, 21, 21, 22, 23, 23, 24, 24, 24, 26, 26, 26, 27, 27, 28, 29, 29, 29, 30, 31, 34, 37, 39, 41, 47, 49, 54, 57, 61, 66, 69, 72, 74, 77, 80}, /* Delay = 85 */ - {22, 22, 23, 24, 24, 24, 25, 26, 26, 27, 27, 27, 29, 29, 29, 30, 30, 31, 32, 32, 32, 33, 34, 37, 40, 43, 45, 52, 54, 59, 62, 67, 73, 76, 79, 81, 84, 88}, /* Delay = 95 */ - {40, 40, 41, 42, 42, 42, 43, 44, 44, 45, 45, 45, 47, 47, 47, 48, 48, 49, 50, 50, 50, 51, 52, 56, 61, 65, 69, 78, 82, 89, 94, 102, 110, 115, 119, 122, 127, 132} /* Delay = 150 */ - }; - - if( Weapon->Delay < 65 ) - { - return ucDelayOver59Levels28to65[0][ucPlayerLevel-28]; - } - else if( Weapon->Delay < 70 ) - { - return ucDelayOver59Levels28to65[1][ucPlayerLevel-28]; - } - else if( Weapon->Delay < 85 ) - { - return ucDelayOver59Levels28to65[2][ucPlayerLevel-28]; - } - else if( Weapon->Delay < 95 ) - { - return ucDelayOver59Levels28to65[3][ucPlayerLevel-28]; - } - else if( Weapon->Delay < 150 ) - { - return ucDelayOver59Levels28to65[4][ucPlayerLevel-28]; - } - else - { - return ucDelayOver59Levels28to65[5][ucPlayerLevel-28]; - } - } -} - -int Mob::GetMonkHandToHandDamage(void) -{ - // Kaiyodo - Determine a monk's fist damage. Table data from www.monkly-business.com - // saved as static array - this should speed this function up considerably - static int damage[66] = { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 - 99, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 7, 7, 7, 7, 7, - 8, 8, 8, 8, 8, 9, 9, 9, 9, 9,10,10,10,10,10,11,11,11,11,11, - 12,12,12,12,12,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14, - 14,14,15,15,15,15 }; - - // Have a look to see if we have epic fists on - - if (IsClient() && CastToClient()->GetItemIDAt(12) == 10652) - return(9); - else - { - int Level = GetLevel(); - if (Level > 65) - return(19); - else - return damage[Level]; - } -} - -int Mob::GetMonkHandToHandDelay(void) -{ - // Kaiyodo - Determine a monk's fist delay. Table data from www.monkly-business.com - // saved as static array - this should speed this function up considerably - static int delayshuman[66] = { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 - 99,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, - 36,36,36,36,36,35,35,35,35,35,34,34,34,34,34,33,33,33,33,33, - 32,32,32,32,32,31,31,31,31,31,30,30,30,29,29,29,28,28,28,27, - 26,24,22,20,20,20 }; - static int delaysiksar[66] = { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 - 99,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36, - 36,36,36,36,36,36,36,36,36,36,35,35,35,35,35,34,34,34,34,34, - 33,33,33,33,33,32,32,32,32,32,31,31,31,30,30,30,29,29,29,28, - 27,24,22,20,20,20 }; - - // Have a look to see if we have epic fists on - if (IsClient() && CastToClient()->GetItemIDAt(12) == 10652) - return(16); - else - { - int Level = GetLevel(); - if (GetRace() == HUMAN) - { - if (Level > 65) - return(24); - else - return delayshuman[Level]; - } - else //heko: iksar table - { - if (Level > 65) - return(25); - else - return delaysiksar[Level]; - } - } -} - - -int32 Mob::ReduceDamage(int32 damage) -{ - if(damage <= 0) - return damage; - - int32 slot = -1; - bool DisableMeleeRune = false; - - if (spellbonuses.NegateAttacks[0]){ - slot = spellbonuses.NegateAttacks[1]; - if(slot >= 0) { - if(--buffs[slot].numhits == 0) { - - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot , true); - } - return -6; - } - } - - //Only mitigate if damage is above the minimium specified. - if (spellbonuses.MeleeThresholdGuard[0]){ - slot = spellbonuses.MeleeThresholdGuard[1]; - - if (slot >= 0 && (damage > spellbonuses.MeleeThresholdGuard[2])) - { - DisableMeleeRune = true; - int damage_to_reduce = damage * spellbonuses.MeleeThresholdGuard[0] / 100; - if(damage_to_reduce > buffs[slot].melee_rune) - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MeleeThresholdGuard %d damage negated, %d" - " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); - damage -= damage_to_reduce; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MeleeThresholdGuard %d damage negated, %d" - " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); - buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); - damage -= damage_to_reduce; - } - } - } - - - if (spellbonuses.MitigateMeleeRune[0] && !DisableMeleeRune){ - slot = spellbonuses.MitigateMeleeRune[1]; - if(slot >= 0) - { - int damage_to_reduce = damage * spellbonuses.MitigateMeleeRune[0] / 100; - if(damage_to_reduce > buffs[slot].melee_rune) - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" - " damage remaining, fading buff.", damage_to_reduce, buffs[slot].melee_rune); - damage -= damage_to_reduce; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" - " damage remaining.", damage_to_reduce, buffs[slot].melee_rune); - buffs[slot].melee_rune = (buffs[slot].melee_rune - damage_to_reduce); - damage -= damage_to_reduce; - } - } - } - - if (spellbonuses.TriggerMeleeThreshold[2]){ - slot = spellbonuses.TriggerMeleeThreshold[1]; - - if (slot >= 0) { - if(damage > buffs[slot].melee_rune) { - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else{ - buffs[slot].melee_rune = (buffs[slot].melee_rune - damage); - } - } - } - - if(damage < 1) - return -6; - - if (spellbonuses.MeleeRune[0] && spellbonuses.MeleeRune[1] >= 0) - damage = RuneAbsorb(damage, SE_Rune); - - if(damage < 1) - return -6; - - return(damage); -} - -int32 Mob::AffectMagicalDamage(int32 damage, uint16 spell_id, const bool iBuffTic, Mob* attacker) -{ - if(damage <= 0) - return damage; - - bool DisableSpellRune = false; - int32 slot = -1; - - // See if we block the spell outright first - if (spellbonuses.NegateAttacks[0]){ - slot = spellbonuses.NegateAttacks[1]; - if(slot >= 0) { - if(--buffs[slot].numhits == 0) { - - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot , true); - } - return 0; - } - } - - // If this is a DoT, use DoT Shielding... - if(iBuffTic) { - damage -= (damage * itembonuses.DoTShielding / 100); - - if (spellbonuses.MitigateDotRune[0]){ - slot = spellbonuses.MitigateDotRune[1]; - if(slot >= 0) - { - int damage_to_reduce = damage * spellbonuses.MitigateDotRune[0] / 100; - if(damage_to_reduce > buffs[slot].dot_rune) - { - damage -= damage_to_reduce; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - buffs[slot].dot_rune = (buffs[slot].dot_rune - damage_to_reduce); - damage -= damage_to_reduce; - } - } - } - } - - // This must be a DD then so lets apply Spell Shielding and runes. - else - { - // Reduce damage by the Spell Shielding first so that the runes don't take the raw damage. - damage -= (damage * itembonuses.SpellShield / 100); - - - //Only mitigate if damage is above the minimium specified. - if (spellbonuses.SpellThresholdGuard[0]){ - slot = spellbonuses.SpellThresholdGuard[1]; - - if (slot >= 0 && (damage > spellbonuses.MeleeThresholdGuard[2])) - { - DisableSpellRune = true; - int damage_to_reduce = damage * spellbonuses.SpellThresholdGuard[0] / 100; - if(damage_to_reduce > buffs[slot].magic_rune) - { - damage -= damage_to_reduce; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - buffs[slot].melee_rune = (buffs[slot].magic_rune - damage_to_reduce); - damage -= damage_to_reduce; - } - } - } - - - // Do runes now. - if (spellbonuses.MitigateSpellRune[0] && !DisableSpellRune){ - slot = spellbonuses.MitigateSpellRune[1]; - if(slot >= 0) - { - int damage_to_reduce = damage * spellbonuses.MitigateSpellRune[0] / 100; - if(damage_to_reduce > buffs[slot].magic_rune) - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateSpellDamage %d damage negated, %d" - " damage remaining, fading buff.", damage_to_reduce, buffs[slot].magic_rune); - damage -= damage_to_reduce; - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else - { - mlog(SPELLS__EFFECT_VALUES, "Mob::ReduceDamage SE_MitigateMeleeDamage %d damage negated, %d" - " damage remaining.", damage_to_reduce, buffs[slot].magic_rune); - buffs[slot].magic_rune = (buffs[slot].magic_rune - damage_to_reduce); - damage -= damage_to_reduce; - } - } - } - - if (spellbonuses.TriggerSpellThreshold[2]){ - slot = spellbonuses.TriggerSpellThreshold[1]; - - if (slot >= 0) { - if(damage > buffs[slot].magic_rune) { - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - else{ - buffs[slot].magic_rune = (buffs[slot].magic_rune - damage); - } - } - } - - if(damage < 1) - return 0; - - - if (spellbonuses.AbsorbMagicAtt[0] && spellbonuses.AbsorbMagicAtt[1] >= 0) - damage = RuneAbsorb(damage, SE_AbsorbMagicAtt); - - if(damage < 1) - return 0; - } - return damage; -} - -int32 Mob::ReduceAllDamage(int32 damage) -{ - if(damage <= 0) - return damage; - - if(spellbonuses.ManaAbsorbPercentDamage[0] && (GetMana() > damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100)) { - damage -= (damage * spellbonuses.ManaAbsorbPercentDamage[0] / 100); - SetMana(GetMana() - damage); - TryTriggerOnValueAmount(false, true); - } - - CheckNumHitsRemaining(8); - - return(damage); -} - -bool Mob::HasProcs() const -{ - for (int i = 0; i < MAX_PROCS; i++) - if (PermaProcs[i].spellID != SPELL_UNKNOWN || SpellProcs[i].spellID != SPELL_UNKNOWN) - return true; - return false; -} - -bool Mob::HasDefensiveProcs() const -{ - for (int i = 0; i < MAX_PROCS; i++) - if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) - return true; - return false; -} - -bool Mob::HasSkillProcs() const -{ - for (int i = 0; i < MAX_PROCS; i++) - if (SkillProcs[i].spellID != SPELL_UNKNOWN) - return true; - return false; -} - -bool Mob::HasRangedProcs() const -{ - for (int i = 0; i < MAX_PROCS; i++) - if (RangedProcs[i].spellID != SPELL_UNKNOWN) - return true; - return false; -} - -bool Client::CheckDoubleAttack(bool tripleAttack) { - - //Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA) - uint16 bonusGiveDA = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; - - if(!HasSkill(SkillDoubleAttack) && !bonusGiveDA) - return false; - - float chance = 0.0f; - - uint16 skill = GetSkill(SkillDoubleAttack); - - int16 bonusDA = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance; - - //Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base. - if (skill) - chance = (float(skill+GetLevel()) * (float(100.0f+bonusDA+bonusGiveDA) /100.0f)) /500.0f; - else - chance = (float(bonusGiveDA) * (float(100.0f+bonusDA)/100.0f) ) /100.0f; - - //Live now uses a static Triple Attack skill (lv 46 = 2% lv 60 = 20%) - We do not have this skill on EMU ATM. - //A reasonable forumla would then be TA = 20% * chance - //AA's can also give triple attack skill over cap. (ie Burst of Power) NOTE: Skill ID in spell data is 76 (Triple Attack) - //Kayen: Need to decide if we can implement triple attack skill before working in over the cap effect. - if(tripleAttack) { - // Only some Double Attack classes get Triple Attack [This is already checked in client_processes.cpp] - int16 triple_bonus = spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance; - chance *= 0.2f; //Baseline chance is 20% of your double attack chance. - chance *= float(100.0f+triple_bonus)/100.0f; //Apply modifiers. - } - - if((MakeRandomFloat(0, 1) < chance)) - return true; - - return false; -} - -bool Client::CheckDoubleRangedAttack() { - - int16 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; - - if(chance && (MakeRandomInt(0, 100) < chance)) - return true; - - return false; -} - -void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, const SkillUseTypes skill_used, bool &avoidable, const int8 buffslot, const bool iBuffTic) { - // This method is called with skill_used=ABJURE for Damage Shield damage. - bool FromDamageShield = (skill_used == SkillAbjuration); - - mlog(COMBAT__HITS, "Applying damage %d done by %s with skill %d and spell %d, avoidable? %s, is %sa buff tic in slot %d", - damage, attacker?attacker->GetName():"NOBODY", skill_used, spell_id, avoidable?"yes":"no", iBuffTic?"":"not ", buffslot); - - if (GetInvul() || DivineAura()) { - mlog(COMBAT__DAMAGE, "Avoiding %d damage due to invulnerability.", damage); - damage = -5; - } - - if( spell_id != SPELL_UNKNOWN || attacker == nullptr ) - avoidable = false; - - // only apply DS if physical damage (no spell damage) - // damage shield calls this function with spell_id set, so its unavoidable - if (attacker && damage > 0 && spell_id == SPELL_UNKNOWN && skill_used != SkillArchery && skill_used != SkillThrowing) { - DamageShield(attacker); - } - - if (spell_id == SPELL_UNKNOWN && skill_used) { - CheckNumHitsRemaining(1); //Incoming Hit Attempts - - if (attacker) - attacker->CheckNumHitsRemaining(2); //Outgoing Hit Attempts - } - - if(attacker){ - if(attacker->IsClient()){ - if(!RuleB(Combat, EXPFromDmgShield)) { - // Damage shield damage shouldn't count towards who gets EXP - if(!attacker->CastToClient()->GetFeigned() && !FromDamageShield) - AddToHateList(attacker, 0, damage, true, false, iBuffTic); - } - else { - if(!attacker->CastToClient()->GetFeigned()) - AddToHateList(attacker, 0, damage, true, false, iBuffTic); - } - } - else - AddToHateList(attacker, 0, damage, true, false, iBuffTic); - } - - if(damage > 0) { - //if there is some damage being done and theres an attacker involved - if(attacker) { - if(spell_id == SPELL_HARM_TOUCH2 && attacker->IsClient() && attacker->CastToClient()->CheckAAEffect(aaEffectLeechTouch)){ - int healed = damage; - healed = attacker->GetActSpellHealing(spell_id, healed); - attacker->HealDamage(healed); - entity_list.MessageClose(this, true, 300, MT_Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName() ); - attacker->CastToClient()->DisableAAEffect(aaEffectLeechTouch); - } - - // if spell is lifetap add hp to the caster - if (spell_id != SPELL_UNKNOWN && IsLifetapSpell( spell_id )) { - int healed = damage; - - healed = attacker->GetActSpellHealing(spell_id, healed); - mlog(COMBAT__DAMAGE, "Applying lifetap heal of %d to %s", healed, attacker->GetName()); - attacker->HealDamage(healed); - - //we used to do a message to the client, but its gone now. - // emote goes with every one ... even npcs - entity_list.MessageClose(this, true, 300, MT_Emote, "%s beams a smile at %s", attacker->GetCleanName(), this->GetCleanName() ); - } - } //end `if there is some damage being done and theres anattacker person involved` - - Mob *pet = GetPet(); - if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse()) - { - if (!pet->IsHeld()) { - mlog(PETS__AGGRO, "Sending pet %s into battle due to attack.", pet->GetName()); - pet->AddToHateList(attacker, 1); - pet->SetTarget(attacker); - Message_StringID(10, PET_ATTACKING, pet->GetCleanName(), attacker->GetCleanName()); - } - } - - //see if any runes want to reduce this damage - if(spell_id == SPELL_UNKNOWN) { - damage = ReduceDamage(damage); - mlog(COMBAT__HITS, "Melee Damage reduced to %d", damage); - } else { - int32 origdmg = damage; - damage = AffectMagicalDamage(damage, spell_id, iBuffTic, attacker); - if (origdmg != damage && attacker && attacker->IsClient()) { - if(attacker->CastToClient()->GetFilter(FilterDamageShields) != FilterHide) - attacker->Message(15, "The Spellshield absorbed %d of %d points of damage", origdmg - damage, origdmg); - } - if (damage == 0 && attacker && origdmg != damage && IsClient()) { - //Kayen: Probably need to add a filter for this - Not sure if this msg is correct but there should be a message for spell negate/runes. - Message(263, "%s tries to cast on YOU, but YOUR magical skin absorbs the spell.",attacker->GetCleanName()); - } - - } - - if (skill_used) - CheckNumHitsRemaining(6); //Incomming Hit Success on Defender - - ReduceAllDamage(damage); - - if(IsClient() && CastToClient()->sneaking){ - CastToClient()->sneaking = false; - SendAppearancePacket(AT_Sneak, 0); - } - if(attacker && attacker->IsClient() && attacker->CastToClient()->sneaking){ - attacker->CastToClient()->sneaking = false; - attacker->SendAppearancePacket(AT_Sneak, 0); - } - - //final damage has been determined. - - SetHP(GetHP() - damage); - - if(HasDied()) { - bool IsSaved = false; - - if(TryDivineSave()) - IsSaved = true; - - if(!IsSaved && !TrySpellOnDeath()) { - SetHP(-500); - - if(Death(attacker, damage, spell_id, skill_used)) { - return; - } - } - } - else{ - if(GetHPRatio() < 16) - TryDeathSave(); - } - - TryTriggerOnValueAmount(true); - - //fade mez if we are mezzed - if (IsMezzed()) { - mlog(COMBAT__HITS, "Breaking mez due to attack."); - BuffFadeByEffect(SE_Mez); - } - - //check stun chances if bashing - if (damage > 0 && ((skill_used == SkillBash || skill_used == SkillKick) && attacker)) { - // NPCs can stun with their bash/kick as soon as they receive it. - // Clients can stun mobs under level 56 with their kick when they get level 55 or greater. - // Clients have a chance to stun if the mob is 56+ - - // Calculate the chance to stun - int stun_chance = 0; - if (!GetSpecialAbility(UNSTUNABLE)) { - if (attacker->IsNPC()) { - stun_chance = RuleI(Combat, NPCBashKickStunChance); - } else if (attacker->IsClient()) { - // Less than base immunity - // Client vs. Client always uses the chance - if (!IsClient() && GetLevel() <= RuleI(Spells, BaseImmunityLevel)) { - if (skill_used == SkillBash) // Bash always will - stun_chance = 100; - else if (attacker->GetLevel() >= RuleI(Combat, ClientStunLevel)) - stun_chance = 100; // only if you're over level 55 and using kick - } else { // higher than base immunity or Client vs. Client - // not sure on this number, use same as NPC for now - if (skill_used == SkillKick && attacker->GetLevel() < RuleI(Combat, ClientStunLevel)) - stun_chance = RuleI(Combat, NPCBashKickStunChance); - else if (skill_used == SkillBash) - stun_chance = RuleI(Combat, NPCBashKickStunChance) + - attacker->spellbonuses.StunBashChance + - attacker->itembonuses.StunBashChance + - attacker->aabonuses.StunBashChance; - } - } - } - - if (stun_chance && MakeRandomInt(0, 99) < stun_chance) { - // Passed stun, try to resist now - int stun_resist = itembonuses.StunResist + spellbonuses.StunResist; - int frontal_stun_resist = itembonuses.FrontalStunResist + spellbonuses.FrontalStunResist; - - mlog(COMBAT__HITS, "Stun passed, checking resists. Was %d chance.", stun_chance); - if (IsClient()) { - stun_resist += aabonuses.StunResist; - frontal_stun_resist += aabonuses.FrontalStunResist; - } - - // frontal stun check for ogres/bonuses - if (((GetBaseRace() == OGRE && IsClient()) || - (frontal_stun_resist && MakeRandomInt(0, 99) < frontal_stun_resist)) && - !attacker->BehindMob(this, attacker->GetX(), attacker->GetY())) { - mlog(COMBAT__HITS, "Frontal stun resisted. %d chance.", frontal_stun_resist); - } else { - // Normal stun resist check. - if (stun_resist && MakeRandomInt(0, 99) < stun_resist) { - if (IsClient()) - Message_StringID(MT_Stun, SHAKE_OFF_STUN); - mlog(COMBAT__HITS, "Stun Resisted. %d chance.", stun_resist); - } else { - mlog(COMBAT__HITS, "Stunned. %d resist chance.", stun_resist); - Stun(MakeRandomInt(0, 2) * 1000); // 0-2 seconds - } - } - } else { - mlog(COMBAT__HITS, "Stun failed. %d chance.", stun_chance); - } - } - - if(spell_id != SPELL_UNKNOWN && !iBuffTic) { - //see if root will break - if (IsRooted() && !FromDamageShield) // neotoyko: only spells cancel root - TryRootFadeByDamage(buffslot, attacker); - } - else if(spell_id == SPELL_UNKNOWN) - { - //increment chances of interrupting - if(IsCasting()) { //shouldnt interrupt on regular spell damage - attacked_count++; - mlog(COMBAT__HITS, "Melee attack while casting. Attack count %d", attacked_count); - } - } - - //send an HP update if we are hurt - if(GetHP() < GetMaxHP()) - SendHPUpdate(); - } //end `if damage was done` - - //send damage packet... - if(!iBuffTic) { //buff ticks do not send damage, instead they just call SendHPUpdate(), which is done below - EQApplicationPacket* outapp = new EQApplicationPacket(OP_Damage, sizeof(CombatDamage_Struct)); - CombatDamage_Struct* a = (CombatDamage_Struct*)outapp->pBuffer; - a->target = GetID(); - if (attacker == nullptr) - a->source = 0; - else if (attacker->IsClient() && attacker->CastToClient()->GMHideMe()) - a->source = 0; - else - a->source = attacker->GetID(); - a->type = SkillDamageTypes[skill_used]; // was 0x1c - a->damage = damage; - a->spellid = spell_id; - - //Note: if players can become pets, they will not receive damage messages of their own - //this was done to simplify the code here (since we can only effectively skip one mob on queue) - eqFilterType filter; - Mob *skip = attacker; - if(attacker && attacker->GetOwnerID()) { - //attacker is a pet, let pet owners see their pet's damage - Mob* owner = attacker->GetOwner(); - if (owner && owner->IsClient()) { - if (((spell_id != SPELL_UNKNOWN) || (FromDamageShield)) && damage>0) { - //special crap for spell damage, looks hackish to me - char val1[20]={0}; - owner->Message_StringID(MT_NonMelee,OTHER_HIT_NONMELEE,GetCleanName(),ConvertArray(damage,val1)); - } else { - if(damage > 0) { - if(spell_id != SPELL_UNKNOWN) - filter = iBuffTic ? FilterDOT : FilterSpellDamage; - else - filter = FilterPetHits; - } else if(damage == -5) - filter = FilterNone; //cant filter invulnerable - else - filter = FilterPetMisses; - - if(!FromDamageShield) - owner->CastToClient()->QueuePacket(outapp,true,CLIENT_CONNECTED,filter); - } - } - skip = owner; - } else { - //attacker is not a pet, send to the attacker - - //if the attacker is a client, try them with the correct filter - if(attacker && attacker->IsClient()) { - if (((spell_id != SPELL_UNKNOWN)||(FromDamageShield)) && damage>0) { - //special crap for spell damage, looks hackish to me - char val1[20]={0}; - if (FromDamageShield) - { - if(!attacker->CastToClient()->GetFilter(FilterDamageShields) == FilterHide) - { - attacker->Message_StringID(MT_DS,OTHER_HIT_NONMELEE,GetCleanName(),ConvertArray(damage,val1)); - } - } - else - entity_list.MessageClose_StringID(this, true, 100, MT_NonMelee,HIT_NON_MELEE,attacker->GetCleanName(),GetCleanName(),ConvertArray(damage,val1)); - } else { - if(damage > 0) { - if(spell_id != SPELL_UNKNOWN) - filter = iBuffTic ? FilterDOT : FilterSpellDamage; - else - filter = FilterNone; //cant filter our own hits - } else if(damage == -5) - filter = FilterNone; //cant filter invulnerable - else - filter = FilterMyMisses; - - attacker->CastToClient()->QueuePacket(outapp, true, CLIENT_CONNECTED, filter); - } - } - skip = attacker; - } - - //send damage to all clients around except the specified skip mob (attacker or the attacker's owner) and ourself - if(damage > 0) { - if(spell_id != SPELL_UNKNOWN) - filter = iBuffTic ? FilterDOT : FilterSpellDamage; - else - filter = FilterOthersHit; - } else if(damage == -5) - filter = FilterNone; //cant filter invulnerable - else - filter = FilterOthersMiss; - //make attacker (the attacker) send the packet so we can skip them and the owner - //this call will send the packet to `this` as well (using the wrong filter) (will not happen until PC charm works) - // If this is Damage Shield damage, the correct OP_Damage packets will be sent from Mob::DamageShield, so - // we don't send them here. - if(!FromDamageShield) { - entity_list.QueueCloseClients(this, outapp, true, 200, skip, true, filter); - //send the damage to ourself if we are a client - if(IsClient()) { - //I dont think any filters apply to damage affecting us - CastToClient()->QueuePacket(outapp); - } - } - - safe_delete(outapp); - } else { - //else, it is a buff tic... - // Everhood - So we can see our dot dmg like live shows it. - if(spell_id != SPELL_UNKNOWN && damage > 0 && attacker && attacker != this && attacker->IsClient()) { - //might filter on (attack_skill>200 && attack_skill<250), but I dont think we need it - attacker->FilteredMessage_StringID(attacker, MT_DoTDamage, FilterDOT, - YOUR_HIT_DOT, GetCleanName(), itoa(damage), spells[spell_id].name); - // older clients don't have the below String ID, but it will be filtered - entity_list.FilteredMessageClose_StringID(attacker, true, 200, - MT_DoTDamage, FilterDOT, OTHER_HIT_DOT, GetCleanName(), - itoa(damage), attacker->GetCleanName(), spells[spell_id].name); - } - } //end packet sending - -} - - -void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id) -{ - int32 maxhp = GetMaxHP(); - int32 curhp = GetHP(); - uint32 acthealed = 0; - - if (caster && amount > 0) { - if (caster->IsNPC() && !caster->IsPet()) { - float npchealscale = caster->CastToNPC()->GetHealScale(); - amount = (static_cast(amount) * npchealscale) / 100.0f; - } - } - - if (amount > (maxhp - curhp)) - acthealed = (maxhp - curhp); - else - acthealed = amount; - - if (acthealed > 100) { - if (caster) { - if (IsBuffSpell(spell_id)) { // hots - // message to caster - if (caster->IsClient() && caster == this) { - if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater) - FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, - HOT_HEAL_SELF, itoa(acthealed), spells[spell_id].name); - else - FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, - YOU_HEALED, GetCleanName(), itoa(acthealed)); - } else if (caster->IsClient() && caster != this) { - if (caster->CastToClient()->GetClientVersionBit() & BIT_SoFAndLater) - caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, - HOT_HEAL_OTHER, GetCleanName(), itoa(acthealed), - spells[spell_id].name); - else - caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime, - YOU_HEAL, GetCleanName(), itoa(acthealed)); - } - // message to target - if (IsClient() && caster != this) { - if (CastToClient()->GetClientVersionBit() & BIT_SoFAndLater) - FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime, - HOT_HEALED_OTHER, caster->GetCleanName(), - itoa(acthealed), spells[spell_id].name); - else - FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime, - YOU_HEALED, caster->GetCleanName(), itoa(acthealed)); - } - } else { // normal heals - FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage, - YOU_HEALED, caster->GetCleanName(), itoa(acthealed)); - if (caster != this) - caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterSpellDamage, - YOU_HEAL, GetCleanName(), itoa(acthealed)); - } - } else { - Message(MT_NonMelee, "You have been healed for %d points of damage.", acthealed); - } - } - - if (curhp < maxhp) { - if ((curhp + amount) > maxhp) - curhp = maxhp; - else - curhp += amount; - SetHP(curhp); - - SendHPUpdate(); - } -} - -//proc chance includes proc bonus -float Mob::GetProcChances(float ProcBonus, uint16 weapon_speed, uint16 hand) -{ - int mydex = GetDEX(); - float ProcChance = 0.0f; - - switch (hand) { - case 13: - weapon_speed = attack_timer.GetDuration(); - break; - case 14: - weapon_speed = attack_dw_timer.GetDuration(); - break; - case 11: - weapon_speed = ranged_timer.GetDuration(); - break; - } - - //calculate the weapon speed in ms, so we can use the rule to compare against. - // fast as a client can swing, so should be the floor of the proc chance - if (weapon_speed < RuleI(Combat, MinHastedDelay)) - weapon_speed = RuleI(Combat, MinHastedDelay); - - if (RuleB(Combat, AdjustProcPerMinute)) { - ProcChance = (static_cast(weapon_speed) * - RuleR(Combat, AvgProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - ProcBonus += static_cast(mydex) * RuleR(Combat, ProcPerMinDexContrib); - ProcChance += ProcChance * ProcBonus / 100.0f; - } else { - ProcChance = RuleR(Combat, BaseProcChance) + - static_cast(mydex) / RuleR(Combat, ProcDexDivideBy); - ProcChance += ProcChance * ProcBonus / 100.0f; - } - - mlog(COMBAT__PROCS, "Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); - return ProcChance; -} - -float Mob::GetDefensiveProcChances(float &ProcBonus, float &ProcChance, uint16 weapon_speed, uint16 hand) { - int myagi = GetAGI(); - ProcBonus = 0; - ProcChance = 0; - - switch(hand){ - case 13: - weapon_speed = attack_timer.GetDuration(); - break; - case 14: - weapon_speed = attack_dw_timer.GetDuration(); - break; - case 11: - return 0; - break; - } - - //calculate the weapon speed in ms, so we can use the rule to compare against. - //weapon_speed = ((int)(weapon_speed*(100.0f+attack_speed)*PermaHaste)); - if(weapon_speed < RuleI(Combat, MinHastedDelay)) // fast as a client can swing, so should be the floor of the proc chance - weapon_speed = RuleI(Combat, MinHastedDelay); - - ProcChance = ((float)weapon_speed * RuleR(Combat, AvgDefProcsPerMinute) / 60000.0f); // compensate for weapon_speed being in ms - ProcBonus += float(myagi) * RuleR(Combat, DefProcPerMinAgiContrib) / 100.0f; - ProcChance = ProcChance + (ProcChance * ProcBonus); - - mlog(COMBAT__PROCS, "Defensive Proc chance %.2f (%.2f from bonuses)", ProcChance, ProcBonus); - return ProcChance; -} - -void Mob::TryDefensiveProc(const ItemInst* weapon, Mob *on, uint16 hand, int damage) { - - if (!on) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TryDefensiveProc for evaluation!"); - return; - } - - bool bSkillProc = HasSkillProcs(); - bool bDefensiveProc = HasDefensiveProcs(); - - if (!bDefensiveProc && !bSkillProc) - return; - - if (!bDefensiveProc && (bSkillProc && damage >= 0)) - return; - - float ProcChance, ProcBonus; - if(weapon!=nullptr) - on->GetDefensiveProcChances(ProcBonus, ProcChance, weapon->GetItem()->Delay, hand); - else - on->GetDefensiveProcChances(ProcBonus, ProcChance); - if(hand != 13) - ProcChance /= 2; - - if (bDefensiveProc){ - for (int i = 0; i < MAX_PROCS; i++) { - if (DefensiveProcs[i].spellID != SPELL_UNKNOWN) { - int chance = ProcChance * (DefensiveProcs[i].chance); - if ((MakeRandomInt(0, 100) < chance)) { - ExecWeaponProc(nullptr, DefensiveProcs[i].spellID, on); - CheckNumHitsRemaining(10,0,DefensiveProcs[i].base_spellID); - } - } - } - } - - if (bSkillProc && damage < 0){ - - if (damage == -1) - TrySkillProc(on, SkillBlock, ProcChance); - - if (damage == -2) - TrySkillProc(on, SkillParry, ProcChance); - - if (damage == -3) - TrySkillProc(on, SkillRiposte, ProcChance); - - if (damage == -4) - TrySkillProc(on, SkillDodge, ProcChance); - } -} - -void Mob::TryWeaponProc(const ItemInst* weapon_g, Mob *on, uint16 hand) { - if(!on) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TryWeaponProc for evaluation!"); - return; - } - - if (!IsAttackAllowed(on)) { - mlog(COMBAT__PROCS, "Preventing procing off of unattackable things."); - return; - } - - if(!weapon_g) { - TrySpellProc(nullptr, (const Item_Struct*)nullptr, on); - return; - } - - if(!weapon_g->IsType(ItemClassCommon)) { - TrySpellProc(nullptr, (const Item_Struct*)nullptr, on); - return; - } - - // Innate + aug procs from weapons - // TODO: powersource procs - TryWeaponProc(weapon_g, weapon_g->GetItem(), on, hand); - // Procs from Buffs and AA both melee and range - TrySpellProc(weapon_g, weapon_g->GetItem(), on, hand); - - return; -} - -void Mob::TryWeaponProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand) -{ - if (!weapon) - return; - uint16 skillinuse = 28; - int ourlevel = GetLevel(); - float ProcBonus = static_cast(aabonuses.ProcChanceSPA + - spellbonuses.ProcChanceSPA + itembonuses.ProcChanceSPA); - ProcBonus += static_cast(itembonuses.ProcChance) / 10.0f; // Combat Effects - float ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand); - - if (hand != 13) //Is Archery intened to proc at 50% rate? - ProcChance /= 2; - - // Try innate proc on weapon - // We can proc once here, either weapon or one aug - bool proced = false; // silly bool to prevent augs from going if weapon does - skillinuse = GetSkillByItemType(weapon->ItemType); - if (weapon->Proc.Type == ET_CombatProc) { - float WPC = ProcChance * (100.0f + // Proc chance for this weapon - static_cast(weapon->ProcRate)) / 100.0f; - if (MakeRandomFloat(0, 1) <= WPC) { // 255 dex = 0.084 chance of proc. No idea what this number should be really. - if (weapon->Proc.Level > ourlevel) { - mlog(COMBAT__PROCS, - "Tried to proc (%s), but our level (%d) is lower than required (%d)", - weapon->Name, ourlevel, weapon->Proc.Level); - if (IsPet()) { - Mob *own = GetOwner(); - if (own) - own->Message_StringID(13, PROC_PETTOOLOW); - } else { - Message_StringID(13, PROC_TOOLOW); - } - } else { - mlog(COMBAT__PROCS, - "Attacking weapon (%s) successfully procing spell %d (%.2f percent chance)", - weapon->Name, weapon->Proc.Effect, WPC * 100); - ExecWeaponProc(inst, weapon->Proc.Effect, on); - proced = true; - } - } - } - - if (!proced && inst) { - for (int r = 0; r < MAX_AUGMENT_SLOTS; r++) { - const ItemInst *aug_i = inst->GetAugment(r); - if (!aug_i) // no aug, try next slot! - continue; - const Item_Struct *aug = aug_i->GetItem(); - if (!aug) - continue; - - if (aug->Proc.Type == ET_CombatProc) { - float APC = ProcChance * (100.0f + // Proc chance for this aug - static_cast(aug->ProcRate)) / 100.0f; - if (MakeRandomFloat(0, 1) <= APC) { - if (aug->Proc.Level > ourlevel) { - if (IsPet()) { - Mob *own = GetOwner(); - if (own) - own->Message_StringID(13, PROC_PETTOOLOW); - } else { - Message_StringID(13, PROC_TOOLOW); - } - } else { - ExecWeaponProc(aug_i, aug->Proc.Effect, on); - break; - } - } - } - } - } - // TODO: Powersource procs - if (HasSkillProcs()) - TrySkillProc(on, skillinuse, ProcChance); - - return; -} - -void Mob::TrySpellProc(const ItemInst *inst, const Item_Struct *weapon, Mob *on, uint16 hand) -{ - float ProcBonus = static_cast(spellbonuses.SpellProcChance + - itembonuses.SpellProcChance + aabonuses.SpellProcChance); - float ProcChance = 0.0f; - if (weapon) - ProcChance = GetProcChances(ProcBonus, weapon->Delay, hand); - else - ProcChance = GetProcChances(ProcBonus); - - if (hand != 13) //Is Archery intened to proc at 50% rate? - ProcChance /= 2; - - bool rangedattk = false; - if (weapon && hand == 11) { - if (weapon->ItemType == ItemTypeArrow || - weapon->ItemType == ItemTypeLargeThrowing || - weapon->ItemType == ItemTypeSmallThrowing || - weapon->ItemType == ItemTypeBow) - rangedattk = true; - } - - for (uint32 i = 0; i < MAX_PROCS; i++) { - if (IsPet() && hand != 13) //Pets can only proc spell procs from their primay hand (ie; beastlord pets) - continue; // If pets ever can proc from off hand, this will need to change - - // Not ranged - if (!rangedattk) { - // Perma procs (AAs) - if (PermaProcs[i].spellID != SPELL_UNKNOWN) { - if (MakeRandomInt(0, 99) < PermaProcs[i].chance) { // TODO: Do these get spell bonus? - mlog(COMBAT__PROCS, - "Permanent proc %d procing spell %d (%d percent chance)", - i, PermaProcs[i].spellID, PermaProcs[i].chance); - ExecWeaponProc(nullptr, PermaProcs[i].spellID, on); - } else { - mlog(COMBAT__PROCS, - "Permanent proc %d failed to proc %d (%d percent chance)", - i, PermaProcs[i].spellID, PermaProcs[i].chance); - } - } - - // Spell procs (buffs) - if (SpellProcs[i].spellID != SPELL_UNKNOWN) { - float chance = ProcChance * (SpellProcs[i].chance / 100.0f); - if (MakeRandomFloat(0, 1) <= chance) { - mlog(COMBAT__PROCS, - "Spell proc %d procing spell %d (%.2f percent chance)", - i, SpellProcs[i].spellID, chance); - ExecWeaponProc(nullptr, SpellProcs[i].spellID, on); - CheckNumHitsRemaining(11, 0, SpellProcs[i].base_spellID); - } else { - mlog(COMBAT__PROCS, - "Spell proc %d failed to proc %d (%.2f percent chance)", - i, SpellProcs[i].spellID, chance); - } - } - } else if (rangedattk) { // ranged only - // ranged spell procs (buffs) - if (RangedProcs[i].spellID != SPELL_UNKNOWN) { - float chance = ProcChance * (RangedProcs[i].chance / 100.0f); - if (MakeRandomFloat(0, 1) <= chance) { - mlog(COMBAT__PROCS, - "Ranged proc %d procing spell %d (%.2f percent chance)", - i, RangedProcs[i].spellID, chance); - ExecWeaponProc(nullptr, RangedProcs[i].spellID, on); - CheckNumHitsRemaining(11, 0, RangedProcs[i].base_spellID); - } else { - mlog(COMBAT__PROCS, - "Ranged proc %d failed to proc %d (%.2f percent chance)", - i, RangedProcs[i].spellID, chance); - } - } - } - } - - return; -} - -void Mob::TryPetCriticalHit(Mob *defender, uint16 skill, int32 &damage) -{ - if(damage < 1) - return; - - //Allows pets to perform critical hits. - //Each rank adds an additional 1% chance for any melee hit (primary, secondary, kick, bash, etc) to critical, - //dealing up to 63% more damage. http://www.magecompendium.com/aa-short-library.html - - Mob *owner = nullptr; - float critChance = 0.0f; - critChance += RuleI(Combat, MeleeBaseCritChance); - uint16 critMod = 163; - - if (damage < 1) //We can't critical hit if we don't hit. - return; - - if (!IsPet()) - return; - - owner = GetOwner(); - - if (!owner) - return; - - int16 CritPetChance = owner->aabonuses.PetCriticalHit + owner->itembonuses.PetCriticalHit + owner->spellbonuses.PetCriticalHit; - int16 CritChanceBonus = GetCriticalChanceBonus(skill); - - if (CritPetChance || critChance) { - - //For pets use PetCriticalHit for base chance, pets do not innately critical with without it - //even if buffed with a CritChanceBonus effects. - critChance += CritPetChance; - critChance += critChance*CritChanceBonus/100.0f; - } - - if(critChance > 0){ - - critChance /= 100; - - if(MakeRandomFloat(0, 1) < critChance) - { - critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100 - damage = (damage * critMod) / 100; - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT, - GetCleanName(), itoa(damage)); - } - } -} - -void Mob::TryCriticalHit(Mob *defender, uint16 skill, int32 &damage, ExtraAttackOptions *opts) -{ - if(damage < 1) - return; - - // decided to branch this into it's own function since it's going to be duplicating a lot of the - // code in here, but could lead to some confusion otherwise - if (IsPet() && GetOwner()->IsClient()) { - TryPetCriticalHit(defender,skill,damage); - return; - } - -#ifdef BOTS - if (this->IsPet() && this->GetOwner()->IsBot()) { - this->TryPetCriticalHit(defender,skill,damage); - return; - } -#endif //BOTS - - - float critChance = 0.0f; - - //1: Try Slay Undead - if(defender && defender->GetBodyType() == BT_Undead || defender->GetBodyType() == BT_SummonedUndead || defender->GetBodyType() == BT_Vampire){ - - int16 SlayRateBonus = aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0]; - - if (SlayRateBonus) { - - critChance += (float(SlayRateBonus)/100.0f); - critChance /= 100.0f; - - if(MakeRandomFloat(0, 1) < critChance){ - int16 SlayDmgBonus = aabonuses.SlayUndead[1] + itembonuses.SlayUndead[1] + spellbonuses.SlayUndead[1]; - damage = (damage*SlayDmgBonus*2.25)/100; - entity_list.MessageClose(this, false, 200, MT_CritMelee, "%s cleanses %s target!(%d)", GetCleanName(), this->GetGender() == 0 ? "his" : this->GetGender() == 1 ? "her" : "its", damage); - return; - } - } - } - - //2: Try Melee Critical - - //Base critical rate for all classes is dervived from DEX stat, this rate is then augmented - //by item,spell and AA bonuses allowing you a chance to critical hit. If the following rules - //are defined you will have an innate chance to hit at Level 1 regardless of bonuses. - //Warning: Do not define these rules if you want live like critical hits. - critChance += RuleI(Combat, MeleeBaseCritChance); - - if (IsClient()) { - critChance += RuleI(Combat, ClientBaseCritChance); - - if ((GetClass() == WARRIOR || GetClass() == BERSERKER) && GetLevel() >= 12) { - if (IsBerserk()) - critChance += RuleI(Combat, BerserkBaseCritChance); - else - critChance += RuleI(Combat, WarBerBaseCritChance); - } - } - - int deadlyChance = 0; - int deadlyMod = 0; - if(skill == SkillArchery && GetClass() == RANGER && GetSkill(SkillArchery) >= 65) - critChance += 6; - - if (skill == SkillThrowing && GetClass() == ROGUE && GetSkill(SkillThrowing) >= 65) { - critChance += RuleI(Combat, RogueCritThrowingChance); - deadlyChance = RuleI(Combat, RogueDeadlyStrikeChance); - deadlyMod = RuleI(Combat, RogueDeadlyStrikeMod); - } - - int CritChanceBonus = GetCriticalChanceBonus(skill); - - if (CritChanceBonus || critChance) { - - //Get Base CritChance from Dex. (200 = ~1.6%, 255 = ~2.0%, 355 = ~2.20%) Fall off rate > 255 - //http://giline.versus.jp/shiden/su.htm , http://giline.versus.jp/shiden/damage_e.htm - if (GetDEX() <= 255) - critChance += (float(GetDEX()) / 125.0f); - else if (GetDEX() > 255) - critChance += (float(GetDEX()-255)/ 500.0f) + 2.0f; - critChance += critChance*(float)CritChanceBonus /100.0f; - } - - if(opts) { - critChance *= opts->crit_percent; - critChance += opts->crit_flat; - } - - if(critChance > 0) { - - critChance /= 100; - - if(MakeRandomFloat(0, 1) < critChance) - { - uint16 critMod = 200; - bool crip_success = false; - int16 CripplingBlowChance = GetCrippBlowChance(); - - //Crippling Blow Chance: The percent value of the effect is applied - //to the your Chance to Critical. (ie You have 10% chance to critical and you - //have a 200% Chance to Critical Blow effect, therefore you have a 20% Chance to Critical Blow. - if (CripplingBlowChance || IsBerserk()) { - if (!IsBerserk()) - critChance *= float(CripplingBlowChance)/100.0f; - - if (IsBerserk() || MakeRandomFloat(0, 1) < critChance) { - critMod = 400; - crip_success = true; - } - } - - critMod += GetCritDmgMob(skill) * 2; // To account for base crit mod being 200 not 100 - damage = damage * critMod / 100; - - bool deadlySuccess = false; - if (deadlyChance && MakeRandomFloat(0, 1) < static_cast(deadlyChance) / 100.0f) { - if (BehindMob(defender, GetX(), GetY())) { - damage *= deadlyMod; - deadlySuccess = true; - } - } - - if (crip_success) { - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, CRIPPLING_BLOW, - GetCleanName(), itoa(damage)); - // Crippling blows also have a chance to stun - //Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a staggers message. - if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(IMMUNE_STUN)){ - defender->Emote("staggers."); - defender->Stun(0); - } - } else if (deadlySuccess) { - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, DEADLY_STRIKE, - GetCleanName(), itoa(damage)); - } else { - entity_list.FilteredMessageClose_StringID(this, false, 200, - MT_CritMelee, FilterMeleeCrits, CRITICAL_HIT, - GetCleanName(), itoa(damage)); - } - } - } -} - - -bool Mob::TryFinishingBlow(Mob *defender, SkillUseTypes skillinuse) -{ - - if (!defender) - return false; - - if (aabonuses.FinishingBlow[1] && !defender->IsClient() && defender->GetHPRatio() < 10){ - - uint32 chance = aabonuses.FinishingBlow[0]/10; //500 = 5% chance. - uint32 damage = aabonuses.FinishingBlow[1]; - uint16 levelreq = aabonuses.FinishingBlowLvl[0]; - - if(defender->GetLevel() <= levelreq && (chance >= MakeRandomInt(0, 1000))){ - mlog(COMBAT__ATTACKS, "Landed a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel()); - entity_list.MessageClose_StringID(this, false, 200, MT_CritMelee, FINISHING_BLOW, GetName()); - defender->Damage(this, damage, SPELL_UNKNOWN, skillinuse); - return true; - } - else - { - mlog(COMBAT__ATTACKS, "FAILED a finishing blow: levelreq at %d, other level %d", levelreq , defender->GetLevel()); - return false; - } - } - return false; -} - -void Mob::DoRiposte(Mob* defender) { - mlog(COMBAT__ATTACKS, "Preforming a riposte"); - - if (!defender) - return; - - defender->Attack(this, SLOT_PRIMARY, true); - if (HasDied()) return; - - int16 DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[0] + - defender->spellbonuses.GiveDoubleRiposte[0] + - defender->itembonuses.GiveDoubleRiposte[0]; - - //Live AA - Double Riposte - if(DoubleRipChance && (DoubleRipChance >= MakeRandomInt(0, 100))) { - mlog(COMBAT__ATTACKS, "Preforming a double riposed (%d percent chance)", DoubleRipChance); - defender->Attack(this, SLOT_PRIMARY, true); - if (HasDied()) return; - } - - //Double Riposte effect, allows for a chance to do RIPOSTE with a skill specfic special attack (ie Return Kick). - //Coded narrowly: Limit to one per client. Limit AA only. [1 = Skill Attack Chance, 2 = Skill] - DoubleRipChance = defender->aabonuses.GiveDoubleRiposte[1]; - - if(DoubleRipChance && (DoubleRipChance >= MakeRandomInt(0, 100))) { - mlog(COMBAT__ATTACKS, "Preforming a return SPECIAL ATTACK (%d percent chance)", DoubleRipChance); - - if (defender->GetClass() == MONK) - defender->MonkSpecialAttack(this, defender->aabonuses.GiveDoubleRiposte[2]); - else if (defender->IsClient()) - defender->CastToClient()->DoClassAttacks(this,defender->aabonuses.GiveDoubleRiposte[2], true); - } -} - -void Mob::ApplyMeleeDamageBonus(uint16 skill, int32 &damage){ - - if(!RuleB(Combat, UseIntervalAC)){ - if(IsNPC()){ //across the board NPC damage bonuses. - //only account for STR here, assume their base STR was factored into their DB damages - int dmgbonusmod = 0; - dmgbonusmod += (100*(itembonuses.STR + spellbonuses.STR))/3; - dmgbonusmod += (100*(spellbonuses.ATK + itembonuses.ATK))/5; - mlog(COMBAT__DAMAGE, "Damage bonus: %d percent from ATK and STR bonuses.", (dmgbonusmod/100)); - damage += (damage*dmgbonusmod/10000); - } - } - - damage += damage * GetMeleeDamageMod_SE(skill) / 100; -} - -bool Mob::HasDied() { - bool Result = false; - int16 hp_below = 0; - - hp_below = (GetDelayDeath() * -1); - - if((GetHP()) <= (hp_below)) - Result = true; - - return Result; -} - -uint16 Mob::GetDamageTable(SkillUseTypes skillinuse) -{ - if(GetLevel() <= 51) - { - uint16 ret_table = 0; - int str_over_75 = 0; - if(GetSTR() > 75) - str_over_75 = GetSTR() - 75; - if(str_over_75 > 255) - ret_table = (GetSkill(skillinuse)+255)/2; - else - ret_table = (GetSkill(skillinuse)+str_over_75)/2; - - if(ret_table < 100) - return 100; - - return ret_table; - } - else if(GetLevel() >= 90) - { - if(GetClass() == MONK) - return 379; - else - return 345; - } - else - { - uint16 dmg_table[] = { - 275, 275, 275, 275, 275, - 280, 280, 280, 280, 285, - 285, 285, 290, 290, 295, - 295, 300, 300, 300, 305, - 305, 305, 310, 310, 315, - 315, 320, 320, 320, 325, - 325, 325, 330, 330, 335, - 335, 340, 340, 340, - }; - if(GetClass() == MONK) - return (dmg_table[GetLevel()-51]*(100+RuleI(Combat,MonkDamageTableBonus))/100); - else - return dmg_table[GetLevel()-51]; - } -} - -void Mob::TrySkillProc(Mob *on, uint16 skill, float chance) -{ - - if (!on) { - SetTarget(nullptr); - LogFile->write(EQEMuLog::Error, "A null Mob object was passed to Mob::TrySkillProc for evaluation!"); - return; - } - - for (int i = 0; i < MAX_PROCS; i++) { - if (SkillProcs[i].spellID != SPELL_UNKNOWN){ - if (PassLimitToSkill(SkillProcs[i].base_spellID,skill)){ - int ProcChance = chance * (float)SkillProcs[i].chance; - if ((MakeRandomInt(0, 100) < ProcChance)) { - ExecWeaponProc(nullptr, SkillProcs[i].spellID, on); - CheckNumHitsRemaining(11,0, SkillProcs[i].base_spellID); - } - } - } - } -} - -bool Mob::TryRootFadeByDamage(int buffslot, Mob* attacker) -{ - /*Dev Quote 2010: http://forums.station.sony.com/eq/posts/list.m?topic_id=161443 - The Viscid Roots AA does the following: Reduces the chance for root to break by X percent. - There is no distinction of any kind between the caster inflicted damage, or anyone - else's damage. There is also no distinction between Direct and DOT damage in the root code. - There is however, a provision that if the damage inflicted is greater than 500 per hit, the - chance to break root is increased. My guess is when this code was put in place, the devs at - the time couldn't imagine DOT damage getting that high. - */ - - /* General Mechanics - - Check buffslot to make sure damage from a root does not cancel the root - - If multiple roots on target, always and only checks first root slot and if broken only removes that slots root. - - Only roots on determental spells can be broken by damage. - - Root break chance values obtained from live parses. - */ - - if (!attacker || !spellbonuses.Root[0] || spellbonuses.Root[1] < 0) - return false; - - if (IsDetrimentalSpell(spellbonuses.Root[1]) && spellbonuses.Root[1] != buffslot){ - - int BreakChance = RuleI(Spells, RootBreakFromSpells); - BreakChance -= BreakChance*buffs[spellbonuses.Root[1]].RootBreakChance/100; - int level_diff = attacker->GetLevel() - GetLevel(); - - attacker->Shout("%i ATTACKER LV %i",attacker->GetLevel(), level_diff); - Shout("%i DEF LEVEL %i", GetLevel(), level_diff); - //Use baseline if level difference <= 1 (ie. If target is (1) level less than you, or equal or greater level) - - if (level_diff == 2) - BreakChance = (BreakChance * 80) /100; //Decrease by 20%; - - else if (level_diff >= 3 && level_diff <= 20) - BreakChance = (BreakChance * 60) /100; //Decrease by 40%; - - else if (level_diff > 21) - BreakChance = (BreakChance * 20) /100; //Decrease by 80%; - - Shout("Root Break Chance %i Lv diff %i base %i ", BreakChance, level_diff, RuleI(Spells, RootBreakFromSpells)); - - if (BreakChance < 1) - BreakChance = 1; - - if (MakeRandomInt(0, 99) < BreakChance) { - - if (!TryFadeEffect(spellbonuses.Root[1])) { - BuffFadeBySlot(spellbonuses.Root[1]); - mlog(COMBAT__HITS, "Spell broke root! BreakChance percent chance"); - return true; - } - } - } - - mlog(COMBAT__HITS, "Spell did not break root. BreakChance percent chance"); - return false; -} - -int32 Mob::RuneAbsorb(int32 damage, uint16 type) -{ - uint32 buff_max = GetMaxTotalSlots(); - if (type == SE_Rune){ - for(uint32 slot = 0; slot < buff_max; slot++) { - if(slot == spellbonuses.MeleeRune[1] && spellbonuses.MeleeRune[0] && buffs[slot].melee_rune && IsValidSpell(buffs[slot].spellid)){ - uint32 melee_rune_left = buffs[slot].melee_rune; - - if(melee_rune_left > damage) - { - melee_rune_left -= damage; - buffs[slot].melee_rune = melee_rune_left; - return -6; - } - - else - { - if(melee_rune_left > 0) - damage -= melee_rune_left; - - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - } - } - } - - - else{ - for(uint32 slot = 0; slot < buff_max; slot++) { - if(slot == spellbonuses.AbsorbMagicAtt[1] && spellbonuses.AbsorbMagicAtt[0] && buffs[slot].magic_rune && IsValidSpell(buffs[slot].spellid)){ - uint32 magic_rune_left = buffs[slot].magic_rune; - if(magic_rune_left > damage) - { - magic_rune_left -= damage; - buffs[slot].magic_rune = magic_rune_left; - return 0; - } - - else - { - if(magic_rune_left > 0) - damage -= magic_rune_left; - - if(!TryFadeEffect(slot)) - BuffFadeBySlot(slot); - } - } - } - } - - return damage; -} - diff --git a/zone/bonusesxx.cpp b/zone/bonusesxx.cpp deleted file mode 100644 index 8e23eb5fb..000000000 --- a/zone/bonusesxx.cpp +++ /dev/null @@ -1,4337 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 EQEMu Development Team (http://eqemu.org) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include "../common/debug.h" -#include "../common/spdat.h" -#include "masterentity.h" -#include "../common/packet_dump.h" -#include "../common/moremath.h" -#include "../common/Item.h" -#include "worldserver.h" -#include "../common/skills.h" -#include "../common/bodytypes.h" -#include "../common/classes.h" -#include "../common/rulesys.h" -#include "QuestParserCollection.h" -#include -#include -#include -#ifndef WIN32 -#include -#include "../common/unix.h" -#endif - -#include "StringIDs.h" - -void Mob::CalcBonuses() -{ - CalcSpellBonuses(&spellbonuses); - CalcMaxHP(); - CalcMaxMana(); - SetAttackTimer(); - - rooted = FindType(SE_Root); -} - -void NPC::CalcBonuses() -{ - Mob::CalcBonuses(); - memset(&aabonuses, 0, sizeof(StatBonuses)); - - if(RuleB(NPC, UseItemBonusesForNonPets)){ - memset(&itembonuses, 0, sizeof(StatBonuses)); - CalcItemBonuses(&itembonuses); - } - else{ - if(GetOwner()){ - memset(&itembonuses, 0, sizeof(StatBonuses)); - CalcItemBonuses(&itembonuses); - } - } - - // This has to happen last, so we actually take the item bonuses into account. - Mob::CalcBonuses(); -} - -void Client::CalcBonuses() -{ - memset(&itembonuses, 0, sizeof(StatBonuses)); - CalcItemBonuses(&itembonuses); - CalcEdibleBonuses(&itembonuses); - - CalcSpellBonuses(&spellbonuses); - - _log(AA__BONUSES, "Calculating AA Bonuses for %s.", this->GetCleanName()); - CalcAABonuses(&aabonuses); //we're not quite ready for this - _log(AA__BONUSES, "Finished calculating AA Bonuses for %s.", this->GetCleanName()); - - RecalcWeight(); - - CalcAC(); - CalcATK(); - CalcHaste(); - - CalcSTR(); - CalcSTA(); - CalcDEX(); - CalcAGI(); - CalcINT(); - CalcWIS(); - CalcCHA(); - - CalcMR(); - CalcFR(); - CalcDR(); - CalcPR(); - CalcCR(); - CalcCorrup(); - - CalcMaxHP(); - CalcMaxMana(); - CalcMaxEndurance(); - - rooted = FindType(SE_Root); - - XPRate = 100 + spellbonuses.XPRateMod; -} - -int Client::CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat) -{ - if( (reclevel > 0) && (level < reclevel) ) - { - int32 statmod = (level * 10000 / reclevel) * basestat; - - if( statmod < 0 ) - { - statmod -= 5000; - return (statmod/10000); - } - else - { - statmod += 5000; - return (statmod/10000); - } - } - - return 0; -} - -void Client::CalcItemBonuses(StatBonuses* newbon) { - //memset assumed to be done by caller. - - // Clear item faction mods - ClearItemFactionBonuses(); - ShieldEquiped(false); - - unsigned int i; - //should not include 21 (SLOT_AMMO) - for (i=0; i<21; i++) { - const ItemInst* inst = m_inv[i]; - if(inst == 0) - continue; - AddItemBonuses(inst, newbon); - - //Check if item is secondary slot is a 'shield'. Required for multiple spelll effects. - if (i == 14 && (m_inv.GetItem(14)->GetItem()->ItemType == ItemTypeShield)) - ShieldEquiped(true); - } - - //Power Source Slot - if (GetClientVersion() >= EQClientSoF) - { - const ItemInst* inst = m_inv[9999]; - if(inst) - AddItemBonuses(inst, newbon); - } - - //tribute items - for (i = 0; i < MAX_PLAYER_TRIBUTES; i++) { - const ItemInst* inst = m_inv[TRIBUTE_SLOT_START + i]; - if(inst == 0) - continue; - AddItemBonuses(inst, newbon, false, true); - } - // Caps - if(newbon->HPRegen > CalcHPRegenCap()) - newbon->HPRegen = CalcHPRegenCap(); - - if(newbon->ManaRegen > CalcManaRegenCap()) - newbon->ManaRegen = CalcManaRegenCap(); - - if(newbon->EnduranceRegen > CalcEnduranceRegenCap()) - newbon->EnduranceRegen = CalcEnduranceRegenCap(); - - SetAttackTimer(); -} - -void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug, bool isTribute) { - if(!inst || !inst->IsType(ItemClassCommon)) - { - return; - } - - if(inst->GetAugmentType()==0 && isAug == true) - { - return; - } - - const Item_Struct *item = inst->GetItem(); - - if(!isTribute && !inst->IsEquipable(GetBaseRace(),GetClass())) - { - if(item->ItemType != ItemTypeFood && item->ItemType != ItemTypeDrink) - return; - } - - if(GetLevel() < item->ReqLevel) - { - return; - } - - if(GetLevel() >= item->RecLevel) - { - newbon->AC += item->AC; - newbon->HP += item->HP; - newbon->Mana += item->Mana; - newbon->Endurance += item->Endur; - newbon->STR += (item->AStr + item->HeroicStr); - newbon->STA += (item->ASta + item->HeroicSta); - newbon->DEX += (item->ADex + item->HeroicDex); - newbon->AGI += (item->AAgi + item->HeroicAgi); - newbon->INT += (item->AInt + item->HeroicInt); - newbon->WIS += (item->AWis + item->HeroicWis); - newbon->CHA += (item->ACha + item->HeroicCha); - - newbon->MR += (item->MR + item->HeroicMR); - newbon->FR += (item->FR + item->HeroicFR); - newbon->CR += (item->CR + item->HeroicCR); - newbon->PR += (item->PR + item->HeroicPR); - newbon->DR += (item->DR + item->HeroicDR); - newbon->Corrup += (item->SVCorruption + item->HeroicSVCorrup); - - newbon->STRCapMod += item->HeroicStr; - newbon->STACapMod += item->HeroicSta; - newbon->DEXCapMod += item->HeroicDex; - newbon->AGICapMod += item->HeroicAgi; - newbon->INTCapMod += item->HeroicInt; - newbon->WISCapMod += item->HeroicWis; - newbon->CHACapMod += item->HeroicCha; - newbon->MRCapMod += item->HeroicMR; - newbon->CRCapMod += item->HeroicFR; - newbon->FRCapMod += item->HeroicCR; - newbon->PRCapMod += item->HeroicPR; - newbon->DRCapMod += item->HeroicDR; - newbon->CorrupCapMod += item->HeroicSVCorrup; - - newbon->HeroicSTR += item->HeroicStr; - newbon->HeroicSTA += item->HeroicSta; - newbon->HeroicDEX += item->HeroicDex; - newbon->HeroicAGI += item->HeroicAgi; - newbon->HeroicINT += item->HeroicInt; - newbon->HeroicWIS += item->HeroicWis; - newbon->HeroicCHA += item->HeroicCha; - newbon->HeroicMR += item->HeroicMR; - newbon->HeroicFR += item->HeroicFR; - newbon->HeroicCR += item->HeroicCR; - newbon->HeroicPR += item->HeroicPR; - newbon->HeroicDR += item->HeroicDR; - newbon->HeroicCorrup += item->HeroicSVCorrup; - - } - else - { - int lvl = GetLevel(); - int reclvl = item->RecLevel; - - newbon->AC += CalcRecommendedLevelBonus( lvl, reclvl, item->AC ); - newbon->HP += CalcRecommendedLevelBonus( lvl, reclvl, item->HP ); - newbon->Mana += CalcRecommendedLevelBonus( lvl, reclvl, item->Mana ); - newbon->Endurance += CalcRecommendedLevelBonus( lvl, reclvl, item->Endur ); - newbon->STR += CalcRecommendedLevelBonus( lvl, reclvl, (item->AStr + item->HeroicStr) ); - newbon->STA += CalcRecommendedLevelBonus( lvl, reclvl, (item->ASta + item->HeroicSta) ); - newbon->DEX += CalcRecommendedLevelBonus( lvl, reclvl, (item->ADex + item->HeroicDex) ); - newbon->AGI += CalcRecommendedLevelBonus( lvl, reclvl, (item->AAgi + item->HeroicAgi) ); - newbon->INT += CalcRecommendedLevelBonus( lvl, reclvl, (item->AInt + item->HeroicInt) ); - newbon->WIS += CalcRecommendedLevelBonus( lvl, reclvl, (item->AWis + item->HeroicWis) ); - newbon->CHA += CalcRecommendedLevelBonus( lvl, reclvl, (item->ACha + item->HeroicCha) ); - - newbon->MR += CalcRecommendedLevelBonus( lvl, reclvl, (item->MR + item->HeroicMR) ); - newbon->FR += CalcRecommendedLevelBonus( lvl, reclvl, (item->FR + item->HeroicFR) ); - newbon->CR += CalcRecommendedLevelBonus( lvl, reclvl, (item->CR + item->HeroicCR) ); - newbon->PR += CalcRecommendedLevelBonus( lvl, reclvl, (item->PR + item->HeroicPR) ); - newbon->DR += CalcRecommendedLevelBonus( lvl, reclvl, (item->DR + item->HeroicDR) ); - newbon->Corrup += CalcRecommendedLevelBonus( lvl, reclvl, (item->SVCorruption + item->HeroicSVCorrup) ); - - newbon->STRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicStr ); - newbon->STACapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSta ); - newbon->DEXCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDex ); - newbon->AGICapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicAgi ); - newbon->INTCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicInt ); - newbon->WISCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicWis ); - newbon->CHACapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCha ); - newbon->MRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicMR ); - newbon->CRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicFR ); - newbon->FRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCR ); - newbon->PRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicPR ); - newbon->DRCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDR ); - newbon->CorrupCapMod += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSVCorrup ); - - newbon->HeroicSTR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicStr ); - newbon->HeroicSTA += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSta ); - newbon->HeroicDEX += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDex ); - newbon->HeroicAGI += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicAgi ); - newbon->HeroicINT += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicInt ); - newbon->HeroicWIS += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicWis ); - newbon->HeroicCHA += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCha ); - newbon->HeroicMR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicMR ); - newbon->HeroicFR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicFR ); - newbon->HeroicCR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicCR ); - newbon->HeroicPR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicPR ); - newbon->HeroicDR += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicDR ); - newbon->HeroicCorrup += CalcRecommendedLevelBonus( lvl, reclvl, item->HeroicSVCorrup ); - } - - //FatherNitwit: New style haste, shields, and regens - if(newbon->haste < (int16)item->Haste) { - newbon->haste = item->Haste; - } - if(item->Regen > 0) - newbon->HPRegen += item->Regen; - - if(item->ManaRegen > 0) - newbon->ManaRegen += item->ManaRegen; - - if(item->EnduranceRegen > 0) - newbon->EnduranceRegen += item->EnduranceRegen; - - if(item->Attack > 0) { - - int cap = RuleI(Character, ItemATKCap); - cap += itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; - - if((newbon->ATK + item->Attack) > cap) - newbon->ATK = RuleI(Character, ItemATKCap); - else - newbon->ATK += item->Attack; - } - if(item->DamageShield > 0) { - if((newbon->DamageShield + item->DamageShield) > RuleI(Character, ItemDamageShieldCap)) - newbon->DamageShield = RuleI(Character, ItemDamageShieldCap); - else - newbon->DamageShield += item->DamageShield; - } - if(item->SpellShield > 0) { - if((newbon->SpellShield + item->SpellShield) > RuleI(Character, ItemSpellShieldingCap)) - newbon->SpellShield = RuleI(Character, ItemSpellShieldingCap); - else - newbon->SpellShield += item->SpellShield; - } - if(item->Shielding > 0) { - if((newbon->MeleeMitigation + item->Shielding) > RuleI(Character, ItemShieldingCap)) - newbon->MeleeMitigation = RuleI(Character, ItemShieldingCap); - else - newbon->MeleeMitigation += item->Shielding; - } - if(item->StunResist > 0) { - if((newbon->StunResist + item->StunResist) > RuleI(Character, ItemStunResistCap)) - newbon->StunResist = RuleI(Character, ItemStunResistCap); - else - newbon->StunResist += item->StunResist; - } - if(item->StrikeThrough > 0) { - if((newbon->StrikeThrough + item->StrikeThrough) > RuleI(Character, ItemStrikethroughCap)) - newbon->StrikeThrough = RuleI(Character, ItemStrikethroughCap); - else - newbon->StrikeThrough += item->StrikeThrough; - } - if(item->Avoidance > 0) { - if((newbon->AvoidMeleeChance + item->Avoidance) > RuleI(Character, ItemAvoidanceCap)) - newbon->AvoidMeleeChance = RuleI(Character, ItemAvoidanceCap); - else - newbon->AvoidMeleeChance += item->Avoidance; - } - if(item->Accuracy > 0) { - if((newbon->HitChance + item->Accuracy) > RuleI(Character, ItemAccuracyCap)) - newbon->HitChance = RuleI(Character, ItemAccuracyCap); - else - newbon->HitChance += item->Accuracy; - } - if(item->CombatEffects > 0) { - if((newbon->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)) - newbon->ProcChance = RuleI(Character, ItemCombatEffectsCap); - else - newbon->ProcChance += item->CombatEffects; - } - if(item->DotShielding > 0) { - if((newbon->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)) - newbon->DoTShielding = RuleI(Character, ItemDoTShieldingCap); - else - newbon->DoTShielding += item->DotShielding; - } - - if(item->HealAmt > 0) { - if((newbon->HealAmt + item->HealAmt) > RuleI(Character, ItemHealAmtCap)) - newbon->HealAmt = RuleI(Character, ItemHealAmtCap); - else - newbon->HealAmt += item->HealAmt; - } - if(item->SpellDmg > 0) { - if((newbon->SpellDmg + item->SpellDmg) > RuleI(Character, ItemSpellDmgCap)) - newbon->SpellDmg = RuleI(Character, ItemSpellDmgCap); - else - newbon->SpellDmg += item->SpellDmg; - } - if(item->Clairvoyance > 0) { - if((newbon->Clairvoyance + item->Clairvoyance) > RuleI(Character, ItemClairvoyanceCap)) - newbon->Clairvoyance = RuleI(Character, ItemClairvoyanceCap); - else - newbon->Clairvoyance += item->Clairvoyance; - } - - if(item->DSMitigation > 0) { - if((newbon->DSMitigation + item->DSMitigation) > RuleI(Character, ItemDSMitigationCap)) - newbon->DSMitigation = RuleI(Character, ItemDSMitigationCap); - else - newbon->DSMitigation += item->DSMitigation; - } - if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true); - } - - if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects - ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true); - } - - switch(item->BardType) - { - case 51: /* All (e.g. Singing Short Sword) */ - { - if(item->BardValue > newbon->singingMod) - newbon->singingMod = item->BardValue; - if(item->BardValue > newbon->brassMod) - newbon->brassMod = item->BardValue; - if(item->BardValue > newbon->stringedMod) - newbon->stringedMod = item->BardValue; - if(item->BardValue > newbon->percussionMod) - newbon->percussionMod = item->BardValue; - if(item->BardValue > newbon->windMod) - newbon->windMod = item->BardValue; - break; - } - case 50: /* Singing */ - { - if(item->BardValue > newbon->singingMod) - newbon->singingMod = item->BardValue; - break; - } - case 23: /* Wind */ - { - if(item->BardValue > newbon->windMod) - newbon->windMod = item->BardValue; - break; - } - case 24: /* stringed */ - { - if(item->BardValue > newbon->stringedMod) - newbon->stringedMod = item->BardValue; - break; - } - case 25: /* brass */ - { - if(item->BardValue > newbon->brassMod) - newbon->brassMod = item->BardValue; - break; - } - case 26: /* Percussion */ - { - if(item->BardValue > newbon->percussionMod) - newbon->percussionMod = item->BardValue; - break; - } - } - - if (item->SkillModValue != 0 && item->SkillModType <= HIGHEST_SKILL){ - if ((item->SkillModValue > 0 && newbon->skillmod[item->SkillModType] < item->SkillModValue) || - (item->SkillModValue < 0 && newbon->skillmod[item->SkillModType] > item->SkillModValue)) - { - newbon->skillmod[item->SkillModType] = item->SkillModValue; - } - } - - // Add Item Faction Mods - if (item->FactionMod1) - { - if (item->FactionAmt1 > 0 && item->FactionAmt1 > GetItemFactionBonus(item->FactionMod1)) - { - AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); - } - else if (item->FactionAmt1 < 0 && item->FactionAmt1 < GetItemFactionBonus(item->FactionMod1)) - { - AddItemFactionBonus(item->FactionMod1, item->FactionAmt1); - } - } - if (item->FactionMod2) - { - if (item->FactionAmt2 > 0 && item->FactionAmt2 > GetItemFactionBonus(item->FactionMod2)) - { - AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); - } - else if (item->FactionAmt2 < 0 && item->FactionAmt2 < GetItemFactionBonus(item->FactionMod2)) - { - AddItemFactionBonus(item->FactionMod2, item->FactionAmt2); - } - } - if (item->FactionMod3) - { - if (item->FactionAmt3 > 0 && item->FactionAmt3 > GetItemFactionBonus(item->FactionMod3)) - { - AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); - } - else if (item->FactionAmt3 < 0 && item->FactionAmt3 < GetItemFactionBonus(item->FactionMod3)) - { - AddItemFactionBonus(item->FactionMod3, item->FactionAmt3); - } - } - if (item->FactionMod4) - { - if (item->FactionAmt4 > 0 && item->FactionAmt4 > GetItemFactionBonus(item->FactionMod4)) - { - AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); - } - else if (item->FactionAmt4 < 0 && item->FactionAmt4 < GetItemFactionBonus(item->FactionMod4)) - { - AddItemFactionBonus(item->FactionMod4, item->FactionAmt4); - } - } - - if (item->ExtraDmgSkill != 0 && item->ExtraDmgSkill <= HIGHEST_SKILL) { - if((newbon->SkillDamageAmount[item->ExtraDmgSkill] + item->ExtraDmgAmt) > RuleI(Character, ItemExtraDmgCap)) - newbon->SkillDamageAmount[item->ExtraDmgSkill] = RuleI(Character, ItemExtraDmgCap); - else - newbon->SkillDamageAmount[item->ExtraDmgSkill] += item->ExtraDmgAmt; - } - - if (!isAug) - { - int i; - for(i = 0; i < MAX_AUGMENT_SLOTS; i++) { - AddItemBonuses(inst->GetAugment(i),newbon,true); - } - } - -} - -void Client::CalcEdibleBonuses(StatBonuses* newbon) { -#if EQDEBUG >= 11 - std::cout<<"Client::CalcEdibleBonuses(StatBonuses* newbon)"<GetItem() && inst->IsType(ItemClassCommon)) { - const Item_Struct *item=inst->GetItem(); - if (item->ItemType == ItemTypeFood && !food) - food = true; - else if (item->ItemType == ItemTypeDrink && !drink) - drink = true; - else - continue; - AddItemBonuses(inst, newbon); - } - } - for (i = 251; i <= 330; i++) - { - if (food && drink) - break; - const ItemInst* inst = GetInv().GetItem(i); - if (inst && inst->GetItem() && inst->IsType(ItemClassCommon)) { - const Item_Struct *item=inst->GetItem(); - if (item->ItemType == ItemTypeFood && !food) - food = true; - else if (item->ItemType == ItemTypeDrink && !drink) - drink = true; - else - continue; - AddItemBonuses(inst, newbon); - } - } -} - -void Client::CalcAABonuses(StatBonuses* newbon) { - memset(newbon, 0, sizeof(StatBonuses)); //start fresh - - int i; - uint32 slots = 0; - uint32 aa_AA = 0; - uint32 aa_value = 0; - if(this->aa) { - for (i = 0; i < MAX_PP_AA_ARRAY; i++) { //iterate through all of the client's AAs - if (this->aa[i]) { // make sure aa exists or we'll crash zone - aa_AA = this->aa[i]->AA; //same as aaid from the aa_effects table - aa_value = this->aa[i]->value; //how many points in it - if (aa_AA > 0 || aa_value > 0) { //do we have the AA? if 1 of the 2 is set, we can assume we do - //slots = database.GetTotalAALevels(aa_AA); //find out how many effects from aa_effects table - slots = zone->GetTotalAALevels(aa_AA); //find out how many effects from aa_effects, which is loaded into memory - if (slots > 0) //and does it have any effects? may be able to put this above, not sure if it runs on each iteration - ApplyAABonuses(aa_AA, slots, newbon); //add the bonuses - } - } - } - } -} - - -//A lot of the normal spell functions (IsBlankSpellEffect, etc) are set for just spells (in common/spdat.h). -//For now, we'll just put them directly into the code and comment with the corresponding normal function -//Maybe we'll fix it later? :-D -void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon) -{ - if(slots == 0) //sanity check. why bother if no slots to fill? - return; - - //from AA_Ability struct - uint32 effect = 0; - int32 base1 = 0; - int32 base2 = 0; //only really used for SE_RaiseStatCap & SE_ReduceSkillTimer in aa_effects table - uint32 slot = 0; - - std::map >::const_iterator find_iter = aa_effects.find(aaid); - if(find_iter == aa_effects.end()) - { - return; - } - - for (std::map::const_iterator iter = aa_effects[aaid].begin(); iter != aa_effects[aaid].end(); ++iter) { - effect = iter->second.skill_id; - base1 = iter->second.base1; - base2 = iter->second.base2; - slot = iter->second.slot; - - //we default to 0 (SE_CurrentHP) for the effect, so if there aren't any base1/2 values, we'll just skip it - if (effect == 0 && base1 == 0 && base2 == 0) - continue; - - //IsBlankSpellEffect() - if (effect == SE_Blank || (effect == SE_CHA && base1 == 0) || effect == SE_StackingCommand_Block || effect == SE_StackingCommand_Overwrite) - continue; - - _log(AA__BONUSES, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName()); - - uint8 focus = IsFocusEffect(0, 0, true,effect); - if (focus) - { - newbon->FocusEffects[focus] = effect; - continue; - } - - switch (effect) - { - //Note: AA effects that use accuracy are skill limited, while spell effect is not. - case SE_Accuracy: - if ((base2 == -1) && (newbon->Accuracy[HIGHEST_SKILL+1] < base1)) - newbon->Accuracy[HIGHEST_SKILL+1] = base1; - else if (newbon->Accuracy[base2] < base1) - newbon->Accuracy[base2] += base1; - break; - case SE_CurrentHP: //regens - newbon->HPRegen += base1; - break; - case SE_CurrentEndurance: - newbon->EnduranceRegen += base1; - break; - case SE_MovementSpeed: - newbon->movementspeed += base1; //should we let these stack? - /*if (base1 > newbon->movementspeed) //or should we use a total value? - newbon->movementspeed = base1;*/ - break; - case SE_STR: - newbon->STR += base1; - break; - case SE_DEX: - newbon->DEX += base1; - break; - case SE_AGI: - newbon->AGI += base1; - break; - case SE_STA: - newbon->STA += base1; - break; - case SE_INT: - newbon->INT += base1; - break; - case SE_WIS: - newbon->WIS += base1; - break; - case SE_CHA: - newbon->CHA += base1; - break; - case SE_WaterBreathing: - //handled by client - break; - case SE_CurrentMana: - newbon->ManaRegen += base1; - break; - case SE_ItemManaRegenCapIncrease: - newbon->ItemManaRegenCap += base1; - break; - case SE_ResistFire: - newbon->FR += base1; - break; - case SE_ResistCold: - newbon->CR += base1; - break; - case SE_ResistPoison: - newbon->PR += base1; - break; - case SE_ResistDisease: - newbon->DR += base1; - break; - case SE_ResistMagic: - newbon->MR += base1; - break; - case SE_ResistCorruption: - newbon->Corrup += base1; - break; - case SE_IncreaseSpellHaste: - break; - case SE_IncreaseRange: - break; - case SE_MaxHPChange: - newbon->MaxHP += base1; - break; - case SE_Packrat: - newbon->Packrat += base1; - break; - case SE_TwoHandBash: - break; - case SE_SetBreathLevel: - break; - case SE_RaiseStatCap: - switch(base2) - { - //are these #define'd somewhere? - case 0: //str - newbon->STRCapMod += base1; - break; - case 1: //sta - newbon->STACapMod += base1; - break; - case 2: //agi - newbon->AGICapMod += base1; - break; - case 3: //dex - newbon->DEXCapMod += base1; - break; - case 4: //wis - newbon->WISCapMod += base1; - break; - case 5: //int - newbon->INTCapMod += base1; - break; - case 6: //cha - newbon->CHACapMod += base1; - break; - case 7: //mr - newbon->MRCapMod += base1; - break; - case 8: //cr - newbon->CRCapMod += base1; - break; - case 9: //fr - newbon->FRCapMod += base1; - break; - case 10: //pr - newbon->PRCapMod += base1; - break; - case 11: //dr - newbon->DRCapMod += base1; - break; - case 12: //corruption - newbon->CorrupCapMod += base1; - break; - } - break; - case SE_PetDiscipline2: - break; - case SE_SpellSlotIncrease: - break; - case SE_MysticalAttune: - newbon->BuffSlotIncrease += base1; - break; - case SE_TotalHP: - newbon->HP += base1; - break; - case SE_StunResist: - newbon->StunResist += base1; - break; - case SE_SpellCritChance: - newbon->CriticalSpellChance += base1; - break; - case SE_SpellCritDmgIncrease: - newbon->SpellCritDmgIncrease += base1; - break; - case SE_DotCritDmgIncrease: - newbon->DotCritDmgIncrease += base1; - break; - case SE_ResistSpellChance: - newbon->ResistSpellChance += base1; - break; - case SE_CriticalHealChance: - newbon->CriticalHealChance += base1; - break; - case SE_CriticalHealOverTime: - newbon->CriticalHealOverTime += base1; - break; - case SE_CriticalDoTChance: - newbon->CriticalDoTChance += base1; - break; - case SE_ReduceSkillTimer: - newbon->SkillReuseTime[base2] += base1; - break; - case SE_Fearless: - newbon->Fearless = true; - break; - case SE_PersistantCasting: - newbon->PersistantCasting += base1; - break; - case SE_DelayDeath: - newbon->DelayDeath += base1; - break; - case SE_FrontalStunResist: - newbon->FrontalStunResist += base1; - break; - case SE_ImprovedBindWound: - newbon->BindWound += base1; - break; - case SE_MaxBindWound: - newbon->MaxBindWound += base1; - break; - case SE_ExtraAttackChance: - newbon->ExtraAttackChance += base1; - break; - case SE_SeeInvis: - newbon->SeeInvis = base1; - break; - case SE_BaseMovementSpeed: - newbon->BaseMovementSpeed += base1; - break; - case SE_IncreaseRunSpeedCap: - newbon->IncreaseRunSpeedCap += base1; - break; - case SE_ConsumeProjectile: - newbon->ConsumeProjectile += base1; - break; - case SE_ForageAdditionalItems: - newbon->ForageAdditionalItems += base1; - break; - case SE_Salvage: - newbon->SalvageChance += base1; - break; - case SE_ArcheryDamageModifier: - newbon->ArcheryDamageModifier += base1; - break; - case SE_DoubleRangedAttack: - newbon->DoubleRangedAttack += base1; - break; - case SE_DamageShield: - newbon->DamageShield += base1; - break; - case SE_CharmBreakChance: - newbon->CharmBreakChance += base1; - break; - case SE_OffhandRiposteFail: - newbon->OffhandRiposteFail += base1; - break; - case SE_ItemAttackCapIncrease: - newbon->ItemATKCap += base1; - break; - case SE_GivePetGroupTarget: - newbon->GivePetGroupTarget = true; - break; - case SE_ItemHPRegenCapIncrease: - newbon->ItemHPRegenCap = +base1; - break; - case SE_Ambidexterity: - newbon->Ambidexterity += base1; - break; - case SE_PetMaxHP: - newbon->PetMaxHP += base1; - break; - case SE_AvoidMeleeChance: - newbon->AvoidMeleeChance += base1; - break; - case SE_CombatStability: - newbon->CombatStability += base1; - break; - case SE_AddSingingMod: - switch (base2) - { - case ItemTypeWindInstrument: - newbon->windMod += base1; - break; - case ItemTypeStringedInstrument: - newbon->stringedMod += base1; - break; - case ItemTypeBrassInstrument: - newbon->brassMod += base1; - break; - case ItemTypePercussionInstrument: - newbon->percussionMod += base1; - break; - case ItemTypeSinging: - newbon->singingMod += base1; - break; - } - break; - case SE_SongModCap: - newbon->songModCap += base1; - break; - case SE_PetCriticalHit: - newbon->PetCriticalHit += base1; - break; - case SE_PetAvoidance: - newbon->PetAvoidance += base1; - break; - case SE_ShieldBlock: - newbon->ShieldBlock += base1; - break; - case SE_ShieldEquipHateMod: - newbon->ShieldEquipHateMod += base1; - break; - case SE_ShieldEquipDmgMod: - newbon->ShieldEquipDmgMod[0] += base1; - newbon->ShieldEquipDmgMod[1] += base2; - break; - case SE_SecondaryDmgInc: - newbon->SecondaryDmgInc = true; - break; - case SE_ChangeAggro: - newbon->hatemod += base1; - break; - case SE_EndurancePool: - newbon->Endurance += base1; - break; - case SE_ChannelChanceItems: - newbon->ChannelChanceItems += base1; - break; - case SE_ChannelChanceSpells: - newbon->ChannelChanceSpells += base1; - break; - case SE_DoubleSpecialAttack: - newbon->DoubleSpecialAttack += base1; - break; - case SE_TripleBackstab: - newbon->TripleBackstab += base1; - break; - case SE_FrontalBackstabMinDmg: - newbon->FrontalBackstabMinDmg = true; - break; - case SE_FrontalBackstabChance: - newbon->FrontalBackstabChance += base1; - break; - case SE_BlockBehind: - newbon->BlockBehind += base1; - break; - - case SE_StrikeThrough: - case SE_StrikeThrough2: - newbon->StrikeThrough += base1; - break; - case SE_DoubleAttackChance: - newbon->DoubleAttackChance += base1; - break; - case SE_GiveDoubleAttack: - newbon->GiveDoubleAttack += base1; - break; - case SE_ProcChance: - newbon->ProcChanceSPA += base1; - break; - case SE_RiposteChance: - newbon->RiposteChance += base1; - break; - case SE_Flurry: - newbon->FlurryChance += base1; - break; - case SE_PetFlurry: - newbon->PetFlurry = base1; - break; - case SE_BardSongRange: - newbon->SongRange += base1; - break; - case SE_RootBreakChance: - newbon->RootBreakChance += base1; - break; - case SE_UnfailingDivinity: - newbon->UnfailingDivinity += base1; - break; - case SE_CrippBlowChance: - newbon->CrippBlowChance += base1; - break; - - case SE_ProcOnKillShot: - for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3) - { - if(!newbon->SpellOnKill[i] || ((newbon->SpellOnKill[i] == base2) && (newbon->SpellOnKill[i+1] < base1))) - { - //base1 = chance, base2 = SpellID to be triggered, base3 = min npc level - newbon->SpellOnKill[i] = base2; - newbon->SpellOnKill[i+1] = base1; - - if (GetLevel() > 15) - newbon->SpellOnKill[i+2] = GetLevel() - 15; //AA specifiy "non-trivial" - else - newbon->SpellOnKill[i+2] = 0; - - break; - } - } - break; - - case SE_SpellOnDeath: - for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) - { - if(!newbon->SpellOnDeath[i]) - { - // base1 = SpellID to be triggered, base2 = chance to fire - newbon->SpellOnDeath[i] = base1; - newbon->SpellOnDeath[i+1] = base2; - break; - } - } - break; - - case SE_TriggerOnCast: - - for(int i = 0; i < MAX_SPELL_TRIGGER; i++) - { - if (newbon->SpellTriggers[i] == aaid) - break; - - if(!newbon->SpellTriggers[i]) - { - //Save the 'aaid' of each triggerable effect to an array - newbon->SpellTriggers[i] = aaid; - break; - } - } - break; - - case SE_CriticalHitChance: - { - if(base2 == -1) - newbon->CriticalHitChance[HIGHEST_SKILL+1] += base1; - else - newbon->CriticalHitChance[base2] += base1; - } - break; - - case SE_CriticalDamageMob: - { - // base1 = effect value, base2 = skill restrictions(-1 for all) - if(base2 == -1) - newbon->CritDmgMob[HIGHEST_SKILL+1] += base1; - else - newbon->CritDmgMob[base2] += base1; - break; - } - - case SE_CriticalSpellChance: - { - newbon->CriticalSpellChance += base1; - - if (base2 > newbon->SpellCritDmgIncNoStack) - newbon->SpellCritDmgIncNoStack = base2; - - break; - } - - case SE_ResistFearChance: - { - if(base1 == 100) // If we reach 100% in a single spell/item then we should be immune to negative fear resist effects until our immunity is over - newbon->Fearless = true; - - newbon->ResistFearChance += base1; // these should stack - break; - } - - case SE_SkillDamageAmount: - { - if(base2 == -1) - newbon->SkillDamageAmount[HIGHEST_SKILL+1] += base1; - else - newbon->SkillDamageAmount[base2] += base1; - break; - } - - case SE_SpecialAttackKBProc: - { - //You can only have one of these per client. [AA Dragon Punch] - newbon->SpecialAttackKBProc[0] = base1; //Chance base 100 = 25% proc rate - newbon->SpecialAttackKBProc[1] = base2; //Skill to KB Proc Off - break; - } - - case SE_DamageModifier: - { - if(base2 == -1) - newbon->DamageModifier[HIGHEST_SKILL+1] += base1; - else - newbon->DamageModifier[base2] += base1; - break; - } - - case SE_DamageModifier2: - { - if(base2 == -1) - newbon->DamageModifier2[HIGHEST_SKILL+1] += base1; - else - newbon->DamageModifier2[base2] += base1; - break; - } - - case SE_SlayUndead: - { - if(newbon->SlayUndead[1] < base1) - newbon->SlayUndead[0] = base1; // Rate - newbon->SlayUndead[1] = base2; // Damage Modifier - break; - } - - case SE_DoubleRiposte: - { - newbon->DoubleRiposte += base1; - } - - case SE_GiveDoubleRiposte: - { - //0=Regular Riposte 1=Skill Attack Riposte 2=Skill - if(base2 == 0){ - if(newbon->GiveDoubleRiposte[0] < base1) - newbon->GiveDoubleRiposte[0] = base1; - } - //Only for special attacks. - else if(base2 > 0 && (newbon->GiveDoubleRiposte[1] < base1)){ - newbon->GiveDoubleRiposte[1] = base1; - newbon->GiveDoubleRiposte[2] = base2; - } - - break; - } - - //Kayen: Not sure best way to implement this yet. - //Physically raises skill cap ie if 55/55 it will raise to 55/60 - case SE_RaiseSkillCap: - { - if(newbon->RaiseSkillCap[0] < base1){ - newbon->RaiseSkillCap[0] = base1; //value - newbon->RaiseSkillCap[1] = base2; //skill - } - break; - } - - case SE_MasteryofPast: - { - if(newbon->MasteryofPast < base1) - newbon->MasteryofPast = base1; - break; - } - - case SE_CastingLevel2: - case SE_CastingLevel: - { - newbon->effective_casting_level += base1; - break; - } - - case SE_DivineSave: - { - if(newbon->DivineSaveChance[0] < base1) - { - newbon->DivineSaveChance[0] = base1; - newbon->DivineSaveChance[1] = base2; - } - break; - } - - - case SE_SpellEffectResistChance: - { - for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) - { - if(newbon->SEResist[e+1] && (newbon->SEResist[e] == base2) && (newbon->SEResist[e+1] < base1)){ - newbon->SEResist[e] = base2; //Spell Effect ID - newbon->SEResist[e+1] = base1; //Resist Chance - break; - } - else if (!newbon->SEResist[e+1]){ - newbon->SEResist[e] = base2; //Spell Effect ID - newbon->SEResist[e+1] = base1; //Resist Chance - break; - } - } - break; - } - - case SE_MitigateDamageShield: - { - if (base1 < 0) - base1 = base1*(-1); - - newbon->DSMitigationOffHand += base1; - break; - } - - case SE_FinishingBlow: - { - //base1 = chance, base2 = damage - if (newbon->FinishingBlow[1] < base2){ - newbon->FinishingBlow[0] = base1; - newbon->FinishingBlow[1] = base2; - } - break; - } - - case SE_FinishingBlowLvl: - { - //base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) - if (newbon->FinishingBlowLvl[0] < base1){ - newbon->FinishingBlowLvl[0] = base1; - newbon->FinishingBlowLvl[1] = base2; - } - break; - } - - case SE_StunBashChance: - newbon->StunBashChance += base1; - break; - - case SE_IncreaseChanceMemwipe: - newbon->IncreaseChanceMemwipe += base1; - break; - - case SE_CriticalMend: - newbon->CriticalMend += base1; - break; - - case SE_HealRate: - newbon->HealRate += base1; - break; - - case SE_MeleeLifetap: - { - - if((base1 < 0) && (newbon->MeleeLifetap > base1)) - newbon->MeleeLifetap = base1; - - else if(newbon->MeleeLifetap < base1) - newbon->MeleeLifetap = base1; - break; - } - - case SE_Vampirism: - newbon->Vampirism += base1; - break; - - case SE_FrenziedDevastation: - newbon->FrenziedDevastation += base2; - break; - - case SE_SpellProcChance: - newbon->SpellProcChance += base1; - break; - - case SE_Berserk: - newbon->BerserkSPA = true; - break; - - case SE_Metabolism: - newbon->Metabolism += base1; - break; - - case SE_ImprovedReclaimEnergy: - { - if((base1 < 0) && (newbon->ImprovedReclaimEnergy > base1)) - newbon->ImprovedReclaimEnergy = base1; - - else if(newbon->ImprovedReclaimEnergy < base1) - newbon->ImprovedReclaimEnergy = base1; - break; - } - - case SE_HeadShot: - { - if(newbon->HeadShot[1] < base2){ - newbon->HeadShot[0] = base1; - newbon->HeadShot[1] = base2; - } - break; - } - - case SE_HeadShotLevel: - { - if(newbon->HSLevel < base1) - newbon->HSLevel = base1; - break; - } - - case SE_Assassinate: - { - if(newbon->Assassinate[1] < base2){ - newbon->Assassinate[0] = base1; - newbon->Assassinate[1] = base2; - } - break; - } - - case SE_AssassinateLevel: - { - if(newbon->AssassinateLevel < base1) - newbon->AssassinateLevel = base1; - break; - } - - case SE_PetMeleeMitigation: - newbon->PetMeleeMitigation += base1; - break; - - } - } -} - -void Mob::CalcSpellBonuses(StatBonuses* newbon) -{ - int i; - - memset(newbon, 0, sizeof(StatBonuses)); - newbon->AggroRange = -1; - newbon->AssistRange = -1; - - uint32 buff_count = GetMaxTotalSlots(); - for(i = 0; i < buff_count; i++) { - if(buffs[i].spellid != SPELL_UNKNOWN){ - ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, false, buffs[i].ticsremaining,i); - - if (buffs[i].numhits > 0) - Numhits(true); - } - } - - //Applies any perma NPC spell bonuses from npc_spells_effects table. - if (IsNPC()) - CastToNPC()->ApplyAISpellEffects(newbon); - - //Removes the spell bonuses that are effected by a 'negate' debuff. - if (spellbonuses.NegateEffects){ - for(i = 0; i < buff_count; i++) { - if( (buffs[i].spellid != SPELL_UNKNOWN) && (IsEffectInSpell(buffs[i].spellid, SE_NegateSpellEffect)) ) - NegateSpellsBonuses(buffs[i].spellid); - } - } - //this prolly suffer from roundoff error slightly... - newbon->AC = newbon->AC * 10 / 34; //ratio determined impirically from client. - if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells. -} - -void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterId, bool item_bonus, uint32 ticsremaining, int buffslot, - bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max) -{ - int i, effect_value, base2, max, effectid; - Mob *caster = nullptr; - - if(!IsAISpellEffect && !IsValidSpell(spell_id)) - return; - - if(casterId > 0) - caster = entity_list.GetMob(casterId); - - for (i = 0; i < EFFECT_COUNT; i++) - { - //Buffs/Item effects - if (!IsAISpellEffect) { - - if(IsBlankSpellEffect(spell_id, i)) - continue; - - uint8 focus = IsFocusEffect(spell_id, i); - if (focus) - { - newbon->FocusEffects[focus] = spells[spell_id].effectid[i]; - continue; - } - - - effectid = spells[spell_id].effectid[i]; - effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining); - base2 = spells[spell_id].base2[i]; - max = spells[spell_id].max[i]; - } - //Use AISpellEffects - else { - effectid = effect_id; - effect_value = se_base; - base2 = se_limit; - max = se_max; - i = EFFECT_COUNT; //End the loop - } - - switch (effectid) - { - case SE_CurrentHP: //regens - if(effect_value > 0) { - newbon->HPRegen += effect_value; - } - break; - - case SE_CurrentEndurance: - newbon->EnduranceRegen += effect_value; - break; - - case SE_ChangeFrenzyRad: - { - // redundant to have level check here - if(newbon->AggroRange == -1 || effect_value < newbon->AggroRange) - { - newbon->AggroRange = effect_value; - } - break; - } - - case SE_Harmony: - { - // neotokyo: Harmony effect as buff - kinda tricky - // harmony could stack with a lull spell, which has better aggro range - // take the one with less range in any case - if(newbon->AssistRange == -1 || effect_value < newbon->AssistRange) - { - newbon->AssistRange = effect_value; - } - break; - } - - case SE_AttackSpeed: - { - if ((effect_value - 100) > 0) { // Haste - if (newbon->haste < 0) break; // Slowed - Don't apply haste - if ((effect_value - 100) > newbon->haste) { - newbon->haste = effect_value - 100; - } - } - else if ((effect_value - 100) < 0) { // Slow - int real_slow_value = (100 - effect_value) * -1; - real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); - if (real_slow_value < newbon->haste) - newbon->haste = real_slow_value; - } - break; - } - - case SE_AttackSpeed2: - { - if ((effect_value - 100) > 0) { // Haste V2 - Stacks with V1 but does not Overcap - if (newbon->hastetype2 < 0) break; //Slowed - Don't apply haste2 - if ((effect_value - 100) > newbon->hastetype2) { - newbon->hastetype2 = effect_value - 100; - } - } - else if ((effect_value - 100) < 0) { // Slow - int real_slow_value = (100 - effect_value) * -1; - real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); - if (real_slow_value < newbon->hastetype2) - newbon->hastetype2 = real_slow_value; - } - break; - } - - case SE_AttackSpeed3: - { - if (effect_value < 0){ //Slow - effect_value -= ((effect_value * GetSlowMitigation()/100)); - if (effect_value < newbon->hastetype3) - newbon->hastetype3 = effect_value; - } - - else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps - if (effect_value > newbon->hastetype3) { - newbon->hastetype3 = effect_value; - } - } - break; - } - - case SE_AttackSpeed4: - { - if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow) - effect_value = effect_value * -1; - - if (effect_value > 0 && effect_value > newbon->inhibitmelee) { - effect_value -= ((effect_value * GetSlowMitigation()/100)); - if (effect_value > newbon->inhibitmelee) - newbon->inhibitmelee = effect_value; - } - - break; - } - - case SE_TotalHP: - { - newbon->HP += effect_value; - break; - } - - case SE_ManaRegen_v2: - case SE_CurrentMana: - { - newbon->ManaRegen += effect_value; - break; - } - - case SE_ManaPool: - { - newbon->Mana += effect_value; - break; - } - - case SE_Stamina: - { - newbon->EnduranceReduction += effect_value; - break; - } - - case SE_ACv2: - case SE_ArmorClass: - { - newbon->AC += effect_value; - break; - } - - case SE_ATK: - { - newbon->ATK += effect_value; - break; - } - - case SE_STR: - { - newbon->STR += effect_value; - break; - } - - case SE_DEX: - { - newbon->DEX += effect_value; - break; - } - - case SE_AGI: - { - newbon->AGI += effect_value; - break; - } - - case SE_STA: - { - newbon->STA += effect_value; - break; - } - - case SE_INT: - { - newbon->INT += effect_value; - break; - } - - case SE_WIS: - { - newbon->WIS += effect_value; - break; - } - - case SE_CHA: - { - if (spells[spell_id].base[i] != 0) { - newbon->CHA += effect_value; - } - break; - } - - case SE_AllStats: - { - newbon->STR += effect_value; - newbon->DEX += effect_value; - newbon->AGI += effect_value; - newbon->STA += effect_value; - newbon->INT += effect_value; - newbon->WIS += effect_value; - newbon->CHA += effect_value; - break; - } - - case SE_ResistFire: - { - newbon->FR += effect_value; - break; - } - - case SE_ResistCold: - { - newbon->CR += effect_value; - break; - } - - case SE_ResistPoison: - { - newbon->PR += effect_value; - break; - } - - case SE_ResistDisease: - { - newbon->DR += effect_value; - break; - } - - case SE_ResistMagic: - { - newbon->MR += effect_value; - break; - } - - case SE_ResistAll: - { - newbon->MR += effect_value; - newbon->DR += effect_value; - newbon->PR += effect_value; - newbon->CR += effect_value; - newbon->FR += effect_value; - break; - } - - case SE_ResistCorruption: - { - newbon->Corrup += effect_value; - break; - } - - case SE_RaiseStatCap: - { - switch(spells[spell_id].base2[i]) - { - //are these #define'd somewhere? - case 0: //str - newbon->STRCapMod += effect_value; - break; - case 1: //sta - newbon->STACapMod += effect_value; - break; - case 2: //agi - newbon->AGICapMod += effect_value; - break; - case 3: //dex - newbon->DEXCapMod += effect_value; - break; - case 4: //wis - newbon->WISCapMod += effect_value; - break; - case 5: //int - newbon->INTCapMod += effect_value; - break; - case 6: //cha - newbon->CHACapMod += effect_value; - break; - case 7: //mr - newbon->MRCapMod += effect_value; - break; - case 8: //cr - newbon->CRCapMod += effect_value; - break; - case 9: //fr - newbon->FRCapMod += effect_value; - break; - case 10: //pr - newbon->PRCapMod += effect_value; - break; - case 11: //dr - newbon->DRCapMod += effect_value; - break; - case 12: // corruption - newbon->CorrupCapMod += effect_value; - break; - } - break; - } - - case SE_CastingLevel2: - case SE_CastingLevel: // Brilliance of Ro - { - newbon->effective_casting_level += effect_value; - break; - } - - case SE_MovementSpeed: - newbon->movementspeed += effect_value; - break; - - case SE_SpellDamageShield: - newbon->SpellDamageShield += effect_value; - break; - - case SE_DamageShield: - { - newbon->DamageShield += effect_value; - newbon->DamageShieldSpellID = spell_id; - //When using npc_spells_effects MAX value can be set to determine DS Type - if (IsAISpellEffect && max) - newbon->DamageShieldType = GetDamageShieldType(spell_id, max); - else - newbon->DamageShieldType = GetDamageShieldType(spell_id); - - break; - } - - case SE_ReverseDS: - { - newbon->ReverseDamageShield += effect_value; - newbon->ReverseDamageShieldSpellID = spell_id; - - if (IsAISpellEffect && max) - newbon->ReverseDamageShieldType = GetDamageShieldType(spell_id, max); - else - newbon->ReverseDamageShieldType = GetDamageShieldType(spell_id); - break; - } - - case SE_Reflect: - newbon->reflect_chance += effect_value; - break; - - case SE_Amplification: - newbon->Amplification += effect_value; - break; - - case SE_ChangeAggro: - newbon->hatemod += effect_value; - break; - - case SE_MeleeMitigation: - //for some reason... this value is negative for increased mitigation - newbon->MeleeMitigation -= effect_value; - break; - - case SE_CriticalHitChance: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) { - if(base2 == -1) - newbon->CriticalHitChance[HIGHEST_SKILL+1] += effect_value; - else - newbon->CriticalHitChance[base2] += effect_value; - } - - else if(effect_value < 0) { - - if(base2 == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] > effect_value) - newbon->CriticalHitChance[HIGHEST_SKILL+1] = effect_value; - else if(base2 != -1 && newbon->CriticalHitChance[base2] > effect_value) - newbon->CriticalHitChance[base2] = effect_value; - } - - - else if(base2 == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] < effect_value) - newbon->CriticalHitChance[HIGHEST_SKILL+1] = effect_value; - else if(base2 != -1 && newbon->CriticalHitChance[base2] < effect_value) - newbon->CriticalHitChance[base2] = effect_value; - - break; - } - - case SE_CrippBlowChance: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->CrippBlowChance += effect_value; - - else if((effect_value < 0) && (newbon->CrippBlowChance > effect_value)) - newbon->CrippBlowChance = effect_value; - - else if(newbon->CrippBlowChance < effect_value) - newbon->CrippBlowChance = effect_value; - - break; - } - - case SE_AvoidMeleeChance: - { - //multiplier is to be compatible with item effects, watching for overflow too - effect_value = effect_value<3000? effect_value * 10 : 30000; - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->AvoidMeleeChance += effect_value; - - else if((effect_value < 0) && (newbon->AvoidMeleeChance > effect_value)) - newbon->AvoidMeleeChance = effect_value; - - else if(newbon->AvoidMeleeChance < effect_value) - newbon->AvoidMeleeChance = effect_value; - break; - } - - case SE_RiposteChance: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->RiposteChance += effect_value; - - else if((effect_value < 0) && (newbon->RiposteChance > effect_value)) - newbon->RiposteChance = effect_value; - - else if(newbon->RiposteChance < effect_value) - newbon->RiposteChance = effect_value; - break; - } - - case SE_DodgeChance: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->DodgeChance += effect_value; - - else if((effect_value < 0) && (newbon->DodgeChance > effect_value)) - newbon->DodgeChance = effect_value; - - if(newbon->DodgeChance < effect_value) - newbon->DodgeChance = effect_value; - break; - } - - case SE_ParryChance: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->ParryChance += effect_value; - - else if((effect_value < 0) && (newbon->ParryChance > effect_value)) - newbon->ParryChance = effect_value; - - if(newbon->ParryChance < effect_value) - newbon->ParryChance = effect_value; - break; - } - - case SE_DualWieldChance: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->DualWieldChance += effect_value; - - else if((effect_value < 0) && (newbon->DualWieldChance > effect_value)) - newbon->DualWieldChance = effect_value; - - if(newbon->DualWieldChance < effect_value) - newbon->DualWieldChance = effect_value; - break; - } - - case SE_DoubleAttackChance: - { - - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->DoubleAttackChance += effect_value; - - else if((effect_value < 0) && (newbon->DoubleAttackChance > effect_value)) - newbon->DoubleAttackChance = effect_value; - - if(newbon->DoubleAttackChance < effect_value) - newbon->DoubleAttackChance = effect_value; - break; - } - - case SE_TripleAttackChance: - { - - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->TripleAttackChance += effect_value; - - else if((effect_value < 0) && (newbon->TripleAttackChance > effect_value)) - newbon->TripleAttackChance = effect_value; - - if(newbon->TripleAttackChance < effect_value) - newbon->TripleAttackChance = effect_value; - break; - } - - case SE_MeleeLifetap: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->MeleeLifetap += spells[spell_id].base[i]; - - else if((effect_value < 0) && (newbon->MeleeLifetap > effect_value)) - newbon->MeleeLifetap = effect_value; - - else if(newbon->MeleeLifetap < effect_value) - newbon->MeleeLifetap = effect_value; - break; - } - - case SE_Vampirism: - newbon->Vampirism += effect_value; - break; - - case SE_AllInstrumentMod: - { - if(effect_value > newbon->singingMod) - newbon->singingMod = effect_value; - if(effect_value > newbon->brassMod) - newbon->brassMod = effect_value; - if(effect_value > newbon->percussionMod) - newbon->percussionMod = effect_value; - if(effect_value > newbon->windMod) - newbon->windMod = effect_value; - if(effect_value > newbon->stringedMod) - newbon->stringedMod = effect_value; - break; - } - - case SE_ResistSpellChance: - newbon->ResistSpellChance += effect_value; - break; - - case SE_ResistFearChance: - { - if(effect_value == 100) // If we reach 100% in a single spell/item then we should be immune to negative fear resist effects until our immunity is over - newbon->Fearless = true; - - newbon->ResistFearChance += effect_value; // these should stack - break; - } - - case SE_Fearless: - newbon->Fearless = true; - break; - - case SE_HundredHands: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->HundredHands += effect_value; - - if (effect_value > 0 && effect_value > newbon->HundredHands) - newbon->HundredHands = effect_value; //Increase Weapon Delay - else if (effect_value < 0 && effect_value < newbon->HundredHands) - newbon->HundredHands = effect_value; //Decrease Weapon Delay - break; - } - - case SE_MeleeSkillCheck: - { - if(newbon->MeleeSkillCheck < effect_value) { - newbon->MeleeSkillCheck = effect_value; - newbon->MeleeSkillCheckSkill = base2==-1?255:base2; - } - break; - } - - case SE_HitChance: - { - - if (RuleB(Spells, AdditiveBonusValues) && item_bonus){ - if(base2 == -1) - newbon->HitChanceEffect[HIGHEST_SKILL+1] += effect_value; - else - newbon->HitChanceEffect[base2] += effect_value; - } - - else if(base2 == -1){ - - if ((effect_value < 0) && (newbon->HitChanceEffect[HIGHEST_SKILL+1] > effect_value)) - newbon->HitChanceEffect[HIGHEST_SKILL+1] = effect_value; - - else if (!newbon->HitChanceEffect[HIGHEST_SKILL+1] || - ((newbon->HitChanceEffect[HIGHEST_SKILL+1] > 0) && (newbon->HitChanceEffect[HIGHEST_SKILL+1] < effect_value))) - newbon->HitChanceEffect[HIGHEST_SKILL+1] = effect_value; - } - - else { - - if ((effect_value < 0) && (newbon->HitChanceEffect[base2] > effect_value)) - newbon->HitChanceEffect[base2] = effect_value; - - else if (!newbon->HitChanceEffect[base2] || - ((newbon->HitChanceEffect[base2] > 0) && (newbon->HitChanceEffect[base2] < effect_value))) - newbon->HitChanceEffect[base2] = effect_value; - } - - break; - - } - - case SE_DamageModifier: - { - if(base2 == -1) - newbon->DamageModifier[HIGHEST_SKILL+1] += effect_value; - else - newbon->DamageModifier[base2] += effect_value; - break; - } - - case SE_DamageModifier2: - { - if(base2 == -1) - newbon->DamageModifier2[HIGHEST_SKILL+1] += effect_value; - else - newbon->DamageModifier2[base2] += effect_value; - break; - } - - case SE_MinDamageModifier: - { - if(base2 == -1) - newbon->MinDamageModifier[HIGHEST_SKILL+1] += effect_value; - else - newbon->MinDamageModifier[base2] += effect_value; - break; - } - - case SE_StunResist: - { - if(newbon->StunResist < effect_value) - newbon->StunResist = effect_value; - break; - } - - case SE_ProcChance: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) - newbon->ProcChanceSPA += effect_value; - - else if((effect_value < 0) && (newbon->ProcChanceSPA > effect_value)) - newbon->ProcChanceSPA = effect_value; - - if(newbon->ProcChanceSPA < effect_value) - newbon->ProcChanceSPA = effect_value; - - break; - } - - case SE_ExtraAttackChance: - newbon->ExtraAttackChance += effect_value; - break; - - case SE_PercentXPIncrease: - { - if(newbon->XPRateMod < effect_value) - newbon->XPRateMod = effect_value; - break; - } - - case SE_DeathSave: - { - if(newbon->DeathSave[0] < effect_value) - { - newbon->DeathSave[0] = effect_value; //1='Partial' 2='Full' - newbon->DeathSave[1] = buffslot; - //These are used in later expansion spell effects. - newbon->DeathSave[2] = base2;//Min level for HealAmt - newbon->DeathSave[3] = max;//HealAmt - } - break; - } - - case SE_DivineSave: - { - if (RuleB(Spells, AdditiveBonusValues) && item_bonus) { - newbon->DivineSaveChance[0] += effect_value; - newbon->DivineSaveChance[1] = 0; - } - - else if(newbon->DivineSaveChance[0] < effect_value) - { - newbon->DivineSaveChance[0] = effect_value; - newbon->DivineSaveChance[1] = base2; - //SetDeathSaveChance(true); - } - break; - } - - case SE_Flurry: - newbon->FlurryChance += effect_value; - break; - - case SE_Accuracy: - { - if ((effect_value < 0) && (newbon->Accuracy[HIGHEST_SKILL+1] > effect_value)) - newbon->Accuracy[HIGHEST_SKILL+1] = effect_value; - - else if (!newbon->Accuracy[HIGHEST_SKILL+1] || - ((newbon->Accuracy[HIGHEST_SKILL+1] > 0) && (newbon->Accuracy[HIGHEST_SKILL+1] < effect_value))) - newbon->Accuracy[HIGHEST_SKILL+1] = effect_value; - break; - } - - case SE_MaxHPChange: - newbon->MaxHPChange += effect_value; - break; - - case SE_EndurancePool: - newbon->Endurance += effect_value; - break; - - case SE_HealRate: - newbon->HealRate += effect_value; - break; - - case SE_SkillDamageTaken: - { - //When using npc_spells_effects if MAX value set, use stackable quest based modifier. - if (IsAISpellEffect && max){ - if(base2 == -1) - SkillDmgTaken_Mod[HIGHEST_SKILL+1] = effect_value; - else - SkillDmgTaken_Mod[base2] = effect_value; - } - else { - - if(base2 == -1) - newbon->SkillDmgTaken[HIGHEST_SKILL+1] += effect_value; - else - newbon->SkillDmgTaken[base2] += effect_value; - - } - break; - } - - case SE_TriggerOnCast: - { - for(int e = 0; e < MAX_SPELL_TRIGGER; e++) - { - if(!newbon->SpellTriggers[e]) - { - newbon->SpellTriggers[e] = spell_id; - break; - } - } - break; - } - - case SE_SpellCritChance: - newbon->CriticalSpellChance += effect_value; - break; - - case SE_CriticalSpellChance: - { - newbon->CriticalSpellChance += effect_value; - - if (base2 > newbon->SpellCritDmgIncNoStack) - newbon->SpellCritDmgIncNoStack = base2; - break; - } - - case SE_SpellCritDmgIncrease: - newbon->SpellCritDmgIncrease += effect_value; - break; - - case SE_DotCritDmgIncrease: - newbon->DotCritDmgIncrease += effect_value; - break; - - case SE_CriticalHealChance: - newbon->CriticalHealChance += effect_value; - break; - - case SE_CriticalHealOverTime: - newbon->CriticalHealOverTime += effect_value; - break; - - case SE_CriticalHealDecay: - newbon->CriticalHealDecay = true; - break; - - case SE_CriticalRegenDecay: - newbon->CriticalRegenDecay = true; - break; - - case SE_CriticalDotDecay: - newbon->CriticalDotDecay = true; - break; - - case SE_MitigateDamageShield: - { - if (effect_value < 0) - effect_value = effect_value*-1; - - newbon->DSMitigationOffHand += effect_value; - break; - } - - case SE_CriticalDoTChance: - newbon->CriticalDoTChance += effect_value; - break; - - case SE_ProcOnKillShot: - { - for(int e = 0; e < MAX_SPELL_TRIGGER*3; e+=3) - { - if(!newbon->SpellOnKill[e]) - { - // Base2 = Spell to fire | Base1 = % chance | Base3 = min level - newbon->SpellOnKill[e] = base2; - newbon->SpellOnKill[e+1] = effect_value; - newbon->SpellOnKill[e+2] = max; - break; - } - } - break; - } - - case SE_SpellOnDeath: - { - for(int e = 0; e < MAX_SPELL_TRIGGER; e+=2) - { - if(!newbon->SpellOnDeath[e]) - { - // Base2 = Spell to fire | Base1 = % chance - newbon->SpellOnDeath[e] = base2; - newbon->SpellOnDeath[e+1] = effect_value; - break; - } - } - break; - } - - case SE_CriticalDamageMob: - { - if(base2 == -1) - newbon->CritDmgMob[HIGHEST_SKILL+1] += effect_value; - else - newbon->CritDmgMob[base2] += effect_value; - break; - } - - case SE_ReduceSkillTimer: - { - if(newbon->SkillReuseTime[base2] < effect_value) - newbon->SkillReuseTime[base2] = effect_value; - break; - } - - case SE_SkillDamageAmount: - { - if(base2 == -1) - newbon->SkillDamageAmount[HIGHEST_SKILL+1] += effect_value; - else - newbon->SkillDamageAmount[base2] += effect_value; - break; - } - - case SE_GravityEffect: - newbon->GravityEffect = 1; - break; - - case SE_AntiGate: - newbon->AntiGate = true; - break; - - case SE_MagicWeapon: - newbon->MagicWeapon = true; - break; - - case SE_IncreaseBlockChance: - newbon->IncreaseBlockChance += effect_value; - break; - - case SE_PersistantCasting: - newbon->PersistantCasting += effect_value; - break; - - case SE_LimitHPPercent: - { - if(newbon->HPPercCap[0] != 0 && newbon->HPPercCap[0] > effect_value){ - newbon->HPPercCap[0] = effect_value; - newbon->HPPercCap[1] = base2; - } - else if(newbon->HPPercCap[0] == 0){ - newbon->HPPercCap[0] = effect_value; - newbon->HPPercCap[1] = base2; - } - break; - } - case SE_LimitManaPercent: - { - if(newbon->ManaPercCap[0] != 0 && newbon->ManaPercCap[0] > effect_value){ - newbon->ManaPercCap[0] = effect_value; - newbon->ManaPercCap[1] = base2; - } - else if(newbon->ManaPercCap[0] == 0) { - newbon->ManaPercCap[0] = effect_value; - newbon->ManaPercCap[1] = base2; - } - - break; - } - case SE_LimitEndPercent: - { - if(newbon->EndPercCap[0] != 0 && newbon->EndPercCap[0] > effect_value) { - newbon->EndPercCap[0] = effect_value; - newbon->EndPercCap[1] = base2; - } - - else if(newbon->EndPercCap[0] == 0){ - newbon->EndPercCap[0] = effect_value; - newbon->EndPercCap[1] = base2; - } - - break; - } - - case SE_BlockNextSpellFocus: - newbon->BlockNextSpell = true; - break; - - case SE_NegateSpellEffect: - newbon->NegateEffects = true; - break; - - case SE_ImmuneFleeing: - newbon->ImmuneToFlee = true; - break; - - case SE_DelayDeath: - newbon->DelayDeath += effect_value; - break; - - case SE_SpellProcChance: - newbon->SpellProcChance += effect_value; - break; - - case SE_CharmBreakChance: - newbon->CharmBreakChance += effect_value; - break; - - case SE_BardSongRange: - newbon->SongRange += effect_value; - break; - - case SE_HPToMana: - { - //Lower the ratio the more favorable - if((!newbon->HPToManaConvert) || (newbon->HPToManaConvert >= effect_value)) - newbon->HPToManaConvert = spells[spell_id].base[i]; - break; - } - - case SE_SkillDamageAmount2: - { - if(base2 == -1) - newbon->SkillDamageAmount2[HIGHEST_SKILL+1] += effect_value; - else - newbon->SkillDamageAmount2[base2] += effect_value; - break; - } - - case SE_NegateAttacks: - { - if (!newbon->NegateAttacks[0] || - ((newbon->NegateAttacks[0] && newbon->NegateAttacks[2]) && (newbon->NegateAttacks[2] < max))){ - newbon->NegateAttacks[0] = 1; - newbon->NegateAttacks[1] = buffslot; - newbon->NegateAttacks[2] = max; - } - break; - } - - case SE_MitigateMeleeDamage: - { - if (newbon->MitigateMeleeRune[0] < effect_value){ - newbon->MitigateMeleeRune[0] = effect_value; - newbon->MitigateMeleeRune[1] = buffslot; - newbon->MitigateMeleeRune[2] = base2; - newbon->MitigateMeleeRune[3] = max; - } - break; - } - - - case SE_MeleeThresholdGuard: - { - if (newbon->MeleeThresholdGuard[0] < effect_value){ - newbon->MeleeThresholdGuard[0] = effect_value; - newbon->MeleeThresholdGuard[1] = buffslot; - newbon->MeleeThresholdGuard[2] = base2; - } - break; - } - - case SE_SpellThresholdGuard: - { - if (newbon->SpellThresholdGuard[0] < effect_value){ - newbon->SpellThresholdGuard[0] = effect_value; - newbon->SpellThresholdGuard[1] = buffslot; - newbon->SpellThresholdGuard[2] = base2; - } - break; - } - - case SE_MitigateSpellDamage: - { - if (newbon->MitigateSpellRune[0] < effect_value){ - newbon->MitigateSpellRune[0] = effect_value; - newbon->MitigateSpellRune[1] = buffslot; - newbon->MitigateSpellRune[2] = base2; - newbon->MitigateSpellRune[3] = max; - } - break; - } - - case SE_MitigateDotDamage: - { - if (newbon->MitigateDotRune[0] < effect_value){ - newbon->MitigateDotRune[0] = effect_value; - newbon->MitigateDotRune[1] = buffslot; - newbon->MitigateDotRune[2] = base2; - newbon->MitigateDotRune[3] = max; - } - break; - } - - case SE_ManaAbsorbPercentDamage: - { - if (newbon->ManaAbsorbPercentDamage[0] < effect_value){ - newbon->ManaAbsorbPercentDamage[0] = effect_value; - newbon->ManaAbsorbPercentDamage[1] = buffslot; - } - break; - } - - case SE_TriggerMeleeThreshold: - { - if (newbon->TriggerMeleeThreshold[2] < base2){ - newbon->TriggerMeleeThreshold[0] = effect_value; - newbon->TriggerMeleeThreshold[1] = buffslot; - newbon->TriggerMeleeThreshold[2] = base2; - } - break; - } - - case SE_TriggerSpellThreshold: - { - if (newbon->TriggerSpellThreshold[2] < base2){ - newbon->TriggerSpellThreshold[0] = effect_value; - newbon->TriggerSpellThreshold[1] = buffslot; - newbon->TriggerSpellThreshold[2] = base2; - } - break; - } - - case SE_ShieldBlock: - newbon->ShieldBlock += effect_value; - break; - - case SE_ShieldEquipHateMod: - newbon->ShieldEquipHateMod += effect_value; - break; - - case SE_ShieldEquipDmgMod: - newbon->ShieldEquipDmgMod[0] += effect_value; - newbon->ShieldEquipDmgMod[1] += base2; - break; - - case SE_BlockBehind: - newbon->BlockBehind += effect_value; - break; - - case SE_Fear: - newbon->IsFeared = true; - break; - - //AA bonuses - implemented broadly into spell/item effects - case SE_FrontalStunResist: - newbon->FrontalStunResist += effect_value; - break; - - case SE_ImprovedBindWound: - newbon->BindWound += effect_value; - break; - - case SE_MaxBindWound: - newbon->MaxBindWound += effect_value; - break; - - case SE_BaseMovementSpeed: - newbon->BaseMovementSpeed += effect_value; - break; - - case SE_IncreaseRunSpeedCap: - newbon->IncreaseRunSpeedCap += effect_value; - break; - - case SE_DoubleSpecialAttack: - newbon->DoubleSpecialAttack += effect_value; - break; - - case SE_TripleBackstab: - newbon->TripleBackstab += effect_value; - break; - - case SE_FrontalBackstabMinDmg: - newbon->FrontalBackstabMinDmg = true; - break; - - case SE_FrontalBackstabChance: - newbon->FrontalBackstabChance += effect_value; - break; - - case SE_ConsumeProjectile: - newbon->ConsumeProjectile += effect_value; - break; - - case SE_ForageAdditionalItems: - newbon->ForageAdditionalItems += effect_value; - break; - - case SE_Salvage: - newbon->SalvageChance += effect_value; - break; - - case SE_ArcheryDamageModifier: - newbon->ArcheryDamageModifier += effect_value; - break; - - case SE_DoubleRangedAttack: - newbon->DoubleRangedAttack += effect_value; - break; - - case SE_SecondaryDmgInc: - newbon->SecondaryDmgInc = true; - break; - - case SE_StrikeThrough: - case SE_StrikeThrough2: - newbon->StrikeThrough += effect_value; - break; - - case SE_GiveDoubleAttack: - newbon->GiveDoubleAttack += effect_value; - break; - - case SE_PetCriticalHit: - newbon->PetCriticalHit += effect_value; - break; - - case SE_CombatStability: - newbon->CombatStability += effect_value; - break; - - case SE_AddSingingMod: - switch (base2) - { - case ItemTypeWindInstrument: - newbon->windMod += effect_value; - break; - case ItemTypeStringedInstrument: - newbon->stringedMod += effect_value; - break; - case ItemTypeBrassInstrument: - newbon->brassMod += effect_value; - break; - case ItemTypePercussionInstrument: - newbon->percussionMod += effect_value; - break; - case ItemTypeSinging: - newbon->singingMod += effect_value; - break; - } - break; - - case SE_SongModCap: - newbon->songModCap += effect_value; - break; - - case SE_PetAvoidance: - newbon->PetAvoidance += effect_value; - break; - - case SE_Ambidexterity: - newbon->Ambidexterity += effect_value; - break; - - case SE_PetMaxHP: - newbon->PetMaxHP += effect_value; - break; - - case SE_PetFlurry: - newbon->PetFlurry += effect_value; - break; - - case SE_GivePetGroupTarget: - newbon->GivePetGroupTarget = true; - break; - - case SE_RootBreakChance: - newbon->RootBreakChance += effect_value; - break; - - case SE_ChannelChanceItems: - newbon->ChannelChanceItems += effect_value; - break; - - case SE_ChannelChanceSpells: - newbon->ChannelChanceSpells += effect_value; - break; - - case SE_UnfailingDivinity: - newbon->UnfailingDivinity += effect_value; - break; - - - case SE_ItemHPRegenCapIncrease: - newbon->ItemHPRegenCap += effect_value; - break; - - case SE_OffhandRiposteFail: - newbon->OffhandRiposteFail += effect_value; - break; - - case SE_ItemAttackCapIncrease: - newbon->ItemATKCap += effect_value; - break; - - case SE_TwoHandBluntBlock: - newbon->TwoHandBluntBlock += effect_value; - break; - - case SE_StunBashChance: - newbon->StunBashChance += effect_value; - break; - - case SE_IncreaseChanceMemwipe: - newbon->IncreaseChanceMemwipe += effect_value; - break; - - case SE_CriticalMend: - newbon->CriticalMend += effect_value; - break; - - case SE_SpellEffectResistChance: - { - for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) - { - if(newbon->SEResist[e+1] && (newbon->SEResist[e] == base2) && (newbon->SEResist[e+1] < effect_value)){ - newbon->SEResist[e] = base2; //Spell Effect ID - newbon->SEResist[e+1] = effect_value; //Resist Chance - break; - } - else if (!newbon->SEResist[e+1]){ - newbon->SEResist[e] = base2; //Spell Effect ID - newbon->SEResist[e+1] = effect_value; //Resist Chance - break; - } - } - break; - } - - case SE_MasteryofPast: - { - if(newbon->MasteryofPast < effect_value) - newbon->MasteryofPast = effect_value; - break; - } - - case SE_DoubleRiposte: - { - newbon->DoubleRiposte += effect_value; - } - - case SE_GiveDoubleRiposte: - { - //Only allow for regular double riposte chance. - if(newbon->GiveDoubleRiposte[base2] == 0){ - if(newbon->GiveDoubleRiposte[0] < effect_value) - newbon->GiveDoubleRiposte[0] = effect_value; - } - break; - } - - case SE_SlayUndead: - { - if(newbon->SlayUndead[1] < effect_value) - newbon->SlayUndead[0] = effect_value; // Rate - newbon->SlayUndead[1] = base2; // Damage Modifier - break; - } - - case SE_TriggerOnReqTarget: - case SE_TriggerOnReqCaster: - newbon->TriggerOnValueAmount = true; - break; - - case SE_DivineAura: - newbon->DivineAura = true; - break; - - case SE_ImprovedTaunt: - if (newbon->ImprovedTaunt[0] < effect_value) { - newbon->ImprovedTaunt[0] = effect_value; - newbon->ImprovedTaunt[1] = base2; - newbon->ImprovedTaunt[2] = buffslot; - } - break; - - - case SE_DistanceRemoval: - newbon->DistanceRemoval = true; - break; - - case SE_FrenziedDevastation: - newbon->FrenziedDevastation += base2; - break; - - case SE_Root: - if (newbon->Root[0] && (newbon->Root[1] > buffslot)){ - newbon->Root[0] = 1; - newbon->Root[1] = buffslot; - } - else if (!newbon->Root[0]){ - newbon->Root[0] = 1; - newbon->Root[1] = buffslot; - } - break; - - case SE_Rune: - - if (newbon->MeleeRune[0] && (newbon->MeleeRune[1] > buffslot)){ - - newbon->MeleeRune[0] = effect_value; - newbon->MeleeRune[1] = buffslot; - } - else if (!newbon->MeleeRune[0]){ - newbon->MeleeRune[0] = effect_value; - newbon->MeleeRune[1] = buffslot; - } - - break; - - case SE_AbsorbMagicAtt: - if (newbon->AbsorbMagicAtt[0] && (newbon->AbsorbMagicAtt[1] > buffslot)){ - newbon->AbsorbMagicAtt[0] = effect_value; - newbon->AbsorbMagicAtt[1] = buffslot; - } - else if (!newbon->AbsorbMagicAtt[0]){ - newbon->AbsorbMagicAtt[0] = effect_value; - newbon->AbsorbMagicAtt[1] = buffslot; - } - break; - - case SE_NegateIfCombat: - newbon->NegateIfCombat = true; - break; - - case SE_Screech: - newbon->Screech = effect_value; - break; - - case SE_AlterNPCLevel: - - if (IsNPC()){ - if (!newbon->AlterNPCLevel - || ((effect_value < 0) && (newbon->AlterNPCLevel > effect_value)) - || ((effect_value > 0) && (newbon->AlterNPCLevel < effect_value))) { - - int16 tmp_lv = GetOrigLevel() + effect_value; - if (tmp_lv < 1) - tmp_lv = 1; - else if (tmp_lv > 255) - tmp_lv = 255; - if ((GetLevel() != tmp_lv)){ - newbon->AlterNPCLevel = effect_value; - SetLevel(tmp_lv); - } - } - } - break; - - case SE_AStacker: - newbon->AStacker[0] = 1; - newbon->AStacker[1] = effect_value; - break; - - case SE_BStacker: - newbon->BStacker[0] = 1; - newbon->BStacker[1] = effect_value; - break; - - case SE_CStacker: - newbon->CStacker[0] = 1; - newbon->CStacker[1] = effect_value; - break; - - case SE_DStacker: - newbon->DStacker[0] = 1; - newbon->DStacker[1] = effect_value; - break; - - case SE_Berserk: - newbon->BerserkSPA = true; - break; - - - case SE_Metabolism: - newbon->Metabolism += effect_value; - break; - - case SE_ImprovedReclaimEnergy: - { - if((effect_value < 0) && (newbon->ImprovedReclaimEnergy > effect_value)) - newbon->ImprovedReclaimEnergy = effect_value; - - else if(newbon->ImprovedReclaimEnergy < effect_value) - newbon->ImprovedReclaimEnergy = effect_value; - break; - } - - case SE_HeadShot: - { - if(newbon->HeadShot[1] < base2){ - newbon->HeadShot[0] = effect_value; - newbon->HeadShot[1] = base2; - } - break; - } - - case SE_HeadShotLevel: - { - if(newbon->HSLevel < effect_value) - newbon->HSLevel = effect_value; - break; - } - - case SE_Assassinate: - { - if(newbon->Assassinate[1] < base2){ - newbon->Assassinate[0] = effect_value; - newbon->Assassinate[1] = base2; - } - break; - } - - case SE_AssassinateLevel: - { - if(newbon->AssassinateLevel < effect_value) - newbon->AssassinateLevel = effect_value; - break; - } - - case SE_FinishingBlow: - { - //base1 = chance, base2 = damage - if (newbon->FinishingBlow[1] < base2){ - newbon->FinishingBlow[0] = effect_value; - newbon->FinishingBlow[1] = base2; - } - break; - } - - case SE_FinishingBlowLvl: - { - //base1 = level, base2 = ??? (Set to 200 in AA data, possible proc rate mod?) - if (newbon->FinishingBlowLvl[0] < effect_value){ - newbon->FinishingBlowLvl[0] = effect_value; - newbon->FinishingBlowLvl[1] = base2; - } - break; - } - - case SE_PetMeleeMitigation: - newbon->PetMeleeMitigation += effect_value; - break; - - //Special custom cases for loading effects on to NPC from 'npc_spels_effects' table - if (IsAISpellEffect) { - - //Non-Focused Effect to modify incomming spell damage by resist type. - case SE_FcSpellVulnerability: - ModVulnerability(base2, effect_value); - break; - } - } - } -} - -void NPC::CalcItemBonuses(StatBonuses *newbon) -{ - if(newbon){ - - for(int i = 0; i < MAX_WORN_INVENTORY; i++){ - const Item_Struct *cur = database.GetItem(equipment[i]); - if(cur){ - //basic stats - newbon->AC += cur->AC; - newbon->HP += cur->HP; - newbon->Mana += cur->Mana; - newbon->Endurance += cur->Endur; - newbon->STR += (cur->AStr + cur->HeroicStr); - newbon->STA += (cur->ASta + cur->HeroicSta); - newbon->DEX += (cur->ADex + cur->HeroicDex); - newbon->AGI += (cur->AAgi + cur->HeroicAgi); - newbon->INT += (cur->AInt + cur->HeroicInt); - newbon->WIS += (cur->AWis + cur->HeroicWis); - newbon->CHA += (cur->ACha + cur->HeroicCha); - newbon->MR += (cur->MR + cur->HeroicMR); - newbon->FR += (cur->FR + cur->HeroicFR); - newbon->CR += (cur->CR + cur->HeroicCR); - newbon->PR += (cur->PR + cur->HeroicPR); - newbon->DR += (cur->DR + cur->HeroicDR); - newbon->Corrup += (cur->SVCorruption + cur->HeroicSVCorrup); - - //more complex stats - if(cur->Regen > 0) { - newbon->HPRegen += cur->Regen; - } - if(cur->ManaRegen > 0) { - newbon->ManaRegen += cur->ManaRegen; - } - if(cur->Attack > 0) { - newbon->ATK += cur->Attack; - } - if(cur->DamageShield > 0) { - newbon->DamageShield += cur->DamageShield; - } - if(cur->SpellShield > 0) { - newbon->SpellDamageShield += cur->SpellShield; - } - if(cur->Shielding > 0) { - newbon->MeleeMitigation += cur->Shielding; - } - if(cur->StunResist > 0) { - newbon->StunResist += cur->StunResist; - } - if(cur->StrikeThrough > 0) { - newbon->StrikeThrough += cur->StrikeThrough; - } - if(cur->Avoidance > 0) { - newbon->AvoidMeleeChance += cur->Avoidance; - } - if(cur->Accuracy > 0) { - newbon->HitChance += cur->Accuracy; - } - if(cur->CombatEffects > 0) { - newbon->ProcChance += cur->CombatEffects; - } - if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects - ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon); - } - if (cur->Haste > newbon->haste) - newbon->haste = cur->Haste; - } - } - - } -} - -void Client::CalcItemScale() -{ - bool changed = false; - - if(CalcItemScale(0, 21)) - changed = true; - - if(CalcItemScale(22, 30)) - changed = true; - - if(CalcItemScale(251, 341)) - changed = true; - - if(CalcItemScale(400, 405)) - changed = true; - - //Power Source Slot - if (GetClientVersion() >= EQClientSoF) - { - if(CalcItemScale(9999, 10000)) - changed = true; - } - - if(changed) - { - CalcBonuses(); - } -} - -bool Client::CalcItemScale(uint32 slot_x, uint32 slot_y) -{ - bool changed = false; - int i; - for (i = slot_x; i < slot_y; i++) { - ItemInst* inst = m_inv.GetItem(i); - if(inst == 0) - continue; - - bool update_slot = false; - if(inst->IsScaling()) - { - uint16 oldexp = inst->GetExp(); - parse->EventItem(EVENT_SCALE_CALC, this, inst, nullptr, "", 0); - - if (inst->GetExp() != oldexp) { // if the scaling factor changed, rescale the item and update the client - inst->ScaleItem(); - changed = true; - update_slot = true; - } - } - - //iterate all augments - for(int x = 0; x < MAX_AUGMENT_SLOTS; ++x) - { - ItemInst * a_inst = inst->GetAugment(x); - if(!a_inst) - continue; - - if(a_inst->IsScaling()) - { - uint16 oldexp = a_inst->GetExp(); - parse->EventItem(EVENT_SCALE_CALC, this, a_inst, nullptr, "", 0); - - if (a_inst->GetExp() != oldexp) - { - a_inst->ScaleItem(); - changed = true; - update_slot = true; - } - } - } - - if(update_slot) - { - SendItemPacket(i, inst, ItemPacketCharmUpdate); - } - } - return changed; -} - -void Client::DoItemEnterZone() { - bool changed = false; - - if(DoItemEnterZone(0, 21)) - changed = true; - - if(DoItemEnterZone(22, 30)) - changed = true; - - if(DoItemEnterZone(251, 341)) - changed = true; - - if(DoItemEnterZone(400, 405)) - changed = true; - - //Power Source Slot - if (GetClientVersion() >= EQClientSoF) - { - if(DoItemEnterZone(9999, 10000)) - changed = true; - } - - if(changed) - { - CalcBonuses(); - } -} - -bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) { - bool changed = false; - for(int i = slot_x; i < slot_y; i++) { - ItemInst* inst = m_inv.GetItem(i); - if(!inst) - continue; - - bool update_slot = false; - if(inst->IsScaling()) - { - uint16 oldexp = inst->GetExp(); - - parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, inst, nullptr, "", 0); - if(i < 22 || i == 9999) { - parse->EventItem(EVENT_EQUIP_ITEM, this, inst, nullptr, "", i); - } - - if (inst->GetExp() != oldexp) { // if the scaling factor changed, rescale the item and update the client - inst->ScaleItem(); - changed = true; - update_slot = true; - } - } else { - if(i < 22 || i == 9999) { - parse->EventItem(EVENT_EQUIP_ITEM, this, inst, nullptr, "", i); - } - - parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, inst, nullptr, "", 0); - } - - //iterate all augments - for(int x = 0; x < MAX_AUGMENT_SLOTS; ++x) - { - ItemInst *a_inst = inst->GetAugment(x); - if(!a_inst) - continue; - - if(a_inst->IsScaling()) - { - uint16 oldexp = a_inst->GetExp(); - - parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, a_inst, nullptr, "", 0); - - if (a_inst->GetExp() != oldexp) - { - a_inst->ScaleItem(); - changed = true; - update_slot = true; - } - } else { - parse->EventItem(EVENT_ITEM_ENTER_ZONE, this, a_inst, nullptr, "", 0); - } - } - - if(update_slot) - { - SendItemPacket(i, inst, ItemPacketCharmUpdate); - } - } - return changed; -} - -uint8 Mob::IsFocusEffect(uint16 spell_id,int effect_index, bool AA,uint32 aa_effect) -{ - uint16 effect = 0; - - if (!AA) - effect = spells[spell_id].effectid[effect_index]; - else - effect = aa_effect; - - switch (effect) - { - case SE_ImprovedDamage: - return focusImprovedDamage; - case SE_ImprovedHeal: - return focusImprovedHeal; - case SE_ReduceManaCost: - return focusManaCost; - case SE_IncreaseSpellHaste: - return focusSpellHaste; - case SE_IncreaseSpellDuration: - return focusSpellDuration; - case SE_SpellDurationIncByTic: - return focusSpellDurByTic; - case SE_SwarmPetDuration: - return focusSwarmPetDuration; - case SE_IncreaseRange: - return focusRange; - case SE_ReduceReagentCost: - return focusReagentCost; - case SE_PetPowerIncrease: - return focusPetPower; - case SE_SpellResistReduction: - return focusResistRate; - case SE_SpellHateMod: - return focusSpellHateMod; - case SE_ReduceReuseTimer: - return focusReduceRecastTime; - case SE_TriggerOnCast: - //return focusTriggerOnCast; - return 0; //This is calculated as an actual bonus - case SE_FcSpellVulnerability: - return focusSpellVulnerability; - case SE_BlockNextSpellFocus: - //return focusBlockNextSpell; - return 0; //This is calculated as an actual bonus - case SE_FcTwincast: - return focusTwincast; - case SE_SympatheticProc: - return focusSympatheticProc; - case SE_FcDamageAmt: - return focusFcDamageAmt; - case SE_FcDamageAmtCrit: - return focusFcDamageAmtCrit; - case SE_FcDamagePctCrit: - return focusFcDamagePctCrit; - case SE_FcDamageAmtIncoming: - return focusFcDamageAmtIncoming; - case SE_FcHealAmtIncoming: - return focusFcHealAmtIncoming; - case SE_FcHealPctIncoming: - return focusFcHealPctIncoming; - case SE_FcBaseEffects: - return focusFcBaseEffects; - case SE_FcIncreaseNumHits: - return focusIncreaseNumHits; - case SE_FcLimitUse: - return focusFcLimitUse; - case SE_FcMute: - return focusFcMute; - case SE_FcTimerRefresh: - return focusFcTimerRefresh; - case SE_FcStunTimeMod: - return focusFcStunTimeMod; - case SE_FcHealPctCritIncoming: - return focusFcHealPctCritIncoming; - case SE_FcHealAmt: - return focusFcHealAmt; - case SE_FcHealAmtCrit: - return focusFcHealAmtCrit; - } - return 0; -} - -void Mob::NegateSpellsBonuses(uint16 spell_id) -{ - if(!IsValidSpell(spell_id)) - return; - - int effect_value = 0; - - for (int i = 0; i < EFFECT_COUNT; i++) - { - if (spells[spell_id].effectid[i] == SE_NegateSpellEffect){ - - //Negate focus effects - for(int e = 0; e < HIGHEST_FOCUS+1; e++) - { - if (spellbonuses.FocusEffects[e] == spells[spell_id].base2[i]) - { - spellbonuses.FocusEffects[e] = effect_value; - continue; - } - } - - //Negate bonuses - switch (spells[spell_id].base2[i]) - { - case SE_CurrentHP: - if(spells[spell_id].base[i] == 1) { - spellbonuses.HPRegen = effect_value; - aabonuses.HPRegen = effect_value; - itembonuses.HPRegen = effect_value; - } - break; - - case SE_CurrentEndurance: - spellbonuses.EnduranceRegen = effect_value; - aabonuses.EnduranceRegen = effect_value; - itembonuses.EnduranceRegen = effect_value; - break; - - case SE_ChangeFrenzyRad: - spellbonuses.AggroRange = effect_value; - aabonuses.AggroRange = effect_value; - itembonuses.AggroRange = effect_value; - break; - - case SE_Harmony: - spellbonuses.AssistRange = effect_value; - aabonuses.AssistRange = effect_value; - itembonuses.AssistRange = effect_value; - break; - - case SE_AttackSpeed: - spellbonuses.haste = effect_value; - aabonuses.haste = effect_value; - itembonuses.haste = effect_value; - break; - - case SE_AttackSpeed2: - spellbonuses.hastetype2 = effect_value; - aabonuses.hastetype2 = effect_value; - itembonuses.hastetype2 = effect_value; - break; - - case SE_AttackSpeed3: - { - if (effect_value > 0) { - spellbonuses.hastetype3 = effect_value; - aabonuses.hastetype3 = effect_value; - itembonuses.hastetype3 = effect_value; - - } - break; - } - - case SE_AttackSpeed4: - spellbonuses.inhibitmelee = effect_value; - aabonuses.inhibitmelee = effect_value; - itembonuses.inhibitmelee = effect_value; - break; - - case SE_TotalHP: - spellbonuses.HP = effect_value; - aabonuses.HP = effect_value; - itembonuses.HP = effect_value; - break; - - case SE_ManaRegen_v2: - case SE_CurrentMana: - spellbonuses.ManaRegen = effect_value; - aabonuses.ManaRegen = effect_value; - itembonuses.ManaRegen = effect_value; - break; - - case SE_ManaPool: - spellbonuses.Mana = effect_value; - itembonuses.Mana = effect_value; - aabonuses.Mana = effect_value; - break; - - case SE_Stamina: - spellbonuses.EnduranceReduction = effect_value; - itembonuses.EnduranceReduction = effect_value; - aabonuses.EnduranceReduction = effect_value; - break; - - case SE_ACv2: - case SE_ArmorClass: - spellbonuses.AC = effect_value; - aabonuses.AC = effect_value; - itembonuses.AC = effect_value; - break; - - case SE_ATK: - spellbonuses.ATK = effect_value; - aabonuses.ATK = effect_value; - itembonuses.ATK = effect_value; - break; - - case SE_STR: - spellbonuses.STR = effect_value; - itembonuses.STR = effect_value; - aabonuses.STR = effect_value; - break; - - case SE_DEX: - spellbonuses.DEX = effect_value; - aabonuses.DEX = effect_value; - itembonuses.DEX = effect_value; - break; - - case SE_AGI: - itembonuses.AGI = effect_value; - aabonuses.AGI = effect_value; - spellbonuses.AGI = effect_value; - break; - - case SE_STA: - spellbonuses.STA = effect_value; - itembonuses.STA = effect_value; - aabonuses.STA = effect_value; - break; - - case SE_INT: - spellbonuses.INT = effect_value; - aabonuses.INT = effect_value; - itembonuses.INT = effect_value; - break; - - case SE_WIS: - spellbonuses.WIS = effect_value; - aabonuses.WIS = effect_value; - itembonuses.WIS = effect_value; - break; - - case SE_CHA: - itembonuses.CHA = effect_value; - spellbonuses.CHA = effect_value; - aabonuses.CHA = effect_value; - break; - - case SE_AllStats: - { - spellbonuses.STR = effect_value; - spellbonuses.DEX = effect_value; - spellbonuses.AGI = effect_value; - spellbonuses.STA = effect_value; - spellbonuses.INT = effect_value; - spellbonuses.WIS = effect_value; - spellbonuses.CHA = effect_value; - - itembonuses.STR = effect_value; - itembonuses.DEX = effect_value; - itembonuses.AGI = effect_value; - itembonuses.STA = effect_value; - itembonuses.INT = effect_value; - itembonuses.WIS = effect_value; - itembonuses.CHA = effect_value; - - aabonuses.STR = effect_value; - aabonuses.DEX = effect_value; - aabonuses.AGI = effect_value; - aabonuses.STA = effect_value; - aabonuses.INT = effect_value; - aabonuses.WIS = effect_value; - aabonuses.CHA = effect_value; - break; - } - - case SE_ResistFire: - spellbonuses.FR = effect_value; - itembonuses.FR = effect_value; - aabonuses.FR = effect_value; - break; - - case SE_ResistCold: - spellbonuses.CR = effect_value; - aabonuses.CR = effect_value; - itembonuses.CR = effect_value; - break; - - case SE_ResistPoison: - spellbonuses.PR = effect_value; - aabonuses.PR = effect_value; - itembonuses.PR = effect_value; - break; - - case SE_ResistDisease: - spellbonuses.DR = effect_value; - itembonuses.DR = effect_value; - aabonuses.DR = effect_value; - break; - - case SE_ResistMagic: - spellbonuses.MR = effect_value; - aabonuses.MR = effect_value; - itembonuses.MR = effect_value; - break; - - case SE_ResistAll: - { - spellbonuses.MR = effect_value; - spellbonuses.DR = effect_value; - spellbonuses.PR = effect_value; - spellbonuses.CR = effect_value; - spellbonuses.FR = effect_value; - - aabonuses.MR = effect_value; - aabonuses.DR = effect_value; - aabonuses.PR = effect_value; - aabonuses.CR = effect_value; - aabonuses.FR = effect_value; - - itembonuses.MR = effect_value; - itembonuses.DR = effect_value; - itembonuses.PR = effect_value; - itembonuses.CR = effect_value; - itembonuses.FR = effect_value; - break; - } - - case SE_ResistCorruption: - spellbonuses.Corrup = effect_value; - itembonuses.Corrup = effect_value; - aabonuses.Corrup = effect_value; - break; - - case SE_CastingLevel2: - case SE_CastingLevel: // Brilliance of Ro - spellbonuses.effective_casting_level = effect_value; - aabonuses.effective_casting_level = effect_value; - itembonuses.effective_casting_level = effect_value; - break; - - - case SE_MovementSpeed: - spellbonuses.movementspeed = effect_value; - aabonuses.movementspeed = effect_value; - itembonuses.movementspeed = effect_value; - break; - - case SE_SpellDamageShield: - spellbonuses.SpellDamageShield = effect_value; - aabonuses.SpellDamageShield = effect_value; - itembonuses.SpellDamageShield = effect_value; - break; - - case SE_DamageShield: - spellbonuses.DamageShield = effect_value; - aabonuses.DamageShield = effect_value; - itembonuses.DamageShield = effect_value; - break; - - case SE_ReverseDS: - spellbonuses.ReverseDamageShield = effect_value; - aabonuses.ReverseDamageShield = effect_value; - itembonuses.ReverseDamageShield = effect_value; - break; - - case SE_Reflect: - spellbonuses.reflect_chance = effect_value; - aabonuses.reflect_chance = effect_value; - itembonuses.reflect_chance = effect_value; - break; - - case SE_Amplification: - spellbonuses.Amplification = effect_value; - itembonuses.Amplification = effect_value; - aabonuses.Amplification = effect_value; - break; - - case SE_ChangeAggro: - spellbonuses.hatemod = effect_value; - itembonuses.hatemod = effect_value; - aabonuses.hatemod = effect_value; - break; - - case SE_MeleeMitigation: - spellbonuses.MeleeMitigation = effect_value; - itembonuses.MeleeMitigation = effect_value; - aabonuses.MeleeMitigation = effect_value; - break; - - case SE_CriticalHitChance: - { - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - spellbonuses.CriticalHitChance[e] = effect_value; - aabonuses.CriticalHitChance[e] = effect_value; - itembonuses.CriticalHitChance[e] = effect_value; - } - } - - case SE_CrippBlowChance: - spellbonuses.CrippBlowChance = effect_value; - aabonuses.CrippBlowChance = effect_value; - itembonuses.CrippBlowChance = effect_value; - break; - - case SE_AvoidMeleeChance: - spellbonuses.AvoidMeleeChance = effect_value; - aabonuses.AvoidMeleeChance = effect_value; - itembonuses.AvoidMeleeChance = effect_value; - break; - - case SE_RiposteChance: - spellbonuses.RiposteChance = effect_value; - aabonuses.RiposteChance = effect_value; - itembonuses.RiposteChance = effect_value; - break; - - case SE_DodgeChance: - spellbonuses.DodgeChance = effect_value; - aabonuses.DodgeChance = effect_value; - itembonuses.DodgeChance = effect_value; - break; - - case SE_ParryChance: - spellbonuses.ParryChance = effect_value; - aabonuses.ParryChance = effect_value; - itembonuses.ParryChance = effect_value; - break; - - case SE_DualWieldChance: - spellbonuses.DualWieldChance = effect_value; - aabonuses.DualWieldChance = effect_value; - itembonuses.DualWieldChance = effect_value; - break; - - case SE_DoubleAttackChance: - spellbonuses.DoubleAttackChance = effect_value; - aabonuses.DoubleAttackChance = effect_value; - itembonuses.DoubleAttackChance = effect_value; - break; - - case SE_TripleAttackChance: - spellbonuses.TripleAttackChance = effect_value; - aabonuses.TripleAttackChance = effect_value; - itembonuses.TripleAttackChance = effect_value; - break; - - case SE_MeleeLifetap: - spellbonuses.MeleeLifetap = effect_value; - aabonuses.MeleeLifetap = effect_value; - itembonuses.MeleeLifetap = effect_value; - break; - - case SE_AllInstrumentMod: - { - spellbonuses.singingMod = effect_value; - spellbonuses.brassMod = effect_value; - spellbonuses.percussionMod = effect_value; - spellbonuses.windMod = effect_value; - spellbonuses.stringedMod = effect_value; - - itembonuses.singingMod = effect_value; - itembonuses.brassMod = effect_value; - itembonuses.percussionMod = effect_value; - itembonuses.windMod = effect_value; - itembonuses.stringedMod = effect_value; - - aabonuses.singingMod = effect_value; - aabonuses.brassMod = effect_value; - aabonuses.percussionMod = effect_value; - aabonuses.windMod = effect_value; - aabonuses.stringedMod = effect_value; - break; - } - - case SE_ResistSpellChance: - spellbonuses.ResistSpellChance = effect_value; - aabonuses.ResistSpellChance = effect_value; - itembonuses.ResistSpellChance = effect_value; - break; - - case SE_ResistFearChance: - spellbonuses.Fearless = false; - spellbonuses.ResistFearChance = effect_value; - aabonuses.ResistFearChance = effect_value; - itembonuses.ResistFearChance = effect_value; - break; - - case SE_Fearless: - spellbonuses.Fearless = false; - aabonuses.Fearless = false; - itembonuses.Fearless = false; - break; - - case SE_HundredHands: - spellbonuses.HundredHands = effect_value; - aabonuses.HundredHands = effect_value; - itembonuses.HundredHands = effect_value; - break; - - case SE_MeleeSkillCheck: - { - spellbonuses.MeleeSkillCheck = effect_value; - spellbonuses.MeleeSkillCheckSkill = effect_value; - break; - } - - case SE_HitChance: - { - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - spellbonuses.HitChanceEffect[e] = effect_value; - aabonuses.HitChanceEffect[e] = effect_value; - itembonuses.HitChanceEffect[e] = effect_value; - } - break; - } - - case SE_DamageModifier: - { - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - spellbonuses.DamageModifier[e] = effect_value; - aabonuses.DamageModifier[e] = effect_value; - itembonuses.DamageModifier[e] = effect_value; - } - break; - } - - case SE_DamageModifier2: - { - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - spellbonuses.DamageModifier2[e] = effect_value; - aabonuses.DamageModifier2[e] = effect_value; - itembonuses.DamageModifier2[e] = effect_value; - } - break; - } - - case SE_MinDamageModifier: - { - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - spellbonuses.MinDamageModifier[e] = effect_value; - aabonuses.MinDamageModifier[e] = effect_value; - itembonuses.MinDamageModifier[e] = effect_value; - } - break; - } - - case SE_StunResist: - spellbonuses.StunResist = effect_value; - aabonuses.StunResist = effect_value; - itembonuses.StunResist = effect_value; - break; - - case SE_ProcChance: - spellbonuses.ProcChanceSPA = effect_value; - aabonuses.ProcChanceSPA = effect_value; - itembonuses.ProcChanceSPA = effect_value; - break; - - case SE_ExtraAttackChance: - spellbonuses.ExtraAttackChance = effect_value; - aabonuses.ExtraAttackChance = effect_value; - itembonuses.ExtraAttackChance = effect_value; - break; - - case SE_PercentXPIncrease: - spellbonuses.XPRateMod = effect_value; - aabonuses.XPRateMod = effect_value; - itembonuses.XPRateMod = effect_value; - break; - - case SE_Flurry: - spellbonuses.FlurryChance = effect_value; - aabonuses.FlurryChance = effect_value; - itembonuses.FlurryChance = effect_value; - break; - - case SE_Accuracy: - { - spellbonuses.Accuracy[HIGHEST_SKILL+1] = effect_value; - itembonuses.Accuracy[HIGHEST_SKILL+1] = effect_value; - - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - aabonuses.Accuracy[e] = effect_value; - } - break; - } - - case SE_MaxHPChange: - spellbonuses.MaxHPChange = effect_value; - aabonuses.MaxHPChange = effect_value; - itembonuses.MaxHPChange = effect_value; - break; - - case SE_EndurancePool: - spellbonuses.Endurance = effect_value; - aabonuses.Endurance = effect_value; - itembonuses.Endurance = effect_value; - break; - - case SE_HealRate: - spellbonuses.HealRate = effect_value; - aabonuses.HealRate = effect_value; - itembonuses.HealRate = effect_value; - break; - - case SE_SkillDamageTaken: - { - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - spellbonuses.SkillDmgTaken[e] = effect_value; - aabonuses.SkillDmgTaken[e] = effect_value; - itembonuses.SkillDmgTaken[e] = effect_value; - - } - break; - } - - case SE_TriggerOnCast: - { - for(int e = 0; e < MAX_SPELL_TRIGGER; e++) - { - spellbonuses.SpellTriggers[e] = effect_value; - aabonuses.SpellTriggers[e] = effect_value; - itembonuses.SpellTriggers[e] = effect_value; - } - break; - } - - case SE_SpellCritChance: - spellbonuses.CriticalSpellChance = effect_value; - aabonuses.CriticalSpellChance = effect_value; - itembonuses.CriticalSpellChance = effect_value; - break; - - case SE_CriticalSpellChance: - spellbonuses.CriticalSpellChance = effect_value; - spellbonuses.SpellCritDmgIncrease = effect_value; - aabonuses.CriticalSpellChance = effect_value; - aabonuses.SpellCritDmgIncrease = effect_value; - itembonuses.CriticalSpellChance = effect_value; - itembonuses.SpellCritDmgIncrease = effect_value; - break; - - case SE_SpellCritDmgIncrease: - spellbonuses.SpellCritDmgIncrease = effect_value; - aabonuses.SpellCritDmgIncrease = effect_value; - itembonuses.SpellCritDmgIncrease = effect_value; - break; - - case SE_DotCritDmgIncrease: - spellbonuses.DotCritDmgIncrease = effect_value; - aabonuses.DotCritDmgIncrease = effect_value; - itembonuses.DotCritDmgIncrease = effect_value; - break; - - case SE_CriticalHealChance: - spellbonuses.CriticalHealChance = effect_value; - aabonuses.CriticalHealChance = effect_value; - itembonuses.CriticalHealChance = effect_value; - break; - - case SE_CriticalHealOverTime: - spellbonuses.CriticalHealOverTime = effect_value; - aabonuses.CriticalHealOverTime = effect_value; - itembonuses.CriticalHealOverTime = effect_value; - break; - - case SE_MitigateDamageShield: - spellbonuses.DSMitigationOffHand = effect_value; - itembonuses.DSMitigationOffHand = effect_value; - aabonuses.DSMitigationOffHand = effect_value; - break; - - case SE_CriticalDoTChance: - spellbonuses.CriticalDoTChance = effect_value; - aabonuses.CriticalDoTChance = effect_value; - itembonuses.CriticalDoTChance = effect_value; - break; - - case SE_ProcOnKillShot: - { - for(int e = 0; e < MAX_SPELL_TRIGGER*3; e=3) - { - spellbonuses.SpellOnKill[e] = effect_value; - spellbonuses.SpellOnKill[e+1] = effect_value; - spellbonuses.SpellOnKill[e+2] = effect_value; - - aabonuses.SpellOnKill[e] = effect_value; - aabonuses.SpellOnKill[e+1] = effect_value; - aabonuses.SpellOnKill[e+2] = effect_value; - - itembonuses.SpellOnKill[e] = effect_value; - itembonuses.SpellOnKill[e+1] = effect_value; - itembonuses.SpellOnKill[e+2] = effect_value; - } - break; - } - - /* - case SE_SpellOnDeath: - { - for(int e = 0; e < MAX_SPELL_TRIGGER; e=2) - { - spellbonuses.SpellOnDeath[e] = SPELL_UNKNOWN; - spellbonuses.SpellOnDeath[e+1] = effect_value; - } - break; - } - */ - - case SE_CriticalDamageMob: - { - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - spellbonuses.CritDmgMob[e] = effect_value; - aabonuses.CritDmgMob[e] = effect_value; - itembonuses.CritDmgMob[e] = effect_value; - } - break; - } - - case SE_SkillDamageAmount: - { - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - spellbonuses.SkillDamageAmount[e] = effect_value; - aabonuses.SkillDamageAmount[e] = effect_value; - itembonuses.SkillDamageAmount[e] = effect_value; - } - break; - } - - case SE_IncreaseBlockChance: - spellbonuses.IncreaseBlockChance = effect_value; - aabonuses.IncreaseBlockChance = effect_value; - itembonuses.IncreaseBlockChance = effect_value; - break; - - case SE_PersistantCasting: - spellbonuses.PersistantCasting = effect_value; - itembonuses.PersistantCasting = effect_value; - aabonuses.PersistantCasting = effect_value; - break; - - case SE_ImmuneFleeing: - spellbonuses.ImmuneToFlee = false; - break; - - case SE_DelayDeath: - spellbonuses.DelayDeath = effect_value; - aabonuses.DelayDeath = effect_value; - itembonuses.DelayDeath = effect_value; - break; - - case SE_SpellProcChance: - spellbonuses.SpellProcChance = effect_value; - itembonuses.SpellProcChance = effect_value; - aabonuses.SpellProcChance = effect_value; - break; - - case SE_CharmBreakChance: - spellbonuses.CharmBreakChance = effect_value; - aabonuses.CharmBreakChance = effect_value; - itembonuses.CharmBreakChance = effect_value; - break; - - case SE_BardSongRange: - spellbonuses.SongRange = effect_value; - aabonuses.SongRange = effect_value; - itembonuses.SongRange = effect_value; - break; - - case SE_SkillDamageAmount2: - { - for(int e = 0; e < HIGHEST_SKILL+1; e++) - { - spellbonuses.SkillDamageAmount2[e] = effect_value; - aabonuses.SkillDamageAmount2[e] = effect_value; - itembonuses.SkillDamageAmount2[e] = effect_value; - } - break; - } - - case SE_NegateAttacks: - spellbonuses.NegateAttacks[0] = effect_value; - spellbonuses.NegateAttacks[1] = effect_value; - break; - - case SE_MitigateMeleeDamage: - spellbonuses.MitigateMeleeRune[0] = effect_value; - spellbonuses.MitigateMeleeRune[1] = -1; - break; - - case SE_MeleeThresholdGuard: - spellbonuses.MeleeThresholdGuard[0] = effect_value; - spellbonuses.MeleeThresholdGuard[1] = -1; - spellbonuses.MeleeThresholdGuard[1] = effect_value; - break; - - case SE_SpellThresholdGuard: - spellbonuses.SpellThresholdGuard[0] = effect_value; - spellbonuses.SpellThresholdGuard[1] = -1; - spellbonuses.SpellThresholdGuard[1] = effect_value; - break; - - case SE_MitigateSpellDamage: - spellbonuses.MitigateSpellRune[0] = effect_value; - spellbonuses.MitigateSpellRune[1] = -1; - break; - - case SE_MitigateDotDamage: - spellbonuses.MitigateDotRune[0] = effect_value; - spellbonuses.MitigateDotRune[1] = -1; - break; - - case SE_ManaAbsorbPercentDamage: - spellbonuses.ManaAbsorbPercentDamage[0] = effect_value; - spellbonuses.ManaAbsorbPercentDamage[1] = -1; - break; - - case SE_ShieldBlock: - spellbonuses.ShieldBlock = effect_value; - aabonuses.ShieldBlock = effect_value; - itembonuses.ShieldBlock = effect_value; - - case SE_BlockBehind: - spellbonuses.BlockBehind = effect_value; - aabonuses.BlockBehind = effect_value; - itembonuses.BlockBehind = effect_value; - break; - - case SE_Fear: - spellbonuses.IsFeared = false; - break; - - case SE_FrontalStunResist: - spellbonuses.FrontalStunResist = effect_value; - aabonuses.FrontalStunResist = effect_value; - itembonuses.FrontalStunResist = effect_value; - break; - - case SE_ImprovedBindWound: - aabonuses.BindWound = effect_value; - itembonuses.BindWound = effect_value; - spellbonuses.BindWound = effect_value; - break; - - case SE_MaxBindWound: - spellbonuses.MaxBindWound = effect_value; - aabonuses.MaxBindWound = effect_value; - itembonuses.MaxBindWound = effect_value; - break; - - case SE_BaseMovementSpeed: - spellbonuses.BaseMovementSpeed = effect_value; - aabonuses.BaseMovementSpeed = effect_value; - itembonuses.BaseMovementSpeed = effect_value; - break; - - case SE_IncreaseRunSpeedCap: - itembonuses.IncreaseRunSpeedCap = effect_value; - aabonuses.IncreaseRunSpeedCap = effect_value; - spellbonuses.IncreaseRunSpeedCap = effect_value; - break; - - case SE_DoubleSpecialAttack: - spellbonuses.DoubleSpecialAttack = effect_value; - aabonuses.DoubleSpecialAttack = effect_value; - itembonuses.DoubleSpecialAttack = effect_value; - break; - - case SE_TripleBackstab: - spellbonuses.TripleBackstab = effect_value; - aabonuses.TripleBackstab = effect_value; - itembonuses.TripleBackstab = effect_value; - break; - - case SE_FrontalBackstabMinDmg: - spellbonuses.FrontalBackstabMinDmg = false; - break; - - case SE_FrontalBackstabChance: - spellbonuses.FrontalBackstabChance = effect_value; - aabonuses.FrontalBackstabChance = effect_value; - itembonuses.FrontalBackstabChance = effect_value; - break; - - case SE_ConsumeProjectile: - spellbonuses.ConsumeProjectile = effect_value; - aabonuses.ConsumeProjectile = effect_value; - itembonuses.ConsumeProjectile = effect_value; - break; - - case SE_ForageAdditionalItems: - spellbonuses.ForageAdditionalItems = effect_value; - aabonuses.ForageAdditionalItems = effect_value; - itembonuses.ForageAdditionalItems = effect_value; - break; - - case SE_Salvage: - spellbonuses.SalvageChance = effect_value; - aabonuses.SalvageChance = effect_value; - itembonuses.SalvageChance = effect_value; - break; - - case SE_ArcheryDamageModifier: - spellbonuses.ArcheryDamageModifier = effect_value; - aabonuses.ArcheryDamageModifier = effect_value; - itembonuses.ArcheryDamageModifier = effect_value; - break; - - case SE_SecondaryDmgInc: - spellbonuses.SecondaryDmgInc = false; - aabonuses.SecondaryDmgInc = false; - itembonuses.SecondaryDmgInc = false; - break; - - case SE_StrikeThrough: - spellbonuses.StrikeThrough = effect_value; - aabonuses.StrikeThrough = effect_value; - itembonuses.StrikeThrough = effect_value; - break; - - case SE_StrikeThrough2: - spellbonuses.StrikeThrough = effect_value; - aabonuses.StrikeThrough = effect_value; - itembonuses.StrikeThrough = effect_value; - break; - - case SE_GiveDoubleAttack: - spellbonuses.GiveDoubleAttack = effect_value; - aabonuses.GiveDoubleAttack = effect_value; - itembonuses.GiveDoubleAttack = effect_value; - break; - - case SE_PetCriticalHit: - spellbonuses.PetCriticalHit = effect_value; - aabonuses.PetCriticalHit = effect_value; - itembonuses.PetCriticalHit = effect_value; - break; - - case SE_CombatStability: - spellbonuses.CombatStability = effect_value; - aabonuses.CombatStability = effect_value; - itembonuses.CombatStability = effect_value; - break; - - case SE_PetAvoidance: - spellbonuses.PetAvoidance = effect_value; - aabonuses.PetAvoidance = effect_value; - itembonuses.PetAvoidance = effect_value; - break; - - case SE_Ambidexterity: - spellbonuses.Ambidexterity = effect_value; - aabonuses.Ambidexterity = effect_value; - itembonuses.Ambidexterity = effect_value; - break; - - case SE_PetMaxHP: - spellbonuses.PetMaxHP = effect_value; - aabonuses.PetMaxHP = effect_value; - itembonuses.PetMaxHP = effect_value; - break; - - case SE_PetFlurry: - spellbonuses.PetFlurry = effect_value; - aabonuses.PetFlurry = effect_value; - itembonuses.PetFlurry = effect_value; - break; - - case SE_GivePetGroupTarget: - spellbonuses.GivePetGroupTarget = false; - aabonuses.GivePetGroupTarget = false; - itembonuses.GivePetGroupTarget = false; - break; - - case SE_PetMeleeMitigation: - spellbonuses.PetMeleeMitigation = effect_value; - itembonuses.PetMeleeMitigation = effect_value; - aabonuses.PetMeleeMitigation = effect_value; - break; - - case SE_RootBreakChance: - spellbonuses.RootBreakChance = effect_value; - aabonuses.RootBreakChance = effect_value; - itembonuses.RootBreakChance = effect_value; - break; - - case SE_ChannelChanceItems: - spellbonuses.ChannelChanceItems = effect_value; - aabonuses.ChannelChanceItems = effect_value; - itembonuses.ChannelChanceItems = effect_value; - break; - - case SE_ChannelChanceSpells: - spellbonuses.ChannelChanceSpells = effect_value; - aabonuses.ChannelChanceSpells = effect_value; - itembonuses.ChannelChanceSpells = effect_value; - break; - - case SE_UnfailingDivinity: - spellbonuses.UnfailingDivinity = effect_value; - aabonuses.UnfailingDivinity = effect_value; - itembonuses.UnfailingDivinity = effect_value; - break; - - case SE_ItemHPRegenCapIncrease: - spellbonuses.ItemHPRegenCap = effect_value; - aabonuses.ItemHPRegenCap = effect_value; - itembonuses.ItemHPRegenCap = effect_value; - break; - - case SE_OffhandRiposteFail: - spellbonuses.OffhandRiposteFail = effect_value; - aabonuses.OffhandRiposteFail = effect_value; - itembonuses.OffhandRiposteFail = effect_value; - break; - - case SE_ItemAttackCapIncrease: - aabonuses.ItemATKCap = effect_value; - itembonuses.ItemATKCap = effect_value; - spellbonuses.ItemATKCap = effect_value; - break; - - case SE_SpellEffectResistChance: - { - for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2) - { - spellbonuses.SEResist[e] = effect_value; - spellbonuses.SEResist[e+1] = effect_value; - } - break; - } - - case SE_MasteryofPast: - spellbonuses.MasteryofPast = effect_value; - aabonuses.MasteryofPast = effect_value; - itembonuses.MasteryofPast = effect_value; - break; - - case SE_DoubleRiposte: - spellbonuses.DoubleRiposte = effect_value; - itembonuses.DoubleRiposte = effect_value; - aabonuses.DoubleRiposte = effect_value; - break; - - case SE_GiveDoubleRiposte: - spellbonuses.GiveDoubleRiposte[0] = effect_value; - itembonuses.GiveDoubleRiposte[0] = effect_value; - aabonuses.GiveDoubleRiposte[0] = effect_value; - break; - - case SE_SlayUndead: - spellbonuses.SlayUndead[0] = effect_value; - spellbonuses.SlayUndead[1] = effect_value; - itembonuses.SlayUndead[0] = effect_value; - itembonuses.SlayUndead[1] = effect_value; - aabonuses.SlayUndead[0] = effect_value; - aabonuses.SlayUndead[1] = effect_value; - break; - - case SE_DoubleRangedAttack: - spellbonuses.DoubleRangedAttack = effect_value; - aabonuses.DoubleRangedAttack = effect_value; - itembonuses.DoubleRangedAttack = effect_value; - break; - - case SE_ShieldEquipHateMod: - spellbonuses.ShieldEquipHateMod = effect_value; - aabonuses.ShieldEquipHateMod = effect_value; - itembonuses.ShieldEquipHateMod = effect_value; - break; - - case SE_ShieldEquipDmgMod: - spellbonuses.ShieldEquipDmgMod[0] = effect_value; - spellbonuses.ShieldEquipDmgMod[1] = effect_value; - aabonuses.ShieldEquipDmgMod[0] = effect_value; - aabonuses.ShieldEquipDmgMod[1] = effect_value; - itembonuses.ShieldEquipDmgMod[0] = effect_value; - itembonuses.ShieldEquipDmgMod[1] = effect_value; - break; - - case SE_TriggerMeleeThreshold: - spellbonuses.TriggerMeleeThreshold[0] = effect_value; - spellbonuses.TriggerMeleeThreshold[1] = effect_value; - spellbonuses.TriggerMeleeThreshold[2] = effect_value; - break; - - case SE_TriggerSpellThreshold: - spellbonuses.TriggerSpellThreshold[0] = effect_value; - spellbonuses.TriggerSpellThreshold[1] = effect_value; - spellbonuses.TriggerSpellThreshold[2] = effect_value; - break; - - case SE_DivineAura: - spellbonuses.DivineAura = false; - break; - - case SE_StunBashChance: - spellbonuses.StunBashChance = effect_value; - itembonuses.StunBashChance = effect_value; - aabonuses.StunBashChance = effect_value; - break; - - case SE_IncreaseChanceMemwipe: - spellbonuses.IncreaseChanceMemwipe = effect_value; - itembonuses.IncreaseChanceMemwipe = effect_value; - aabonuses.IncreaseChanceMemwipe = effect_value; - break; - - case SE_CriticalMend: - spellbonuses.CriticalMend = effect_value; - itembonuses.CriticalMend = effect_value; - aabonuses.CriticalMend = effect_value; - break; - - case SE_DistanceRemoval: - spellbonuses.DistanceRemoval = effect_value; - break; - - case SE_ImprovedTaunt: - spellbonuses.ImprovedTaunt[0] = effect_value; - spellbonuses.ImprovedTaunt[1] = effect_value; - spellbonuses.ImprovedTaunt[2] = -1; - break; - - case SE_FrenziedDevastation: - spellbonuses.FrenziedDevastation = effect_value; - aabonuses.FrenziedDevastation = effect_value; - itembonuses.FrenziedDevastation = effect_value; - break; - - case SE_Root: - spellbonuses.Root[0] = effect_value; - spellbonuses.Root[1] = -1; - break; - - case SE_Rune: - spellbonuses.MeleeRune[0] = effect_value; - spellbonuses.MeleeRune[1] = -1; - break; - - case SE_AbsorbMagicAtt: - spellbonuses.AbsorbMagicAtt[0] = effect_value; - spellbonuses.AbsorbMagicAtt[1] = -1; - break; - - case SE_Berserk: - spellbonuses.BerserkSPA = false; - aabonuses.BerserkSPA = false; - itembonuses.BerserkSPA = false; - break; - - case SE_Vampirism: - spellbonuses.Vampirism = effect_value; - aabonuses.Vampirism = effect_value; - itembonuses.Vampirism = effect_value; - break; - - case SE_Metabolism: - spellbonuses.Metabolism = effect_value; - aabonuses.Metabolism = effect_value; - itembonuses.Metabolism = effect_value; - break; - - case SE_ImprovedReclaimEnergy: - spellbonuses.ImprovedReclaimEnergy = effect_value; - aabonuses.ImprovedReclaimEnergy = effect_value; - itembonuses.ImprovedReclaimEnergy = effect_value; - break; - - case SE_HeadShot: - spellbonuses.HeadShot[0] = effect_value; - aabonuses.HeadShot[0] = effect_value; - itembonuses.HeadShot[0] = effect_value; - spellbonuses.HeadShot[1] = effect_value; - aabonuses.HeadShot[1] = effect_value; - itembonuses.HeadShot[1] = effect_value; - break; - - case SE_HeadShotLevel: - spellbonuses.HSLevel = effect_value; - aabonuses.HSLevel = effect_value; - itembonuses.HSLevel = effect_value; - break; - - case SE_Assassinate: - spellbonuses.Assassinate[0] = effect_value; - aabonuses.Assassinate[0] = effect_value; - itembonuses.Assassinate[0] = effect_value; - spellbonuses.Assassinate[1] = effect_value; - aabonuses.Assassinate[1] = effect_value; - itembonuses.Assassinate[1] = effect_value; - break; - - case SE_AssassinateLevel: - spellbonuses.AssassinateLevel = effect_value; - aabonuses.AssassinateLevel = effect_value; - itembonuses.AssassinateLevel = effect_value; - break; - - case SE_FinishingBlow: - spellbonuses.FinishingBlow[0] = effect_value; - aabonuses.FinishingBlow[0] = effect_value; - itembonuses.FinishingBlow[0] = effect_value; - spellbonuses.FinishingBlow[1] = effect_value; - aabonuses.FinishingBlow[1] = effect_value; - itembonuses.FinishingBlow[1] = effect_value; - break; - - case SE_FinishingBlowLvl: - spellbonuses.FinishingBlowLvl[0] = effect_value; - aabonuses.FinishingBlowLvl[0] = effect_value; - itembonuses.FinishingBlowLvl[0] = effect_value; - spellbonuses.FinishingBlowLvl[1] = effect_value; - aabonuses.FinishingBlowLvl[1] = effect_value; - itembonuses.FinishingBlowLvl[1] = effect_value; - break; - - } - } - } -} - diff --git a/zone/groupsx.cpp b/zone/groupsx.cpp deleted file mode 100644 index 6aee5f38b..000000000 --- a/zone/groupsx.cpp +++ /dev/null @@ -1,2194 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include "../common/debug.h" -#include "masterentity.h" -#include "NpcAI.h" -#include "../common/packet_functions.h" -#include "../common/packet_dump.h" -#include "../common/StringUtil.h" -#include "worldserver.h" -extern EntityList entity_list; -extern WorldServer worldserver; - -// -// Xorlac: This will need proper synchronization to make it work correctly. -// Also, should investigate client ack for packet to ensure proper synch. -// - -/* - -note about how groups work: -A group contains 2 list, a list of pointers to members and a -list of member names. All members of a group should have their -name in the membername array, wether they are in the zone or not. -Only members in this zone will have non-null pointers in the -members array. - -*/ - -//create a group which should allready exist in the database -Group::Group(uint32 gid) -: GroupIDConsumer(gid) -{ - leader = nullptr; - memset(members,0,sizeof(Mob*) * MAX_GROUP_MEMBERS); - AssistTargetID = 0; - TankTargetID = 0; - PullerTargetID = 0; - - memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct)); - uint32 i; - for(i=0;iSetGrouped(true); - SetLeader(leader); - AssistTargetID = 0; - TankTargetID = 0; - PullerTargetID = 0; - memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct)); - uint32 i; - for(i=0;iGetName()); - - if(leader->IsClient()) - strcpy(leader->CastToClient()->GetPP().groupMembers[0],leader->GetName()); - - for(int i = 0; i < MAX_MARKED_NPCS; ++i) - MarkedNPCs[i] = 0; - - NPCMarkerID = 0; -} - -Group::~Group() -{ - for(int i = 0; i < MAX_MARKED_NPCS; ++i) - if(MarkedNPCs[i]) - { - Mob* m = entity_list.GetMob(MarkedNPCs[i]); - if(m) - m->IsTargeted(-1); - } -} - -//Cofruben:Split money used in OP_Split. -//Rewritten by Father Nitwit -void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter) { - //avoid unneeded work - if(copper == 0 && silver == 0 && gold == 0 && platinum == 0) - return; - - uint32 i; - uint8 membercount = 0; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] != nullptr) { - membercount++; - } - } - - if (membercount == 0) - return; - - uint32 mod; - //try to handle round off error a little better - if(membercount > 1) { - mod = platinum % membercount; - if((mod) > 0) { - platinum -= mod; - gold += 10 * mod; - } - mod = gold % membercount; - if((mod) > 0) { - gold -= mod; - silver += 10 * mod; - } - mod = silver % membercount; - if((mod) > 0) { - silver -= mod; - copper += 10 * mod; - } - } - - //calculate the splits - //We can still round off copper pieces, but I dont care - uint32 sc; - uint32 cpsplit = copper / membercount; - sc = copper % membercount; - uint32 spsplit = silver / membercount; - uint32 gpsplit = gold / membercount; - uint32 ppsplit = platinum / membercount; - - char buf[128]; - buf[63] = '\0'; - std::string msg = "You receive"; - bool one = false; - - if(ppsplit > 0) { - snprintf(buf, 63, " %u platinum", ppsplit); - msg += buf; - one = true; - } - if(gpsplit > 0) { - if(one) - msg += ","; - snprintf(buf, 63, " %u gold", gpsplit); - msg += buf; - one = true; - } - if(spsplit > 0) { - if(one) - msg += ","; - snprintf(buf, 63, " %u silver", spsplit); - msg += buf; - one = true; - } - if(cpsplit > 0) { - if(one) - msg += ","; - //this message is not 100% accurate for the splitter - //if they are receiving any roundoff - snprintf(buf, 63, " %u copper", cpsplit); - msg += buf; - one = true; - } - msg += " as your split"; - - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] != nullptr && members[i]->IsClient()) { // If Group Member is Client - Client *c = members[i]->CastToClient(); - //I could not get MoneyOnCorpse to work, so we use this - c->AddMoneyToPP(cpsplit, spsplit, gpsplit, ppsplit, true); - - c->Message(2, msg.c_str()); - } - } -} - -bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 CharacterID) -{ - bool InZone = true; - bool ismerc = false; - - // This method should either be passed a Mob*, if the new member is in this zone, or a nullptr Mob* - // and the name and CharacterID of the new member, if they are out of zone. - // - if(!newmember && !NewMemberName) - return false; - - if(GroupCount() >= MAX_GROUP_MEMBERS) //Sanity check for merging groups together. - return false; - - if(!newmember) - InZone = false; - else - { - NewMemberName = newmember->GetCleanName(); - - if(newmember->IsClient()) - CharacterID = newmember->CastToClient()->CharacterID(); - if(newmember->IsMerc()) - { - Client* owner = newmember->CastToMerc()->GetMercOwner(); - if(owner) - { - CharacterID = owner->CastToClient()->CharacterID(); - NewMemberName = newmember->GetName(); - ismerc = true; - } - } - } - - uint32 i = 0; - - // See if they are already in the group - // - for (i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(!strcasecmp(membername[i], NewMemberName)) - return false; - - // Put them in the group - for (i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if (membername[i][0] == '\0') - { - if(InZone) - members[i] = newmember; - - break; - } - } - - if (i == MAX_GROUP_MEMBERS) - return false; - - strcpy(membername[i], NewMemberName); - MemberRoles[i] = 0; - - int x=1; - - //build the template join packet - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); - GroupJoin_Struct* gj = (GroupJoin_Struct*) outapp->pBuffer; - strcpy(gj->membername, NewMemberName); - gj->action = groupActJoin; - - gj->leader_aas = LeaderAbilities; - - for (i = 0;i < MAX_GROUP_MEMBERS; i++) { - if (members[i] != nullptr && members[i] != newmember) { - //fill in group join & send it - if(members[i]->IsMerc()) - { - strcpy(gj->yourname, members[i]->GetName()); - } - else - { - strcpy(gj->yourname, members[i]->GetCleanName()); - } - if(members[i]->IsClient()) { - members[i]->CastToClient()->QueuePacket(outapp); - - //put new member into existing person's list - strcpy(members[i]->CastToClient()->GetPP().groupMembers[this->GroupCount()-1], NewMemberName); - } - - //put this existing person into the new member's list - if(InZone && newmember->IsClient()) { - if(IsLeader(members[i])) - strcpy(newmember->CastToClient()->GetPP().groupMembers[0], members[i]->GetCleanName()); - else { - strcpy(newmember->CastToClient()->GetPP().groupMembers[x], members[i]->GetCleanName()); - x++; - } - } - } - } - - if(InZone) - { - //put new member in his own list. - newmember->SetGrouped(true); - - if(newmember->IsClient()) - { - strcpy(newmember->CastToClient()->GetPP().groupMembers[x], NewMemberName); - newmember->CastToClient()->Save(); - database.SetGroupID(NewMemberName, GetID(), newmember->CastToClient()->CharacterID(), false); - SendMarkedNPCsToMember(newmember->CastToClient()); - - NotifyMainTank(newmember->CastToClient(), 1); - NotifyMainAssist(newmember->CastToClient(), 1); - NotifyPuller(newmember->CastToClient(), 1); - } - - if(newmember->IsMerc()) - { - Client* owner = newmember->CastToMerc()->GetMercOwner(); - if(owner) - { - database.SetGroupID(newmember->GetName(), GetID(), owner->CharacterID(), true); - } - } -#ifdef BOTS - for (i = 0;i < MAX_GROUP_MEMBERS; i++) { - if (members[i] != nullptr && members[i]->IsBot()) { - members[i]->CastToBot()->CalcChanceToCast(); - } - } -#endif //BOTS - } - else - database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc); - - safe_delete(outapp); - - return true; -} - -void Group::AddMember(const char *NewMemberName) -{ - // This method should be called when both the new member and the group leader are in a different zone to this one. - // - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(!strcasecmp(membername[i], NewMemberName)) - { - return; - } - - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if (membername[i][0] == '\0') - { - strcpy(membername[i], NewMemberName); - MemberRoles[i] = 0; - break; - } - } -} - - -void Group::QueuePacket(const EQApplicationPacket *app, bool ack_req) -{ - uint32 i; - for(i = 0; i < MAX_GROUP_MEMBERS; i++) - if(members[i] && members[i]->IsClient()) - members[i]->CastToClient()->QueuePacket(app, ack_req); -} - -// solar: sends the rest of the group's hps to member. this is useful when -// someone first joins a group, but otherwise there shouldn't be a need to -// call it -void Group::SendHPPacketsTo(Mob *member) -{ - if(member && member->IsClient()) - { - EQApplicationPacket hpapp; - EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); - - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) - { - if(members[i] && members[i] != member) - { - members[i]->CreateHPPacket(&hpapp); - member->CastToClient()->QueuePacket(&hpapp, false); - if(member->CastToClient()->GetClientVersion() >= EQClientSoD) - { - outapp.SetOpcode(OP_MobManaUpdate); - MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; - mmus->spawn_id = members[i]->GetID(); - mmus->mana = members[i]->GetManaPercent(); - member->CastToClient()->QueuePacket(&outapp, false); - MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; - outapp.SetOpcode(OP_MobEnduranceUpdate); - meus->endurance = members[i]->GetEndurancePercent(); - member->CastToClient()->QueuePacket(&outapp, false); - } - } - } - } -} - -void Group::SendHPPacketsFrom(Mob *member) -{ - EQApplicationPacket hp_app; - if(!member) - return; - - member->CreateHPPacket(&hp_app); - EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct)); - - uint32 i; - for(i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(members[i] && members[i] != member && members[i]->IsClient()) - { - members[i]->CastToClient()->QueuePacket(&hp_app); - if(members[i]->CastToClient()->GetClientVersion() >= EQClientSoD) - { - outapp.SetOpcode(OP_MobManaUpdate); - MobManaUpdate_Struct *mmus = (MobManaUpdate_Struct *)outapp.pBuffer; - mmus->spawn_id = member->GetID(); - mmus->mana = member->GetManaPercent(); - members[i]->CastToClient()->QueuePacket(&outapp, false); - MobEnduranceUpdate_Struct *meus = (MobEnduranceUpdate_Struct *)outapp.pBuffer; - outapp.SetOpcode(OP_MobEnduranceUpdate); - meus->endurance = member->GetEndurancePercent(); - members[i]->CastToClient()->QueuePacket(&outapp, false); - } - } - } -} - -//updates a group member's client pointer when they zone in -//if the group was in the zone allready -bool Group::UpdatePlayer(Mob* update){ - - VerifyGroup(); - - uint32 i=0; - if(update->IsClient()) { - //update their player profile - PlayerProfile_Struct &pp = update->CastToClient()->GetPP(); - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(membername[i][0] == '\0') - memset(pp.groupMembers[i], 0, 64); - else - strn0cpy(pp.groupMembers[i], membername[i], 64); - } - if(IsNPCMarker(update->CastToClient())) - { - NPCMarkerID = update->GetID(); - SendLeadershipAAUpdate(); - } - } - - for (i = 0; i < MAX_GROUP_MEMBERS; i++) - { - if (!strcasecmp(membername[i],update->GetName())) - { - members[i] = update; - members[i]->SetGrouped(true); - return true; - } - } - return false; -} - - -void Group::MemberZoned(Mob* removemob) { - uint32 i; - - if (removemob == nullptr) - return; - - if(removemob == GetLeader()) - SetLeader(nullptr); - - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] == removemob) { - members[i] = nullptr; - //should NOT clear the name, it is used for world communication. - break; - } -#ifdef BOTS - if (members[i] != nullptr && members[i]->IsBot()) { - members[i]->CastToBot()->CalcChanceToCast(); - } -#endif //BOTS - } - if(removemob->IsClient() && HasRole(removemob, RoleAssist)) - SetGroupAssistTarget(0); - - if(removemob->IsClient() && HasRole(removemob, RoleTank)) - SetGroupTankTarget(0); - - if(removemob->IsClient() && HasRole(removemob, RolePuller)) - SetGroupPullerTarget(0); -} - -bool Group::DelMemberOOZ(const char *Name) { - - if(!Name) return false; - - // If a member out of zone has disbanded, clear out their name. - // - for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(!strcasecmp(Name, membername[i])) - // This shouldn't be called if the member is in this zone. - if(!members[i]) { - if(!strncmp(GetLeaderName(), Name, 64)) - { - //TODO: Transfer leadership if leader disbands OOZ. - UpdateGroupAAs(); - } - - memset(membername[i], 0, 64); - MemberRoles[i] = 0; - if(GroupCount() < 3) - { - UnDelegateMarkNPC(NPCMarkerName.c_str()); - if(GetLeader() && GetLeader()->IsClient() && GetLeader()->CastToClient()->GetClientVersion() < EQClientSoD) { - UnDelegateMainAssist(MainAssistName.c_str()); - } - ClearAllNPCMarks(); - } - return true; - } - } - - return false; -} - -bool Group::DelMember(Mob* oldmember,bool ignoresender) -{ - if (oldmember == nullptr){ - return false; - } - - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] == oldmember) { - members[i] = nullptr; - membername[i][0] = '\0'; - memset(membername[i],0,64); - MemberRoles[i] = 0; - break; - } - } - - //handle leader quitting group gracefully - if (oldmember == GetLeader() && GroupCount() >= 2) { - for(uint32 nl = 0; nl < MAX_GROUP_MEMBERS; nl++) { - if(members[nl]) { - ChangeLeader(members[nl]); - break; - } - } - } - - ServerPacket* pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); - ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; - gl->gid = GetID(); - gl->zoneid = zone->GetZoneID(); - gl->instance_id = zone->GetInstanceID(); - strcpy(gl->member_name, oldmember->GetName()); - worldserver.SendPacket(pack); - safe_delete(pack); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); - GroupJoin_Struct* gu = (GroupJoin_Struct*) outapp->pBuffer; - gu->action = groupActLeave; - strcpy(gu->membername, oldmember->GetCleanName()); - strcpy(gu->yourname, oldmember->GetCleanName()); - - gu->leader_aas = LeaderAbilities; - - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] == nullptr) { - //if (DEBUG>=5) LogFile->write(EQEMuLog::Debug, "Group::DelMember() null member at slot %i", i); - continue; - } - if (members[i] != oldmember) { - strcpy(gu->yourname, members[i]->GetCleanName()); - if(members[i]->IsClient()) - members[i]->CastToClient()->QueuePacket(outapp); - } -#ifdef BOTS - if (members[i] != nullptr && members[i]->IsBot()) { - members[i]->CastToBot()->CalcChanceToCast(); - } -#endif //BOTS - } - - if (!ignoresender) { - strcpy(gu->yourname,oldmember->GetCleanName()); - strcpy(gu->membername,oldmember->GetCleanName()); - gu->action = groupActLeave; - - if(oldmember->IsClient()) - oldmember->CastToClient()->QueuePacket(outapp); - } - - if(oldmember->IsClient()) - database.SetGroupID(oldmember->GetCleanName(), 0, oldmember->CastToClient()->CharacterID()); - - oldmember->SetGrouped(false); - disbandcheck = true; - - safe_delete(outapp); - - if(HasRole(oldmember, RoleTank)) - { - SetGroupTankTarget(0); - UnDelegateMainTank(oldmember->GetName()); - } - - if(HasRole(oldmember, RoleAssist)) - { - SetGroupAssistTarget(0); - UnDelegateMainAssist(oldmember->GetName()); - } - - if(HasRole(oldmember, RolePuller)) - { - SetGroupPullerTarget(0); - UnDelegatePuller(oldmember->GetName()); - } - - if(oldmember->IsClient()) - SendMarkedNPCsToMember(oldmember->CastToClient(), true); - - if(GroupCount() < 3) - { - UnDelegateMarkNPC(NPCMarkerName.c_str()); - if(GetLeader() && GetLeader()->IsClient() && GetLeader()->CastToClient()->GetClientVersion() < EQClientSoD) { - UnDelegateMainAssist(MainAssistName.c_str()); - } - ClearAllNPCMarks(); - } - - return true; -} - -// does the caster + group -void Group::CastGroupSpell(Mob* caster, uint16 spell_id) { - uint32 z; - float range, distance; - - if(!caster) - return; - - castspell = true; - range = caster->GetAOERange(spell_id); - - float range2 = range*range; - -// caster->SpellOnTarget(spell_id, caster); - - for(z=0; z < MAX_GROUP_MEMBERS; z++) - { - if(members[z] == caster) { - caster->SpellOnTarget(spell_id, caster); -#ifdef GROUP_BUFF_PETS - if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) - caster->SpellOnTarget(spell_id, caster->GetPet()); -#endif - } - else if(members[z] != nullptr) - { - distance = caster->DistNoRoot(*members[z]); - if(distance <= range2) { - caster->SpellOnTarget(spell_id, members[z]); -#ifdef GROUP_BUFF_PETS - if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) - caster->SpellOnTarget(spell_id, members[z]->GetPet()); -#endif - } else - _log(SPELLS__CASTING, "Group spell: %s is out of range %f at distance %f from %s", members[z]->GetName(), range, distance, caster->GetName()); - } - } - - castspell = false; - disbandcheck = true; -} - -// does the caster + group -void Group::GroupBardPulse(Mob* caster, uint16 spell_id) { - uint32 z; - float range, distance; - - if(!caster) - return; - - castspell = true; - range = caster->GetAOERange(spell_id); - - float range2 = range*range; - - for(z=0; z < MAX_GROUP_MEMBERS; z++) { - if(members[z] == caster) { - caster->BardPulse(spell_id, caster); -#ifdef GROUP_BUFF_PETS - if(caster->GetPet() && caster->HasPetAffinity() && !caster->GetPet()->IsCharmed()) - caster->BardPulse(spell_id, caster->GetPet()); -#endif - } - else if(members[z] != nullptr) - { - distance = caster->DistNoRoot(*members[z]); - if(distance <= range2) { - members[z]->BardPulse(spell_id, caster); -#ifdef GROUP_BUFF_PETS - if(members[z]->GetPet() && members[z]->HasPetAffinity() && !members[z]->GetPet()->IsCharmed()) - members[z]->GetPet()->BardPulse(spell_id, caster); -#endif - } else - _log(SPELLS__BARDS, "Group bard pulse: %s is out of range %f at distance %f from %s", members[z]->GetName(), range, distance, caster->GetName()); - } - } -} - -bool Group::IsGroupMember(Mob* client) -{ - bool Result = false; - - if(client) { - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] == client) - Result = true; - } - } - - return Result; -} - -bool Group::IsGroupMember(const char *Name) -{ - if(Name) - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) - if((strlen(Name) == strlen(membername[i])) && !strncmp(membername[i], Name, strlen(Name))) - return true; - - return false; -} - -void Group::GroupMessage(Mob* sender, uint8 language, uint8 lang_skill, const char* message) { - uint32 i; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(!members[i]) - continue; - - if (members[i]->IsClient() && members[i]->CastToClient()->GetFilter(FilterGroupChat)!=0) - members[i]->CastToClient()->ChannelMessageSend(sender->GetName(),members[i]->GetName(),2,language,lang_skill,message); - } - - ServerPacket* pack = new ServerPacket(ServerOP_OOZGroupMessage, sizeof(ServerGroupChannelMessage_Struct) + strlen(message) + 1); - ServerGroupChannelMessage_Struct* gcm = (ServerGroupChannelMessage_Struct*)pack->pBuffer; - gcm->zoneid = zone->GetZoneID(); - gcm->groupid = GetID(); - gcm->instanceid = zone->GetInstanceID(); - strcpy(gcm->from, sender->GetName()); - strcpy(gcm->message, message); - worldserver.SendPacket(pack); - safe_delete(pack); -} - -uint32 Group::GetTotalGroupDamage(Mob* other) { - uint32 total = 0; - - uint32 i; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(!members[i]) - continue; - if (other->CheckAggro(members[i])) - total += other->GetHateAmount(members[i],true); - } - return total; -} - -void Group::DisbandGroup() { - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupUpdate_Struct)); - - GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer; - gu->action = groupActDisband; - - Client *Leader = nullptr; - - uint32 i; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] == nullptr) { - continue; - } - - if (members[i]->IsClient()) { - if(IsLeader(members[i])) - Leader = members[i]->CastToClient(); - - strcpy(gu->yourname, members[i]->GetName()); - database.SetGroupID(members[i]->GetName(), 0, members[i]->CastToClient()->CharacterID()); - members[i]->CastToClient()->QueuePacket(outapp); - SendMarkedNPCsToMember(members[i]->CastToClient(), true); - - } - - members[i]->SetGrouped(false); - members[i] = nullptr; - membername[i][0] = '\0'; - } - - ClearAllNPCMarks(); - - ServerPacket* pack = new ServerPacket(ServerOP_DisbandGroup, sizeof(ServerDisbandGroup_Struct)); - ServerDisbandGroup_Struct* dg = (ServerDisbandGroup_Struct*)pack->pBuffer; - dg->zoneid = zone->GetZoneID(); - dg->groupid = GetID(); - dg->instance_id = zone->GetInstanceID(); - worldserver.SendPacket(pack); - safe_delete(pack); - - entity_list.RemoveGroup(GetID()); - if(GetID() != 0) - database.ClearGroup(GetID()); - - if(Leader && (Leader->IsLFP())) { - Leader->UpdateLFP(); - } - - safe_delete(outapp); -} - -bool Group::Process() { - if(disbandcheck && !GroupCount()) - return false; - else if(disbandcheck && GroupCount()) - disbandcheck = false; - return true; -} - -void Group::SendUpdate(uint32 type, Mob* member) -{ - if(!member->IsClient()) - return; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate2_Struct)); - GroupUpdate2_Struct* gu = (GroupUpdate2_Struct*)outapp->pBuffer; - gu->action = type; - strcpy(gu->yourname,member->GetName()); - - int x = 0; - - gu->leader_aas = LeaderAbilities; - - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - if((members[i] != nullptr) && IsLeader(members[i])) - { - strcpy(gu->leadersname, members[i]->GetName()); - break; - } - - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - if (members[i] != nullptr && members[i] != member) - strcpy(gu->membername[x++], members[i]->GetName()); - - member->CastToClient()->QueuePacket(outapp); - - safe_delete(outapp); -} - -void Group::SendLeadershipAAUpdate() -{ - // This method updates other members of the group in the current zone with the Leader's group leadership AAs. - // - // It is called when the leader purchases a leadership AA or enters a zone. - // - // If a group member is not in the same zone as the leader when the leader purchases a new AA, they will not become - // aware of it until they are next in the same zone as the leader. - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); - - GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer; - - gu->action = groupActAAUpdate; - - uint32 i = 0; - - gu->leader_aas = LeaderAbilities; - - gu->NPCMarkerID = GetNPCMarkerID(); - - for (i = 0;i < MAX_GROUP_MEMBERS; ++i) - if(members[i] && members[i]->IsClient()) - { - strcpy(gu->yourname, members[i]->GetName()); - strcpy(gu->membername, members[i]->GetName()); - members[i]->CastToClient()->QueuePacket(outapp); - } - - safe_delete(outapp); -} - -uint8 Group::GroupCount() { - - uint8 MemberCount = 0; - - for(uint8 i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(membername[i][0]) - ++MemberCount; - - return MemberCount; -} - -uint32 Group::GetHighestLevel() -{ -uint32 level = 1; -uint32 i; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) - { - if (members[i]) - { - if(members[i]->GetLevel() > level) - level = members[i]->GetLevel(); - } - } - return level; -} -uint32 Group::GetLowestLevel() -{ -uint32 level = 255; -uint32 i; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) - { - if (members[i]) - { - if(members[i]->GetLevel() < level) - level = members[i]->GetLevel(); - } - } - return level; -} - -void Group::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float x, float y, float z, float heading) -{ - uint32 i; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) - { - if (members[i] != nullptr && members[i]->IsClient() && members[i] != sender) - { - members[i]->CastToClient()->MovePC(zoneID, instance_id, x, y, z, heading, 0, ZoneSolicited); - } - } -} - -bool Group::LearnMembers() { - char errbuf[MYSQL_ERRMSG_SIZE]; - char* query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - if (database.RunQuery(query,MakeAnyLenString(&query, "SELECT name FROM group_id WHERE groupid=%lu", (unsigned long)GetID()), - errbuf,&result)){ - safe_delete_array(query); - if(mysql_num_rows(result) < 1) { //could prolly be 2 - mysql_free_result(result); - LogFile->write(EQEMuLog::Error, "Error getting group members for group %lu: %s", (unsigned long)GetID(), errbuf); - return(false); - } - int i = 0; - while((row = mysql_fetch_row(result))) { - if(!row[0]) - continue; - members[i] = nullptr; - strn0cpy(membername[i], row[0], 64); - - i++; - } - mysql_free_result(result); - } - - return(true); -} - -void Group::VerifyGroup() { - /* - The purpose of this method is to make sure that a group - is in a valid state, to prevent dangling pointers. - Only called every once in a while (on member re-join for now). - */ - - uint32 i; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (membername[i][0] == '\0') { -#if EQDEBUG >= 7 -LogFile->write(EQEMuLog::Debug, "Group %lu: Verify %d: Empty.\n", (unsigned long)GetID(), i); -#endif - members[i] = nullptr; - continue; - } - - //it should be safe to use GetClientByName, but Group is trying - //to be generic, so we'll go for general Mob - Mob *them = entity_list.GetMob(membername[i]); - if(them == nullptr && members[i] != nullptr) { //they arnt here anymore.... -#if EQDEBUG >= 6 - LogFile->write(EQEMuLog::Debug, "Member of group %lu named '%s' has disappeared!!", (unsigned long)GetID(), membername[i]); -#endif - membername[i][0] = '\0'; - members[i] = nullptr; - continue; - } - - if(them != nullptr && members[i] != them) { //our pointer is out of date... not so good. -#if EQDEBUG >= 5 - LogFile->write(EQEMuLog::Debug, "Member of group %lu named '%s' had an out of date pointer!!", (unsigned long)GetID(), membername[i]); -#endif - members[i] = them; - continue; - } -#if EQDEBUG >= 8 - LogFile->write(EQEMuLog::Debug, "Member of group %lu named '%s' is valid.", (unsigned long)GetID(), membername[i]); -#endif - } -} - - -void Group::GroupMessage_StringID(Mob* sender, uint32 type, uint32 string_id, const char* message,const char* message2,const char* message3,const char* message4,const char* message5,const char* message6,const char* message7,const char* message8,const char* message9, uint32 distance) { - uint32 i; - for (i = 0; i < MAX_GROUP_MEMBERS; i++) { - if(members[i] == nullptr) - continue; - - if(members[i] == sender) - continue; - - members[i]->Message_StringID(type, string_id, message, message2, message3, message4, message5, message6, message7, message8, message9, 0); - } -} - - - -void Client::LeaveGroup() { - Group *g = GetGroup(); - - if(g) { - if(g->GroupCount() < 3) - g->DisbandGroup(); - else - g->DelMember(this); - } else { - //force things a little - database.SetGroupID(GetName(), 0, CharacterID()); - } - - isgrouped = false; -} - -void Group::HealGroup(uint32 heal_amt, Mob* caster, int32 range) -{ - if (!caster) - return; - - if (!range) - range = 200; - - float distance; - float range2 = range*range; - - - int numMem = 0; - unsigned int gi = 0; - for(; gi < MAX_GROUP_MEMBERS; gi++) - { - if(members[gi]){ - distance = caster->DistNoRoot(*members[gi]); - if(distance <= range2){ - numMem += 1; - } - } - } - - heal_amt /= numMem; - for(gi = 0; gi < MAX_GROUP_MEMBERS; gi++) - { - if(members[gi]){ - distance = caster->DistNoRoot(*members[gi]); - if(distance <= range2){ - members[gi]->HealDamage(heal_amt, caster); - members[gi]->SendHPUpdate(); - } - } - } -} - - -void Group::BalanceHP(int32 penalty, int32 range, Mob* caster, int32 limit) -{ - if (!caster) - return; - - if (!range) - range = 200; - - int dmgtaken = 0, numMem = 0, dmgtaken_tmp = 0; - - float distance; - float range2 = range*range; - - unsigned int gi = 0; - for(; gi < MAX_GROUP_MEMBERS; gi++) - { - if(members[gi]){ - distance = caster->DistNoRoot(*members[gi]); - if(distance <= range2){ - - dmgtaken_tmp = members[gi]->GetMaxHP() - members[gi]->GetHP(); - if (limit && (dmgtaken_tmp > limit)) - dmgtaken_tmp = limit; - - dmgtaken += (dmgtaken_tmp); - numMem += 1; - } - } - } - - dmgtaken += dmgtaken * penalty / 100; - dmgtaken /= numMem; - for(gi = 0; gi < MAX_GROUP_MEMBERS; gi++) - { - if(members[gi]){ - distance = caster->DistNoRoot(*members[gi]); - if(distance <= range2){ - if((members[gi]->GetMaxHP() - dmgtaken) < 1){ //this way the ability will never kill someone - members[gi]->SetHP(1); //but it will come darn close - members[gi]->SendHPUpdate(); - } - else{ - members[gi]->SetHP(members[gi]->GetMaxHP() - dmgtaken); - members[gi]->SendHPUpdate(); - } - } - } - } -} - -void Group::BalanceMana(int32 penalty, int32 range, Mob* caster, int32 limit) -{ - if (!caster) - return; - - if (!range) - range = 200; - - float distance; - float range2 = range*range; - - int manataken = 0, numMem = 0, manataken_tmp = 0; - unsigned int gi = 0; - for(; gi < MAX_GROUP_MEMBERS; gi++) - { - if(members[gi] && (members[gi]->GetMaxMana() > 0)){ - distance = caster->DistNoRoot(*members[gi]); - if(distance <= range2){ - - manataken_tmp = members[gi]->GetMaxMana() - members[gi]->GetMana(); - if (limit && (manataken_tmp > limit)) - manataken_tmp = limit; - - manataken += (manataken_tmp); - numMem += 1; - } - } - } - - manataken += manataken * penalty / 100; - manataken /= numMem; - - if (limit && (manataken > limit)) - manataken = limit; - - for(gi = 0; gi < MAX_GROUP_MEMBERS; gi++) - { - if(members[gi]){ - distance = caster->DistNoRoot(*members[gi]); - if(distance <= range2){ - if((members[gi]->GetMaxMana() - manataken) < 1){ - members[gi]->SetMana(1); - if (members[gi]->IsClient()) - members[gi]->CastToClient()->SendManaUpdate(); - } - else{ - members[gi]->SetMana(members[gi]->GetMaxMana() - manataken); - if (members[gi]->IsClient()) - members[gi]->CastToClient()->SendManaUpdate(); - } - } - } - } -} - -uint16 Group::GetAvgLevel() -{ - double levelHolder = 0; - uint8 i = 0; - uint8 numMem = 0; - while(i < MAX_GROUP_MEMBERS) - { - if (members[i]) - { - numMem++; - levelHolder = levelHolder + (members[i]->GetLevel()); - } - i++; - } - levelHolder = ((levelHolder/numMem)+.5); // total levels divided by num of characters - return (uint16(levelHolder)); -} - -void Group::MarkNPC(Mob* Target, int Number) -{ - // Send a packet to all group members in this zone causing the client to prefix the Target mob's name - // with the specified Number. - // - if(!Target || Target->IsClient()) - return; - - if((Number < 1) || (Number > MAX_MARKED_NPCS)) - return; - - bool AlreadyMarked = false; - - uint16 EntityID = Target->GetID(); - - for(int i = 0; i < MAX_MARKED_NPCS; ++i) - if(MarkedNPCs[i] == EntityID) - { - if(i == (Number - 1)) - return; - - UpdateXTargetMarkedNPC(i+1, nullptr); - MarkedNPCs[i] = 0; - - AlreadyMarked = true; - - break; - } - - if(!AlreadyMarked) - { - if(MarkedNPCs[Number - 1]) - { - Mob* m = entity_list.GetMob(MarkedNPCs[Number-1]); - if(m) - m->IsTargeted(-1); - - UpdateXTargetMarkedNPC(Number, nullptr); - } - - if(EntityID) - { - Mob* m = entity_list.GetMob(Target->GetID()); - if(m) - m->IsTargeted(1); - } - } - - MarkedNPCs[Number - 1] = EntityID; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MarkNPC, sizeof(MarkNPC_Struct)); - - MarkNPC_Struct* mnpcs = (MarkNPC_Struct *)outapp->pBuffer; - - mnpcs->TargetID = EntityID; - - mnpcs->Number = Number; - - Mob *m = entity_list.GetMob(EntityID); - - if(m) - sprintf(mnpcs->Name, "%s", m->GetCleanName()); - - QueuePacket(outapp); - - safe_delete(outapp); - - UpdateXTargetMarkedNPC(Number, m); -} - -void Group::DelegateMainTank(const char *NewMainTankName, uint8 toggle) -{ - // This method is called when the group leader Delegates the Main Tank role to a member of the group - // (or himself). All group members in the zone are notified of the new Main Tank and it is recorded - // in the group_leaders table so as to persist across zones. - // - - bool updateDB = false; - - if(!NewMainTankName) - return; - - Mob *m = entity_list.GetMob(NewMainTankName); - - if(!m) - return; - - if(MainTankName != NewMainTankName || !toggle) - updateDB = true; - - if(m->GetTarget()) - TankTargetID = m->GetTarget()->GetID(); - else - TankTargetID = 0; - - Mob *mtt = TankTargetID ? entity_list.GetMob(TankTargetID) : 0; - - SetMainTank(NewMainTankName); - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if(members[i] && members[i]->IsClient()) - { - NotifyMainTank(members[i]->CastToClient(), toggle); - members[i]->CastToClient()->UpdateXTargetType(GroupTank, m, NewMainTankName); - members[i]->CastToClient()->UpdateXTargetType(GroupTankTarget, mtt); - } - } - - if(updateDB) { - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = nullptr; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET maintank='%s' WHERE gid=%i LIMIT 1", - MainTankName.c_str(), GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group main tank: %s\n", errbuff); - - safe_delete_array(Query); - } -} - -void Group::DelegateMainAssist(const char *NewMainAssistName, uint8 toggle) -{ - // This method is called when the group leader Delegates the Main Assist role to a member of the group - // (or himself). All group members in the zone are notified of the new Main Assist and it is recorded - // in the group_leaders table so as to persist across zones. - // - - bool updateDB = false; - - if(!NewMainAssistName) - return; - - Mob *m = entity_list.GetMob(NewMainAssistName); - - if(!m) - return; - - if(MainAssistName != NewMainAssistName || !toggle) - updateDB = true; - - if(m->GetTarget()) - AssistTargetID = m->GetTarget()->GetID(); - else - AssistTargetID = 0; - - SetMainAssist(NewMainAssistName); - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { - if(members[i] && members[i]->IsClient()) - { - NotifyMainAssist(members[i]->CastToClient(), toggle); - members[i]->CastToClient()->UpdateXTargetType(GroupAssist, m, NewMainAssistName); - members[i]->CastToClient()->UpdateXTargetType(GroupAssistTarget, m->GetTarget()); - } - } - - if(updateDB) { - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = nullptr; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET assist='%s' WHERE gid=%i LIMIT 1", - MainAssistName.c_str(), GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group main assist: %s\n", errbuff); - - safe_delete_array(Query); - } -} - -void Group::DelegatePuller(const char *NewPullerName, uint8 toggle) -{ - // This method is called when the group leader Delegates the Puller role to a member of the group - // (or himself). All group members in the zone are notified of the new Puller and it is recorded - // in the group_leaders table so as to persist across zones. - // - - bool updateDB = false; - - if(!NewPullerName) - return; - - Mob *m = entity_list.GetMob(NewPullerName); - - if(!m) - return; - - if(PullerName != NewPullerName || !toggle) - updateDB = true; - - if(m->GetTarget()) - PullerTargetID = m->GetTarget()->GetID(); - else - PullerTargetID = 0; - - SetPuller(NewPullerName); - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { - if(members[i] && members[i]->IsClient()) - { - NotifyPuller(members[i]->CastToClient(), toggle); - members[i]->CastToClient()->UpdateXTargetType(Puller, m, NewPullerName); - members[i]->CastToClient()->UpdateXTargetType(PullerTarget, m->GetTarget()); - } - } - - if(updateDB) { - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = nullptr; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET puller='%s' WHERE gid=%i LIMIT 1", - PullerName.c_str(), GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group main puller: %s\n", errbuff); - - safe_delete_array(Query); - } - -} - -void Group::NotifyMainTank(Client *c, uint8 toggle) -{ - // Send a packet to the specified Client notifying them who the new Main Tank is. This causes the client to display - // a message with the name of the Main Tank. - // - - if(!c) - return; - - if(!MainTankName.size()) - return; - - if(c->GetClientVersion() < EQClientSoD) - { - if(toggle) - c->Message(0, "%s is now Main Tank.", MainTankName.c_str()); - else - c->Message(0, "%s is no longer Main Tank.", MainTankName.c_str()); - } - else - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupRoles, sizeof(GroupRole_Struct)); - - GroupRole_Struct *grs = (GroupRole_Struct*)outapp->pBuffer; - - strn0cpy(grs->Name1, MainTankName.c_str(), sizeof(grs->Name1)); - - strn0cpy(grs->Name2, GetLeaderName(), sizeof(grs->Name2)); - - grs->RoleNumber = 1; - - grs->Toggle = toggle; - - c->QueuePacket(outapp); - - safe_delete(outapp); - } - -} - -void Group::NotifyMainAssist(Client *c, uint8 toggle) -{ - // Send a packet to the specified Client notifying them who the new Main Assist is. This causes the client to display - // a message with the name of the Main Assist. - // - - if(!c) - return; - - if(!MainAssistName.size()) - return; - - if(c->GetClientVersion() < EQClientSoD) - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_DelegateAbility, sizeof(DelegateAbility_Struct)); - - DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; - - das->DelegateAbility = 0; - - das->MemberNumber = 0; - - das->Action = 0; - - das->EntityID = 0; - - strn0cpy(das->Name, MainAssistName.c_str(), sizeof(das->Name)); - - c->QueuePacket(outapp); - - safe_delete(outapp); - } - else - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupRoles, sizeof(GroupRole_Struct)); - - GroupRole_Struct *grs = (GroupRole_Struct*)outapp->pBuffer; - - strn0cpy(grs->Name1, MainAssistName.c_str(), sizeof(grs->Name1)); - - strn0cpy(grs->Name2, GetLeaderName(), sizeof(grs->Name2)); - - grs->RoleNumber = 2; - - grs->Toggle = toggle; - - c->QueuePacket(outapp); - - safe_delete(outapp); - } - - NotifyAssistTarget(c); - -} - -void Group::NotifyPuller(Client *c, uint8 toggle) -{ - // Send a packet to the specified Client notifying them who the new Puller is. This causes the client to display - // a message with the name of the Puller. - // - - if(!c) - return; - - if(!PullerName.size()) - return; - - if(c->GetClientVersion() < EQClientSoD) - { - if(toggle) - c->Message(0, "%s is now Puller.", PullerName.c_str()); - else - c->Message(0, "%s is no longer Puller.", PullerName.c_str()); - } - else - { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_GroupRoles, sizeof(GroupRole_Struct)); - - GroupRole_Struct *grs = (GroupRole_Struct*)outapp->pBuffer; - - strn0cpy(grs->Name1, PullerName.c_str(), sizeof(grs->Name1)); - - strn0cpy(grs->Name2, GetLeaderName(), sizeof(grs->Name2)); - - grs->RoleNumber = 3; - - grs->Toggle = toggle; - - c->QueuePacket(outapp); - - safe_delete(outapp); - } - -} - -void Group::UnDelegateMainTank(const char *OldMainTankName, uint8 toggle) -{ - // Called when the group Leader removes the Main Tank delegation. Sends a packet to each group member in the zone - // informing them of the change and update the group_leaders table. - // - if(OldMainTankName == MainTankName) { - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET maintank='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group main tank: %s\n", errbuff); - - safe_delete_array(Query); - - if(!toggle) { - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { - if(members[i] && members[i]->IsClient()) - { - NotifyMainTank(members[i]->CastToClient(), toggle); - members[i]->CastToClient()->UpdateXTargetType(GroupTank, nullptr, ""); - members[i]->CastToClient()->UpdateXTargetType(GroupTankTarget, nullptr); - } - } - } - - SetMainTank(""); - } -} - -void Group::UnDelegateMainAssist(const char *OldMainAssistName, uint8 toggle) -{ - // Called when the group Leader removes the Main Assist delegation. Sends a packet to each group member in the zone - // informing them of the change and update the group_leaders table. - // - if(OldMainAssistName == MainAssistName) { - EQApplicationPacket *outapp = new EQApplicationPacket(OP_DelegateAbility, sizeof(DelegateAbility_Struct)); - - DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; - - das->DelegateAbility = 0; - - das->MemberNumber = 0; - - das->Action = 1; - - das->EntityID = 0; - - strn0cpy(das->Name, OldMainAssistName, sizeof(das->Name)); - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(members[i] && members[i]->IsClient()) - { - members[i]->CastToClient()->QueuePacket(outapp); - members[i]->CastToClient()->UpdateXTargetType(GroupAssist, nullptr, ""); - } - - safe_delete(outapp); - - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET assist='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group main assist: %s\n", errbuff); - - safe_delete_array(Query); - - if(!toggle) - { - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if(members[i] && members[i]->IsClient()) - { - NotifyMainAssist(members[i]->CastToClient(), toggle); - members[i]->CastToClient()->UpdateXTargetType(GroupAssistTarget, nullptr); - } - } - } - - SetMainAssist(""); - } -} - -void Group::UnDelegatePuller(const char *OldPullerName, uint8 toggle) -{ - // Called when the group Leader removes the Puller delegation. Sends a packet to each group member in the zone - // informing them of the change and update the group_leaders table. - // - if(OldPullerName == PullerName) { - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET puller='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group main puller: %s\n", errbuff); - - safe_delete_array(Query); - - if(!toggle) { - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) { - if(members[i] && members[i]->IsClient()) - { - NotifyPuller(members[i]->CastToClient(), toggle); - members[i]->CastToClient()->UpdateXTargetType(Puller, nullptr, ""); - members[i]->CastToClient()->UpdateXTargetType(PullerTarget, nullptr); - } - } - } - - SetPuller(""); - } -} - -bool Group::IsNPCMarker(Client *c) -{ - // Returns true if the specified client has been delegated the NPC Marker Role - // - if(!c) - return false; - - if(NPCMarkerName.size()) - return(c->GetName() == NPCMarkerName); - - return false; - -} - -void Group::SetGroupAssistTarget(Mob *m) -{ - // Notify all group members in the zone of the new target the Main Assist has selected. - // - AssistTargetID = m ? m->GetID() : 0; - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if(members[i] && members[i]->IsClient()) - { - NotifyAssistTarget(members[i]->CastToClient()); - } - } -} - -void Group::SetGroupTankTarget(Mob *m) -{ - TankTargetID = m ? m->GetID() : 0; - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if(members[i] && members[i]->IsClient()) - { - members[i]->CastToClient()->UpdateXTargetType(GroupTankTarget, m); - } - } -} - -void Group::SetGroupPullerTarget(Mob *m) -{ - PullerTargetID = m ? m->GetID() : 0; - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if(members[i] && members[i]->IsClient()) - { - members[i]->CastToClient()->UpdateXTargetType(PullerTarget, m); - } - } -} - -void Group::NotifyAssistTarget(Client *c) -{ - // Send a packet to the specified client notifying them of the group target selected by the Main Assist. - - if(!c) - return; - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SetGroupTarget, sizeof(MarkNPC_Struct)); - - MarkNPC_Struct* mnpcs = (MarkNPC_Struct *)outapp->pBuffer; - - mnpcs->TargetID = AssistTargetID; - - mnpcs->Number = 0; - - c->QueuePacket(outapp); - - safe_delete(outapp); - - Mob *m = entity_list.GetMob(AssistTargetID); - - c->UpdateXTargetType(GroupAssistTarget, m); - -} - -void Group::NotifyTankTarget(Client *c) -{ - if(!c) - return; - - Mob *m = entity_list.GetMob(TankTargetID); - - c->UpdateXTargetType(GroupTankTarget, m); -} - -void Group::NotifyPullerTarget(Client *c) -{ - if(!c) - return; - - Mob *m = entity_list.GetMob(PullerTargetID); - - c->UpdateXTargetType(PullerTarget, m); -} - -void Group::DelegateMarkNPC(const char *NewNPCMarkerName) -{ - // Called when the group leader has delegated the Mark NPC ability to a group member. - // Notify all group members in the zone of the change and save the change in the group_leaders - // table to persist across zones. - // - if(NPCMarkerName.size() > 0) - UnDelegateMarkNPC(NPCMarkerName.c_str()); - - if(!NewNPCMarkerName) - return; - - SetNPCMarker(NewNPCMarkerName); - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(members[i] && members[i]->IsClient()) - NotifyMarkNPC(members[i]->CastToClient()); - - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET marknpc='%s' WHERE gid=%i LIMIT 1", - NewNPCMarkerName, GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to set group mark npc: %s\n", errbuff); - - safe_delete_array(Query); - -} - -void Group::NotifyMarkNPC(Client *c) -{ - // Notify the specified client who the group member is who has been delgated the Mark NPC ability. - - if(!c) - return; - - if(!NPCMarkerName.size()) - return; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_DelegateAbility, sizeof(DelegateAbility_Struct)); - - DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; - - das->DelegateAbility = 1; - - das->MemberNumber = 0; - - das->Action = 0; - - das->EntityID = NPCMarkerID; - - strn0cpy(das->Name, NPCMarkerName.c_str(), sizeof(das->Name)); - - c->QueuePacket(outapp); - - safe_delete(outapp); - -} -void Group::SetNPCMarker(const char *NewNPCMarkerName) -{ - NPCMarkerName = NewNPCMarkerName; - - Client *m = entity_list.GetClientByName(NPCMarkerName.c_str()); - - if(!m) - NPCMarkerID = 0; - else - NPCMarkerID = m->GetID(); -} - -void Group::UnDelegateMarkNPC(const char *OldNPCMarkerName) -{ - // Notify all group members in the zone that the Mark NPC ability has been rescinded from the specified - // group member. - - if(!OldNPCMarkerName) - return; - - if(!NPCMarkerName.size()) - return; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_DelegateAbility, sizeof(DelegateAbility_Struct)); - - DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; - - das->DelegateAbility = 1; - - das->MemberNumber = 0; - - das->Action = 1; - - das->EntityID = 0; - - strn0cpy(das->Name, OldNPCMarkerName, sizeof(das->Name)); - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(members[i] && members[i]->IsClient()) - members[i]->CastToClient()->QueuePacket(outapp); - - safe_delete(outapp); - - NPCMarkerName.clear(); - - char errbuff[MYSQL_ERRMSG_SIZE]; - - char *Query = 0; - - if (!database.RunQuery(Query, MakeAnyLenString(&Query, "UPDATE group_leaders SET marknpc='' WHERE gid=%i LIMIT 1", - GetID()), errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to clear group marknpc: %s\n", errbuff); - - safe_delete_array(Query); -} - -void Group::SaveGroupLeaderAA() -{ - // Stores the Group Leaders Leadership AA data from the Player Profile as a blob in the group_leaders table. - // This is done so that group members not in the same zone as the Leader still have access to this information. - - char *Query = new char[200 + sizeof(GroupLeadershipAA_Struct)*2]; - - char *End = Query; - - End += sprintf(End, "UPDATE group_leaders SET leadershipaa='"); - - End += database.DoEscapeString(End, (char*)&LeaderAbilities, sizeof(GroupLeadershipAA_Struct)); - - End += sprintf(End,"' WHERE gid=%i LIMIT 1", GetID()); - - char errbuff[MYSQL_ERRMSG_SIZE]; - if (!database.RunQuery(Query, End - Query, errbuff)) - LogFile->write(EQEMuLog::Error, "Unable to store LeadershipAA: %s\n", errbuff); - - safe_delete_array(Query); -} - -void Group::UnMarkNPC(uint16 ID) -{ - // Called from entity_list when the mob with the specified ID is being destroyed. - // - // If the given mob has been marked by this group, it is removed from the list of marked NPCs. - // The primary reason for doing this is so that when a new group member joins or zones in, we - // send them correct details of which NPCs are currently marked. - - if(AssistTargetID == ID) - AssistTargetID = 0; - - - if(TankTargetID == ID) - TankTargetID = 0; - - if(PullerTargetID == ID) - PullerTargetID = 0; - - for(int i = 0; i < MAX_MARKED_NPCS; ++i) - { - if(MarkedNPCs[i] == ID) - { - MarkedNPCs[i] = 0; - UpdateXTargetMarkedNPC(i + 1, nullptr); - } - } -} - -void Group::SendMarkedNPCsToMember(Client *c, bool Clear) -{ - // Send the Entity IDs of the NPCs marked by the Group Leader or delegate to the specified client. - // If Clear == true, then tell the client to unmark the NPCs (when a member disbands). - // - // - if(!c) - return; - - EQApplicationPacket *outapp = new EQApplicationPacket(OP_MarkNPC, sizeof(MarkNPC_Struct)); - - MarkNPC_Struct *mnpcs = (MarkNPC_Struct *)outapp->pBuffer; - - for(int i = 0; i < MAX_MARKED_NPCS; ++i) - { - if(MarkedNPCs[i]) - { - mnpcs->TargetID = MarkedNPCs[i]; - - Mob *m = entity_list.GetMob(MarkedNPCs[i]); - - if(m) - sprintf(mnpcs->Name, "%s", m->GetCleanName()); - - if(!Clear) - mnpcs->Number = i + 1; - else - mnpcs->Number = 0; - - c->QueuePacket(outapp); - c->UpdateXTargetType((mnpcs->Number == 1) ? GroupMarkTarget1 : ((mnpcs->Number == 2) ? GroupMarkTarget2 : GroupMarkTarget3), m); - } - } - - safe_delete(outapp); -} - -void Group::ClearAllNPCMarks() -{ - // This method is designed to be called when the number of members in the group drops below 3 and leadership AA - // may no longer be used. It removes all NPC marks. - // - for(uint8 i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(members[i] && members[i]->IsClient()) - SendMarkedNPCsToMember(members[i]->CastToClient(), true); - - for(int i = 0; i < MAX_MARKED_NPCS; ++i) - { - if(MarkedNPCs[i]) - { - Mob* m = entity_list.GetMob(MarkedNPCs[i]); - - if(m) - m->IsTargeted(-1); - } - - MarkedNPCs[i] = 0; - } - -} - -int8 Group::GetNumberNeedingHealedInGroup(int8 hpr, bool includePets) { - int8 needHealed = 0; - - for( int i = 0; iqglobal) { - - if(members[i]->GetHPRatio() <= hpr) - needHealed++; - - if(includePets) { - if(members[i]->GetPet() && members[i]->GetPet()->GetHPRatio() <= hpr) { - needHealed++; - } - } - } - } - - - return needHealed; -} - -void Group::UpdateGroupAAs() -{ - // This method updates the Groups Leadership abilities from the Player Profile of the Leader. - // - Mob *m = GetLeader(); - - if(m && m->IsClient()) - m->CastToClient()->GetGroupAAs(&LeaderAbilities); - else - memset(&LeaderAbilities, 0, sizeof(GroupLeadershipAA_Struct)); - - SaveGroupLeaderAA(); -} - -void Group::QueueHPPacketsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app) -{ - // Send a mobs HP packets to group members if the leader has the NPC Health AA and the mob is the - // target of the group's main assist, or is marked, and the member doesn't already have the mob targeted. - - if(!sender || !app || !GetLeadershipAA(groupAANPCHealth)) - return; - - uint16 SenderID = sender->GetID(); - - if(SenderID != AssistTargetID) - { - bool Marked = false; - - for(int i = 0; i < MAX_MARKED_NPCS; ++i) - { - if(MarkedNPCs[i] == SenderID) - { - Marked = true; - break; - } - } - - if(!Marked) - return; - - } - - for(unsigned int i = 0; i < MAX_GROUP_MEMBERS; ++i) - if(members[i] && members[i]->IsClient()) - { - if(!members[i]->GetTarget() || (members[i]->GetTarget()->GetID() != SenderID)) - { - members[i]->CastToClient()->QueuePacket(app); - } - } - -} - -void Group::ChangeLeader(Mob* newleader) -{ - // this changes the current group leader, notifies other members, and updates leadship AA - - // if the new leader is invalid, do nothing - if (!newleader) - return; - - Mob* oldleader = GetLeader(); - - EQApplicationPacket* outapp = new EQApplicationPacket(OP_GroupUpdate,sizeof(GroupJoin_Struct)); - GroupJoin_Struct* gu = (GroupJoin_Struct*) outapp->pBuffer; - gu->action = groupActMakeLeader; - - strcpy(gu->membername, newleader->GetName()); - strcpy(gu->yourname, oldleader->GetName()); - SetLeader(newleader); - database.SetGroupLeaderName(GetID(), newleader->GetName()); - UpdateGroupAAs(); - gu->leader_aas = LeaderAbilities; - for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (members[i] && members[i]->IsClient()) - { - if(members[i]->CastToClient()->GetClientVersion() >= EQClientSoD) - members[i]->CastToClient()->SendGroupLeaderChangePacket(newleader->GetName()); - - members[i]->CastToClient()->QueuePacket(outapp); - } - } - safe_delete(outapp); -} - -const char *Group::GetClientNameByIndex(uint8 index) -{ - return membername[index]; -} - -void Group::UpdateXTargetMarkedNPC(uint32 Number, Mob *m) -{ - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if(members[i] && members[i]->IsClient()) - { - members[i]->CastToClient()->UpdateXTargetType((Number == 1) ? GroupMarkTarget1 : ((Number == 2) ? GroupMarkTarget2 : GroupMarkTarget3), m); - } - } - -} - -void Group::SetMainTank(const char *NewMainTankName) -{ - MainTankName = NewMainTankName; - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if(!strncasecmp(membername[i], NewMainTankName, 64)) - MemberRoles[i] |= RoleTank; - else - MemberRoles[i] &= ~RoleTank; - } -} - -void Group::SetMainAssist(const char *NewMainAssistName) -{ - MainAssistName = NewMainAssistName; - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if(!strncasecmp(membername[i], NewMainAssistName, 64)) - MemberRoles[i] |= RoleAssist; - else - MemberRoles[i] &= ~RoleAssist; - } -} - -void Group::SetPuller(const char *NewPullerName) -{ - PullerName = NewPullerName; - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if(!strncasecmp(membername[i], NewPullerName, 64)) - MemberRoles[i] |= RolePuller; - else - MemberRoles[i] &= ~RolePuller; - } -} - -bool Group::HasRole(Mob *m, uint8 Role) -{ - if(!m) - return false; - - for(uint32 i = 0; i < MAX_GROUP_MEMBERS; ++i) - { - if((m == members[i]) && (MemberRoles[i] & Role)) - return true; - } - return false; -} - diff --git a/zone/npcx.cpp b/zone/npcx.cpp deleted file mode 100644 index 7d2766854..000000000 --- a/zone/npcx.cpp +++ /dev/null @@ -1,2492 +0,0 @@ -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ -#include "../common/debug.h" -#include -#include -#include -#include -#include "../common/moremath.h" -#include -#include "../common/packet_dump_file.h" -#include "zone.h" -#ifdef _WINDOWS -#define snprintf _snprintf -#define strncasecmp _strnicmp -#define strcasecmp _stricmp -#else -#include -#include -#endif - -#include "npc.h" -#include "map.h" -#include "entity.h" -#include "masterentity.h" -#include "../common/spdat.h" -#include "../common/bodytypes.h" -#include "spawngroup.h" -#include "../common/MiscFunctions.h" -#include "../common/StringUtil.h" -#include "../common/rulesys.h" -#include "StringIDs.h" - -//#define SPELLQUEUE //Use only if you want to be spammed by spell testing - - -extern Zone* zone; -extern volatile bool ZoneLoaded; -extern EntityList entity_list; - -#include "QuestParserCollection.h" - -NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float heading, int iflymode, bool IsCorpse) -: Mob(d->name, - d->lastname, - d->max_hp, - d->max_hp, - d->gender, - d->race, - d->class_, - (bodyType)d->bodytype, - d->deity, - d->level, - d->npc_id, - d->size, - d->runspeed, - heading, - x, - y, - z, - d->light, - d->texture, - d->helmtexture, - d->AC, - d->ATK, - d->STR, - d->STA, - d->DEX, - d->AGI, - d->INT, - d->WIS, - d->CHA, - d->haircolor, - d->beardcolor, - d->eyecolor1, - d->eyecolor2, - d->hairstyle, - d->luclinface, - d->beard, - d->drakkin_heritage, - d->drakkin_tattoo, - d->drakkin_details, - (uint32*)d->armor_tint, - 0, - d->see_invis, // pass see_invis/see_ivu flags to mob constructor - d->see_invis_undead, - d->see_hide, - d->see_improved_hide, - d->hp_regen, - d->mana_regen, - d->qglobal, - d->maxlevel, - d->scalerate ), - attacked_timer(CombatEventTimer_expire), - swarm_timer(100), - classattack_timer(1000), - knightattack_timer(1000), - assist_timer(AIassistcheck_delay), - qglobal_purge_timer(30000), - sendhpupdate_timer(1000), - enraged_timer(1000), - taunt_timer(TauntReuseTime * 1000) -{ - //What is the point of this, since the names get mangled.. - Mob* mob = entity_list.GetMob(name); - if(mob != 0) - entity_list.RemoveEntity(mob->GetID()); - - int moblevel=GetLevel(); - - NPCTypedata = d; - NPCTypedata_ours = nullptr; - respawn2 = in_respawn; - swarm_timer.Disable(); - - taunting = false; - proximity = nullptr; - copper = 0; - silver = 0; - gold = 0; - platinum = 0; - max_dmg = d->max_dmg; - min_dmg = d->min_dmg; - attack_count = d->attack_count; - grid = 0; - wp_m = 0; - max_wp=0; - save_wp = 0; - spawn_group = 0; - swarmInfoPtr = nullptr; - spellscale = d->spellscale; - healscale = d->healscale; - - logging_enabled = NPC_DEFAULT_LOGGING_ENABLED; - - pAggroRange = d->aggroradius; - pAssistRange = d->assistradius; - findable = d->findable; - trackable = d->trackable; - - MR = d->MR; - CR = d->CR; - DR = d->DR; - FR = d->FR; - PR = d->PR; - Corrup = d->Corrup; - PhR = d->PhR; - - STR = d->STR; - STA = d->STA; - AGI = d->AGI; - DEX = d->DEX; - INT = d->INT; - WIS = d->WIS; - CHA = d->CHA; - npc_mana = d->Mana; - - //quick fix of ordering if they screwed it up in the DB - if(max_dmg < min_dmg) { - int tmp = min_dmg; - min_dmg = max_dmg; - max_dmg = tmp; - } - - // Max Level and Stat Scaling if maxlevel is set - if(maxlevel > level) - { - LevelScale(); - } - - // Set Resists if they are 0 in the DB - CalcNPCResists(); - - // Set Mana and HP Regen Rates if they are 0 in the DB - CalcNPCRegen(); - - // Set Min and Max Damage if they are 0 in the DB - if(max_dmg == 0){ - CalcNPCDamage(); - } - - accuracy_rating = d->accuracy_rating; - ATK = d->ATK; - - CalcMaxMana(); - SetMana(GetMaxMana()); - - MerchantType = d->merchanttype; - merchant_open = GetClass() == MERCHANT; - adventure_template_id = d->adventure_template; - org_x = x; - org_y = y; - org_z = z; - flymode = iflymode; - guard_x = -1; //just some value we might be able to recongize as "unset" - guard_y = -1; - guard_z = -1; - guard_heading = 0; - guard_anim = eaStanding; - roambox_distance = 0; - roambox_max_x = -2; - roambox_max_y = -2; - roambox_min_x = -2; - roambox_min_y = -2; - roambox_movingto_x = -2; - roambox_movingto_y = -2; - roambox_min_delay = 1000; - roambox_delay = 1000; - org_heading = heading; - p_depop = false; - loottable_id = d->loottable_id; - - no_target_hotkey = d->no_target_hotkey; - - primary_faction = 0; - SetNPCFactionID(d->npc_faction_id); - - npc_spells_id = 0; - HasAISpell = false; - HasAISpellEffects = false; - - if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs)) - { - LoadMercTypes(); - LoadMercs(); - } - - SpellFocusDMG = 0; - SpellFocusHeal = 0; - - pet_spell_id = 0; - - delaytimer = false; - combat_event = false; - attack_speed = d->attack_speed; - slow_mitigation = d->slow_mitigation; - - EntityList::RemoveNumbers(name); - entity_list.MakeNameUnique(name); - - npc_aggro = d->npc_aggro; - - if(!IsMerc()) - AI_Start(); - - d_meele_texture1 = d->d_meele_texture1; - d_meele_texture2 = d->d_meele_texture2; - memset(equipment, 0, sizeof(equipment)); - prim_melee_type = d->prim_melee_type; - sec_melee_type = d->sec_melee_type; - - // If Melee Textures are not set, set attack type to Hand to Hand as default - if(!d_meele_texture1) - prim_melee_type = 28; - if(!d_meele_texture2) - sec_melee_type = 28; - - //give NPCs skill values... - int r; - for(r = 0; r <= HIGHEST_SKILL; r++) { - skills[r] = database.GetSkillCap(GetClass(),(SkillUseTypes)r,moblevel); - } - - if(d->trap_template > 0) - { - std::map >::iterator trap_ent_iter; - std::list trap_list; - - trap_ent_iter = zone->ldon_trap_entry_list.find(d->trap_template); - if(trap_ent_iter != zone->ldon_trap_entry_list.end()) - { - trap_list = trap_ent_iter->second; - if(trap_list.size() > 0) - { - std::list::iterator trap_list_iter = trap_list.begin(); - std::advance(trap_list_iter, MakeRandomInt(0, trap_list.size() - 1)); - LDoNTrapTemplate* tt = (*trap_list_iter); - if(tt) - { - if((uint8)tt->spell_id > 0) - { - ldon_trapped = true; - ldon_spell_id = tt->spell_id; - } - else - { - ldon_trapped = false; - ldon_spell_id = 0; - } - - ldon_trap_type = (uint8)tt->type; - if(tt->locked > 0) - { - ldon_locked = true; - ldon_locked_skill = tt->skill; - } - else - { - ldon_locked = false; - ldon_locked_skill = 0; - } - ldon_trap_detected = 0; - } - } - else - { - ldon_trapped = false; - ldon_trap_type = 0; - ldon_spell_id = 0; - ldon_locked = false; - ldon_locked_skill = 0; - ldon_trap_detected = 0; - } - } - else - { - ldon_trapped = false; - ldon_trap_type = 0; - ldon_spell_id = 0; - ldon_locked = false; - ldon_locked_skill = 0; - ldon_trap_detected = 0; - } - } - else - { - ldon_trapped = false; - ldon_trap_type = 0; - ldon_spell_id = 0; - ldon_locked = false; - ldon_locked_skill = 0; - ldon_trap_detected = 0; - } - reface_timer = new Timer(15000); - reface_timer->Disable(); - qGlobals = nullptr; - guard_x_saved = 0; - guard_y_saved = 0; - guard_z_saved = 0; - guard_heading_saved = 0; - SetEmoteID(d->emoteid); - InitializeBuffSlots(); - CalcBonuses(); -} - -NPC::~NPC() -{ - AI_Stop(); - - if(proximity != nullptr) { - entity_list.RemoveProximity(GetID()); - safe_delete(proximity); - } - - safe_delete(NPCTypedata_ours); - - { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - safe_delete(item); - } - itemlist.clear(); - } - - { - std::list::iterator cur,end; - cur = faction_list.begin(); - end = faction_list.end(); - for(; cur != end; ++cur) { - struct NPCFaction* fac = *cur; - safe_delete(fac); - } - faction_list.clear(); - } - - safe_delete(reface_timer); - safe_delete(swarmInfoPtr); - safe_delete(qGlobals); - //Moved this from ~Mob, because it could cause a crash in some cases where a hate list was still used and that mob was the only one on the hate list. - entity_list.RemoveFromTargets(this, true); - UninitializeBuffSlots(); -} - -void NPC::SetTarget(Mob* mob) { - if(mob == GetTarget()) //dont bother if they are allready our target - return; - - //our target is already set, do not turn from the course, unless our current target is dead. - if(GetSwarmInfo() && GetTarget() && (GetTarget()->GetHP() > 0)) { - Mob *targ = entity_list.GetMob(GetSwarmInfo()->target); - if(targ != mob){ - return; - } - } - - if (mob) { - SetAttackTimer(); - } else { - ranged_timer.Disable(); - //attack_timer.Disable(); - attack_dw_timer.Disable(); - } - Mob::SetTarget(mob); -} - -ServerLootItem_Struct* NPC::GetItem(int slot_id) { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - if (item->equipSlot == slot_id) { - return item; - } - } - return(nullptr); -} - -void NPC::RemoveItem(uint32 item_id, uint16 quantity, uint16 slot) { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - if (item->item_id == item_id && slot <= 0 && quantity <= 0) { - itemlist.erase(cur); - return; - } - else if (item->item_id == item_id && item->equipSlot == slot && quantity >= 1) { - //std::cout<<"NPC::RemoveItem"<<" equipSlot:"<equipSlot<<" quantity:"<< quantity<charges <= quantity) - itemlist.erase(cur); - else - item->charges -= quantity; - return; - } - } -} - -void NPC::CheckMinMaxLevel(Mob *them) -{ - if(them == nullptr || !them->IsClient()) - return; - - uint16 themlevel = them->GetLevel(); - uint8 material; - - std::list::iterator cur = itemlist.begin(); - while(cur != itemlist.end()) - { - if(!(*cur)) - return; - - if(themlevel < (*cur)->minlevel || themlevel > (*cur)->maxlevel) - { - material = Inventory::CalcMaterialFromSlot((*cur)->equipSlot); - if(material != 0xFF) - SendWearChange(material); - - cur = itemlist.erase(cur); - continue; - } - ++cur; - } - -} - -void NPC::ClearItemList() { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - ServerLootItem_Struct* item = *cur; - safe_delete(item); - } - itemlist.clear(); -} - -void NPC::QueryLoot(Client* to) { - int x = 0; - to->Message(0, "Coin: %ip %ig %is %ic", platinum, gold, silver, copper); - - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end; ++cur) { - const Item_Struct* item = database.GetItem((*cur)->item_id); - if (item) - if (to->GetClientVersion() >= EQClientRoF) - { - to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X0000000000000000000000000000000000000000000000000%s%c",(*cur)->minlevel, (*cur)->maxlevel, (int) item->ID,0x12, item->ID, item->Name, 0x12); - } - else if (to->GetClientVersion() >= EQClientSoF) - { - to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X00000000000000000000000000000000000000000000%s%c",(*cur)->minlevel, (*cur)->maxlevel, (int) item->ID,0x12, item->ID, item->Name, 0x12); - } - else - { - to->Message(0, "minlvl: %i maxlvl: %i %i: %c%06X000000000000000000000000000000000000000%s%c",(*cur)->minlevel, (*cur)->maxlevel, (int) item->ID,0x12, item->ID, item->Name, 0x12); - } - else - LogFile->write(EQEMuLog::Error, "Database error, invalid item"); - x++; - } - to->Message(0, "%i items on %s.", x, GetName()); -} - -void NPC::AddCash(uint16 in_copper, uint16 in_silver, uint16 in_gold, uint16 in_platinum) { - if(in_copper >= 0) - copper = in_copper; - else - copper = 0; - - if(in_silver >= 0) - silver = in_silver; - else - silver = 0; - - if(in_gold >= 0) - gold = in_gold; - else - gold = 0; - - if(in_platinum >= 0) - platinum = in_platinum; - else - platinum = 0; -} - -void NPC::AddCash() { - copper = MakeRandomInt(1, 100); - silver = MakeRandomInt(1, 50); - gold = MakeRandomInt(1, 10); - platinum = MakeRandomInt(1, 5); -} - -void NPC::RemoveCash() { - copper = 0; - silver = 0; - gold = 0; - platinum = 0; -} - -bool NPC::Process() -{ - if (IsStunned() && stunned_timer.Check()) - { - this->stunned = false; - this->stunned_timer.Disable(); - this->spun_timer.Disable(); - } - - if (p_depop) - { - Mob* owner = entity_list.GetMob(this->ownerid); - if (owner != 0) - { - //if(GetBodyType() != BT_SwarmPet) - // owner->SetPetID(0); - this->ownerid = 0; - this->petid = 0; - } - return false; - } - - SpellProcess(); - - if(tic_timer.Check()) - { - BuffProcess(); - - if(curfp) - ProcessFlee(); - - uint32 bonus = 0; - - if(GetAppearance() == eaSitting) - bonus+=3; - - int32 OOCRegen = 0; - if(oocregen > 0){ //should pull from Mob class - OOCRegen += GetMaxHP() * oocregen / 100; - } - //Lieka Edit:Fixing NPC regen.NPCs should regen to full during a set duration, not based on their HPs.Increase NPC's HPs by % of total HPs / tick. - if((GetHP() < GetMaxHP()) && !IsPet()) { - if(!IsEngaged()) {//NPC out of combat - if(hp_regen > OOCRegen) - SetHP(GetHP() + hp_regen); - else - SetHP(GetHP() + OOCRegen); - } else - SetHP(GetHP()+hp_regen); - } else if(GetHP() < GetMaxHP() && GetOwnerID() !=0) { - if(!IsEngaged()) //pet - SetHP(GetHP()+hp_regen+bonus+(GetLevel()/5)); - else - SetHP(GetHP()+hp_regen+bonus); - } else - SetHP(GetHP()+hp_regen); - - if(GetMana() < GetMaxMana()) { - SetMana(GetMana()+mana_regen+bonus); - } - - - if(zone->adv_data && !p_depop) - { - ServerZoneAdventureDataReply_Struct* ds = (ServerZoneAdventureDataReply_Struct*)zone->adv_data; - if(ds->type == Adventure_Rescue && ds->data_id == GetNPCTypeID()) - { - Mob *o = GetOwner(); - if(o && o->IsClient()) - { - float x_diff = ds->dest_x - GetX(); - float y_diff = ds->dest_y - GetY(); - float z_diff = ds->dest_z - GetZ(); - float dist = ((x_diff * x_diff) + (y_diff * y_diff) + (z_diff * z_diff)); - if(dist < RuleR(Adventure, DistanceForRescueComplete)) - { - zone->DoAdventureCountIncrease(); - Say("You don't know what this means to me. Thank you so much for finding and saving me from" - " this wretched place. I'll find my way from here."); - Depop(); - } - } - } - } - } - - if (sendhpupdate_timer.Check() && (IsTargeted() || (IsPet() && GetOwner() && GetOwner()->IsClient()))) { - if(!IsFullHP || cur_hp 999) - viral_timer_counter = 0; - } - - if(projectile_timer.Check()) - SpellProjectileEffect(); - - if(spellbonuses.GravityEffect == 1) { - if(gravity_timer.Check()) - DoGravityEffect(); - } - - if(reface_timer->Check() && !IsEngaged() && (guard_x == GetX() && guard_y == GetY() && guard_z == GetZ())) { - SetHeading(guard_heading); - SendPosition(); - reface_timer->Disable(); - } - - if (IsMezzed()) - return true; - - if(IsStunned()) { - if(spun_timer.Check()) - Spin(); - return true; - } - - if (enraged_timer.Check()){ - ProcessEnrage(); - } - - //Handle assists... - if(assist_timer.Check() && IsEngaged() && !Charmed()) { - entity_list.AIYellForHelp(this, GetTarget()); - } - - if(qGlobals) - { - if(qglobal_purge_timer.Check()) - { - qGlobals->PurgeExpiredGlobals(); - } - } - - AI_Process(); - - return true; -} - -uint32 NPC::CountLoot() { - return(itemlist.size()); -} - -void NPC::Depop(bool StartSpawnTimer) { - uint16 emoteid = this->GetEmoteID(); - if(emoteid != 0) - this->DoNPCEmote(ONDESPAWN,emoteid); - p_depop = true; - if (StartSpawnTimer) { - if (respawn2 != 0) { - respawn2->DeathReset(); - } - } -} - -bool NPC::DatabaseCastAccepted(int spell_id) { - for (int i=0; i < 12; i++) { - switch(spells[spell_id].effectid[i]) { - case SE_Stamina: { - if(IsEngaged() && GetHPRatio() < 100) - return true; - else - return false; - break; - } - case SE_CurrentHPOnce: - case SE_CurrentHP: { - if(this->GetHPRatio() < 100 && spells[spell_id].buffduration == 0) - return true; - else - return false; - break; - } - - case SE_HealOverTime: { - if(this->GetHPRatio() < 100) - return true; - else - return false; - break; - } - case SE_DamageShield: { - return true; - } - case SE_NecPet: - case SE_SummonPet: { - if(GetPet()){ -#ifdef SPELLQUEUE - printf("%s: Attempted to make a second pet, denied.\n",GetName()); -#endif - return false; - } - break; - } - case SE_LocateCorpse: - case SE_SummonCorpse: { - return false; //Pfft, npcs don't need to summon corpses/locate corpses! - break; - } - default: - if(spells[spell_id].goodEffect == 1 && !(spells[spell_id].buffduration == 0 && this->GetHPRatio() == 100) && !IsEngaged()) - return true; - return false; - } - } - return false; -} - -NPC* NPC::SpawnNPC(const char* spawncommand, float in_x, float in_y, float in_z, float in_heading, Client* client) { - if(spawncommand == 0 || spawncommand[0] == 0) { - return 0; - } - else { - Seperator sep(spawncommand); - //Lets see if someone didn't fill out the whole #spawn function properly - if (!sep.IsNumber(1)) - sprintf(sep.arg[1],"1"); - if (!sep.IsNumber(2)) - sprintf(sep.arg[2],"1"); - if (!sep.IsNumber(3)) - sprintf(sep.arg[3],"0"); - if (atoi(sep.arg[4]) > 2100000000 || atoi(sep.arg[4]) <= 0) - sprintf(sep.arg[4]," "); - if (!strcmp(sep.arg[5],"-")) - sprintf(sep.arg[5]," "); - if (!sep.IsNumber(5)) - sprintf(sep.arg[5]," "); - if (!sep.IsNumber(6)) - sprintf(sep.arg[6],"1"); - if (!sep.IsNumber(8)) - sprintf(sep.arg[8],"0"); - if (!sep.IsNumber(9)) - sprintf(sep.arg[9], "0"); - if (!sep.IsNumber(7)) - sprintf(sep.arg[7],"0"); - if (!strcmp(sep.arg[4],"-")) - sprintf(sep.arg[4]," "); - if (!sep.IsNumber(10)) // bodytype - sprintf(sep.arg[10], "0"); - //Calc MaxHP if client neglected to enter it... - if (!sep.IsNumber(4)) { - //Stolen from Client::GetMaxHP... - uint8 multiplier = 0; - int tmplevel = atoi(sep.arg[2]); - switch(atoi(sep.arg[5])) - { - case WARRIOR: - if (tmplevel < 20) - multiplier = 22; - else if (tmplevel < 30) - multiplier = 23; - else if (tmplevel < 40) - multiplier = 25; - else if (tmplevel < 53) - multiplier = 27; - else if (tmplevel < 57) - multiplier = 28; - else - multiplier = 30; - break; - - case DRUID: - case CLERIC: - case SHAMAN: - multiplier = 15; - break; - - case PALADIN: - case SHADOWKNIGHT: - if (tmplevel < 35) - multiplier = 21; - else if (tmplevel < 45) - multiplier = 22; - else if (tmplevel < 51) - multiplier = 23; - else if (tmplevel < 56) - multiplier = 24; - else if (tmplevel < 60) - multiplier = 25; - else - multiplier = 26; - break; - - case MONK: - case BARD: - case ROGUE: - //case BEASTLORD: - if (tmplevel < 51) - multiplier = 18; - else if (tmplevel < 58) - multiplier = 19; - else - multiplier = 20; - break; - - case RANGER: - if (tmplevel < 58) - multiplier = 20; - else - multiplier = 21; - break; - - case MAGICIAN: - case WIZARD: - case NECROMANCER: - case ENCHANTER: - multiplier = 12; - break; - - default: - if (tmplevel < 35) - multiplier = 21; - else if (tmplevel < 45) - multiplier = 22; - else if (tmplevel < 51) - multiplier = 23; - else if (tmplevel < 56) - multiplier = 24; - else if (tmplevel < 60) - multiplier = 25; - else - multiplier = 26; - break; - } - sprintf(sep.arg[4],"%i",5+multiplier*atoi(sep.arg[2])+multiplier*atoi(sep.arg[2])*75/300); - } - - // Autoselect NPC Gender - if (sep.arg[5][0] == 0) { - sprintf(sep.arg[5], "%i", (int) Mob::GetDefaultGender(atoi(sep.arg[1]))); - } - - //Time to create the NPC!! - NPCType* npc_type = new NPCType; - memset(npc_type, 0, sizeof(NPCType)); - - strncpy(npc_type->name, sep.arg[0], 60); - npc_type->cur_hp = atoi(sep.arg[4]); - npc_type->max_hp = atoi(sep.arg[4]); - npc_type->race = atoi(sep.arg[1]); - npc_type->gender = atoi(sep.arg[5]); - npc_type->class_ = atoi(sep.arg[6]); - npc_type->deity = 1; - npc_type->level = atoi(sep.arg[2]); - npc_type->npc_id = 0; - npc_type->loottable_id = 0; - npc_type->texture = atoi(sep.arg[3]); - npc_type->light = 0; - npc_type->runspeed = 1.25; - npc_type->d_meele_texture1 = atoi(sep.arg[7]); - npc_type->d_meele_texture2 = atoi(sep.arg[8]); - npc_type->merchanttype = atoi(sep.arg[9]); - npc_type->bodytype = atoi(sep.arg[10]); - - npc_type->STR = 150; - npc_type->STA = 150; - npc_type->DEX = 150; - npc_type->AGI = 150; - npc_type->INT = 150; - npc_type->WIS = 150; - npc_type->CHA = 150; - - npc_type->prim_melee_type = 28; - npc_type->sec_melee_type = 28; - - NPC* npc = new NPC(npc_type, 0, in_x, in_y, in_z, in_heading/8, FlyMode3); - npc->GiveNPCTypeData(npc_type); - - entity_list.AddNPC(npc); - - if (client) { - // Notify client of spawn data - client->Message(0, "New spawn:"); - client->Message(0, "Name: %s", npc->name); - client->Message(0, "Race: %u", npc->race); - client->Message(0, "Level: %u", npc->level); - client->Message(0, "Material: %u", npc->texture); - client->Message(0, "Current/Max HP: %i", npc->max_hp); - client->Message(0, "Gender: %u", npc->gender); - client->Message(0, "Class: %u", npc->class_); - client->Message(0, "Weapon Item Number: %u/%u", npc->d_meele_texture1, npc->d_meele_texture2); - client->Message(0, "MerchantID: %u", npc->MerchantType); - client->Message(0, "Bodytype: %u", npc->bodytype); - } - - return npc; - } -} - -uint32 ZoneDatabase::NPCSpawnDB(uint8 command, const char* zone, uint32 zone_version, Client *c, NPC* spawn, uint32 extra) { - char errbuf[MYSQL_ERRMSG_SIZE]; - char *query = 0; - MYSQL_RES *result; - MYSQL_ROW row; - uint32 tmp = 0; - uint32 tmp2 = 0; - uint32 last_insert_id = 0; - switch (command) { - case 0: { // Create a new NPC and add all spawn related data - uint32 npc_type_id = 0; - uint32 spawngroupid; - if (extra && c && c->GetZoneID()) - { - // Set an npc_type ID within the standard range for the current zone if possible (zone_id * 1000) - int starting_npc_id = c->GetZoneID() * 1000; - if (RunQuery(query, MakeAnyLenString(&query, "SELECT MAX(id) FROM npc_types WHERE id >= %i AND id < %i", starting_npc_id, (starting_npc_id + 1000)), errbuf, &result)) { - row = mysql_fetch_row(result); - if(row) - { - if (row[0]) - { - npc_type_id = atoi(row[0]) + 1; - // Prevent the npc_type id from exceeding the range for this zone - if (npc_type_id >= (starting_npc_id + 1000)) - { - npc_type_id = 0; - } - } - else - { - // row[0] is nullptr - No npc_type IDs set in this range yet - npc_type_id = starting_npc_id; - } - } - - safe_delete_array(query); - mysql_free_result(result); - } - } - char tmpstr[64]; - EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); - if (npc_type_id) - { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (id, name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(%i,\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", npc_type_id, tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - } - else - { - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - } - if(c) c->LogSQL(query); - safe_delete_array(query); - snprintf(tmpstr, sizeof(tmpstr), "%s-%s", zone, spawn->GetName()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (id, name) values(%i, '%s')", tmp, tmpstr), errbuf, 0, 0, &spawngroupid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), 1200, spawn->GetHeading(), spawngroupid), errbuf, 0, 0, &tmp)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", spawngroupid, npc_type_id, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - break; - } - case 1:{ // Add new spawn group and spawn point for an existing NPC Type ID - tmp2 = spawn->GetNPCTypeID(); - char tmpstr[64]; - snprintf(tmpstr, sizeof(tmpstr), "%s%s%i", zone, spawn->GetName(),Timer::GetCurrentTime()); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawngroup (name) values('%s')", tmpstr), errbuf, 0, 0, &last_insert_id)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - uint32 respawntime = 0; - uint32 spawnid = 0; - if (extra) - respawntime = extra; - else if(spawn->respawn2 && spawn->respawn2->RespawnTimer() != 0) - respawntime = spawn->respawn2->RespawnTimer(); - else - respawntime = 1200; - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, spawn->GetX(), spawn->GetY(), spawn->GetZ(), respawntime, spawn->GetHeading(), last_insert_id), errbuf, 0, 0, &spawnid)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawnentry (spawngroupID, npcID, chance) values(%i, %i, %i)", last_insert_id, tmp2, 100), errbuf, 0)) { - LogFile->write(EQEMuLog::Error, "NPCSpawnDB Error: %s %s", query, errbuf); - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return spawnid; - break; - } - case 2: { // Update npc_type appearance and other data on targeted spawn - if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE npc_types SET name=\"%s\", level=%i, race=%i, class=%i, hp=%i, gender=%i, texture=%i, helmtexture=%i, size=%i, loottable_id=%i, merchant_id=%i, face=%i, WHERE id=%i", spawn->GetName(), spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, spawn->GetNPCTypeID()), errbuf, 0)) { - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - } - else { - safe_delete_array(query); - return false; - } - break; - } - case 3: { // delete spawn from spawning, but leave in npc_types table - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND spawngroupID=%i", zone, spawn->GetSp2()), errbuf, &result)) { - safe_delete_array(query); - return 0; - } - safe_delete_array(query); - - row = mysql_fetch_row(result); - if (row == nullptr) return false; - if (row[0]) tmp = atoi(row[0]); - if (row[1]) tmp2 = atoi(row[1]); - - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - - - break; - } - case 4: { //delete spawn from DB (including npc_type) - if (!RunQuery(query, MakeAnyLenString(&query, "SELECT id,spawngroupID from spawn2 where zone='%s' AND version=%u AND spawngroupID=%i", zone, zone_version, spawn->GetSp2()), errbuf, &result)) { - safe_delete_array(query); - return(0); - } - safe_delete_array(query); - - row = mysql_fetch_row(result); - if (row == nullptr) return false; - if (row[0]) tmp = atoi(row[0]); - if (row[1]) tmp2 = atoi(row[1]); - mysql_free_result(result); - - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawn2 WHERE id='%i'", tmp), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawngroup WHERE id='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM spawnentry WHERE spawngroupID='%i'", tmp2), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if (!RunQuery(query, MakeAnyLenString(&query, "DELETE FROM npc_types WHERE id='%i'", spawn->GetNPCTypeID()), errbuf,0)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - return true; - break; - } - case 5: { // add a spawn from spawngroup - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO spawn2 (zone, version, x, y, z, respawntime, heading, spawngroupID) values('%s', %u, %f, %f, %f, %i, %f, %i)", zone, zone_version, c->GetX(), c->GetY(), c->GetZ(), 120, c->GetHeading(), extra), errbuf, 0, 0, &tmp)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - - return true; - break; - } - case 6: { // add npc_type - uint32 npc_type_id; - char tmpstr[64]; - EntityList::RemoveNumbers(strn0cpy(tmpstr, spawn->GetName(), sizeof(tmpstr))); - if (!RunQuery(query, MakeAnyLenString(&query, "INSERT INTO npc_types (name, level, race, class, hp, gender, texture, helmtexture, size, loottable_id, merchant_id, face, runspeed, prim_melee_type, sec_melee_type) values(\"%s\",%i,%i,%i,%i,%i,%i,%i,%f,%i,%i,%i,%f,%i,%i)", tmpstr, spawn->GetLevel(), spawn->GetRace(), spawn->GetClass(), spawn->GetMaxHP(), spawn->GetGender(), spawn->GetTexture(), spawn->GetHelmTexture(), spawn->GetSize(), spawn->GetLoottableID(), spawn->MerchantType, 0, spawn->GetRunspeed(), 28, 28), errbuf, 0, 0, &npc_type_id)) { - safe_delete(query); - return false; - } - if(c) c->LogSQL(query); - safe_delete_array(query); - if(c) c->Message(0, "%s npc_type ID %i created successfully!", tmpstr, npc_type_id); - return true; - break; - } - } - return false; -} - -int32 NPC::GetEquipmentMaterial(uint8 material_slot) const -{ - if (material_slot >= _MaterialCount) - return 0; - - int inv_slot = Inventory::CalcSlotFromMaterial(material_slot); - if (inv_slot == -1) - return 0; - if(equipment[inv_slot] == 0) { - switch(material_slot) { - case MaterialHead: - return helmtexture; - case MaterialChest: - return texture; - case MaterialPrimary: - return d_meele_texture1; - case MaterialSecondary: - return d_meele_texture2; - default: - //they have nothing in the slot, and its not a special slot... they get nothing. - return(0); - } - } - - //they have some loot item in this slot, pass it up to the default handler - return(Mob::GetEquipmentMaterial(material_slot)); -} - -uint32 NPC::GetMaxDamage(uint8 tlevel) -{ - uint32 dmg = 0; - if (tlevel < 40) - dmg = tlevel*2+2; - else if (tlevel < 50) - dmg = level*25/10+2; - else if (tlevel < 60) - dmg = (tlevel*3+2)+((tlevel-50)*30); - else - dmg = (tlevel*3+2)+((tlevel-50)*35); - return dmg; -} - -void NPC::PickPocket(Client* thief) { - - thief->CheckIncreaseSkill(SkillPickPockets, nullptr, 5); - - //make sure were allowed to targte them: - int olevel = GetLevel(); - if(olevel > (thief->GetLevel() + THIEF_PICKPOCKET_OVER)) { - thief->Message(13, "You are too inexperienced to pick pocket this target"); - thief->SendPickPocketResponse(this, 0, PickPocketFailed); - //should we check aggro - return; - } - - if(MakeRandomInt(0, 100) > 95){ - AddToHateList(thief, 50); - Say("Stop thief!"); - thief->Message(13, "You are noticed trying to steal!"); - thief->SendPickPocketResponse(this, 0, PickPocketFailed); - return; - } - - int steal_skill = thief->GetSkill(SkillPickPockets); - int stealchance = steal_skill*100/(5*olevel+5); - ItemInst* inst = 0; - int x = 0; - int slot[50]; - int steal_items[50]; - int charges[50]; - int money[4]; - money[0] = GetPlatinum(); - money[1] = GetGold(); - money[2] = GetSilver(); - money[3] = GetCopper(); - if (steal_skill < 125) - money[0] = 0; - if (steal_skill < 60) - money[1] = 0; - memset(slot,0,50); - memset(steal_items,0,50); - memset(charges,0,50); - //Determine wheter to steal money or an item. - bool no_coin = ((money[0] + money[1] + money[2] + money[3]) == 0); - bool steal_item = (MakeRandomInt(0, 99) < 50 || no_coin); - if (steal_item) - { - ItemList::iterator cur,end; - cur = itemlist.begin(); - end = itemlist.end(); - for(; cur != end && x < 49; ++cur) { - ServerLootItem_Struct* citem = *cur; - const Item_Struct* item = database.GetItem(citem->item_id); - if (item) - { - inst = database.CreateItem(item, citem->charges); - bool is_arrow = (item->ItemType == ItemTypeArrow) ? true : false; - int slot_id = thief->GetInv().FindFreeSlot(false, true, inst->GetItem()->Size, is_arrow); - if (/*!Equipped(item->ID) &&*/ - !item->Magic && item->NoDrop != 0 && !inst->IsType(ItemClassContainer) && slot_id != SLOT_INVALID - /*&& steal_skill > item->StealSkill*/ ) - { - slot[x] = slot_id; - steal_items[x] = item->ID; - if (inst->IsStackable()) - charges[x] = 1; - else - charges[x] = citem->charges; - x++; - } - } - } - if (x > 0) - { - int random = MakeRandomInt(0, x-1); - inst = database.CreateItem(steal_items[random], charges[random]); - if (inst) - { - const Item_Struct* item = inst->GetItem(); - if (item) - { - if (/*item->StealSkill || */steal_skill >= stealchance) - { - thief->PutItemInInventory(slot[random], *inst); - thief->SendItemPacket(slot[random], inst, ItemPacketTrade); - RemoveItem(item->ID); - thief->SendPickPocketResponse(this, 0, PickPocketItem, item); - } - else - steal_item = false; - } - else - steal_item = false; - } - else - steal_item = false; - } - else if (!no_coin) - { - steal_item = false; - } - else - { - thief->Message(0, "This target's pockets are empty"); - thief->SendPickPocketResponse(this, 0, PickPocketFailed); - } - } - if (!steal_item) //Steal money - { - uint32 amt = MakeRandomInt(1, (steal_skill/25)+1); - int steal_type = 0; - if (!money[0]) - { - steal_type = 1; - if (!money[1]) - { - steal_type = 2; - if (!money[2]) - { - steal_type = 3; - } - } - } - - if (MakeRandomInt(0, 100) <= stealchance) - { - switch (steal_type) - { - case 0:{ - if (amt > GetPlatinum()) - amt = GetPlatinum(); - SetPlatinum(GetPlatinum()-amt); - thief->AddMoneyToPP(0,0,0,amt,false); - thief->SendPickPocketResponse(this, amt, PickPocketPlatinum); - break; - } - case 1:{ - if (amt > GetGold()) - amt = GetGold(); - SetGold(GetGold()-amt); - thief->AddMoneyToPP(0,0,amt,0,false); - thief->SendPickPocketResponse(this, amt, PickPocketGold); - break; - } - case 2:{ - if (amt > GetSilver()) - amt = GetSilver(); - SetSilver(GetSilver()-amt); - thief->AddMoneyToPP(0,amt,0,0,false); - thief->SendPickPocketResponse(this, amt, PickPocketSilver); - break; - } - case 3:{ - if (amt > GetCopper()) - amt = GetCopper(); - SetCopper(GetCopper()-amt); - thief->AddMoneyToPP(amt,0,0,0,false); - thief->SendPickPocketResponse(this, amt, PickPocketCopper); - break; - } - } - } - else - { - thief->SendPickPocketResponse(this, 0, PickPocketFailed); - } - } - safe_delete(inst); -} - -void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool remove) { - if(reset) - { - ClearSpecialAbilities(); - } - - const char* orig_parse = parse; - while (*parse) - { - switch(*parse) - { - case 'E': - SetSpecialAbility(SPECATK_ENRAGE, remove ? 0 : 1); - break; - case 'F': - SetSpecialAbility(SPECATK_FLURRY, remove ? 0 : 1); - break; - case 'R': - SetSpecialAbility(SPECATK_RAMPAGE, remove ? 0 : 1); - break; - case 'r': - SetSpecialAbility(SPECATK_AREA_RAMPAGE, remove ? 0 : 1); - break; - case 'S': - if(remove) { - SetSpecialAbility(SPECATK_SUMMON, 0); - StopSpecialAbilityTimer(SPECATK_SUMMON); - } else { - SetSpecialAbility(SPECATK_SUMMON, 1); - } - break; - case 'T': - SetSpecialAbility(SPECATK_TRIPLE, remove ? 0 : 1); - break; - case 'Q': - //quad requires triple to work properly - if(remove) { - SetSpecialAbility(SPECATK_QUAD, 0); - } else { - SetSpecialAbility(SPECATK_TRIPLE, 1); - SetSpecialAbility(SPECATK_QUAD, 1); - } - break; - case 'b': - SetSpecialAbility(SPECATK_BANE, remove ? 0 : 1); - break; - case 'm': - SetSpecialAbility(SPECATK_MAGICAL, remove ? 0 : 1); - break; - case 'U': - SetSpecialAbility(UNSLOWABLE, remove ? 0 : 1); - break; - case 'M': - SetSpecialAbility(UNMEZABLE, remove ? 0 : 1); - break; - case 'C': - SetSpecialAbility(UNCHARMABLE, remove ? 0 : 1); - break; - case 'N': - SetSpecialAbility(UNSTUNABLE, remove ? 0 : 1); - break; - case 'I': - SetSpecialAbility(UNSNAREABLE, remove ? 0 : 1); - break; - case 'D': - SetSpecialAbility(UNFEARABLE, remove ? 0 : 1); - break; - case 'K': - SetSpecialAbility(UNDISPELLABLE, remove ? 0 : 1); - break; - case 'A': - SetSpecialAbility(IMMUNE_MELEE, remove ? 0 : 1); - break; - case 'B': - SetSpecialAbility(IMMUNE_MAGIC, remove ? 0 : 1); - break; - case 'f': - SetSpecialAbility(IMMUNE_FLEEING, remove ? 0 : 1); - break; - case 'O': - SetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE, remove ? 0 : 1); - break; - case 'W': - SetSpecialAbility(IMMUNE_MELEE_NONMAGICAL, remove ? 0 : 1); - break; - case 'H': - SetSpecialAbility(IMMUNE_AGGRO, remove ? 0 : 1); - break; - case 'G': - SetSpecialAbility(IMMUNE_AGGRO_ON, remove ? 0 : 1); - break; - case 'g': - SetSpecialAbility(IMMUNE_CASTING_FROM_RANGE, remove ? 0 : 1); - break; - case 'd': - SetSpecialAbility(IMMUNE_FEIGN_DEATH, remove ? 0 : 1); - break; - case 'Y': - SetSpecialAbility(SPECATK_RANGED_ATK, remove ? 0 : 1); - break; - case 'L': - SetSpecialAbility(SPECATK_INNATE_DW, remove ? 0 : 1); - break; - case 't': - SetSpecialAbility(NPC_TUNNELVISION, remove ? 0 : 1); - break; - case 'n': - SetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS, remove ? 0 : 1); - break; - case 'p': - SetSpecialAbility(IMMUNE_PACIFY, remove ? 0 : 1); - break; - case 'J': - SetSpecialAbility(LEASH, remove ? 0 : 1); - break; - case 'j': - SetSpecialAbility(TETHER, remove ? 0 : 1); - break; - case 'o': - SetSpecialAbility(DESTRUCTIBLE_OBJECT, remove ? 0 : 1); - SetDestructibleObject(remove ? true : false); - break; - case 'Z': - SetSpecialAbility(NO_HARM_FROM_CLIENT, remove ? 0 : 1); - break; - case 'i': - SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1); - break; - case 'e': - SetSpecialAbility(ALWAYS_FLEE, remove ? 0 : 1); - break; - case 'h': - SetSpecialAbility(FLEE_PERCENT, remove ? 0 : 1); - break; - - default: - break; - } - parse++; - } - - if(permtag == 1 && this->GetNPCTypeID() > 0) - { - if(database.SetSpecialAttkFlag(this->GetNPCTypeID(), orig_parse)) - { - LogFile->write(EQEMuLog::Normal, "NPCTypeID: %i flagged to '%s' for Special Attacks.\n",this->GetNPCTypeID(),orig_parse); - } - } -} - -bool Mob::HasNPCSpecialAtk(const char* parse) { - - bool HasAllAttacks = true; - - while (*parse && HasAllAttacks == true) - { - switch(*parse) - { - case 'E': - if (!GetSpecialAbility(SPECATK_ENRAGE)) - HasAllAttacks = false; - break; - case 'F': - if (!GetSpecialAbility(SPECATK_FLURRY)) - HasAllAttacks = false; - break; - case 'R': - if (!GetSpecialAbility(SPECATK_RAMPAGE)) - HasAllAttacks = false; - break; - case 'r': - if (!GetSpecialAbility(SPECATK_AREA_RAMPAGE)) - HasAllAttacks = false; - break; - case 'S': - if (!GetSpecialAbility(SPECATK_SUMMON)) - HasAllAttacks = false; - break; - case 'T': - if (!GetSpecialAbility(SPECATK_TRIPLE)) - HasAllAttacks = false; - break; - case 'Q': - if (!GetSpecialAbility(SPECATK_QUAD)) - HasAllAttacks = false; - break; - case 'b': - if (!GetSpecialAbility(SPECATK_BANE)) - HasAllAttacks = false; - break; - case 'm': - if (!GetSpecialAbility(SPECATK_MAGICAL)) - HasAllAttacks = false; - break; - case 'U': - if (!GetSpecialAbility(UNSLOWABLE)) - HasAllAttacks = false; - break; - case 'M': - if (!GetSpecialAbility(UNMEZABLE)) - HasAllAttacks = false; - break; - case 'C': - if (!GetSpecialAbility(UNCHARMABLE)) - HasAllAttacks = false; - break; - case 'N': - if (!GetSpecialAbility(UNSTUNABLE)) - HasAllAttacks = false; - break; - case 'I': - if (!GetSpecialAbility(UNSNAREABLE)) - HasAllAttacks = false; - break; - case 'D': - if (!GetSpecialAbility(UNFEARABLE)) - HasAllAttacks = false; - break; - case 'A': - if (!GetSpecialAbility(IMMUNE_MELEE)) - HasAllAttacks = false; - break; - case 'B': - if (!GetSpecialAbility(IMMUNE_MAGIC)) - HasAllAttacks = false; - break; - case 'f': - if (!GetSpecialAbility(IMMUNE_FLEEING)) - HasAllAttacks = false; - break; - case 'O': - if (!GetSpecialAbility(IMMUNE_MELEE_EXCEPT_BANE)) - HasAllAttacks = false; - break; - case 'W': - if (!GetSpecialAbility(IMMUNE_MELEE_NONMAGICAL)) - HasAllAttacks = false; - break; - case 'H': - if (!GetSpecialAbility(IMMUNE_AGGRO)) - HasAllAttacks = false; - break; - case 'G': - if (!GetSpecialAbility(IMMUNE_AGGRO_ON)) - HasAllAttacks = false; - break; - case 'g': - if (!GetSpecialAbility(IMMUNE_CASTING_FROM_RANGE)) - HasAllAttacks = false; - break; - case 'd': - if (!GetSpecialAbility(IMMUNE_FEIGN_DEATH)) - HasAllAttacks = false; - break; - case 'Y': - if (!GetSpecialAbility(SPECATK_RANGED_ATK)) - HasAllAttacks = false; - break; - case 'L': - if (!GetSpecialAbility(SPECATK_INNATE_DW)) - HasAllAttacks = false; - break; - case 't': - if (!GetSpecialAbility(NPC_TUNNELVISION)) - HasAllAttacks = false; - break; - case 'n': - if (!GetSpecialAbility(NPC_NO_BUFFHEAL_FRIENDS)) - HasAllAttacks = false; - break; - case 'p': - if(!GetSpecialAbility(IMMUNE_PACIFY)) - HasAllAttacks = false; - break; - case 'J': - if(!GetSpecialAbility(LEASH)) - HasAllAttacks = false; - break; - case 'j': - if(!GetSpecialAbility(TETHER)) - HasAllAttacks = false; - break; - case 'o': - if(!GetSpecialAbility(DESTRUCTIBLE_OBJECT)) - { - HasAllAttacks = false; - SetDestructibleObject(false); - } - break; - case 'Z': - if(!GetSpecialAbility(NO_HARM_FROM_CLIENT)){ - HasAllAttacks = false; - } - break; - case 'e': - if(!GetSpecialAbility(ALWAYS_FLEE)) - HasAllAttacks = false; - break; - case 'h': - if(!GetSpecialAbility(FLEE_PERCENT)) - HasAllAttacks = false; - break; - default: - HasAllAttacks = false; - break; - } - parse++; - } - - return HasAllAttacks; -} - -void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) -{ - Mob::FillSpawnStruct(ns, ForWho); - - //Basic settings to make sure swarm pets work properly. - if (GetSwarmOwner()) { - Client *c = entity_list.GetClientByID(GetSwarmOwner()); - if(c) { - SetAllowBeneficial(1); //Allow client cast swarm pets to be heal/buffed. - //This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'! - if (RuleB(Pets, SwarmPetNotTargetableWithHotKey)) - ns->spawn.IsMercenary = 1; - } - //NPC cast swarm pets should still be targetable with F8. - else - ns->spawn.IsMercenary = 0; - } - - //Not recommended if using above (However, this will work better on older clients). - if (RuleB(Pets, UnTargetableSwarmPet)) { - if(GetOwnerID() || GetSwarmOwner()) { - ns->spawn.is_pet = 1; - if (!IsCharmed() && GetOwnerID()) { - Client *c = entity_list.GetClientByID(GetOwnerID()); - if(c) - sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); - } - else if (GetSwarmOwner()) { - ns->spawn.bodytype = 11; - if(!IsCharmed()) - { - Client *c = entity_list.GetClientByID(GetSwarmOwner()); - if(c) - sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); - } - } - } - } else { - if(GetOwnerID()) { - ns->spawn.is_pet = 1; - if (!IsCharmed() && GetOwnerID()) { - Client *c = entity_list.GetClientByID(GetOwnerID()); - if(c) - sprintf(ns->spawn.lastName, "%s's Pet", c->GetName()); - } - } else - ns->spawn.is_pet = 0; - } - - ns->spawn.is_npc = 1; -} - -void NPC::SetLevel(uint8 in_level, bool command) -{ - if(in_level > level) - SendLevelAppearance(); - level = in_level; - SendAppearancePacket(AT_WhoLevel, in_level); -} - -void NPC::ModifyNPCStat(const char *identifier, const char *newValue) -{ - std::string id = identifier; - std::string val = newValue; - for(int i = 0; i < id.length(); ++i) - { - id[i] = std::tolower(id[i]); - } - - if(id == "ac") - { - AC = atoi(val.c_str()); - return; - } - - if(id == "str") - { - STR = atoi(val.c_str()); - return; - } - - if(id == "sta") - { - STA = atoi(val.c_str()); - return; - } - - if(id == "agi") - { - AGI = atoi(val.c_str()); - return; - } - - if(id == "dex") - { - DEX = atoi(val.c_str()); - return; - } - - if(id == "wis") - { - WIS = atoi(val.c_str()); - CalcMaxMana(); - return; - } - - if(id == "int" || id == "_int") - { - INT = atoi(val.c_str()); - CalcMaxMana(); - return; - } - - if(id == "cha") - { - CHA = atoi(val.c_str()); - return; - } - - if(id == "max_hp") - { - base_hp = atoi(val.c_str()); - CalcMaxHP(); - if(cur_hp > max_hp) - cur_hp = max_hp; - return; - } - - if(id == "max_mana") - { - npc_mana = atoi(val.c_str()); - CalcMaxMana(); - if(cur_mana > max_mana) - cur_mana = max_mana; - return; - } - - if(id == "mr") - { - MR = atoi(val.c_str()); - return; - } - - if(id == "fr") - { - FR = atoi(val.c_str()); - return; - } - - if(id == "cr") - { - CR = atoi(val.c_str()); - return; - } - - if(id == "pr") - { - PR = atoi(val.c_str()); - return; - } - - if(id == "dr") - { - DR = atoi(val.c_str()); - return; - } - - if(id == "PhR") - { - PhR = atoi(val.c_str()); - return; - } - - if(id == "runspeed") - { - runspeed = (float)atof(val.c_str()); - CalcBonuses(); - return; - } - - if(id == "special_attacks") - { - //Added reset flag. - NPCSpecialAttacks(val.c_str(), 0, 1); - return; - } - - if(id == "attack_speed") - { - attack_speed = (float)atof(val.c_str()); - CalcBonuses(); - return; - } - - if(id == "atk") - { - ATK = atoi(val.c_str()); - return; - } - - if(id == "accuracy") - { - accuracy_rating = atoi(val.c_str()); - return; - } - - if(id == "trackable") - { - trackable = atoi(val.c_str()); - return; - } - - if(id == "min_hit") - { - min_dmg = atoi(val.c_str()); - return; - } - - if(id == "max_hit") - { - max_dmg = atoi(val.c_str()); - return; - } - - if(id == "attack_count") - { - attack_count = atoi(val.c_str()); - return; - } - - if(id == "see_invis") - { - see_invis = atoi(val.c_str()); - return; - } - - if(id == "see_invis_undead") - { - see_invis_undead = atoi(val.c_str()); - return; - } - - if(id == "see_hide") - { - see_hide = atoi(val.c_str()); - return; - } - - if(id == "see_improved_hide") - { - see_improved_hide = atoi(val.c_str()); - return; - } - - if(id == "hp_regen") - { - hp_regen = atoi(val.c_str()); - return; - } - - if(id == "mana_regen") - { - mana_regen = atoi(val.c_str()); - return; - } - - if(id == "level") - { - SetLevel(atoi(val.c_str())); - return; - } - - if(id == "aggro") - { - pAggroRange = atof(val.c_str()); - return; - } - - if(id == "assist") - { - pAssistRange = atof(val.c_str()); - return; - } - - if(id == "slow_mitigation") - { - slow_mitigation = atoi(val.c_str()); - return; - } - if(id == "loottable_id") - { - loottable_id = atof(val.c_str()); - return; - } - if(id == "healscale") - { - healscale = atof(val.c_str()); - return; - } - if(id == "spellscale") - { - spellscale = atof(val.c_str()); - return; - } -} - -void NPC::LevelScale() { - - uint8 random_level = (MakeRandomInt(level, maxlevel)); - - float scaling = (((random_level / (float)level) - 1) * (scalerate / 100.0f)); - - // Compensate for scale rates at low levels so they don't add too much - uint8 scale_adjust = 1; - if(level > 0 && level <= 5) - scale_adjust = 10; - if(level > 5 && level <= 10) - scale_adjust = 5; - if(level > 10 && level <= 15) - scale_adjust = 3; - if(level > 15 && level <= 25) - scale_adjust = 2; - - base_hp += (int)(base_hp * scaling); - max_hp += (int)(max_hp * scaling); - cur_hp = max_hp; - STR += (int)(STR * scaling / scale_adjust); - STA += (int)(STA * scaling / scale_adjust); - AGI += (int)(AGI * scaling / scale_adjust); - DEX += (int)(DEX * scaling / scale_adjust); - INT += (int)(INT * scaling / scale_adjust); - WIS += (int)(WIS * scaling / scale_adjust); - CHA += (int)(CHA * scaling / scale_adjust); - if (MR) - MR += (int)(MR * scaling / scale_adjust); - if (CR) - CR += (int)(CR * scaling / scale_adjust); - if (DR) - DR += (int)(DR * scaling / scale_adjust); - if (FR) - FR += (int)(FR * scaling / scale_adjust); - if (PR) - PR += (int)(PR * scaling / scale_adjust); - - if (max_dmg) - { - max_dmg += (int)(max_dmg * scaling / scale_adjust); - min_dmg += (int)(min_dmg * scaling / scale_adjust); - } - - level = random_level; - - return; -} - -void NPC::CalcNPCResists() { - - if (!MR) - MR = (GetLevel() * 11)/10; - if (!CR) - CR = (GetLevel() * 11)/10; - if (!DR) - DR = (GetLevel() * 11)/10; - if (!FR) - FR = (GetLevel() * 11)/10; - if (!PR) - PR = (GetLevel() * 11)/10; - if (!Corrup) - Corrup = 15; - if (!PhR) - PhR = 10; - return; -} - -void NPC::CalcNPCRegen() { - - // Fix for lazy db-updaters (regen values left at 0) - if (GetCasterClass() != 'N' && mana_regen == 0) - mana_regen = (GetLevel() / 10) + 4; - else if(mana_regen < 0) - mana_regen = 0; - else - mana_regen = mana_regen; - - // Gives low end monsters no regen if set to 0 in database. Should make low end monsters killable - // Might want to lower this to /5 rather than 10. - if(hp_regen == 0) - { - if(GetLevel() <= 6) - hp_regen = 1; - else if(GetLevel() > 6 && GetLevel() <= 10) - hp_regen = 2; - else if(GetLevel() > 10 && GetLevel() <= 15) - hp_regen = 3; - else if(GetLevel() > 15 && GetLevel() <= 20) - hp_regen = 5; - else if(GetLevel() > 20 && GetLevel() <= 30) - hp_regen = 7; - else if(GetLevel() > 30 && GetLevel() <= 35) - hp_regen = 9; - else if(GetLevel() > 35 && GetLevel() <= 40) - hp_regen = 12; - else if(GetLevel() > 40 && GetLevel() <= 45) - hp_regen = 18; - else if(GetLevel() > 45 && GetLevel() <= 50) - hp_regen = 21; - else - hp_regen = 30; - } else if(hp_regen < 0) { - hp_regen = 0; - } else - hp_regen = hp_regen; - - return; -} - -void NPC::CalcNPCDamage() { - - int AC_adjust=12; - - if (GetLevel() >= 66) { - if (min_dmg==0) - min_dmg = 220; - if (max_dmg==0) - max_dmg = ((((99000)*(GetLevel()-64))/400)*AC_adjust/10); - } - else if (GetLevel() >= 60 && GetLevel() <= 65){ - if(min_dmg==0) - min_dmg = (GetLevel()+(GetLevel()/3)); - if(max_dmg==0) - max_dmg = (GetLevel()*3)*AC_adjust/10; - } - else if (GetLevel() >= 51 && GetLevel() <= 59){ - if(min_dmg==0) - min_dmg = (GetLevel()+(GetLevel()/3)); - if(max_dmg==0) - max_dmg = (GetLevel()*3)*AC_adjust/10; - } - else if (GetLevel() >= 40 && GetLevel() <= 50) { - if (min_dmg==0) - min_dmg = GetLevel(); - if(max_dmg==0) - max_dmg = (GetLevel()*3)*AC_adjust/10; - } - else if (GetLevel() >= 28 && GetLevel() <= 39) { - if (min_dmg==0) - min_dmg = GetLevel() / 2; - if (max_dmg==0) - max_dmg = ((GetLevel()*2)+2)*AC_adjust/10; - } - else if (GetLevel() <= 27) { - if (min_dmg==0) - min_dmg=1; - if (max_dmg==0) - max_dmg = (GetLevel()*2)*AC_adjust/10; - } - - int clfact = GetClassLevelFactor(); - min_dmg = (min_dmg * clfact) / 220; - max_dmg = (max_dmg * clfact) / 220; - - return; -} - - -uint32 NPC::GetSpawnPointID() const -{ - if(respawn2) - { - return respawn2->GetID(); - } - return 0; -} - -void NPC::NPCSlotTexture(uint8 slot, uint16 texture) -{ - if (slot == 7) { - d_meele_texture1 = texture; - } - else if (slot == 8) { - d_meele_texture2 = texture; - } - else if (slot < 6) { - // Reserved for texturing individual armor slots - } - return; -} - -uint32 NPC::GetSwarmOwner() -{ - if(GetSwarmInfo() != nullptr) - { - return GetSwarmInfo()->owner_id; - } - return 0; -} - -uint32 NPC::GetSwarmTarget() -{ - if(GetSwarmInfo() != nullptr) - { - return GetSwarmInfo()->target; - } - return 0; -} - -void NPC::SetSwarmTarget(int target_id) -{ - if(GetSwarmInfo() != nullptr) - { - GetSwarmInfo()->target = target_id; - } - return; -} - -int32 NPC::CalcMaxMana() { - if(npc_mana == 0) { - switch (GetCasterClass()) { - case 'I': - max_mana = (((GetINT()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; - break; - case 'W': - max_mana = (((GetWIS()/2)+1) * GetLevel()) + spellbonuses.Mana + itembonuses.Mana; - break; - case 'N': - default: - max_mana = 0; - break; - } - if (max_mana < 0) { - max_mana = 0; - } - - return max_mana; - } else { - switch (GetCasterClass()) { - case 'I': - max_mana = npc_mana + spellbonuses.Mana + itembonuses.Mana; - break; - case 'W': - max_mana = npc_mana + spellbonuses.Mana + itembonuses.Mana; - break; - case 'N': - default: - max_mana = 0; - break; - } - if (max_mana < 0) { - max_mana = 0; - } - - return max_mana; - } -} - -void NPC::SignalNPC(int _signal_id) -{ - signal_q.push_back(_signal_id); -} - -NPC_Emote_Struct* NPC::GetNPCEmote(uint16 emoteid, uint8 event_) { - LinkedListIterator iterator(zone->NPCEmoteList); - iterator.Reset(); - while(iterator.MoreElements()) - { - NPC_Emote_Struct* nes = iterator.GetData(); - if (emoteid == nes->emoteid && event_ == nes->event_) { - return (nes); - } - iterator.Advance(); - } - return (nullptr); -} - -void NPC::DoNPCEmote(uint8 event_, uint16 emoteid) -{ - if(this == nullptr || emoteid == 0) - { - return; - } - - NPC_Emote_Struct* nes = GetNPCEmote(emoteid,event_); - if(nes == nullptr) - { - return; - } - - if(emoteid == nes->emoteid) - { - if(nes->type == 1) - this->Emote("%s",nes->text); - else if(nes->type == 2) - this->Shout("%s",nes->text); - else if(nes->type == 3) - entity_list.MessageClose_StringID(this, true, 200, 10, GENERIC_STRING, nes->text); - else - this->Say("%s",nes->text); - } -} - -bool NPC::CanTalk() -{ - //Races that should be able to talk. (Races up to Titanium) - - uint16 TalkRace[473] = - {1,2,3,4,5,6,7,8,9,10,11,12,0,0,15,16,0,18,19,20,0,0,23,0,25,0,0,0,0,0,0, - 32,0,0,0,0,0,0,39,40,0,0,0,44,0,0,0,0,49,0,51,0,53,54,55,56,57,58,0,0,0, - 62,0,64,65,66,67,0,0,70,71,0,0,0,0,0,77,78,79,0,81,82,0,0,0,86,0,0,0,90, - 0,92,93,94,95,0,0,98,99,0,101,0,103,0,0,0,0,0,0,110,111,112,0,0,0,0,0,0, - 0,0,0,0,123,0,0,126,0,128,0,130,131,0,0,0,0,136,137,0,139,140,0,0,0,144, - 0,0,0,0,0,150,151,152,153,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,183,184,0,0,187,188,189,0,0,0,0,0,195,196,0,198,0,0,0,202,0, - 0,205,0,0,208,0,0,0,0,0,0,0,0,217,0,219,0,0,0,0,0,0,226,0,0,229,230,0,0, - 0,0,235,236,0,238,239,240,241,242,243,244,0,246,247,0,0,0,251,0,0,254,255, - 256,257,0,0,0,0,0,0,0,0,266,267,0,0,270,271,0,0,0,0,0,277,278,0,0,0,0,283, - 284,0,286,0,288,289,290,0,0,0,0,295,296,297,298,299,300,0,0,0,304,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,320,0,322,323,324,325,0,0,0,0,330,331,332,333,334,335, - 336,337,338,339,340,341,342,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,359,360,361,362, - 0,364,365,366,0,368,369,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,385,386,0,0,0,0,0,392, - 393,394,395,396,397,398,0,400,402,0,0,0,0,406,0,408,0,0,411,0,413,0,0,0,417, - 0,0,420,0,0,0,0,425,0,0,0,0,0,0,0,433,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,458,0,0,0,0,0,0,0,0,467,0,0,470,0,0,473}; - - int talk_check = TalkRace[GetRace() - 1]; - - if (TalkRace[GetRace() - 1] > 0) - return true; - - return false; -} - -//this is called with 'this' as the mob being looked at, and -//iOther the mob who is doing the looking. It should figure out -//what iOther thinks about 'this' -FACTION_VALUE NPC::GetReverseFactionCon(Mob* iOther) { - iOther = iOther->GetOwnerOrSelf(); - int primaryFaction= iOther->GetPrimaryFaction(); - - //I am pretty sure that this special faction call is backwards - //and should be iOther->GetSpecialFactionCon(this) - if (primaryFaction < 0) - return GetSpecialFactionCon(iOther); - - if (primaryFaction == 0) - return FACTION_INDIFFERENT; - - //if we are a pet, use our owner's faction stuff - Mob *own = GetOwner(); - if (own != nullptr) - return own->GetReverseFactionCon(iOther); - - //make sure iOther is an npc - //also, if we dont have a faction, then they arnt gunna think anything of us either - if(!iOther->IsNPC() || GetPrimaryFaction() == 0) - return(FACTION_INDIFFERENT); - - //if we get here, iOther is an NPC too - - //otherwise, employ the npc faction stuff - //so we need to look at iOther's faction table to see - //what iOther thinks about our primary faction - return(iOther->CastToNPC()->CheckNPCFactionAlly(GetPrimaryFaction())); -} - -//Look through our faction list and return a faction con based -//on the npc_value for the other person's primary faction in our list. -FACTION_VALUE NPC::CheckNPCFactionAlly(int32 other_faction) { - std::list::iterator cur,end; - cur = faction_list.begin(); - end = faction_list.end(); - for(; cur != end; ++cur) { - struct NPCFaction* fac = *cur; - if ((int32)fac->factionID == other_faction) { - if (fac->npc_value > 0) - return FACTION_ALLY; - else if (fac->npc_value < 0) - return FACTION_SCOWLS; - else - return FACTION_INDIFFERENT; - } - } - return FACTION_INDIFFERENT; -} - -bool NPC::IsFactionListAlly(uint32 other_faction) { - return(CheckNPCFactionAlly(other_faction) == FACTION_ALLY); -} - -int NPC::GetScore() -{ - int lv = std::min(70, (int)GetLevel()); - int basedmg = (lv*2)*(1+(lv / 100)) - (lv / 2); - int minx = 0; - int basehp = 0; - int hpcontrib = 0; - int dmgcontrib = 0; - int spccontrib = 0; - int hp = GetMaxHP(); - int mindmg = min_dmg; - int maxdmg = max_dmg; - int final; - - if(lv < 46) - { - minx = static_cast (ceil( ((lv - (lv / 10.0)) - 1.0) )); - basehp = (lv * 10) + (lv * lv); - } - else - { - minx = static_cast (ceil( ((lv - (lv / 10.0)) - 1.0) - (( lv - 45.0 ) / 2.0) )); - basehp = (lv * 10) + ((lv * lv) * 4); - } - - if(hp > basehp) - { - hpcontrib = static_cast (((hp / static_cast (basehp)) * 1.5)); - if(hpcontrib > 5) { hpcontrib = 5; } - - if(maxdmg > basedmg) - { - dmgcontrib = static_cast (ceil( ((maxdmg / basedmg) * 1.5) )); - } - - if(HasNPCSpecialAtk("E")) { spccontrib++; } //Enrage - if(HasNPCSpecialAtk("F")) { spccontrib++; } //Flurry - if(HasNPCSpecialAtk("R")) { spccontrib++; } //Rampage - if(HasNPCSpecialAtk("r")) { spccontrib++; } //Area Rampage - if(HasNPCSpecialAtk("S")) { spccontrib++; } //Summon - if(HasNPCSpecialAtk("T")) { spccontrib += 2; } //Triple - if(HasNPCSpecialAtk("Q")) { spccontrib += 3; } //Quad - if(HasNPCSpecialAtk("U")) { spccontrib += 5; } //Unslowable - if(HasNPCSpecialAtk("L")) { spccontrib++; } //Innate Dual Wield - } - - if(npc_spells_id > 12) - { - if(lv < 16) - spccontrib++; - else - spccontrib += static_cast (floor(lv/15.0)); - } - - final = minx + hpcontrib + dmgcontrib + spccontrib; - final = std::max(1, final); - final = std::min(100, final); - return(final); -} - -uint32 NPC::GetSpawnKillCount() -{ - uint32 sid = GetSpawnPointID(); - - if(sid > 0) - { - return(zone->GetSpawnKillCount(sid)); - } - - return(0); -} - -void NPC::DoQuestPause(Mob *other) { - if(IsMoving() && !IsOnHatelist(other)) { - PauseWandering(RuleI(NPC, SayPauseTimeInSec)); - FaceTarget(other); - } else if(!IsMoving()) { - FaceTarget(other); - } - -} diff --git a/zone/perl_npcX.cpp b/zone/perl_npcX.cpp deleted file mode 100644 index 957baf7ab..000000000 --- a/zone/perl_npcX.cpp +++ /dev/null @@ -1,2487 +0,0 @@ -/* -* This file was generated automatically by xsubpp version 1.9508 from the -* contents of tmp. Do not edit this file, edit tmp instead. -* -* ANY CHANGES MADE HERE WILL BE LOST! -* -*/ - - -/* EQEMu: Everquest Server Emulator - Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net) - - This program is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; version 2 of the License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY except by those people which sell it, which - are required to give you total support for your newly bought product; - without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -*/ - -#include "../common/features.h" -#ifdef EMBPERL_XS_CLASSES -#include "../common/debug.h" -#include "embperl.h" - -#ifdef seed -#undef seed -#endif - -typedef const char Const_char; - -#include "npc.h" - -#ifdef THIS /* this macro seems to leak out on some systems */ -#undef THIS -#endif - - -XS(XS_NPC_SignalNPC); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SignalNPC) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SignalNPC(THIS, _signal_id)"); - { - NPC * THIS; - int _signal_id = (int)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SignalNPC(_signal_id); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_CheckNPCFactionAlly); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_CheckNPCFactionAlly) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::CheckNPCFactionAlly(THIS, other_faction)"); - { - NPC * THIS; - FACTION_VALUE RETVAL; - dXSTARG; - int32 other_faction = (int32)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->CheckNPCFactionAlly(other_faction); - XSprePUSH; PUSHi((IV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_AddItem); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_AddItem) -{ - dXSARGS; - if (items < 2 || items > 4) - Perl_croak(aTHX_ "Usage: NPC::AddItem(THIS, itemid, charges = 0, equipitem = true)"); - { - NPC * THIS; - uint32 itemid = (uint32)SvUV(ST(1)); - uint16 charges = 0; - bool equipitem = true; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (items > 2) - charges = (uint16)SvUV(ST(2)); - if (items > 3) - equipitem = (bool)SvTRUE(ST(3)); - - THIS->AddItem(itemid, charges, equipitem); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_AddLootTable); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_AddLootTable) -{ - dXSARGS; - if (items < 1) - Perl_croak(aTHX_ "Usage: NPC::AddLootTable(THIS, [loottable_id])"); - { - NPC * THIS; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - uint32 loottable_id = 0; - - if(items > 1) - { - loottable_id = (uint32)SvUV(ST(1)); - THIS->AddLootTable(loottable_id); - } - else - { - THIS->AddLootTable(); - } - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_RemoveItem); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_RemoveItem) -{ - dXSARGS; - if (items < 2 || items > 4) - Perl_croak(aTHX_ "Usage: NPC::RemoveItem(THIS, item_id, quantity= 0, slot= 0)"); - { - NPC * THIS; - uint32 item_id = (uint32)SvUV(ST(1)); - uint16 quantity; - uint16 slot; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (items < 3) - quantity = 0; - else { - quantity = (uint16)SvUV(ST(2)); - } - - if (items < 4) - slot = 0; - else { - slot = (uint16)SvUV(ST(3)); - } - - THIS->RemoveItem(item_id, quantity, slot); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_ClearItemList); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_ClearItemList) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::ClearItemList(THIS)"); - { - NPC * THIS; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->ClearItemList(); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_AddCash); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_AddCash) -{ - dXSARGS; - if (items != 5) - Perl_croak(aTHX_ "Usage: NPC::AddCash(THIS, in_copper, in_silver, in_gold, in_platinum)"); - { - NPC * THIS; - uint16 in_copper = (uint16)SvUV(ST(1)); - uint16 in_silver = (uint16)SvUV(ST(2)); - uint16 in_gold = (uint16)SvUV(ST(3)); - uint16 in_platinum = (uint16)SvUV(ST(4)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->AddCash(in_copper, in_silver, in_gold, in_platinum); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_RemoveCash); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_RemoveCash) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::RemoveCash(THIS)"); - { - NPC * THIS; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->RemoveCash(); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_CountLoot); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_CountLoot) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::CountLoot(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->CountLoot(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetLoottableID); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetLoottableID) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetLoottableID(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetLoottableID(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetCopper); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetCopper) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetCopper(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetCopper(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSilver); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSilver) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSilver(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetSilver(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetGold); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetGold) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetGold(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetGold(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetPlatinum); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetPlatinum) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetPlatinum(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetPlatinum(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_SetCopper); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetCopper) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetCopper(THIS, amt)"); - { - NPC * THIS; - uint32 amt = (uint32)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetCopper(amt); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SetSilver); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetSilver) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetSilver(THIS, amt)"); - { - NPC * THIS; - uint32 amt = (uint32)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetSilver(amt); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SetGold); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetGold) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetGold(THIS, amt)"); - { - NPC * THIS; - uint32 amt = (uint32)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetGold(amt); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SetPlatinum); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetPlatinum) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetPlatinum(THIS, amt)"); - { - NPC * THIS; - uint32 amt = (uint32)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetPlatinum(amt); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SetGrid); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetGrid) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetGrid(THIS, grid_)"); - { - NPC * THIS; - int32 grid_ = (int32)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetGrid(grid_); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SetSaveWaypoint); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetSaveWaypoint) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetSaveWaypoint(THIS, waypoint)"); - { - NPC * THIS; - uint16 waypoint = (uint16)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetSaveWaypoint(waypoint); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SetSp2); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetSp2) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetSp2(THIS, sg2)"); - { - NPC * THIS; - uint32 sg2 = (uint32)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetSp2(sg2); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_GetWaypointMax); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetWaypointMax) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetWaypointMax(THIS)"); - { - NPC * THIS; - uint16 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetWaypointMax(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetGrid); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetGrid) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetGrid(THIS)"); - { - NPC * THIS; - int32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetGrid(); - XSprePUSH; PUSHi((IV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSp2); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSp2) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSp2(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetSp2(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetNPCFactionID); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetNPCFactionID) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetNPCFactionID(THIS)"); - { - NPC * THIS; - int32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetNPCFactionID(); - XSprePUSH; PUSHi((IV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetPrimaryFaction); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetPrimaryFaction) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetPrimaryFaction(THIS)"); - { - NPC * THIS; - int32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetPrimaryFaction(); - XSprePUSH; PUSHi((IV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetNPCHate); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetNPCHate) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::GetNPCHate(THIS, in_ent)"); - { - NPC * THIS; - int32 RETVAL; - dXSTARG; - Mob* in_ent; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (sv_derived_from(ST(1), "Mob")) { - IV tmp = SvIV((SV*)SvRV(ST(1))); - in_ent = INT2PTR(Mob *,tmp); - } - else - Perl_croak(aTHX_ "in_ent is not of type Mob"); - if(in_ent == nullptr) - Perl_croak(aTHX_ "in_ent is nullptr, avoiding crash."); - - RETVAL = THIS->GetNPCHate(in_ent); - XSprePUSH; PUSHi((IV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_IsOnHatelist); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_IsOnHatelist) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::IsOnHatelist(THIS, p)"); - { - NPC * THIS; - bool RETVAL; - Mob* p; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (sv_derived_from(ST(1), "Mob")) { - IV tmp = SvIV((SV*)SvRV(ST(1))); - p = INT2PTR(Mob *,tmp); - } - else - Perl_croak(aTHX_ "p is not of type Mob"); - if(p == nullptr) - Perl_croak(aTHX_ "p is nullptr, avoiding crash."); - - RETVAL = THIS->IsOnHatelist(p); - ST(0) = boolSV(RETVAL); - sv_2mortal(ST(0)); - } - XSRETURN(1); -} - -XS(XS_NPC_SetNPCFactionID); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetNPCFactionID) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetNPCFactionID(THIS, in)"); - { - NPC * THIS; - int32 in = (int32)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetNPCFactionID(in); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_GetMaxDMG); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetMaxDMG) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetMaxDMG(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetMaxDMG(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetMinDMG); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetMinDMG) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetMinDMG(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetMinDMG(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - - -XS(XS_NPC_IsAnimal); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_IsAnimal) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::IsAnimal(THIS)"); - { - NPC * THIS; - bool RETVAL; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->IsAnimal(); - ST(0) = boolSV(RETVAL); - sv_2mortal(ST(0)); - } - XSRETURN(1); -} - -XS(XS_NPC_GetPetSpellID); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetPetSpellID) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetPetSpellID(THIS)"); - { - NPC * THIS; - uint16 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetPetSpellID(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_SetPetSpellID); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetPetSpellID) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetPetSpellID(THIS, amt)"); - { - NPC * THIS; - uint16 amt = (uint16)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetPetSpellID(amt); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_GetMaxDamage); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetMaxDamage) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::GetMaxDamage(THIS, tlevel)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - uint8 tlevel = (uint8)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetMaxDamage(tlevel); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_SetTaunting); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetTaunting) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetTaunting(THIS, tog)"); - { - NPC * THIS; - bool tog = (bool)SvTRUE(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetTaunting(tog); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_PickPocket); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_PickPocket) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::PickPocket(THIS, thief)"); - { - NPC * THIS; - Client* thief; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (sv_derived_from(ST(1), "Client")) { - IV tmp = SvIV((SV*)SvRV(ST(1))); - thief = INT2PTR(Client *,tmp); - } - else - Perl_croak(aTHX_ "thief is not of type Client"); - if(thief == nullptr) - Perl_croak(aTHX_ "thief is nullptr, avoiding crash."); - - THIS->PickPocket(thief); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_StartSwarmTimer); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_StartSwarmTimer) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::StartSwarmTimer(THIS, duration)"); - { - NPC * THIS; - uint32 duration = (uint32)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->StartSwarmTimer(duration); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_DoClassAttacks); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_DoClassAttacks) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::DoClassAttacks(THIS, target)"); - { - NPC * THIS; - Mob * target; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (sv_derived_from(ST(1), "Mob")) { - IV tmp = SvIV((SV*)SvRV(ST(1))); - target = INT2PTR(Mob *,tmp); - } - else - Perl_croak(aTHX_ "target is not of type Mob"); - if(target == nullptr) - Perl_croak(aTHX_ "target is nullptr, avoiding crash."); - - THIS->DoClassAttacks(target); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_GetMaxWp); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetMaxWp) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetMaxWp(THIS)"); - { - NPC * THIS; - int RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetMaxWp(); - XSprePUSH; PUSHi((IV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_DisplayWaypointInfo); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_DisplayWaypointInfo) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::DisplayWaypointInfo(THIS, to)"); - { - NPC * THIS; - Client * to; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (sv_derived_from(ST(1), "Client")) { - IV tmp = SvIV((SV*)SvRV(ST(1))); - to = INT2PTR(Client *,tmp); - } - else - Perl_croak(aTHX_ "to is not of type Client"); - if(to == nullptr) - Perl_croak(aTHX_ "to is nullptr, avoiding crash."); - - THIS->DisplayWaypointInfo(to); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_CalculateNewWaypoint); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_CalculateNewWaypoint) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::CalculateNewWaypoint(THIS)"); - { - NPC * THIS; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->CalculateNewWaypoint(); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_AssignWaypoints); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_AssignWaypoints) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::AssignWaypoints(THIS, grid)"); - { - NPC * THIS; - uint32 grid = (uint32)SvUV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->AssignWaypoints(grid); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SetWaypointPause); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetWaypointPause) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::SetWaypointPause(THIS)"); - { - NPC * THIS; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetWaypointPause(); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_UpdateWaypoint); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_UpdateWaypoint) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::UpdateWaypoint(THIS, wp_index)"); - { - NPC * THIS; - int wp_index = (int)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->UpdateWaypoint(wp_index); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_StopWandering); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_StopWandering) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::StopWandering(THIS)"); - { - NPC * THIS; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->StopWandering(); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_ResumeWandering); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_ResumeWandering) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::ResumeWandering(THIS)"); - { - NPC * THIS; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->ResumeWandering(); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_PauseWandering); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_PauseWandering) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::PauseWandering(THIS, pausetime)"); - { - NPC * THIS; - int pausetime = (int)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->PauseWandering(pausetime); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_MoveTo); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_MoveTo) -{ - dXSARGS; - if (items != 4 && items != 5 && items != 6) - Perl_croak(aTHX_ "Usage: NPC::MoveTo(THIS, mtx, mty, mtz, [mth, saveguard?])"); - { - NPC * THIS; - float mtx = (float)SvNV(ST(1)); - float mty = (float)SvNV(ST(2)); - float mtz = (float)SvNV(ST(3)); - float mth; - bool saveguard; - - if(items > 4) - mth = (float)SvNV(ST(4)); - else - mth = 0; - - if(items > 5) - saveguard = (bool)SvTRUE(ST(5)); - else - saveguard = false; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->MoveTo(mtx, mty, mtz, mth, saveguard); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_NextGuardPosition); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_NextGuardPosition) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::NextGuardPosition(THIS)"); - { - NPC * THIS; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->NextGuardPosition(); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SaveGuardSpot); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SaveGuardSpot) -{ - dXSARGS; - if (items < 1 || items > 2) - Perl_croak(aTHX_ "Usage: NPC::SaveGuardSpot(THIS, iClearGuardSpot= false)"); - { - NPC * THIS; - bool iClearGuardSpot; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (items < 2) - iClearGuardSpot = false; - else { - iClearGuardSpot = (bool)SvTRUE(ST(1)); - } - - THIS->SaveGuardSpot(iClearGuardSpot); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_IsGuarding); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_IsGuarding) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::IsGuarding(THIS)"); - { - NPC * THIS; - bool RETVAL; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->IsGuarding(); - ST(0) = boolSV(RETVAL); - sv_2mortal(ST(0)); - } - XSRETURN(1); -} - -XS(XS_NPC_AI_SetRoambox); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_AI_SetRoambox) -{ - dXSARGS; - if (items < 6 || items > 8) - Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500, iMinDelay= 2500)"); - { - NPC * THIS; - float iDist = (float)SvNV(ST(1)); - float iMaxX = (float)SvNV(ST(2)); - float iMinX = (float)SvNV(ST(3)); - float iMaxY = (float)SvNV(ST(4)); - float iMinY = (float)SvNV(ST(5)); - uint32 iDelay; - uint32 iMinDelay; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - if (items < 7){ - iMinDelay = 2500; - iDelay = 2500; - } - else if (items < 8){ - iMinDelay = 2500; - iDelay = (uint32)SvUV(ST(6)); - } - else { - iDelay = (uint32)SvUV(ST(6)); - iMinDelay = (uint32)SvUV(ST(7)); - } - - THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay, iMinDelay); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_GetNPCSpellsID); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetNPCSpellsID) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetNPCSpellsID(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetNPCSpellsID(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSpawnPointID); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSpawnPointID) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointID(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetSpawnPointID(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSpawnPointX); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSpawnPointX) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointX(THIS)"); - { - NPC * THIS; - float RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - - RETVAL = THIS->GetSpawnPointX(); - XSprePUSH; PUSHn((double)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSpawnPointY); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSpawnPointY) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointY(THIS)"); - { - NPC * THIS; - float RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - - RETVAL = THIS->GetSpawnPointY(); - XSprePUSH; PUSHn((double)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSpawnPointZ); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSpawnPointZ) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointZ(THIS)"); - { - NPC * THIS; - float RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - - RETVAL = THIS->GetSpawnPointZ(); - XSprePUSH; PUSHn((double)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSpawnPointH); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSpawnPointH) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSpawnPointH(THIS)"); - { - NPC * THIS; - float RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - - RETVAL = THIS->GetSpawnPointH(); - XSprePUSH; PUSHn((double)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetGuardPointX); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetGuardPointX) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetGuardPointX(THIS)"); - { - NPC * THIS; - float RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - - RETVAL = THIS->GetGuardPointX(); - XSprePUSH; PUSHn((double)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetGuardPointY); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetGuardPointY) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetGuardPointY(THIS)"); - { - NPC * THIS; - float RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - - RETVAL = THIS->GetGuardPointY(); - XSprePUSH; PUSHn((double)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetGuardPointZ); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetGuardPointZ) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetGuardPointZ(THIS)"); - { - NPC * THIS; - float RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - - RETVAL = THIS->GetGuardPointZ(); - XSprePUSH; PUSHn((double)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_SetPrimSkill); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetPrimSkill) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetPrimSkill(THIS, skill_id)"); - { - NPC * THIS; - int skill_id = (int)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetPrimSkill(skill_id); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SetSecSkill); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetSecSkill) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetSecSkill(THIS, skill_id)"); - { - NPC * THIS; - int skill_id = (int)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetSecSkill(skill_id); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_GetPrimSkill); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetPrimSkill) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetPrimSkill(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetPrimSkill(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSecSkill); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSecSkill) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSecSkill(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetSecSkill(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSwarmOwner); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSwarmOwner) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSwarmOwner(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetSwarmOwner(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSwarmTarget); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSwarmTarget) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSwarmTarget(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetSwarmTarget(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_SetSwarmTarget); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetSwarmTarget) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetSwarmTarget(THIS, target_id)"); - { - NPC * THIS; - int target_id = (int)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetSwarmTarget(target_id); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_ModifyNPCStat); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_ModifyNPCStat) -{ - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: NPC::ModifyNPCStat(THIS, identifier, newValue)"); - { - NPC * THIS; - Const_char * identifier = (Const_char *)SvPV_nolen(ST(1)); - Const_char * newValue = (Const_char *)SvPV_nolen(ST(2)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->ModifyNPCStat(identifier, newValue); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_AddSpellToNPCList); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_AddSpellToNPCList) -{ - dXSARGS; - if (items != 7) - Perl_croak(aTHX_ "Usage: NPC::AddAISpell(THIS, priority, spell_id, type, mana_cost, recast_delay, resist_adjust)"); - { - NPC * THIS; - int priority = (int)SvIV(ST(1)); - int spell_id = (int)SvIV(ST(2)); - int type = (int)SvIV(ST(3)); - int mana_cost = (int)SvIV(ST(4)); - int recast_delay = (int)SvIV(ST(5)); - int resist_adjust = (int)SvIV(ST(6)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->AddSpellToNPCList(priority, spell_id, type, mana_cost, recast_delay, resist_adjust); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_RemoveSpellFromNPCList); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_RemoveSpellFromNPCList) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::RemoveAISpell(THIS, spell_id)"); - { - NPC * THIS; - int spell_id = (int)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->RemoveSpellFromNPCList(spell_id); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_SetSpellFocusDMG); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetSpellFocusDMG) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetSpellFocusDMG(THIS, NewSpellFocusDMG)"); - { - NPC * THIS; - int32 NewSpellFocusDMG = (int32)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetSpellFocusDMG(NewSpellFocusDMG); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_GetSpellFocusDMG); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSpellFocusDMG) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSpellFocusDMG(THIS)"); - { - NPC * THIS; - int32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetSpellFocusDMG(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_SetSpellFocusHeal); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_SetSpellFocusHeal) -{ - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetSpellFocusHeal(THIS, NewSpellFocusHeal)"); - { - NPC * THIS; - int32 NewSpellFocusHeal = (int32)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetSpellFocusHeal(NewSpellFocusHeal); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_GetSpellFocusHeal); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSpellFocusHeal) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSpellFocusHeal(THIS)"); - { - NPC * THIS; - int32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetSpellFocusHeal(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSlowMitigation); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSlowMitigation) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)"); - { - NPC * THIS; - int16 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetSlowMitigation(); - XSprePUSH; PUSHn((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetAttackSpeed); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetAttackSpeed) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetAttackSpeed(THIS)"); - { - NPC * THIS; - float RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetAttackSpeed(); - XSprePUSH; PUSHn((double)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetAttackDelay); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetAttackDelay) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetAttackDelay(THIS)"); - { - NPC * THIS; - float RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetAttackDelay(); - XSprePUSH; PUSHn((double)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetAccuracyRating); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetAccuracyRating) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetAccuracyRating(THIS)"); - { - NPC * THIS; - int32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetAccuracyRating(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetAvoidanceRating); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetAvoidanceRating) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetAvoidanceyRating(THIS)"); - { - NPC * THIS; - int32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - RETVAL = THIS->GetAvoidanceRating(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetSpawnKillCount); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetSpawnKillCount) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetSpawnKillCount(THIS)"); - { - NPC * THIS; - uint32 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == NULL) - Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); - - RETVAL = THIS->GetSpawnKillCount(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_GetScore); /* prototype to pass -Wmissing-prototypes */ -XS(XS_NPC_GetScore) -{ - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetScore(THIS)"); - { - NPC * THIS; - int RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == NULL) - Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); - - RETVAL = THIS->GetScore(); - XSprePUSH; PUSHi((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_SetMerchantProbability); -XS(XS_NPC_SetMerchantProbability) { - dXSARGS; - if (items != 2) - Perl_croak(aTHX_ "Usage: NPC::SetMerchantProbability(THIS, Probability)"); - { - NPC *THIS; - uint8 Probability = (uint8)SvIV(ST(1)); - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == nullptr) - Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); - - THIS->SetMerchantProbability(Probability); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_GetMerchantProbability); -XS(XS_NPC_GetMerchantProbability) { - dXSARGS; - if (items != 1) - Perl_croak(aTHX_ "Usage: NPC::GetMerchantProbability(THIS)"); - { - NPC *THIS; - uint8 RETVAL; - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == NULL) - Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); - - RETVAL = THIS->GetMerchantProbability(); - XSprePUSH; PUSHu((UV)RETVAL); - } - XSRETURN(1); -} - -XS(XS_NPC_AddMeleeProc); -XS(XS_NPC_AddMeleeProc) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: NPC::AddMeleeProc(THIS,spellid,chance)"); - { - NPC * THIS; - int spell_id = (int)SvIV(ST(1)); - int chance = (int)SvIV(ST(2)); - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == NULL) - Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); - - THIS->AddProcToWeapon(spell_id, true, chance); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_AddRangedProc); -XS(XS_NPC_AddRangedProc) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: NPC::AddRangedProc(THIS,spellid,chance)"); - { - NPC * THIS; - int spell_id = (int)SvIV(ST(1)); - int chance = (int)SvIV(ST(2)); - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == NULL) - Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); - - THIS->AddDefensiveProc(spell_id,chance); - } - XSRETURN_EMPTY; -} - -XS(XS_NPC_AddDefensiveProc); -XS(XS_NPC_AddDefensiveProc) { - dXSARGS; - if (items != 3) - Perl_croak(aTHX_ "Usage: NPC::AddDefensiveProc(THIS,spellid,chance)"); - { - NPC * THIS; - int spell_id = (int)SvIV(ST(1)); - int chance = (int)SvIV(ST(2)); - dXSTARG; - - if (sv_derived_from(ST(0), "NPC")) { - IV tmp = SvIV((SV*)SvRV(ST(0))); - THIS = INT2PTR(NPC *,tmp); - } - else - Perl_croak(aTHX_ "THIS is not of type NPC"); - if(THIS == NULL) - Perl_croak(aTHX_ "THIS is NULL, avoiding crash."); - - THIS->AddProcToWeapon(spell_id, true, chance); - } - XSRETURN_EMPTY; -} - - -#ifdef __cplusplus -extern "C" -#endif -XS(boot_NPC); /* prototype to pass -Wmissing-prototypes */ -XS(boot_NPC) -{ - dXSARGS; - char file[256]; - strncpy(file, __FILE__, 256); - file[255] = 0; - - if(items != 1) - fprintf(stderr, "boot_quest does not take any arguments."); - char buf[128]; - - //add the strcpy stuff to get rid of const warnings.... - - XS_VERSION_BOOTCHECK ; - - newXSproto(strcpy(buf, "SignalNPC"), XS_NPC_SignalNPC, file, "$$"); - newXSproto(strcpy(buf, "CheckNPCFactionAlly"), XS_NPC_CheckNPCFactionAlly, file, "$$"); - newXSproto(strcpy(buf, "AddItem"), XS_NPC_AddItem, file, "$$;$$"); - newXSproto(strcpy(buf, "AddLootTable"), XS_NPC_AddLootTable, file, "$"); - newXSproto(strcpy(buf, "RemoveItem"), XS_NPC_RemoveItem, file, "$$;$$"); - newXSproto(strcpy(buf, "ClearItemList"), XS_NPC_ClearItemList, file, "$"); - newXSproto(strcpy(buf, "AddCash"), XS_NPC_AddCash, file, "$$$$$"); - newXSproto(strcpy(buf, "RemoveCash"), XS_NPC_RemoveCash, file, "$"); - newXSproto(strcpy(buf, "CountLoot"), XS_NPC_CountLoot, file, "$"); - newXSproto(strcpy(buf, "GetLoottableID"), XS_NPC_GetLoottableID, file, "$"); - newXSproto(strcpy(buf, "GetCopper"), XS_NPC_GetCopper, file, "$"); - newXSproto(strcpy(buf, "GetSilver"), XS_NPC_GetSilver, file, "$"); - newXSproto(strcpy(buf, "GetGold"), XS_NPC_GetGold, file, "$"); - newXSproto(strcpy(buf, "GetPlatinum"), XS_NPC_GetPlatinum, file, "$"); - newXSproto(strcpy(buf, "SetCopper"), XS_NPC_SetCopper, file, "$$"); - newXSproto(strcpy(buf, "SetSilver"), XS_NPC_SetSilver, file, "$$"); - newXSproto(strcpy(buf, "SetGold"), XS_NPC_SetGold, file, "$$"); - newXSproto(strcpy(buf, "SetPlatinum"), XS_NPC_SetPlatinum, file, "$$"); - newXSproto(strcpy(buf, "SetGrid"), XS_NPC_SetGrid, file, "$$"); - newXSproto(strcpy(buf, "SetSaveWaypoint"), XS_NPC_SetSaveWaypoint, file, "$$"); - newXSproto(strcpy(buf, "SetSp2"), XS_NPC_SetSp2, file, "$$"); - newXSproto(strcpy(buf, "GetWaypointMax"), XS_NPC_GetWaypointMax, file, "$"); - newXSproto(strcpy(buf, "GetGrid"), XS_NPC_GetGrid, file, "$"); - newXSproto(strcpy(buf, "GetSp2"), XS_NPC_GetSp2, file, "$"); - newXSproto(strcpy(buf, "GetNPCFactionID"), XS_NPC_GetNPCFactionID, file, "$"); - newXSproto(strcpy(buf, "GetPrimaryFaction"), XS_NPC_GetPrimaryFaction, file, "$"); - newXSproto(strcpy(buf, "GetNPCHate"), XS_NPC_GetNPCHate, file, "$$"); - newXSproto(strcpy(buf, "IsOnHatelist"), XS_NPC_IsOnHatelist, file, "$$"); - newXSproto(strcpy(buf, "SetNPCFactionID"), XS_NPC_SetNPCFactionID, file, "$$"); - newXSproto(strcpy(buf, "GetMaxDMG"), XS_NPC_GetMaxDMG, file, "$"); - newXSproto(strcpy(buf, "GetMinDMG"), XS_NPC_GetMinDMG, file, "$"); - newXSproto(strcpy(buf, "IsAnimal"), XS_NPC_IsAnimal, file, "$"); - newXSproto(strcpy(buf, "GetPetSpellID"), XS_NPC_GetPetSpellID, file, "$"); - newXSproto(strcpy(buf, "SetPetSpellID"), XS_NPC_SetPetSpellID, file, "$$"); - newXSproto(strcpy(buf, "GetMaxDamage"), XS_NPC_GetMaxDamage, file, "$$"); - newXSproto(strcpy(buf, "SetTaunting"), XS_NPC_SetTaunting, file, "$$"); - newXSproto(strcpy(buf, "PickPocket"), XS_NPC_PickPocket, file, "$$"); - newXSproto(strcpy(buf, "StartSwarmTimer"), XS_NPC_StartSwarmTimer, file, "$$"); - newXSproto(strcpy(buf, "DoClassAttacks"), XS_NPC_DoClassAttacks, file, "$$"); - newXSproto(strcpy(buf, "GetMaxWp"), XS_NPC_GetMaxWp, file, "$"); - newXSproto(strcpy(buf, "DisplayWaypointInfo"), XS_NPC_DisplayWaypointInfo, file, "$$"); - newXSproto(strcpy(buf, "CalculateNewWaypoint"), XS_NPC_CalculateNewWaypoint, file, "$"); - newXSproto(strcpy(buf, "AssignWaypoints"), XS_NPC_AssignWaypoints, file, "$$"); - newXSproto(strcpy(buf, "SetWaypointPause"), XS_NPC_SetWaypointPause, file, "$"); - newXSproto(strcpy(buf, "UpdateWaypoint"), XS_NPC_UpdateWaypoint, file, "$$"); - newXSproto(strcpy(buf, "StopWandering"), XS_NPC_StopWandering, file, "$"); - newXSproto(strcpy(buf, "ResumeWandering"), XS_NPC_ResumeWandering, file, "$"); - newXSproto(strcpy(buf, "PauseWandering"), XS_NPC_PauseWandering, file, "$$"); - newXSproto(strcpy(buf, "MoveTo"), XS_NPC_MoveTo, file, "$$$$"); - newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$"); - newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$"); - newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$"); - newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$$"); - newXSproto(strcpy(buf, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$"); - newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$"); - newXSproto(strcpy(buf, "GetSpawnPointX"), XS_NPC_GetSpawnPointX, file, "$"); - newXSproto(strcpy(buf, "GetSpawnPointY"), XS_NPC_GetSpawnPointY, file, "$"); - newXSproto(strcpy(buf, "GetSpawnPointZ"), XS_NPC_GetSpawnPointZ, file, "$"); - newXSproto(strcpy(buf, "GetSpawnPointH"), XS_NPC_GetSpawnPointH, file, "$"); - newXSproto(strcpy(buf, "GetGuardPointX"), XS_NPC_GetGuardPointX, file, "$"); - newXSproto(strcpy(buf, "GetGuardPointY"), XS_NPC_GetGuardPointY, file, "$"); - newXSproto(strcpy(buf, "GetGuardPointZ"), XS_NPC_GetGuardPointZ, file, "$"); - newXSproto(strcpy(buf, "SetPrimSkill"), XS_NPC_SetPrimSkill, file, "$$"); - newXSproto(strcpy(buf, "SetSecSkill"), XS_NPC_SetSecSkill, file, "$$"); - newXSproto(strcpy(buf, "GetPrimSkill"), XS_NPC_GetPrimSkill, file, "$"); - newXSproto(strcpy(buf, "GetSecSkill"), XS_NPC_GetSecSkill, file, "$"); - newXSproto(strcpy(buf, "GetSwarmOwner"), XS_NPC_GetSwarmOwner, file, "$"); - newXSproto(strcpy(buf, "GetSwarmTarget"), XS_NPC_GetSwarmTarget, file, "$"); - newXSproto(strcpy(buf, "SetSwarmTarget"), XS_NPC_SetSwarmTarget, file, "$$"); - newXSproto(strcpy(buf, "ModifyNPCStat"), XS_NPC_ModifyNPCStat, file, "$$$"); - newXSproto(strcpy(buf, "AddAISpell"), XS_NPC_AddSpellToNPCList, file, "$$$$$$$"); - newXSproto(strcpy(buf, "RemoveAISpell"), XS_NPC_RemoveSpellFromNPCList, file, "$$"); - newXSproto(strcpy(buf, "SetSpellFocusDMG"), XS_NPC_SetSpellFocusDMG, file, "$$"); - newXSproto(strcpy(buf, "SetSpellFocusHeal"), XS_NPC_SetSpellFocusHeal, file, "$$"); - newXSproto(strcpy(buf, "GetSpellFocusDMG"), XS_NPC_GetSpellFocusDMG, file, "$"); - newXSproto(strcpy(buf, "GetSpellFocusHeal"), XS_NPC_GetSpellFocusHeal, file, "$"); - newXSproto(strcpy(buf, "GetSlowMitigation"), XS_NPC_GetSlowMitigation, file, "$"); - newXSproto(strcpy(buf, "GetAttackSpeed"), XS_NPC_GetAttackSpeed, file, "$"); - newXSproto(strcpy(buf, "GetAttackDelay"), XS_NPC_GetAttackDelay, file, "$"); - newXSproto(strcpy(buf, "GetAccuracyRating"), XS_NPC_GetAccuracyRating, file, "$"); - newXSproto(strcpy(buf, "GetAvoidanceRating"), XS_NPC_GetAvoidanceRating, file, "$"); - newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$"); - newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$"); - newXSproto(strcpy(buf, "SetMerchantProbability"), XS_NPC_SetMerchantProbability, file, "$$"); - newXSproto(strcpy(buf, "GetMerchantProbability"), XS_NPC_GetMerchantProbability, file, "$"); - newXSproto(strcpy(buf, "AddMeleeProc"), XS_NPC_AddMeleeProc, file, "$$$"); - newXSproto(strcpy(buf, "AddRangedProc"), XS_NPC_AddRangedProc, file, "$$$"); - newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$"); - XSRETURN_YES; -} - -#endif //EMBPERL_XS_CLASSES - From 365a08ee869f6048f1a7c3a084701a677fe16ca1 Mon Sep 17 00:00:00 2001 From: KayenEQ Date: Sun, 27 Mar 2016 11:08:08 -0400 Subject: [PATCH 2/2] Removed unneccessary entitylist check from ApplySpellBonuses Fixed an issue with FCBaseEffects not applying bonus when cast on targets from runes. --- zone/bonuses.cpp | 6 +----- zone/mob.h | 6 +++--- zone/mod_functions.cpp | 2 +- zone/spell_effects.cpp | 19 +++++++++++++------ 4 files changed, 18 insertions(+), 15 deletions(-) diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 413b8d6c6..f57e92247 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -1543,14 +1543,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne { int i, effect_value, base2, max, effectid; bool AdditiveWornBonus = false; - Mob *caster = nullptr; if(!IsAISpellEffect && !IsValidSpell(spell_id)) return; - if(casterId > 0) - caster = entity_list.GetMob(casterId); - for (i = 0; i < EFFECT_COUNT; i++) { //Buffs/Item effects @@ -1577,7 +1573,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne AdditiveWornBonus = true; effectid = spells[spell_id].effectid[i]; - effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, caster, ticsremaining); + effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, nullptr, ticsremaining, casterId); base2 = spells[spell_id].base2[i]; max = spells[spell_id].max[i]; } diff --git a/zone/mob.h b/zone/mob.h index 7083e45ca..8cb899d0f 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -662,7 +662,7 @@ public: bool TryReflectSpell(uint32 spell_id); bool CanBlockSpell() const { return(spellbonuses.BlockNextSpell); } bool DoHPToManaCovert(uint16 mana_cost = 0); - int32 ApplySpellEffectiveness(Mob* caster, int16 spell_id, int32 value, bool IsBard = false); + int32 ApplySpellEffectiveness(int16 spell_id, int32 value, bool IsBard = false, uint16 caster_id=0); int8 GetDecayEffectValue(uint16 spell_id, uint16 spelleffect); int32 GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg); void MeleeLifeTap(int32 damage); @@ -898,7 +898,7 @@ public: virtual int32 CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible = 0); uint32 GetInstrumentMod(uint16 spell_id) const; - int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0); + int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0,uint16 casterid=0); int CalcSpellEffectValue_formula(int formula, int base, int max, int caster_level, uint16 spell_id, int ticsremaining = 0); virtual int CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2, int caster_level2, Mob* caster1 = nullptr, Mob* caster2 = nullptr, int buffslot = -1); uint32 GetCastedSpellInvSlot() const { return casting_spell_inventory_slot; } @@ -956,7 +956,7 @@ public: inline uint16 GetEmoteID() { return emoteid; } bool HasSpellEffect(int effectid); - int mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster); + int mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster, uint16 caster_id); float mod_hit_chance(float chancetohit, SkillUseTypes skillinuse, Mob* attacker); float mod_riposte_chance(float ripostchance, Mob* attacker); float mod_block_chance(float blockchance, Mob* attacker); diff --git a/zone/mod_functions.cpp b/zone/mod_functions.cpp index 6ae0eddad..5c98541c0 100644 --- a/zone/mod_functions.cpp +++ b/zone/mod_functions.cpp @@ -108,7 +108,7 @@ int Client::mod_food_value(const Item_Struct *item, int change) { return(change) int Client::mod_drink_value(const Item_Struct *item, int change) { return(change); } //effect_vallue - Spell effect value as calculated by default formulas. You will want to ignore effects that don't lend themselves to scaling - pet ID's, gate coords, etc. -int Mob::mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster) { return(effect_value); } +int Mob::mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster, uint16 caster_id) { return(effect_value); } //chancetohit - 0 to 100 percent - set over 1000 for a guaranteed hit float Mob::mod_hit_chance(float chancetohit, SkillUseTypes skillinuse, Mob* attacker) { return(chancetohit); } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index ecf2bb7ba..f0b93ca9c 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1270,7 +1270,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Melee Absorb Rune: %+i", effect_value); #endif - effect_value = ApplySpellEffectiveness(caster, spell_id, effect_value); + if (caster) + effect_value = caster->ApplySpellEffectiveness(spell_id, effect_value); + buffs[buffslot].melee_rune = effect_value; break; } @@ -3020,7 +3022,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove } int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, uint32 instrument_mod, Mob *caster, - int ticsremaining) + int ticsremaining, uint16 caster_id) { int formula, base, max, effect_value; @@ -3048,13 +3050,13 @@ int Mob::CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level, spells[spell_id].effectid[effect_id] != SE_ManaRegen_v2) { int oval = effect_value; - int mod = ApplySpellEffectiveness(caster, spell_id, instrument_mod, true); + int mod = ApplySpellEffectiveness(spell_id, instrument_mod, true, caster_id); effect_value = effect_value * mod / 10; Log.Out(Logs::Detail, Logs::Spells, "Effect value %d altered with bard modifier of %d to yeild %d", oval, mod, effect_value); } - effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster); + effect_value = mod_effect_value(effect_value, spell_id, spells[spell_id].effectid[effect_id], caster, caster_id); return effect_value; } @@ -6043,16 +6045,21 @@ int32 Mob::GetFocusIncoming(focusType type, int effect, Mob *caster, uint32 spel return value; } -int32 Mob::ApplySpellEffectiveness(Mob* caster, int16 spell_id, int32 value, bool IsBard) { +int32 Mob::ApplySpellEffectiveness(int16 spell_id, int32 value, bool IsBard, uint16 caster_id) { // 9-17-12: This is likely causing crashes, disabled till can resolve. if (IsBard) return value; + Mob* caster = this; + + if (caster_id && caster_id != GetID())//Make sure we are checking the casters focus + caster = entity_list.GetMob(caster_id); + if (!caster) return value; - int16 focus = GetFocusEffect(focusFcBaseEffects, spell_id); + int16 focus = caster->GetFocusEffect(focusFcBaseEffects, spell_id); if (IsBard) value += focus;