mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
Merge fix
This commit is contained in:
commit
d402b25d69
@ -1,5 +1,12 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 02/20/2017 ==
|
||||
Uleat: Reworked bard bot spell twisting and updated their spell (song) list
|
||||
Uleat: Added ability to shift to pre-combat song buffing by selecting a non-pet npc target, eliminating the need to mix all bard buff songs together
|
||||
|
||||
== 2/19/2017 ==
|
||||
Akkadius: Added a fix for limiting the amount of items sold in a stack when the resulting return coin is higher than the supporting struct for returning coin
|
||||
|
||||
== 01/31/2017 ==
|
||||
Uleat: Modifed bot movement behavior in an attempt to 'normalize' it. This is a hack fix and will be revisited at some point. (Probably just need a follow function rather than use movement, when the leader of the follow chain is moving.)
|
||||
|
||||
|
||||
@ -307,6 +307,7 @@ bool Database::DeleteCharacter(char *name) {
|
||||
query = StringFormat("DELETE FROM `quest_globals` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_activities` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_enabledtasks` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `character_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `completed_tasks` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `friends` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
query = StringFormat("DELETE FROM `mail` WHERE `charid` = '%d'", charid); QueryDatabase(query);
|
||||
|
||||
@ -25,6 +25,9 @@ N(OP_AdventureRequest),
|
||||
N(OP_AdventureStatsReply),
|
||||
N(OP_AdventureStatsRequest),
|
||||
N(OP_AdventureUpdate),
|
||||
N(OP_AggroMeterLockTarget),
|
||||
N(OP_AggroMeterTargetInfo),
|
||||
N(OP_AggroMeterUpdate),
|
||||
N(OP_AltCurrency),
|
||||
N(OP_AltCurrencyMerchantReply),
|
||||
N(OP_AltCurrencyMerchantRequest),
|
||||
|
||||
@ -126,26 +126,21 @@ void EQEmuLogSys::LoadLogSettingsDefaults()
|
||||
|
||||
std::string EQEmuLogSys::FormatOutMessageString(uint16 log_category, const std::string &in_message)
|
||||
{
|
||||
std::string category_string;
|
||||
if (log_category > 0 && Logs::LogCategoryName[log_category])
|
||||
category_string = StringFormat("[%s] ", Logs::LogCategoryName[log_category]);
|
||||
return StringFormat("%s%s", category_string.c_str(), in_message.c_str());
|
||||
std::string ret;
|
||||
ret.push_back('[');
|
||||
ret.append(Logs::LogCategoryName[log_category]);
|
||||
ret.push_back(']');
|
||||
ret.push_back(' ');
|
||||
ret.append(in_message);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void EQEmuLogSys::ProcessGMSay(uint16 debug_level, uint16 log_category, const std::string &message)
|
||||
{
|
||||
/* Check if category enabled for process */
|
||||
if (log_settings[log_category].log_to_gmsay == 0)
|
||||
return;
|
||||
|
||||
/* Enabling Netcode based GMSay output creates a feedback loop that ultimately ends in a crash */
|
||||
if (log_category == Logs::LogCategory::Netcode)
|
||||
return;
|
||||
|
||||
/* Make sure the message inbound is at a debug level we're set at */
|
||||
if (log_settings[log_category].log_to_gmsay < debug_level)
|
||||
return;
|
||||
|
||||
/* Check to see if the process that actually ran this is zone */
|
||||
if (EQEmuLogSys::log_platform == EQEmuExePlatform::ExePlatformZone)
|
||||
on_log_gmsay_hook(log_category, message);
|
||||
@ -163,14 +158,6 @@ void EQEmuLogSys::ProcessLogWrite(uint16 debug_level, uint16 log_category, const
|
||||
crash_log.close();
|
||||
}
|
||||
|
||||
/* Check if category enabled for process */
|
||||
if (log_settings[log_category].log_to_file == 0)
|
||||
return;
|
||||
|
||||
/* Make sure the message inbound is at a debug level we're set at */
|
||||
if (log_settings[log_category].log_to_file < debug_level)
|
||||
return;
|
||||
|
||||
char time_stamp[80];
|
||||
EQEmuLogSys::SetCurrentTimeStamp(time_stamp);
|
||||
|
||||
@ -249,13 +236,6 @@ uint16 EQEmuLogSys::GetGMSayColorFromCategory(uint16 log_category) {
|
||||
|
||||
void EQEmuLogSys::ProcessConsoleMessage(uint16 debug_level, uint16 log_category, const std::string &message)
|
||||
{
|
||||
/* Check if category enabled for process */
|
||||
if (log_settings[log_category].log_to_console == 0)
|
||||
return;
|
||||
|
||||
/* Make sure the message inbound is at a debug level we're set at */
|
||||
if (log_settings[log_category].log_to_console < debug_level)
|
||||
return;
|
||||
|
||||
#ifdef _WINDOWS
|
||||
HANDLE console_handle;
|
||||
@ -276,12 +256,25 @@ void EQEmuLogSys::ProcessConsoleMessage(uint16 debug_level, uint16 log_category,
|
||||
|
||||
void EQEmuLogSys::Out(Logs::DebugLevel debug_level, uint16 log_category, std::string message, ...)
|
||||
{
|
||||
const bool log_to_console = log_settings[log_category].log_to_console > 0;
|
||||
const bool log_to_file = log_settings[log_category].log_to_file > 0;
|
||||
const bool log_to_gmsay = log_settings[log_category].log_to_gmsay > 0;
|
||||
const bool nothing_to_log = !log_to_console && !log_to_file && !log_to_gmsay;
|
||||
|
||||
if (nothing_to_log) return;
|
||||
bool log_to_console = true;
|
||||
if (log_settings[log_category].log_to_console < debug_level) {
|
||||
log_to_console = false;
|
||||
}
|
||||
|
||||
bool log_to_file = true;
|
||||
if (log_settings[log_category].log_to_file < debug_level) {
|
||||
log_to_file = false;
|
||||
}
|
||||
|
||||
bool log_to_gmsay = true;
|
||||
if (log_settings[log_category].log_to_gmsay < debug_level) {
|
||||
log_to_gmsay = false;
|
||||
}
|
||||
|
||||
const bool nothing_to_log = !log_to_console && !log_to_file && !log_to_gmsay;
|
||||
if (nothing_to_log)
|
||||
return;
|
||||
|
||||
va_list args;
|
||||
va_start(args, message);
|
||||
|
||||
@ -234,6 +234,9 @@ enum { //some random constants
|
||||
|
||||
#define ZONE_CONTROLLER_NPC_ID 10
|
||||
|
||||
// Timer to update aggrometer
|
||||
#define AGGRO_METER_UPDATE_MS 1000
|
||||
|
||||
//Some hard coded statuses from commands and other places:
|
||||
enum {
|
||||
minStatusToBeGM = 40,
|
||||
|
||||
@ -114,6 +114,7 @@ RULE_BOOL(Character, CheckCursorEmptyWhenLooting, true) // If true, a player can
|
||||
RULE_BOOL(Character, MaintainIntoxicationAcrossZones, true) // If true, alcohol effects are maintained across zoning and logging out/in.
|
||||
RULE_BOOL(Character, EnableDiscoveredItems, true) // If enabled, it enables EVENT_DISCOVER_ITEM and also saves character names and timestamps for the first time an item is discovered.
|
||||
RULE_BOOL(Character, EnableXTargetting, true) // Enable Extended Targetting Window, for users with UF and later clients.
|
||||
RULE_BOOL(Character, EnableAggroMeter, false) // Enable Aggro Meter, for users with RoF and later clients.
|
||||
RULE_BOOL(Character, KeepLevelOverMax, false) // Don't delevel a character that has somehow gone over the level cap
|
||||
RULE_INT(Character, FoodLossPerUpdate, 35) // How much food/water you lose per stamina update
|
||||
RULE_INT(Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods, 36 commonly referred to as "3.6" as well.
|
||||
@ -258,6 +259,7 @@ RULE_BOOL(Zone, EnableLoggedOffReplenishments, true)
|
||||
RULE_INT(Zone, MinOfflineTimeToReplenishments, 21600) // 21600 seconds is 6 Hours
|
||||
RULE_BOOL(Zone, UseZoneController, true) // Enables the ability to use persistent quest based zone controllers (zone_controller.pl/lua)
|
||||
RULE_BOOL(Zone, EnableZoneControllerGlobals, false) // Enables the ability to use quest globals with the zone controller NPC
|
||||
RULE_INT(Zone, GlobalLootMultiplier, 1) // Sets Global Loot drop multiplier for database based drops, useful for double, triple loot etc.
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Map)
|
||||
|
||||
@ -43,29 +43,37 @@
|
||||
|
||||
const int Z_AGGRO=10;
|
||||
|
||||
const int MobAISpellRange=100; // max range of buffs
|
||||
const int SpellType_Nuke=1;
|
||||
const int SpellType_Heal=2;
|
||||
const int SpellType_Root=4;
|
||||
const int SpellType_Buff=8;
|
||||
const int SpellType_Escape=16;
|
||||
const int SpellType_Pet=32;
|
||||
const int SpellType_Lifetap=64;
|
||||
const int SpellType_Snare=128;
|
||||
const int SpellType_DOT=256;
|
||||
const int SpellType_Dispel=512;
|
||||
const int SpellType_InCombatBuff=1024;
|
||||
const int SpellType_Mez=2048;
|
||||
const int SpellType_Charm=4096;
|
||||
const int SpellType_Slow = 8192;
|
||||
const int SpellType_Debuff = 16384;
|
||||
const int SpellType_Cure = 32768;
|
||||
const int SpellType_Resurrect = 65536;
|
||||
const uint32 MobAISpellRange=100; // max range of buffs
|
||||
|
||||
const int SpellTypes_Detrimental = SpellType_Nuke|SpellType_Root|SpellType_Lifetap|SpellType_Snare|SpellType_DOT|SpellType_Dispel|SpellType_Mez|SpellType_Charm|SpellType_Debuff|SpellType_Slow;
|
||||
const int SpellTypes_Beneficial = SpellType_Heal|SpellType_Buff|SpellType_Escape|SpellType_Pet|SpellType_InCombatBuff|SpellType_Cure;
|
||||
enum SpellTypes : uint32
|
||||
{
|
||||
SpellType_Nuke = (1 << 0),
|
||||
SpellType_Heal = (1 << 1),
|
||||
SpellType_Root = (1 << 2),
|
||||
SpellType_Buff = (1 << 3),
|
||||
SpellType_Escape = (1 << 4),
|
||||
SpellType_Pet = (1 << 5),
|
||||
SpellType_Lifetap = (1 << 6),
|
||||
SpellType_Snare = (1 << 7),
|
||||
SpellType_DOT = (1 << 8),
|
||||
SpellType_Dispel = (1 << 9),
|
||||
SpellType_InCombatBuff = (1 << 10),
|
||||
SpellType_Mez = (1 << 11),
|
||||
SpellType_Charm = (1 << 12),
|
||||
SpellType_Slow = (1 << 13),
|
||||
SpellType_Debuff = (1 << 14),
|
||||
SpellType_Cure = (1 << 15),
|
||||
SpellType_Resurrect = (1 << 16),
|
||||
SpellType_HateRedux = (1 << 17),
|
||||
SpellType_InCombatBuffSong = (1 << 18), // bard in-combat group/ae buffs
|
||||
SpellType_OutOfCombatBuffSong = (1 << 19), // bard out-of-combat group/ae buffs
|
||||
|
||||
SpellTypes_Detrimental = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow),
|
||||
SpellTypes_Beneficial = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong),
|
||||
|
||||
SpellType_Any = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
#define SpellType_Any 0xFFFF
|
||||
|
||||
// These should not be used to determine spell category..
|
||||
// They are a graphical affects (effects?) index only
|
||||
|
||||
@ -30,9 +30,9 @@
|
||||
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9103
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9105
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9008
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9010
|
||||
#else
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
||||
#endif
|
||||
|
||||
@ -110,9 +110,11 @@ bool Client::Process()
|
||||
}
|
||||
default:
|
||||
{
|
||||
char dump[64];
|
||||
app->build_header_dump(dump);
|
||||
Log.Out(Logs::General, Logs::Error, "Recieved unhandled application packet from the client: %s.", dump);
|
||||
if (Log.log_settings[Logs::Client_Server_Packet_Unhandled].is_category_enabled == 1) {
|
||||
char dump[64];
|
||||
app->build_header_dump(dump);
|
||||
Log.Out(Logs::General, Logs::Error, "Recieved unhandled application packet from the client: %s.", dump);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -359,6 +359,9 @@ OP_OpenContainer=0x654f
|
||||
OP_Marquee=0x288a
|
||||
OP_Fling=0x6b8e
|
||||
OP_CancelSneakHide=0x265f
|
||||
OP_AggroMeterLockTarget=0x70b7
|
||||
OP_AggroMeterTargetInfo=0x18fe
|
||||
OP_AggroMeterUpdate=0x75aa
|
||||
|
||||
OP_DzQuit=0x5fc8
|
||||
OP_DzListTimers=0x67b9
|
||||
|
||||
@ -360,6 +360,9 @@ OP_ItemRecastDelay=0x15a9
|
||||
OP_ResetAA=0x1669
|
||||
OP_Fling=0x6f80
|
||||
OP_CancelSneakHide=0x0927
|
||||
OP_AggroMeterLockTarget=0x1643
|
||||
OP_AggroMeterTargetInfo=0x16bc
|
||||
OP_AggroMeterUpdate=0x1781
|
||||
|
||||
# Expeditions
|
||||
OP_DzAddPlayer=0x4701
|
||||
|
||||
@ -334,7 +334,7 @@ sub build_linux_source {
|
||||
}
|
||||
print "Building EQEmu Server code. This will take a while.";
|
||||
|
||||
#::: Build
|
||||
#::: Build
|
||||
print `make`;
|
||||
|
||||
chdir ($current_directory);
|
||||
@ -1340,7 +1340,7 @@ sub do_linux_login_server_setup {
|
||||
|
||||
get_remote_file($install_repository_request_url . "linux/login.ini", "login_template.ini");
|
||||
get_remote_file($install_repository_request_url . "linux/login_opcodes.conf", "login_opcodes.conf");
|
||||
get_remote_file($install_repository_request_url . "linux/login_opcodes.conf", "login_opcodes_sod.conf");
|
||||
get_remote_file($install_repository_request_url . "linux/login_opcodes_sod.conf", "login_opcodes_sod.conf");
|
||||
|
||||
get_installation_variables();
|
||||
my $db_name = $installation_variables{"mysql_eqemu_db_name"};
|
||||
|
||||
@ -357,6 +357,8 @@
|
||||
9101|2016_12_01_pcnpc_only.sql|SHOW COLUMNS FROM `spells_new` LIKE 'pcnpc_only_flag'|empty|
|
||||
9102|2017_01_10_book_languages.sql|SHOW COLUMNS FROM `books` LIKE 'language'|empty|
|
||||
9103|2017_01_30_book_languages_fix.sql|SELECT `language` from `books` WHERE `language` IS NULL|not_empty|
|
||||
9104|2017_02_09_npc_spells_entries_type_update.sql|SHOW COLUMNS IN `npc_spells_entries` LIKE `type`|contains|smallint(5) unsigned
|
||||
9105|2017_02_15_bot_spells_entries.sql|SELECT `id` FROM `npc_spells_entries` WHERE `npc_spells_id` >= 701 AND `npc_spells_id` <= 712|not_empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@ -7,6 +7,8 @@
|
||||
9006|2016_04_12_bots_inventory_window.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'inventorywindow'|empty|
|
||||
9007|2016_06_23_bots_camel_case_name_rule.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE 'Bots:AllowCamelCaseNames'|empty|
|
||||
9008|2016_06_28_bots_inventory_charges_update.sql|SELECT * FROM `information_schema`.`COLUMNS` isc WHERE isc.`TABLE_SCHEMA` = DATABASE() AND isc.`TABLE_NAME` = 'bot_inventories' AND isc.`COLUMN_NAME` = 'inst_charges' AND isc.`DATA_TYPE` = 'tinyint'|not_empty|
|
||||
9009|2017_02_15_bots_bot_spells_entries.sql|SELECT `id` FROM `npc_spells_entries` WHERE `npc_spells_id` >= 701 AND `npc_spells_id` <= 712|not_empty|
|
||||
9010|2017_02_20_bots_bard_spell_update.sql|SELECT * FROM `bot_spells_entries` WHERE `npc_spells_id` = 711 AND (`type` & 0xFFFF0000) = 0xFFFF0000|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
-- Delete any existing `bots_spells_entries` table
|
||||
DROP TABLE IF EXISTS `bots_spells_entries`;
|
||||
|
||||
-- Create new bot spells entries table (new table does not have spells_id_spellid constraint)
|
||||
CREATE TABLE `bot_spells_entries` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`npc_spells_id` INT(11) NOT NULL DEFAULT '0',
|
||||
`spellid` SMALLINT(5) NOT NULL DEFAULT '0',
|
||||
`type` INT(10) 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',
|
||||
`resist_adjust` INT(11) NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
)
|
||||
COLLATE='latin1_swedish_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=1
|
||||
;
|
||||
|
||||
-- Copy bots spells into new table
|
||||
INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`, `manacost`, `recast_delay`, `priority`, `resist_adjust`)
|
||||
SELECT `npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`, `manacost`, `recast_delay`, `priority`, `resist_adjust`
|
||||
FROM `npc_spells_entries` WHERE `npc_spells_id` >= '701' AND `npc_spells_id` <= '712';
|
||||
|
||||
-- Delete bot spells from old table
|
||||
DELETE FROM `npc_spells_entries` WHERE `npc_spells_id` >= '701' AND `npc_spells_id` <= '712';
|
||||
@ -0,0 +1,153 @@
|
||||
DELETE FROM `bot_spells_entries` WHERE `npc_spells_id` = '711';
|
||||
|
||||
INSERT INTO `bot_spells_entries`(`npc_spells_id`,`spellid`,`type`,`minlevel`,`maxlevel`,`manacost`,`recast_delay`,`priority`,`resist_adjust`) VALUES
|
||||
|
||||
-- versioning entry
|
||||
('711', '0', '4294901760', '0', '0', '-1', '-1', '0', NULL), -- 0xFFFF0000
|
||||
|
||||
-- nuke
|
||||
('711', '704', '1', '12', '54', '-1', '-1', '1', NULL),
|
||||
('711', '1747', '1', '55', '127', '-1', '-1', '1', NULL),
|
||||
|
||||
-- escape
|
||||
('711', '1749', '16', '60', '127', '-1', '-1', '1', NULL),
|
||||
|
||||
-- dot
|
||||
('711', '743', '256', '38', '64', '-1', '-1', '1', NULL),
|
||||
('711', '3367', '256', '65', '69', '-1', '-1', '1', NULL),
|
||||
('711', '5385', '256', '70', '79', '-1', '-1', '1', NULL),
|
||||
('711', '14074', '256', '80', '84', '-1', '-1', '1', NULL),
|
||||
('711', '18059', '256', '85', '89', '-1', '-1', '1', NULL),
|
||||
('711', '26084', '256', '90', '94', '-1', '-1', '1', NULL),
|
||||
('711', '29182', '256', '95', '127', '-1', '-1', '1', NULL),
|
||||
('711', '3566', '256', '50', '62', '-1', '-1', '2', NULL),
|
||||
('711', '3370', '256', '63', '67', '-1', '-1', '2', NULL),
|
||||
('711', '5378', '256', '68', '77', '-1', '-1', '2', NULL),
|
||||
('711', '14071', '256', '78', '82', '-1', '-1', '2', NULL),
|
||||
('711', '18056', '256', '83', '87', '-1', '-1', '2', NULL),
|
||||
('711', '26033', '256', '88', '92', '-1', '-1', '2', NULL),
|
||||
('711', '29128', '256', '93', '127', '-1', '-1', '2', NULL),
|
||||
('711', '744', '256', '46', '62', '-1', '-1', '3', NULL),
|
||||
('711', '3373', '256', '63', '66', '-1', '-1', '3', NULL),
|
||||
('711', '5379', '256', '67', '76', '-1', '-1', '3', NULL),
|
||||
('711', '14068', '256', '77', '81', '-1', '-1', '3', NULL),
|
||||
('711', '18053', '256', '82', '86', '-1', '-1', '3', NULL),
|
||||
('711', '26003', '256', '87', '91', '-1', '-1', '3', NULL),
|
||||
('711', '29101', '256', '92', '127', '-1', '-1', '3', NULL),
|
||||
('711', '3567', '256', '42', '60', '-1', '-1', '4', NULL),
|
||||
('711', '3363', '256', '61', '65', '-1', '-1', '4', NULL),
|
||||
('711', '5371', '256', '66', '75', '-1', '-1', '4', NULL),
|
||||
('711', '14065', '256', '76', '80', '-1', '-1', '4', NULL),
|
||||
('711', '18050', '256', '81', '85', '-1', '-1', '4', NULL),
|
||||
('711', '25976', '256', '86', '90', '-1', '-1', '4', NULL),
|
||||
('711', '29077', '256', '91', '127', '-1', '-1', '4', NULL),
|
||||
('711', '707', '256', '30', '59', '-1', '-1', '5', NULL),
|
||||
('711', '4210', '256', '60', '127', '-1', '-1', '5', NULL),
|
||||
|
||||
-- slow
|
||||
('711', '738', '8192', '23', '50', '-1', '-1', '1', NULL),
|
||||
('711', '1751', '8192', '51', '59', '-1', '-1', '1', NULL),
|
||||
('711', '1748', '8192', '60', '63', '-1', '-1', '1', NULL),
|
||||
('711', '3066', '8192', '64', '127', '-1', '-1', '1', NULL),
|
||||
('711', '738', '8192', '51', '63', '-1', '-1', '2', NULL),
|
||||
('711', '1751', '8192', '64', '127', '-1', '-1', '2', NULL),
|
||||
('711', '738', '8192', '64', '127', '-1', '-1', '3', NULL),
|
||||
|
||||
-- cure
|
||||
('711', '3682', '32768', '45', '85', '-1', '-1', '1', NULL),
|
||||
('711', '25958', '32768', '86', '90', '-1', '-1', '1', NULL),
|
||||
('711', '29059', '32768', '91', '127', '-1', '-1', '1', NULL),
|
||||
('711', '3681', '32768', '52', '127', '-1', '-1', '2', NULL),
|
||||
('711', '10448', '32768', '74', '78', '-1', '-1', '3', NULL),
|
||||
('711', '14029', '32768', '79', '83', '-1', '-1', '3', NULL),
|
||||
('711', '18023', '32768', '84', '127', '-1', '-1', '3', NULL),
|
||||
|
||||
-- hate redux
|
||||
('711', '1754', '131072', '53', '127', '-1', '-1', '1', NULL),
|
||||
('711', '10436', '131072', '73', '127', '-1', '-1', '2', NULL),
|
||||
|
||||
-- in-combat buff songs
|
||||
('711', '2606', '262144', '52', '59', '-1', '-1', '1', NULL),
|
||||
('711', '2610', '262144', '60', '127', '-1', '-1', '1', NULL),
|
||||
('711', '700', '262144', '1', '9', '-1', '-1', '2', NULL),
|
||||
('711', '701', '262144', '10', '35', '-1', '-1', '2', NULL),
|
||||
('711', '740', '262144', '36', '41', '-1', '-1', '2', NULL),
|
||||
('711', '702', '262144', '42', '49', '-1', '-1', '2', NULL),
|
||||
('711', '747', '262144', '50', '61', '-1', '-1', '2', NULL),
|
||||
('711', '3374', '262144', '62', '64', '-1', '-1', '2', NULL),
|
||||
('711', '4871', '262144', '65', '67', '-1', '-1', '2', NULL),
|
||||
('711', '5376', '262144', '68', '78', '-1', '-1', '2', NULL),
|
||||
('711', '14080', '262144', '79', '83', '-1', '-1', '2', NULL),
|
||||
('711', '18065', '262144', '84', '88', '-1', '-1', '2', NULL),
|
||||
('711', '26042', '262144', '89', '93', '-1', '-1', '2', NULL),
|
||||
('711', '29143', '262144', '94', '127', '-1', '-1', '2', NULL),
|
||||
('711', '7', '262144', '6', '19', '-1', '-1', '2', NULL),
|
||||
('711', '1287', '262144', '20', '31', '-1', '-1', '3', NULL),
|
||||
('711', '723', '262144', '32', '33', '-1', '-1', '3', NULL),
|
||||
('711', '1448', '262144', '34', '54', '-1', '-1', '3', NULL),
|
||||
('711', '1759', '262144', '55', '61', '-1', '-1', '3', NULL),
|
||||
('711', '3651', '262144', '62', '66', '-1', '-1', '3', NULL),
|
||||
('711', '5377', '262144', '67', '70', '-1', '-1', '3', NULL),
|
||||
('711', '10421', '262144', '71', '75', '-1', '-1', '3', NULL),
|
||||
('711', '14008', '262144', '76', '80', '-1', '-1', '3', NULL),
|
||||
('711', '18008', '262144', '81', '87', '-1', '-1', '3', NULL),
|
||||
('711', '26015', '262144', '88', '92', '-1', '-1', '3', NULL),
|
||||
('711', '29107', '262144', '93', '127', '-1', '-1', '3', NULL),
|
||||
('711', '734', '262144', '7', '8', '-1', '-1', '4', NULL),
|
||||
('711', '710', '262144', '9', '12', '-1', '-1', '4', NULL),
|
||||
('711', '711', '262144', '13', '16', '-1', '-1', '4', NULL),
|
||||
('711', '709', '262144', '17', '40', '-1', '-1', '4', NULL),
|
||||
('711', '714', '262144', '41', '46', '-1', '-1', '4', NULL),
|
||||
('711', '748', '262144', '47', '57', '-1', '-1', '4', NULL),
|
||||
('711', '1763', '262144', '58', '72', '-1', '-1', '4', NULL),
|
||||
('711', '11881', '262144', '73', '77', '-1', '-1', '4', NULL),
|
||||
('711', '14056', '262144', '78', '82', '-1', '-1', '4', NULL),
|
||||
('711', '18041', '262144', '83', '87', '-1', '-1', '4', NULL),
|
||||
('711', '26027', '262144', '88', '92', '-1', '-1', '4', NULL),
|
||||
('711', '29122', '262144', '93', '127', '-1', '-1', '4', NULL),
|
||||
('711', '734', '262144', '9', '24', '-1', '-1', '5', NULL),
|
||||
('711', '712', '262144', '25', '28', '-1', '-1', '5', NULL),
|
||||
('711', '715', '262144', '29', '32', '-1', '-1', '5', NULL),
|
||||
('711', '713', '262144', '33', '36', '-1', '-1', '5', NULL),
|
||||
('711', '716', '262144', '37', '44', '-1', '-1', '5', NULL),
|
||||
('711', '4083', '262144', '45', '52', '-1', '-1', '5', NULL),
|
||||
('711', '4084', '262144', '53', '63', '-1', '-1', '5', NULL),
|
||||
('711', '3362', '262144', '64', '64', '-1', '-1', '5', NULL),
|
||||
('711', '4872', '262144', '65', '68', '-1', '-1', '5', NULL),
|
||||
('711', '5382', '262144', '69', '75', '-1', '-1', '5', NULL),
|
||||
('711', '14062', '262144', '76', '80', '-1', '-1', '5', NULL),
|
||||
('711', '18047', '262144', '81', '85', '-1', '-1', '5', NULL),
|
||||
('711', '25961', '262144', '86', '90', '-1', '-1', '5', NULL),
|
||||
('711', '29062', '262144', '91', '127', '-1', '-1', '5', NULL),
|
||||
('711', '734', '262144', '25', '43', '-1', '-1', '6', NULL),
|
||||
('711', '4085', '262144', '44', '51', '-1', '-1', '6', NULL),
|
||||
('711', '4086', '262144', '52', '62', '-1', '-1', '6', NULL),
|
||||
('711', '4087', '262144', '63', '68', '-1', '-1', '6', NULL),
|
||||
('711', '5374', '262144', '69', '71', '-1', '-1', '6', NULL),
|
||||
('711', '10439', '262144', '72', '76', '-1', '-1', '6', NULL),
|
||||
('711', '14020', '262144', '77', '81', '-1', '-1', '6', NULL),
|
||||
('711', '18014', '262144', '82', '86', '-1', '-1', '6', NULL),
|
||||
('711', '25991', '262144', '87', '127', '-1', '-1', '6', NULL),
|
||||
('711', '734', '262144', '30', '82', '-1', '-1', '7', NULL),
|
||||
('711', '18020', '262144', '83', '127', '-1', '-1', '7', NULL),
|
||||
('711', '734', '262144', '83', '127', '-1', '-1', '8', NULL),
|
||||
('711', '2603', '262144', '30', '127', '-1', '-1', '9', NULL),
|
||||
|
||||
-- out-of-combat buff songs
|
||||
('711', '7', '524288', '6', '19', '-1', '-1', '1', NULL),
|
||||
('711', '1287', '524288', '20', '31', '-1', '-1', '1', NULL),
|
||||
('711', '723', '524288', '32', '33', '-1', '-1', '1', NULL),
|
||||
('711', '1448', '524288', '34', '54', '-1', '-1', '1', NULL),
|
||||
('711', '1759', '524288', '55', '61', '-1', '-1', '1', NULL),
|
||||
('711', '3651', '524288', '62', '66', '-1', '-1', '1', NULL),
|
||||
('711', '5377', '524288', '67', '70', '-1', '-1', '1', NULL),
|
||||
('711', '10421', '524288', '71', '75', '-1', '-1', '1', NULL),
|
||||
('711', '14008', '524288', '76', '80', '-1', '-1', '1', NULL),
|
||||
('711', '18008', '524288', '81', '87', '-1', '-1', '1', NULL),
|
||||
('711', '26015', '524288', '88', '92', '-1', '-1', '1', NULL),
|
||||
('711', '29107', '524288', '93', '127', '-1', '-1', '1', NULL),
|
||||
('711', '717', '524288', '5', '29', '-1', '-1','2', NULL),
|
||||
('711', '2603', '524288', '30', '127', '-1', '-1','2', NULL),
|
||||
('711', '717', '524288', '30', '48', '-1', '-1','3', NULL),
|
||||
('711', '2605', '524288', '49', '127', '-1', '-1','3', NULL),
|
||||
('711', '2602', '524288', '15', '127', '-1', '-1','4', NULL);
|
||||
@ -0,0 +1 @@
|
||||
ALTER TABLE `npc_spells_entries` MODIFY COLUMN `type` INT(10) UNSIGNED NOT NULL DEFAULT '0';
|
||||
31
utils/sql/git/required/2017_02_15_bot_spells_entries.sql
Normal file
31
utils/sql/git/required/2017_02_15_bot_spells_entries.sql
Normal file
@ -0,0 +1,31 @@
|
||||
-- Delete any existing `bots_spells_entries` table
|
||||
DROP TABLE IF EXISTS `bots_spells_entries`;
|
||||
|
||||
-- Create new bot spells entries table (new table does not have spells_id_spellid constraint)
|
||||
CREATE TABLE `bot_spells_entries` (
|
||||
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`npc_spells_id` INT(11) NOT NULL DEFAULT '0',
|
||||
`spellid` SMALLINT(5) NOT NULL DEFAULT '0',
|
||||
`type` INT(10) 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',
|
||||
`resist_adjust` INT(11) NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
)
|
||||
COLLATE='latin1_swedish_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=1
|
||||
;
|
||||
|
||||
-- Copy bots spells into new table
|
||||
INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`, `manacost`, `recast_delay`, `priority`, `resist_adjust`)
|
||||
SELECT `npc_spells_id`, `spellid`, `type`, `minlevel`, `maxlevel`, `manacost`, `recast_delay`, `priority`, `resist_adjust`
|
||||
FROM `npc_spells_entries` WHERE `npc_spells_id` >= '701' AND `npc_spells_id` <= '712';
|
||||
|
||||
-- Delete bot spells from old table
|
||||
DELETE FROM `npc_spells_entries` WHERE `npc_spells_id` >= '701' AND `npc_spells_id` <= '712';
|
||||
|
||||
-- Admins can remove this new table if they are 100% certain they will never use bots
|
||||
@ -4,6 +4,7 @@ SET(zone_sources
|
||||
aa.cpp
|
||||
aa_ability.cpp
|
||||
aggro.cpp
|
||||
aggromanager.cpp
|
||||
attack.cpp
|
||||
beacon.cpp
|
||||
bonuses.cpp
|
||||
@ -122,6 +123,7 @@ SET(zone_sources
|
||||
water_map_v2.cpp
|
||||
waypoints.cpp
|
||||
worldserver.cpp
|
||||
xtargetautohaters.cpp
|
||||
zone.cpp
|
||||
zone_config.cpp
|
||||
zonedb.cpp
|
||||
@ -131,6 +133,7 @@ SET(zone_sources
|
||||
SET(zone_headers
|
||||
aa.h
|
||||
aa_ability.h
|
||||
aggromanager.h
|
||||
basic_functions.h
|
||||
beacon.h
|
||||
bot.h
|
||||
@ -215,6 +218,7 @@ SET(zone_headers
|
||||
water_map_v1.h
|
||||
water_map_v2.h
|
||||
worldserver.h
|
||||
xtargetautohaters.h
|
||||
zone.h
|
||||
zone_config.h
|
||||
zonedb.h
|
||||
|
||||
11
zone/aggromanager.cpp
Normal file
11
zone/aggromanager.cpp
Normal file
@ -0,0 +1,11 @@
|
||||
#include "aggromanager.h"
|
||||
|
||||
AggroMeter::AggroMeter() : lock_id(0), target_id(0), secondary_id(0), lock_changed(false)
|
||||
{
|
||||
for (int i = 0; i < AT_Max; ++i) {
|
||||
data[i].type = i;
|
||||
data[i].pct = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
80
zone/aggromanager.h
Normal file
80
zone/aggromanager.h
Normal file
@ -0,0 +1,80 @@
|
||||
#ifndef AGGROMANAGER_H
|
||||
#define AGGROMANAGER_H
|
||||
|
||||
#include "../common/types.h"
|
||||
#include <assert.h>
|
||||
#include <cstddef>
|
||||
|
||||
class AggroMeter
|
||||
{
|
||||
public:
|
||||
enum AggroTypes {
|
||||
AT_Player,
|
||||
AT_Secondary,
|
||||
AT_Group1,
|
||||
AT_Group2,
|
||||
AT_Group3,
|
||||
AT_Group4,
|
||||
AT_Group5,
|
||||
AT_XTarget1,
|
||||
AT_XTarget2,
|
||||
AT_XTarget3,
|
||||
AT_XTarget4,
|
||||
AT_XTarget5,
|
||||
AT_XTarget6,
|
||||
AT_XTarget7,
|
||||
AT_XTarget8,
|
||||
AT_XTarget9,
|
||||
AT_XTarget10,
|
||||
AT_XTarget11,
|
||||
AT_XTarget12,
|
||||
AT_XTarget13,
|
||||
AT_XTarget14,
|
||||
AT_XTarget15,
|
||||
AT_XTarget16,
|
||||
AT_XTarget17,
|
||||
AT_XTarget18,
|
||||
AT_XTarget19,
|
||||
AT_XTarget20,
|
||||
AT_Max
|
||||
};
|
||||
|
||||
private:
|
||||
struct AggroData {
|
||||
int16 type;
|
||||
int16 pct;
|
||||
};
|
||||
|
||||
AggroData data[AT_Max];
|
||||
int lock_id; // we set this
|
||||
int target_id; // current target or if PC targeted, their Target
|
||||
// so secondary depends on if we have aggro or not
|
||||
// When we are the current target, this will be the 2nd person on list
|
||||
// When we are not tanking, this will be the current tank
|
||||
int secondary_id;
|
||||
|
||||
// so we need some easy way to detect the client changing but still delaying the packet
|
||||
bool lock_changed;
|
||||
public:
|
||||
AggroMeter();
|
||||
~AggroMeter() {}
|
||||
|
||||
inline void set_lock_id(int in) { lock_id = in; lock_changed = true; }
|
||||
inline bool update_lock() { bool ret = lock_changed; lock_changed = false; return ret; }
|
||||
inline void set_target_id(int in) { target_id = in; }
|
||||
inline void set_secondary_id(int in) { secondary_id = in; }
|
||||
// returns true when changed
|
||||
inline bool set_pct(AggroTypes t, int pct) { assert(t >= AT_Player && t < AT_Max); if (data[t].pct == pct) return false; data[t].pct = pct; return true; }
|
||||
|
||||
inline int get_lock_id() const { return lock_id; }
|
||||
inline int get_target_id() const { return target_id; }
|
||||
inline int get_secondary_id() const { return secondary_id; }
|
||||
inline int get_pct(AggroTypes t) const { assert(t >= AT_Player && t < AT_Max); return data[t].pct; }
|
||||
// the ID of the spawn for player entry depends on lock_id
|
||||
inline int get_player_aggro_id() const { return lock_id ? lock_id : target_id; }
|
||||
// fuck it, lets just use a buffer the size of the largest to work with
|
||||
const inline size_t max_packet_size() const { return sizeof(uint8) + sizeof(uint32) + sizeof(uint8) + (sizeof(uint8) + sizeof(uint16)) * AT_Max; }
|
||||
};
|
||||
|
||||
|
||||
#endif /* !AGGROMANAGER_H */
|
||||
@ -359,7 +359,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
|
||||
}
|
||||
|
||||
// riposte -- it may seem crazy, but if the attacker has SPA 173 on them, they are immune to Ripo
|
||||
bool ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance;
|
||||
bool ImmuneRipo = attacker->aabonuses.RiposteChance || attacker->spellbonuses.RiposteChance || attacker->itembonuses.RiposteChance || attacker->IsEnraged();
|
||||
// Need to check if we have something in MainHand to actually attack with (or fists)
|
||||
if (hit.hand != EQEmu::inventory::slotRange && (CanThisClassRiposte() || IsEnraged()) && InFront && !ImmuneRipo) {
|
||||
if (IsEnraged()) {
|
||||
@ -2433,9 +2433,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
||||
Mob* mypet = this->GetPet();
|
||||
Mob* myowner = this->GetOwner();
|
||||
Mob* targetmob = this->GetTarget();
|
||||
bool on_hatelist = CheckAggro(other);
|
||||
|
||||
if(other){
|
||||
bool on_hatelist = CheckAggro(other);
|
||||
AddRampage(other);
|
||||
if (on_hatelist) { // odd reason, if you're not on the hate list, subtlety etc don't apply!
|
||||
// Spell Casting Subtlety etc
|
||||
@ -2511,7 +2511,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
||||
|
||||
hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic);
|
||||
|
||||
if(other->IsClient())
|
||||
if(other->IsClient() && !on_hatelist)
|
||||
other->CastToClient()->AddAutoXTarget(this);
|
||||
|
||||
#ifdef BOTS
|
||||
@ -2550,7 +2550,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
||||
if(!owner->GetSpecialAbility(IMMUNE_AGGRO))
|
||||
{
|
||||
hate_list.AddEntToHateList(owner, 0, 0, false, !iBuffTic);
|
||||
if(owner->IsClient())
|
||||
if(owner->IsClient() && !CheckAggro(owner))
|
||||
owner->CastToClient()->AddAutoXTarget(this);
|
||||
}
|
||||
}
|
||||
|
||||
45
zone/bot.cpp
45
zone/bot.cpp
@ -76,7 +76,6 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
|
||||
SetAltOutOfCombatBehavior(GetClass() == BARD); // will need to be updated if more classes make use of this flag
|
||||
SetShowHelm(true);
|
||||
SetPauseAI(false);
|
||||
CalcChanceToCast();
|
||||
rest_timer.Disable();
|
||||
SetFollowDistance(BOT_DEFAULT_FOLLOW_DISTANCE);
|
||||
// Do this once and only in this constructor
|
||||
@ -151,7 +150,6 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == BotStanceAggressive));
|
||||
SetPauseAI(false);
|
||||
|
||||
CalcChanceToCast();
|
||||
rest_timer.Disable();
|
||||
SetFollowDistance(BOT_DEFAULT_FOLLOW_DISTANCE);
|
||||
strcpy(this->name, this->GetCleanName());
|
||||
@ -1636,6 +1634,19 @@ bool Bot::LoadPet()
|
||||
auto bot_owner = GetBotOwner();
|
||||
if (!bot_owner)
|
||||
return false;
|
||||
|
||||
if (GetClass() == WIZARD) {
|
||||
auto buffs_max = GetMaxBuffSlots();
|
||||
auto my_buffs = GetBuffs();
|
||||
if (buffs_max && my_buffs) {
|
||||
for (int index = 0; index < buffs_max; ++index) {
|
||||
if (IsEffectInSpell(my_buffs[index].spellid, SE_Familiar)) {
|
||||
MakePet(my_buffs[index].spellid, spells[my_buffs[index].spellid].teleport_zone);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string error_message;
|
||||
|
||||
@ -1651,7 +1662,7 @@ bool Bot::LoadPet()
|
||||
if (!botdb.LoadPetSpellID(GetBotID(), saved_pet_spell_id)) {
|
||||
bot_owner->Message(13, "%s for %s's pet", BotDatabase::fail::LoadPetSpellID(), GetCleanName());
|
||||
}
|
||||
if (!saved_pet_spell_id || saved_pet_spell_id > SPDAT_RECORDS) {
|
||||
if (!IsValidSpell(saved_pet_spell_id)) {
|
||||
bot_owner->Message(13, "Invalid spell id for %s's pet", GetCleanName());
|
||||
DeletePet();
|
||||
return false;
|
||||
@ -1695,11 +1706,11 @@ bool Bot::LoadPet()
|
||||
|
||||
bool Bot::SavePet()
|
||||
{
|
||||
if (!GetPet() /*|| dead*/)
|
||||
if (!GetPet() || GetPet()->IsFamiliar() /*|| dead*/)
|
||||
return true;
|
||||
|
||||
NPC *pet_inst = GetPet()->CastToNPC();
|
||||
if (pet_inst->IsFamiliar() || !pet_inst->GetPetSpellID() || pet_inst->GetPetSpellID() > SPDAT_RECORDS)
|
||||
if (!pet_inst->GetPetSpellID() || !IsValidSpell(pet_inst->GetPetSpellID()))
|
||||
return false;
|
||||
|
||||
auto bot_owner = GetBotOwner();
|
||||
@ -7495,7 +7506,7 @@ bool Bot::GroupHasClass(Group* group, uint8 classId) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 iSpellTypes) {
|
||||
bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
|
||||
if((iSpellTypes&SpellTypes_Detrimental) != 0) {
|
||||
Log.Out(Logs::General, Logs::Error, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!");
|
||||
return false;
|
||||
@ -7639,6 +7650,24 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iSpellTypes == SpellType_HateRedux) {
|
||||
if (!caster->IsEngaged())
|
||||
return false;
|
||||
|
||||
if (caster->HasGroup()) {
|
||||
Group *g = caster->GetGroup();
|
||||
if (g) {
|
||||
for (int i = 0; i < MAX_GROUP_MEMBERS; i++) {
|
||||
if (g->members[i] && caster->GetNeedsHateRedux(g->members[i])) {
|
||||
if (caster->AICastSpell(g->members[i], caster->GetChanceToCastBySpellType(SpellType_HateRedux), SpellType_HateRedux))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -8017,7 +8046,7 @@ bool Bot::GetNeedsHateRedux(Mob *tar) {
|
||||
// This really should be a scalar function based in class Mob that returns 'this' state..but, is inline with current Bot coding...
|
||||
// TODO: Good starting point..but, can be refined..
|
||||
// TODO: Still awaiting bot spell rework..
|
||||
if (!tar || !tar->HasTargetReflection())
|
||||
if (!tar || !tar->IsEngaged() || !tar->HasTargetReflection() || !tar->GetTarget()->IsNPC())
|
||||
return false;
|
||||
|
||||
if (tar->IsClient()) {
|
||||
@ -8031,7 +8060,7 @@ bool Bot::GetNeedsHateRedux(Mob *tar) {
|
||||
else if (tar->IsBot()) {
|
||||
switch (tar->GetClass()) {
|
||||
case ROGUE:
|
||||
if (tar->CastToBot()->evade_timer.Check(false))
|
||||
if (tar->CanFacestab() || tar->CastToBot()->evade_timer.Check(false))
|
||||
return false;
|
||||
case CLERIC:
|
||||
case DRUID:
|
||||
|
||||
25
zone/bot.h
25
zone/bot.h
@ -47,8 +47,6 @@ const int MaxSpellTimer = 15;
|
||||
const int MaxDisciplineTimer = 10;
|
||||
const int DisciplineReuseStart = MaxSpellTimer + 1;
|
||||
const int MaxTimer = MaxSpellTimer + MaxDisciplineTimer;
|
||||
const int MaxStances = 7;
|
||||
const int MaxSpellTypes = 16;
|
||||
|
||||
enum BotStanceType {
|
||||
BotStancePassive,
|
||||
@ -58,7 +56,8 @@ enum BotStanceType {
|
||||
BotStanceAggressive,
|
||||
BotStanceBurn,
|
||||
BotStanceBurnAE,
|
||||
BotStanceUnknown
|
||||
BotStanceUnknown,
|
||||
MaxStances = BotStanceUnknown
|
||||
};
|
||||
|
||||
#define BOT_STANCE_COUNT 8
|
||||
@ -125,7 +124,12 @@ enum SpellTypeIndex {
|
||||
SpellType_CharmIndex,
|
||||
SpellType_SlowIndex,
|
||||
SpellType_DebuffIndex,
|
||||
SpellType_CureIndex
|
||||
SpellType_CureIndex,
|
||||
SpellType_ResurrectIndex,
|
||||
SpellType_HateReduxIndex,
|
||||
SpellType_InCombatBuffSongIndex,
|
||||
SpellType_OutOfCombatBuffSongIndex,
|
||||
MaxSpellTypes
|
||||
};
|
||||
|
||||
class Bot : public NPC {
|
||||
@ -235,7 +239,7 @@ public:
|
||||
virtual void Depop();
|
||||
void CalcBotStats(bool showtext = true);
|
||||
uint16 BotGetSpells(int spellslot) { return AIspells[spellslot].spellid; }
|
||||
uint16 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; }
|
||||
uint32 BotGetSpellType(int spellslot) { return AIspells[spellslot].type; }
|
||||
uint16 BotGetSpellPriority(int spellslot) { return AIspells[spellslot].priority; }
|
||||
virtual float GetProcChances(float ProcBonus, uint16 hand);
|
||||
virtual int GetHandToHandDamage(void);
|
||||
@ -296,7 +300,6 @@ public:
|
||||
bool GetNeedsHateRedux(Mob *tar);
|
||||
bool HasOrMayGetAggro();
|
||||
void SetDefaultBotStance();
|
||||
void CalcChanceToCast();
|
||||
|
||||
inline virtual int32 GetMaxStat();
|
||||
inline virtual int32 GetMaxResist();
|
||||
@ -350,7 +353,7 @@ public:
|
||||
void DoEnduranceUpkeep(); //does the endurance upkeep
|
||||
|
||||
// AI Methods
|
||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes);
|
||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes);
|
||||
virtual bool AI_EngagedCastCheck();
|
||||
virtual bool AI_PursueCastCheck();
|
||||
virtual bool AI_IdleCastCheck();
|
||||
@ -416,8 +419,9 @@ public:
|
||||
static uint32 GetDisciplineRemainingTime(Bot *caster, int timer_index);
|
||||
static std::list<BotSpell> GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect);
|
||||
static std::list<BotSpell> GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType);
|
||||
static std::list<BotSpell> GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType);
|
||||
static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType);
|
||||
static std::list<BotSpell> GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType);
|
||||
static std::list<BotSpell_wPriority> GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint32 spellType);
|
||||
static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType);
|
||||
static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster);
|
||||
static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster);
|
||||
@ -467,7 +471,7 @@ public:
|
||||
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
|
||||
BotRoleType GetBotRole() { return _botRole; }
|
||||
BotStanceType GetBotStance() { return _botStance; }
|
||||
uint8 GetChanceToCastBySpellType(uint16 spellType);
|
||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||
bool IsGroupPrimaryHealer();
|
||||
bool IsGroupPrimarySlower();
|
||||
bool IsBotCaster() { return IsCasterClass(GetClass()); }
|
||||
@ -669,7 +673,6 @@ private:
|
||||
uint32 timers[MaxTimer];
|
||||
bool _hasBeenSummoned;
|
||||
glm::vec3 m_PreSummonLocation;
|
||||
uint8 _spellCastingChances[MaxStances][MaxSpellTypes];
|
||||
|
||||
Timer evade_timer;
|
||||
|
||||
|
||||
@ -5117,7 +5117,6 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
|
||||
|
||||
if (!current_flag) {
|
||||
bot_iter->SetBotStance(bst);
|
||||
bot_iter->CalcChanceToCast();
|
||||
bot_iter->Save();
|
||||
}
|
||||
|
||||
|
||||
@ -56,6 +56,10 @@ struct BotSpell {
|
||||
int16 ManaCost;
|
||||
};
|
||||
|
||||
struct BotSpell_wPriority : public BotSpell {
|
||||
uint8 Priority;
|
||||
};
|
||||
|
||||
struct BotAA {
|
||||
uint32 aa_id;
|
||||
uint8 req_level;
|
||||
|
||||
4495
zone/botspellsai.cpp
4495
zone/botspellsai.cpp
File diff suppressed because it is too large
Load Diff
361
zone/client.cpp
361
zone/client.cpp
@ -154,6 +154,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
afk_toggle_timer(250),
|
||||
helm_toggle_timer(250),
|
||||
light_update_timer(600),
|
||||
aggro_meter_timer(AGGRO_METER_UPDATE_MS),
|
||||
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
|
||||
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
|
||||
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
|
||||
@ -310,6 +311,8 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
}
|
||||
MaxXTargets = 5;
|
||||
XTargetAutoAddHaters = true;
|
||||
m_autohatermgr.SetOwner(this, nullptr, nullptr);
|
||||
m_activeautohatermgr = &m_autohatermgr;
|
||||
LoadAccountFlags();
|
||||
|
||||
initial_respawn_selection = 0;
|
||||
@ -4158,6 +4161,18 @@ bool Client::GroupFollow(Client* inviter) {
|
||||
}
|
||||
if (raid->RaidCount() < MAX_RAID_MEMBERS)
|
||||
{
|
||||
// okay, so we now have a single client (this) joining a group in a raid
|
||||
// And they're not already in the raid (which is above and doesn't need xtarget shit)
|
||||
if (!GetXTargetAutoMgr()->empty()) {
|
||||
raid->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr());
|
||||
GetXTargetAutoMgr()->clear();
|
||||
RemoveAutoXTargets();
|
||||
}
|
||||
|
||||
SetXTargetAutoMgr(GetXTargetAutoMgr());
|
||||
if (!GetXTargetAutoMgr()->empty())
|
||||
SetDirtyAutoHaters();
|
||||
|
||||
if (raid->GroupCount(groupToUse) < 6)
|
||||
{
|
||||
raid->SendRaidCreate(this);
|
||||
@ -4233,7 +4248,9 @@ bool Client::GroupFollow(Client* inviter) {
|
||||
inviter->SendGroupLeaderChangePacket(inviter->GetName());
|
||||
inviter->SendGroupJoinAcknowledge();
|
||||
}
|
||||
|
||||
group->GetXTargetAutoMgr()->merge(*inviter->GetXTargetAutoMgr());
|
||||
inviter->GetXTargetAutoMgr()->clear();
|
||||
inviter->SetXTargetAutoMgr(group->GetXTargetAutoMgr());
|
||||
}
|
||||
|
||||
if (!group)
|
||||
@ -7169,12 +7186,12 @@ void Client::UpdateClientXTarget(Client *c)
|
||||
}
|
||||
}
|
||||
|
||||
// IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO
|
||||
void Client::AddAutoXTarget(Mob *m, bool send)
|
||||
{
|
||||
if(!XTargettingAvailable() || !XTargetAutoAddHaters)
|
||||
return;
|
||||
m_activeautohatermgr->increment_count(m);
|
||||
|
||||
if(IsXTarget(m))
|
||||
if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m))
|
||||
return;
|
||||
|
||||
for(int i = 0; i < GetMaxXTargets(); ++i)
|
||||
@ -7193,60 +7210,15 @@ void Client::AddAutoXTarget(Mob *m, bool send)
|
||||
|
||||
void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
|
||||
{
|
||||
if (!XTargettingAvailable())
|
||||
return;
|
||||
|
||||
bool HadFreeAutoSlotsBefore = false;
|
||||
|
||||
int FreedAutoSlots = 0;
|
||||
|
||||
if (m->GetID() == 0)
|
||||
return;
|
||||
|
||||
m_activeautohatermgr->decrement_count(m);
|
||||
// now we may need to clean up our CurrentTargetNPC entries
|
||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||
if (OnlyAutoSlots && XTargets[i].Type != Auto)
|
||||
continue;
|
||||
|
||||
if (XTargets[i].ID == m->GetID()) {
|
||||
if (XTargets[i].Type == CurrentTargetNPC)
|
||||
XTargets[i].Type = Auto;
|
||||
|
||||
if (XTargets[i].Type == Auto)
|
||||
++FreedAutoSlots;
|
||||
|
||||
if (XTargets[i].Type == CurrentTargetNPC && XTargets[i].ID == m->GetID()) {
|
||||
XTargets[i].Type = Auto;
|
||||
XTargets[i].ID = 0;
|
||||
XTargets[i].dirty = true;
|
||||
} else {
|
||||
if (XTargets[i].Type == Auto && XTargets[i].ID == 0)
|
||||
HadFreeAutoSlotsBefore = true;
|
||||
}
|
||||
}
|
||||
|
||||
// move shit up! If the removed NPC was in a CurrentTargetNPC slot it becomes Auto
|
||||
// and we need to potentially fill it
|
||||
std::queue<int> empty_slots;
|
||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||
if (XTargets[i].Type != Auto)
|
||||
continue;
|
||||
|
||||
if (XTargets[i].ID == 0) {
|
||||
empty_slots.push(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (XTargets[i].ID != 0 && !empty_slots.empty()) {
|
||||
int temp = empty_slots.front();
|
||||
std::swap(XTargets[i], XTargets[temp]);
|
||||
XTargets[i].dirty = XTargets[temp].dirty = true;
|
||||
empty_slots.pop();
|
||||
empty_slots.push(i);
|
||||
}
|
||||
}
|
||||
// If there are more mobs aggro on us than we had auto-hate slots, add one of those haters into the slot(s) we
|
||||
// just freed up.
|
||||
if (!HadFreeAutoSlotsBefore && FreedAutoSlots)
|
||||
entity_list.RefreshAutoXTargets(this);
|
||||
SendXTargetUpdates();
|
||||
}
|
||||
|
||||
void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name)
|
||||
@ -7403,6 +7375,123 @@ void Client::ShowXTargets(Client *c)
|
||||
|
||||
for(int i = 0; i < GetMaxXTargets(); ++i)
|
||||
c->Message(0, "Xtarget Slot: %i, Type: %2i, ID: %4i, Name: %s", i, XTargets[i].Type, XTargets[i].ID, XTargets[i].Name);
|
||||
auto &list = GetXTargetAutoMgr()->get_list();
|
||||
// yeah, I kept having to do something for debugging to tell if managers were the same object or not :P
|
||||
// so lets use the address as an "ID"
|
||||
c->Message(0, "XTargetAutoMgr ID %p size %d", GetXTargetAutoMgr(), list.size());
|
||||
int count = 0;
|
||||
for (auto &e : list) {
|
||||
c->Message(0, "spawn id %d count %d", e.spawn_id, e.count);
|
||||
count++;
|
||||
if (count == 20) { // lets not spam too many ...
|
||||
c->Message(0, " ... ");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::ProcessXTargetAutoHaters()
|
||||
{
|
||||
if (!XTargettingAvailable())
|
||||
return;
|
||||
|
||||
// move shit up! If the removed NPC was in a CurrentTargetNPC slot it becomes Auto
|
||||
// and we need to potentially fill it
|
||||
std::queue<int> empty_slots;
|
||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||
if (XTargets[i].Type != Auto)
|
||||
continue;
|
||||
|
||||
if (XTargets[i].ID != 0 && !GetXTargetAutoMgr()->contains_mob(XTargets[i].ID)) {
|
||||
XTargets[i].ID = 0;
|
||||
XTargets[i].dirty = true;
|
||||
}
|
||||
|
||||
if (XTargets[i].ID == 0) {
|
||||
empty_slots.push(i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (XTargets[i].ID != 0 && !empty_slots.empty()) {
|
||||
int temp = empty_slots.front();
|
||||
std::swap(XTargets[i], XTargets[temp]);
|
||||
XTargets[i].dirty = XTargets[temp].dirty = true;
|
||||
empty_slots.pop();
|
||||
empty_slots.push(i);
|
||||
}
|
||||
}
|
||||
// okay, now we need to check if we have any empty slots and if we have aggro
|
||||
// We make the assumption that if we shuffled the NPCs up that they're still on the aggro
|
||||
// list in the same order. We could probably do this better and try to calc if
|
||||
// there are new NPCs for our empty slots on the manager, but ahhh fuck it.
|
||||
if (!empty_slots.empty() && !GetXTargetAutoMgr()->empty() && XTargetAutoAddHaters) {
|
||||
auto &haters = GetXTargetAutoMgr()->get_list();
|
||||
for (auto &e : haters) {
|
||||
auto *mob = entity_list.GetMob(e.spawn_id);
|
||||
if (mob && !IsXTarget(mob)) {
|
||||
auto slot = empty_slots.front();
|
||||
empty_slots.pop();
|
||||
XTargets[slot].dirty = true;
|
||||
XTargets[slot].ID = mob->GetID();
|
||||
strn0cpy(XTargets[slot].Name, mob->GetCleanName(), 64);
|
||||
}
|
||||
if (empty_slots.empty())
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_dirtyautohaters = false;
|
||||
SendXTargetUpdates();
|
||||
}
|
||||
|
||||
// This function is called when a client is added to a group
|
||||
// Group leader joining isn't handled by this function
|
||||
void Client::JoinGroupXTargets(Group *g)
|
||||
{
|
||||
if (!g)
|
||||
return;
|
||||
|
||||
if (!GetXTargetAutoMgr()->empty()) {
|
||||
g->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr());
|
||||
GetXTargetAutoMgr()->clear();
|
||||
RemoveAutoXTargets();
|
||||
}
|
||||
|
||||
SetXTargetAutoMgr(g->GetXTargetAutoMgr());
|
||||
|
||||
if (!GetXTargetAutoMgr()->empty())
|
||||
SetDirtyAutoHaters();
|
||||
}
|
||||
|
||||
// This function is called when a client leaves a group
|
||||
void Client::LeaveGroupXTargets(Group *g)
|
||||
{
|
||||
if (!g)
|
||||
return;
|
||||
|
||||
SetXTargetAutoMgr(nullptr); // this will set it back to our manager
|
||||
RemoveAutoXTargets();
|
||||
entity_list.RefreshAutoXTargets(this); // this will probably break the temporal ordering, but whatever
|
||||
// We now have a rebuilt, valid auto hater manager, so we need to demerge from the groups
|
||||
if (!GetXTargetAutoMgr()->empty()) {
|
||||
GetXTargetAutoMgr()->demerge(*g->GetXTargetAutoMgr()); // this will remove entries where we only had aggro
|
||||
SetDirtyAutoHaters();
|
||||
}
|
||||
}
|
||||
|
||||
// This function is called when a client leaves a group
|
||||
void Client::LeaveRaidXTargets(Raid *r)
|
||||
{
|
||||
if (!r)
|
||||
return;
|
||||
|
||||
SetXTargetAutoMgr(nullptr); // this will set it back to our manager
|
||||
RemoveAutoXTargets();
|
||||
entity_list.RefreshAutoXTargets(this); // this will probably break the temporal ordering, but whatever
|
||||
// We now have a rebuilt, valid auto hater manager, so we need to demerge from the groups
|
||||
if (!GetXTargetAutoMgr()->empty()) {
|
||||
GetXTargetAutoMgr()->demerge(*r->GetXTargetAutoMgr()); // this will remove entries where we only had aggro
|
||||
SetDirtyAutoHaters();
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SetMaxXTargets(uint8 NewMax)
|
||||
@ -8681,3 +8770,169 @@ void Client::CheckRegionTypeChanges()
|
||||
else if (GetPVP())
|
||||
SetPVP(false, false);
|
||||
}
|
||||
|
||||
void Client::ProcessAggroMeter()
|
||||
{
|
||||
if (!AggroMeterAvailable()) {
|
||||
aggro_meter_timer.Disable();
|
||||
return;
|
||||
}
|
||||
|
||||
// we need to decide if we need to send OP_AggroMeterTargetInfo now
|
||||
// This packet sends the current lock target ID and the current target ID
|
||||
// target ID will be either our target or our target of target when we're targeting a PC
|
||||
bool send_targetinfo = false;
|
||||
auto cur_tar = GetTarget();
|
||||
|
||||
// probably should have PVP rules ...
|
||||
if (cur_tar && cur_tar != this) {
|
||||
if (cur_tar->IsNPC() && !cur_tar->IsPetOwnerClient() && cur_tar->GetID() != m_aggrometer.get_target_id()) {
|
||||
m_aggrometer.set_target_id(cur_tar->GetID());
|
||||
send_targetinfo = true;
|
||||
} else if ((cur_tar->IsPetOwnerClient() || cur_tar->IsClient()) && cur_tar->GetTarget() && cur_tar->GetTarget()->GetID() != m_aggrometer.get_target_id()) {
|
||||
m_aggrometer.set_target_id(cur_tar->GetTarget()->GetID());
|
||||
send_targetinfo = true;
|
||||
}
|
||||
} else if (m_aggrometer.get_target_id()) {
|
||||
m_aggrometer.set_target_id(0);
|
||||
send_targetinfo = true;
|
||||
}
|
||||
|
||||
if (m_aggrometer.update_lock())
|
||||
send_targetinfo = true;
|
||||
|
||||
if (send_targetinfo) {
|
||||
auto app = new EQApplicationPacket(OP_AggroMeterTargetInfo, sizeof(uint32) * 2);
|
||||
app->WriteUInt32(m_aggrometer.get_lock_id());
|
||||
app->WriteUInt32(m_aggrometer.get_target_id());
|
||||
FastQueuePacket(&app);
|
||||
}
|
||||
|
||||
// we could just calculate how big the packet would need to be ... but it's easier this way :P should be 87 bytes
|
||||
auto app = new EQApplicationPacket(OP_AggroMeterUpdate, m_aggrometer.max_packet_size());
|
||||
|
||||
cur_tar = entity_list.GetMob(m_aggrometer.get_target_id());
|
||||
|
||||
// first we must check the secondary
|
||||
// TODO: lock target should affect secondary as well
|
||||
bool send = false;
|
||||
Mob *secondary = nullptr;
|
||||
bool has_aggro = false;
|
||||
if (cur_tar) {
|
||||
if (cur_tar->GetTarget() == this) {// we got aggro
|
||||
secondary = cur_tar->GetSecondaryHate(this);
|
||||
has_aggro = true;
|
||||
} else {
|
||||
secondary = cur_tar->GetTarget();
|
||||
}
|
||||
}
|
||||
|
||||
if (secondary && secondary->GetID() != m_aggrometer.get_secondary_id()) {
|
||||
m_aggrometer.set_secondary_id(secondary->GetID());
|
||||
app->WriteUInt8(1);
|
||||
app->WriteUInt32(m_aggrometer.get_secondary_id());
|
||||
send = true;
|
||||
} else if (!secondary && m_aggrometer.get_secondary_id()) {
|
||||
m_aggrometer.set_secondary_id(0);
|
||||
app->WriteUInt8(1);
|
||||
app->WriteUInt32(0);
|
||||
send = true;
|
||||
} else { // might not need to send in this case
|
||||
app->WriteUInt8(0);
|
||||
}
|
||||
|
||||
auto count_offset = app->GetWritePosition();
|
||||
app->WriteUInt8(0);
|
||||
|
||||
int count = 0;
|
||||
auto add_entry = [&app, &count, this](AggroMeter::AggroTypes i) {
|
||||
count++;
|
||||
app->WriteUInt8(i);
|
||||
app->WriteUInt16(m_aggrometer.get_pct(i));
|
||||
};
|
||||
// TODO: Player entry should either be lock or yourself, ignoring lock for now
|
||||
// player, secondary, and group depend on your target/lock
|
||||
if (cur_tar) {
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Player, cur_tar->GetHateRatio(cur_tar->GetTarget(), this)))
|
||||
add_entry(AggroMeter::AT_Player);
|
||||
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Secondary, has_aggro ? cur_tar->GetHateRatio(this, secondary) : secondary ? 100 : 0))
|
||||
add_entry(AggroMeter::AT_Secondary);
|
||||
|
||||
// fuuuuuuuuuuuuuuuuuuuuuuuucckkkkkkkkkkkkkkk raids
|
||||
if (IsRaidGrouped()) {
|
||||
auto raid = GetRaid();
|
||||
if (raid) {
|
||||
auto gid = raid->GetGroup(this);
|
||||
if (gid < 12) {
|
||||
int at_id = AggroMeter::AT_Group1;
|
||||
for (int i = 0; i < MAX_RAID_MEMBERS; ++i) {
|
||||
if (raid->members[i].member && raid->members[i].member != this && raid->members[i].GroupNumber == gid) {
|
||||
if (m_aggrometer.set_pct(static_cast<AggroMeter::AggroTypes>(at_id), cur_tar->GetHateRatio(cur_tar->GetTarget(), raid->members[i].member)))
|
||||
add_entry(static_cast<AggroMeter::AggroTypes>(at_id));
|
||||
at_id++;
|
||||
if (at_id > AggroMeter::AT_Group5)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (IsGrouped()) {
|
||||
auto group = GetGroup();
|
||||
if (group) {
|
||||
int at_id = AggroMeter::AT_Group1;
|
||||
for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) {
|
||||
if (group->members[i] && group->members[i] != this) {
|
||||
if (m_aggrometer.set_pct(static_cast<AggroMeter::AggroTypes>(at_id), cur_tar->GetHateRatio(cur_tar->GetTarget(), group->members[i])))
|
||||
add_entry(static_cast<AggroMeter::AggroTypes>(at_id));
|
||||
at_id++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // we might need to clear out some data now
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Player, 0))
|
||||
add_entry(AggroMeter::AT_Player);
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Secondary, 0))
|
||||
add_entry(AggroMeter::AT_Secondary);
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Group1, 0))
|
||||
add_entry(AggroMeter::AT_Group1);
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Group2, 0))
|
||||
add_entry(AggroMeter::AT_Group2);
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Group3, 0))
|
||||
add_entry(AggroMeter::AT_Group3);
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Group4, 0))
|
||||
add_entry(AggroMeter::AT_Group5);
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Group5, 0))
|
||||
add_entry(AggroMeter::AT_Group5);
|
||||
}
|
||||
|
||||
// now to go over our xtargets
|
||||
// if the entry is an NPC it's our hate relative to the NPCs current tank
|
||||
// if it's a PC, it's their hate relative to our current target
|
||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||
if (XTargets[i].ID) {
|
||||
auto mob = entity_list.GetMob(XTargets[i].ID);
|
||||
if (mob) {
|
||||
int ratio = 0;
|
||||
if (mob->IsNPC())
|
||||
ratio = mob->GetHateRatio(mob->GetTarget(), this);
|
||||
else if (cur_tar)
|
||||
ratio = cur_tar->GetHateRatio(cur_tar->GetTarget(), mob);
|
||||
if (m_aggrometer.set_pct(static_cast<AggroMeter::AggroTypes>(AggroMeter::AT_XTarget1 + i), ratio))
|
||||
add_entry(static_cast<AggroMeter::AggroTypes>(AggroMeter::AT_XTarget1 + i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (send || count) {
|
||||
app->size = app->GetWritePosition(); // this should be safe, although not recommended
|
||||
// but this way we can have a smaller buffer created for the packet dispatched to the client w/o resizing this one
|
||||
app->SetWritePosition(count_offset);
|
||||
app->WriteUInt8(count);
|
||||
FastQueuePacket(&app);
|
||||
} else {
|
||||
safe_delete(app);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -48,6 +48,8 @@ namespace EQEmu
|
||||
#include "../common/inventory_profile.h"
|
||||
#include "../common/guilds.h"
|
||||
//#include "../common/item_data.h"
|
||||
#include "xtargetautohaters.h"
|
||||
#include "aggromanager.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "merc.h"
|
||||
@ -1122,9 +1124,21 @@ public:
|
||||
void RemoveGroupXTargets();
|
||||
void RemoveAutoXTargets();
|
||||
void ShowXTargets(Client *c);
|
||||
inline XTargetAutoHaters *GetXTargetAutoMgr() { return m_activeautohatermgr; } // will be either raid or group or self
|
||||
inline void SetXTargetAutoMgr(XTargetAutoHaters *in) { if (in) m_activeautohatermgr = in; else m_activeautohatermgr = &m_autohatermgr; }
|
||||
inline void SetDirtyAutoHaters() { m_dirtyautohaters = true; }
|
||||
void ProcessXTargetAutoHaters(); // fixes up our auto haters
|
||||
void JoinGroupXTargets(Group *g);
|
||||
void LeaveGroupXTargets(Group *g);
|
||||
void LeaveRaidXTargets(Raid *r);
|
||||
bool GroupFollow(Client* inviter);
|
||||
inline bool GetRunMode() const { return runmode; }
|
||||
|
||||
inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQEmu::versions::bit_RoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested
|
||||
inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); }
|
||||
|
||||
void ProcessAggroMeter(); // builds packet and sends
|
||||
|
||||
void InitializeMercInfo();
|
||||
bool CheckCanSpawnMerc(uint32 template_id);
|
||||
bool CheckCanHireMerc(Mob* merchant, uint32 template_id);
|
||||
@ -1462,6 +1476,7 @@ private:
|
||||
Timer afk_toggle_timer;
|
||||
Timer helm_toggle_timer;
|
||||
Timer light_update_timer;
|
||||
Timer aggro_meter_timer;
|
||||
|
||||
glm::vec3 m_Proximity;
|
||||
|
||||
@ -1546,8 +1561,13 @@ private:
|
||||
|
||||
uint8 MaxXTargets;
|
||||
bool XTargetAutoAddHaters;
|
||||
bool m_dirtyautohaters;
|
||||
|
||||
struct XTarget_Struct XTargets[XTARGET_HARDCAP];
|
||||
XTargetAutoHaters m_autohatermgr;
|
||||
XTargetAutoHaters *m_activeautohatermgr;
|
||||
|
||||
AggroMeter m_aggrometer;
|
||||
|
||||
Timer ItemTickTimer;
|
||||
Timer ItemQuestTimer;
|
||||
|
||||
@ -120,6 +120,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_AdventureMerchantSell] = &Client::Handle_OP_AdventureMerchantSell;
|
||||
ConnectedOpcodes[OP_AdventureRequest] = &Client::Handle_OP_AdventureRequest;
|
||||
ConnectedOpcodes[OP_AdventureStatsRequest] = &Client::Handle_OP_AdventureStatsRequest;
|
||||
ConnectedOpcodes[OP_AggroMeterLockTarget] = &Client::Handle_OP_AggroMeterLockTarget;
|
||||
ConnectedOpcodes[OP_AltCurrencyMerchantRequest] = &Client::Handle_OP_AltCurrencyMerchantRequest;
|
||||
ConnectedOpcodes[OP_AltCurrencyPurchase] = &Client::Handle_OP_AltCurrencyPurchase;
|
||||
ConnectedOpcodes[OP_AltCurrencyReclaim] = &Client::Handle_OP_AltCurrencyReclaim;
|
||||
@ -575,6 +576,11 @@ void Client::CompleteConnect()
|
||||
}
|
||||
}
|
||||
raid->SendGroupLeadershipAA(this, grpID); // this may get sent an extra time ...
|
||||
|
||||
SetXTargetAutoMgr(raid->GetXTargetAutoMgr());
|
||||
if (!GetXTargetAutoMgr()->empty())
|
||||
SetDirtyAutoHaters();
|
||||
|
||||
if (raid->IsLocked())
|
||||
raid->SendRaidLockTo(this);
|
||||
}
|
||||
@ -1548,8 +1554,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
// we purchased a new one while out-of-zone.
|
||||
if (group->IsLeader(this))
|
||||
group->SendLeadershipAAUpdate();
|
||||
|
||||
}
|
||||
JoinGroupXTargets(group);
|
||||
group->UpdatePlayer(this);
|
||||
LFG = false;
|
||||
}
|
||||
@ -2394,6 +2400,17 @@ void Client::Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app)
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
|
||||
void Client::Handle_OP_AggroMeterLockTarget(const EQApplicationPacket *app)
|
||||
{
|
||||
if (app->size < sizeof(uint32)) {
|
||||
Log.Out(Logs::General, Logs::Error, "Handle_OP_AggroMeterLockTarget had a packet that was too small.");
|
||||
return;
|
||||
}
|
||||
|
||||
SetAggroMeterLock(app->ReadUInt32(0));
|
||||
ProcessAggroMeter();
|
||||
}
|
||||
|
||||
void Client::Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app)
|
||||
{
|
||||
VERIFY_PACKET_LENGTH(OP_AltCurrencyMerchantRequest, app, uint32);
|
||||
@ -10804,7 +10821,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
}
|
||||
g->DisbandGroup();
|
||||
g->JoinRaidXTarget(r);
|
||||
g->DisbandGroup(true);
|
||||
r->GroupUpdate(freeGroup);
|
||||
}
|
||||
else{
|
||||
@ -10869,7 +10887,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
}
|
||||
ig->DisbandGroup();
|
||||
ig->JoinRaidXTarget(r, true);
|
||||
ig->DisbandGroup(true);
|
||||
r->GroupUpdate(groupFree);
|
||||
groupFree = r->GetFreeGroup();
|
||||
}
|
||||
@ -10922,10 +10941,11 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
}
|
||||
g->DisbandGroup();
|
||||
g->JoinRaidXTarget(r);
|
||||
g->DisbandGroup(true);
|
||||
r->GroupUpdate(groupFree);
|
||||
}
|
||||
else
|
||||
else // target does not have a group
|
||||
{
|
||||
if (ig){
|
||||
r = new Raid(i);
|
||||
@ -10979,14 +10999,15 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
||||
r->SendRaidCreate(this);
|
||||
r->SendMakeLeaderPacketTo(r->leadername, this);
|
||||
r->SendBulkRaid(this);
|
||||
ig->JoinRaidXTarget(r, true);
|
||||
r->AddMember(this);
|
||||
ig->DisbandGroup();
|
||||
ig->DisbandGroup(true);
|
||||
r->GroupUpdate(0);
|
||||
if (r->IsLocked()) {
|
||||
r->SendRaidLockTo(this);
|
||||
}
|
||||
}
|
||||
else{
|
||||
else{ // neither has a group
|
||||
r = new Raid(i);
|
||||
entity_list.AddRaid(r);
|
||||
r->SetRaidDetails();
|
||||
@ -12416,14 +12437,33 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
|
||||
int cost_quantity = mp->quantity;
|
||||
uint32 cost_quantity = mp->quantity;
|
||||
if (inst->IsCharged())
|
||||
int cost_quantity = 1;
|
||||
uint32 cost_quantity = 1;
|
||||
|
||||
uint32 i;
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
for (i = 1; i <= cost_quantity; i++) {
|
||||
price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price
|
||||
if (price > 4000000000) {
|
||||
cost_quantity = i;
|
||||
mp->quantity = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 1; i <= cost_quantity; i++) {
|
||||
price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod)) + 0.5); // need to round up, because client does it automatically when displaying price
|
||||
if (price > 4000000000) {
|
||||
cost_quantity = i;
|
||||
mp->quantity = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod))
|
||||
price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price
|
||||
else
|
||||
price = (int)((item->Price*cost_quantity)*(RuleR(Merchant, BuyCostMod)) + 0.5);
|
||||
AddMoneyToPP(price, false);
|
||||
|
||||
if (inst->IsStackable() || inst->IsCharged())
|
||||
@ -14079,6 +14119,7 @@ void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
XTargetAutoAddHaters = app->ReadUInt8(0);
|
||||
SetDirtyAutoHaters();
|
||||
}
|
||||
|
||||
void Client::Handle_OP_XTargetOpen(const EQApplicationPacket *app)
|
||||
|
||||
@ -32,6 +32,7 @@
|
||||
void Handle_OP_AdventureMerchantSell(const EQApplicationPacket *app);
|
||||
void Handle_OP_AdventureRequest(const EQApplicationPacket *app);
|
||||
void Handle_OP_AdventureStatsRequest(const EQApplicationPacket *app);
|
||||
void Handle_OP_AggroMeterLockTarget(const EQApplicationPacket *app);
|
||||
void Handle_OP_AltCurrencyMerchantRequest(const EQApplicationPacket *app);
|
||||
void Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app);
|
||||
void Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app);
|
||||
|
||||
@ -681,6 +681,13 @@ bool Client::Process() {
|
||||
Message(0, "Your enemies have forgotten you!");
|
||||
}
|
||||
|
||||
if (client_state == CLIENT_CONNECTED) {
|
||||
if (m_dirtyautohaters)
|
||||
ProcessXTargetAutoHaters();
|
||||
if (aggro_meter_timer.Check())
|
||||
ProcessAggroMeter();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@ -1404,15 +1404,15 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
|
||||
if (!m)
|
||||
continue;
|
||||
|
||||
m->RemoveFromHateList(mob);
|
||||
|
||||
if (RemoveFromXTargets) {
|
||||
if (m->IsClient())
|
||||
if (m->IsClient() && mob->CheckAggro(m))
|
||||
m->CastToClient()->RemoveXTarget(mob, false);
|
||||
// FadingMemories calls this function passing the client.
|
||||
else if (mob->IsClient())
|
||||
else if (mob->IsClient() && m->CheckAggro(mob))
|
||||
mob->CastToClient()->RemoveXTarget(m, false);
|
||||
}
|
||||
|
||||
m->RemoveFromHateList(mob);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2557,6 +2557,8 @@ void EntityList::RemoveFromHateLists(Mob *mob, bool settoone)
|
||||
it->second->RemoveFromHateList(mob);
|
||||
else
|
||||
it->second->SetHateAmountOnEnt(mob, 1);
|
||||
if (mob->IsClient())
|
||||
mob->CastToClient()->RemoveXTarget(it->second, false); // gotta do book keeping
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
@ -413,7 +413,7 @@ public:
|
||||
Mob* AICheckCloseAggro(Mob* sender, float iAggroRange, float iAssistRange);
|
||||
int GetHatedCount(Mob *attacker, Mob *exclude);
|
||||
void AIYellForHelp(Mob* sender, Mob* attacker);
|
||||
bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes);
|
||||
bool AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes);
|
||||
bool Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes);
|
||||
Mob* GetTargetForMez(Mob* caster);
|
||||
uint32 CheckNPCsClose(Mob *center);
|
||||
@ -502,7 +502,7 @@ private:
|
||||
Bot* GetBotByBotName(std::string botName);
|
||||
std::list<Bot*> GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID);
|
||||
|
||||
bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate
|
||||
bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate
|
||||
void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff
|
||||
private:
|
||||
std::list<Bot*> bot_list;
|
||||
|
||||
@ -64,6 +64,8 @@ Group::Group(uint32 gid)
|
||||
MarkedNPCs[i] = 0;
|
||||
|
||||
NPCMarkerID = 0;
|
||||
|
||||
m_autohatermgr.SetOwner(nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
//creating a new group
|
||||
@ -94,6 +96,7 @@ Group::Group(Mob* leader)
|
||||
MarkedNPCs[i] = 0;
|
||||
|
||||
NPCMarkerID = 0;
|
||||
m_autohatermgr.SetOwner(nullptr, this, nullptr);
|
||||
}
|
||||
|
||||
Group::~Group()
|
||||
@ -333,19 +336,15 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte
|
||||
database.SetGroupID(NewMemberName, 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);
|
||||
}
|
||||
|
||||
if (newmember && newmember->IsClient())
|
||||
newmember->CastToClient()->JoinGroupXTargets(this);
|
||||
|
||||
safe_delete(outapp);
|
||||
|
||||
return true;
|
||||
@ -496,16 +495,11 @@ void Group::MemberZoned(Mob* removemob) {
|
||||
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();
|
||||
if (members[i] == removemob) {
|
||||
members[i] = nullptr;
|
||||
//should NOT clear the name, it is used for world communication.
|
||||
break;
|
||||
}
|
||||
#endif //BOTS
|
||||
}
|
||||
|
||||
if(removemob->IsClient() && HasRole(removemob, RoleAssist))
|
||||
@ -661,11 +655,6 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
|
||||
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)
|
||||
@ -718,8 +707,10 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
|
||||
if (oldmember->GetName() == mentoree_name)
|
||||
ClearGroupMentor();
|
||||
|
||||
if(oldmember->IsClient())
|
||||
if(oldmember->IsClient()) {
|
||||
SendMarkedNPCsToMember(oldmember->CastToClient(), true);
|
||||
oldmember->CastToClient()->LeaveGroupXTargets(this);
|
||||
}
|
||||
|
||||
if(GroupCount() < 3)
|
||||
{
|
||||
@ -872,7 +863,7 @@ uint32 Group::GetTotalGroupDamage(Mob* other) {
|
||||
return total;
|
||||
}
|
||||
|
||||
void Group::DisbandGroup() {
|
||||
void Group::DisbandGroup(bool joinraid) {
|
||||
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate_Struct));
|
||||
|
||||
GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer;
|
||||
@ -899,6 +890,8 @@ void Group::DisbandGroup() {
|
||||
database.SetGroupID(members[i]->GetCleanName(), 0, members[i]->CastToClient()->CharacterID(), false);
|
||||
members[i]->CastToClient()->QueuePacket(outapp);
|
||||
SendMarkedNPCsToMember(members[i]->CastToClient(), true);
|
||||
if (!joinraid)
|
||||
members[i]->CastToClient()->LeaveGroupXTargets(this);
|
||||
}
|
||||
|
||||
if (members[i]->IsMerc())
|
||||
@ -2292,6 +2285,30 @@ void Group::UpdateXTargetMarkedNPC(uint32 Number, Mob *m)
|
||||
|
||||
}
|
||||
|
||||
void Group::SetDirtyAutoHaters()
|
||||
{
|
||||
for (int i = 0; i < MAX_GROUP_MEMBERS; ++i)
|
||||
if (members[i] && members[i]->IsClient())
|
||||
members[i]->CastToClient()->SetDirtyAutoHaters();
|
||||
}
|
||||
|
||||
void Group::JoinRaidXTarget(Raid *raid, bool first)
|
||||
{
|
||||
if (!GetXTargetAutoMgr()->empty())
|
||||
raid->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr());
|
||||
|
||||
for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) {
|
||||
if (members[i] && members[i]->IsClient()) {
|
||||
auto *client = members[i]->CastToClient();
|
||||
if (!first)
|
||||
client->RemoveAutoXTargets();
|
||||
client->SetXTargetAutoMgr(raid->GetXTargetAutoMgr());
|
||||
if (!client->GetXTargetAutoMgr()->empty())
|
||||
client->SetDirtyAutoHaters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Group::SetMainTank(const char *NewMainTankName)
|
||||
{
|
||||
MainTankName = NewMainTankName;
|
||||
|
||||
@ -22,6 +22,7 @@
|
||||
#include "../common/types.h"
|
||||
|
||||
#include "mob.h"
|
||||
#include "xtargetautohaters.h"
|
||||
|
||||
class Client;
|
||||
class EQApplicationPacket;
|
||||
@ -58,7 +59,7 @@ public:
|
||||
void SendWorldGroup(uint32 zone_id,Mob* zoningmember);
|
||||
bool DelMemberOOZ(const char *Name);
|
||||
bool DelMember(Mob* oldmember,bool ignoresender = false);
|
||||
void DisbandGroup();
|
||||
void DisbandGroup(bool joinraid = false);
|
||||
void GetMemberList(std::list<Mob*>& member_list, bool clear_list = true);
|
||||
void GetClientList(std::list<Client*>& client_list, bool clear_list = true);
|
||||
#ifdef BOTS
|
||||
@ -140,6 +141,9 @@ public:
|
||||
void ChangeLeader(Mob* newleader);
|
||||
const char *GetClientNameByIndex(uint8 index);
|
||||
void UpdateXTargetMarkedNPC(uint32 Number, Mob *m);
|
||||
void SetDirtyAutoHaters();
|
||||
inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; }
|
||||
void JoinRaidXTarget(Raid *raid, bool first = false);
|
||||
|
||||
void SetGroupMentor(int percent, char *name);
|
||||
void ClearGroupMentor();
|
||||
@ -168,6 +172,8 @@ private:
|
||||
std::string mentoree_name;
|
||||
Client *mentoree;
|
||||
int mentor_percent;
|
||||
|
||||
XTargetAutoHaters m_autohatermgr;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -23,6 +23,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "raids.h"
|
||||
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/data_verification.h"
|
||||
|
||||
#include "hate_list.h"
|
||||
#include "quest_parser_collection.h"
|
||||
@ -277,7 +278,24 @@ int HateList::GetSummonedPetCountOnHateList(Mob *hater) {
|
||||
return pet_count;
|
||||
}
|
||||
|
||||
Mob *HateList::GetEntWithMostHateOnList(Mob *center)
|
||||
int HateList::GetHateRatio(Mob *top, Mob *other)
|
||||
{
|
||||
auto other_entry = Find(other);
|
||||
|
||||
if (!other_entry || other_entry->stored_hate_amount < 1)
|
||||
return 0;
|
||||
|
||||
auto top_entry = Find(top);
|
||||
|
||||
if (!top_entry || top_entry->stored_hate_amount < 1)
|
||||
return 999; // shouldn't happen if you call it right :P
|
||||
|
||||
return EQEmu::Clamp(static_cast<int>((other_entry->stored_hate_amount * 100) / top_entry->stored_hate_amount), 1, 999);
|
||||
}
|
||||
|
||||
// skip is used to ignore a certain mob on the list
|
||||
// Currently used for getting 2nd on list for aggro meter
|
||||
Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
|
||||
{
|
||||
// hack fix for zone shutdown crashes on some servers
|
||||
if (!zone->IsLoaded())
|
||||
@ -310,6 +328,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur->entity_on_hatelist == skip) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto hateEntryPosition = glm::vec3(cur->entity_on_hatelist->GetX(), cur->entity_on_hatelist->GetY(), cur->entity_on_hatelist->GetZ());
|
||||
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
|
||||
if (!zone->watermap->InLiquid(hateEntryPosition)) {
|
||||
@ -436,6 +459,11 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
|
||||
while (iterator != list.end())
|
||||
{
|
||||
struct_HateList *cur = (*iterator);
|
||||
if (cur->entity_on_hatelist == skip) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
|
||||
if(!zone->watermap->InLiquid(glm::vec3(cur->entity_on_hatelist->GetPosition()))) {
|
||||
skipped_count++;
|
||||
|
||||
@ -41,9 +41,9 @@ public:
|
||||
|
||||
Mob *GetClosestEntOnHateList(Mob *hater);
|
||||
Mob *GetDamageTopOnHateList(Mob *hater);
|
||||
Mob *GetEntWithMostHateOnList(Mob *center);
|
||||
Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr);
|
||||
Mob *GetRandomEntOnHateList();
|
||||
Mob* GetEntWithMostHateOnList();
|
||||
Mob *GetEntWithMostHateOnList();
|
||||
|
||||
bool IsEntOnHateList(Mob *mob);
|
||||
bool IsHateListEmpty();
|
||||
@ -51,6 +51,7 @@ public:
|
||||
|
||||
int AreaRampage(Mob *caster, Mob *target, int count, ExtraAttackOptions *opts);
|
||||
int GetSummonedPetCountOnHateList(Mob *hater);
|
||||
int GetHateRatio(Mob *top, Mob *other);
|
||||
|
||||
int32 GetEntHateAmount(Mob *ent, bool in_damage = false);
|
||||
|
||||
@ -73,4 +74,5 @@ private:
|
||||
Mob *hate_owner;
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@ -80,10 +80,11 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
|
||||
|
||||
*copper = cash;
|
||||
}
|
||||
uint32 global_loot_multiplier = RuleI(Zone, GlobalLootMultiplier);
|
||||
|
||||
// Do items
|
||||
for (uint32 i=0; i<lts->NumEntries; i++) {
|
||||
for (uint32 k = 1; k <= lts->Entries[i].multiplier; k++) {
|
||||
for (uint32 k = 1; k <= (lts->Entries[i].multiplier * global_loot_multiplier); k++) {
|
||||
uint8 droplimit = lts->Entries[i].droplimit;
|
||||
uint8 mindrop = lts->Entries[i].mindrop;
|
||||
|
||||
|
||||
@ -1985,7 +1985,7 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Merc::AICastSpell(int8 iChance, int32 iSpellTypes) {
|
||||
bool Merc::AICastSpell(int8 iChance, uint32 iSpellTypes) {
|
||||
|
||||
if(!AI_HasSpells())
|
||||
return false;
|
||||
@ -2746,7 +2746,7 @@ int32 Merc::GetActSpellCasttime(uint16 spell_id, int32 casttime)
|
||||
return casttime;
|
||||
}
|
||||
|
||||
int8 Merc::GetChanceToCastBySpellType(int16 spellType) {
|
||||
int8 Merc::GetChanceToCastBySpellType(uint32 spellType) {
|
||||
int mercStance = (int)GetStance();
|
||||
int8 mercClass = GetClass();
|
||||
int8 chance = 0;
|
||||
@ -2888,7 +2888,7 @@ bool Merc::CheckStance(int16 stance) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::list<MercSpell> Merc::GetMercSpellsBySpellType(Merc* caster, int spellType) {
|
||||
std::list<MercSpell> Merc::GetMercSpellsBySpellType(Merc* caster, uint32 spellType) {
|
||||
std::list<MercSpell> result;
|
||||
|
||||
if(caster && caster->AI_HasSpells()) {
|
||||
@ -2918,7 +2918,7 @@ std::list<MercSpell> Merc::GetMercSpellsBySpellType(Merc* caster, int spellType)
|
||||
return result;
|
||||
}
|
||||
|
||||
MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, int spellType) {
|
||||
MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, uint32 spellType) {
|
||||
MercSpell result;
|
||||
|
||||
result.spellid = 0;
|
||||
|
||||
10
zone/merc.h
10
zone/merc.h
@ -78,8 +78,8 @@ public:
|
||||
virtual void AI_Stop();
|
||||
virtual void AI_Process();
|
||||
|
||||
//virtual bool AICastSpell(Mob* tar, int8 iChance, int16 iSpellTypes);
|
||||
virtual bool AICastSpell(int8 iChance, int32 iSpellTypes);
|
||||
//virtual bool AICastSpell(Mob* tar, int8 iChance, uint32 iSpellTypes);
|
||||
virtual bool AICastSpell(int8 iChance, uint32 iSpellTypes);
|
||||
virtual bool AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
virtual bool AI_EngagedCastCheck();
|
||||
//virtual bool AI_PursueCastCheck();
|
||||
@ -97,7 +97,7 @@ public:
|
||||
// Merc Spell Casting Methods
|
||||
virtual int32 GetActSpellCasttime(uint16 spell_id, int32 casttime);
|
||||
virtual int32 GetActSpellCost(uint16 spell_id, int32 cost);
|
||||
int8 GetChanceToCastBySpellType(int16 spellType);
|
||||
int8 GetChanceToCastBySpellType(uint32 spellType);
|
||||
void SetSpellRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay);
|
||||
void SetDisciplineRecastTimer(uint16 timer_id, uint16 spellid, uint32 recast_delay);
|
||||
void SetSpellTimeCanCast(uint16 spellid, uint32 recast_delay);
|
||||
@ -108,8 +108,8 @@ public:
|
||||
static int32 GetDisciplineRemainingTime(Merc *caster, uint16 timer_id);
|
||||
static std::list<MercSpell> GetMercSpellsForSpellEffect(Merc* caster, int spellEffect);
|
||||
static std::list<MercSpell> GetMercSpellsForSpellEffectAndTargetType(Merc* caster, int spellEffect, SpellTargetType targetType);
|
||||
static std::list<MercSpell> GetMercSpellsBySpellType(Merc* caster, int spellType);
|
||||
static MercSpell GetFirstMercSpellBySpellType(Merc* caster, int spellType);
|
||||
static std::list<MercSpell> GetMercSpellsBySpellType(Merc* caster, uint32 spellType);
|
||||
static MercSpell GetFirstMercSpellBySpellType(Merc* caster, uint32 spellType);
|
||||
static MercSpell GetFirstMercSpellForSingleTargetHeal(Merc* caster);
|
||||
static MercSpell GetMercSpellBySpellID(Merc* caster, uint16 spellid);
|
||||
static MercSpell GetBestMercSpellForVeryFastHeal(Merc* caster);
|
||||
|
||||
@ -539,7 +539,9 @@ public:
|
||||
void DoubleAggro(Mob *other) { uint32 in_hate = GetHateAmount(other); SetHateAmountOnEnt(other, (in_hate ? in_hate * 2 : 1)); }
|
||||
uint32 GetHateAmount(Mob* tmob, bool is_dam = false) { return hate_list.GetEntHateAmount(tmob,is_dam);}
|
||||
uint32 GetDamageAmount(Mob* tmob) { return hate_list.GetEntHateAmount(tmob, true);}
|
||||
int GetHateRatio(Mob *first, Mob *with) { return hate_list.GetHateRatio(first, with); }
|
||||
Mob* GetHateTop() { return hate_list.GetEntWithMostHateOnList(this);}
|
||||
Mob* GetSecondaryHate(Mob *skip) { return hate_list.GetEntWithMostHateOnList(this, skip); }
|
||||
Mob* GetHateDamageTop(Mob* other) { return hate_list.GetDamageTopOnHateList(other);}
|
||||
Mob* GetHateRandom() { return hate_list.GetRandomEntOnHateList();}
|
||||
Mob* GetHateMost() { return hate_list.GetEntWithMostHateOnList();}
|
||||
|
||||
@ -47,7 +47,7 @@ extern Zone *zone;
|
||||
#endif
|
||||
|
||||
//NOTE: do NOT pass in beneficial and detrimental spell types into the same call here!
|
||||
bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes) {
|
||||
bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if (!tar)
|
||||
return false;
|
||||
|
||||
@ -344,7 +344,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
|
||||
return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
|
||||
}
|
||||
|
||||
bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint16 iSpellTypes) {
|
||||
bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 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
|
||||
@ -2496,7 +2496,7 @@ bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID) {
|
||||
}
|
||||
|
||||
// adds a spell to the list, taking into account priority and resorting list as needed.
|
||||
void NPC::AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint16 iType,
|
||||
void NPC::AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint32 iType,
|
||||
int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust)
|
||||
{
|
||||
|
||||
@ -2608,8 +2608,13 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
|
||||
|
||||
query = StringFormat("SELECT spellid, type, minlevel, maxlevel, "
|
||||
"manacost, recast_delay, priority, resist_adjust "
|
||||
#ifdef BOTS
|
||||
"FROM %s "
|
||||
"WHERE npc_spells_id=%d ORDER BY minlevel", (iDBSpellsID >= 701 && iDBSpellsID <= 712 ? "bot_spells_entries" : "npc_spells_entries"), iDBSpellsID);
|
||||
#else
|
||||
"FROM npc_spells_entries "
|
||||
"WHERE npc_spells_id=%d ORDER BY minlevel", iDBSpellsID);
|
||||
"WHERE npc_spells_id=%d ORDER BY minlevel", iDBSpellsID);
|
||||
#endif
|
||||
results = QueryDatabase(query);
|
||||
|
||||
if (!results.Success())
|
||||
@ -2646,7 +2651,7 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
|
||||
{
|
||||
int spell_id = atoi(row[0]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].spellid = spell_id;
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].type = atoi(row[1]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].type = atoul(row[1]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].minlevel = atoi(row[2]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].maxlevel = atoi(row[3]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].manacost = atoi(row[4]);
|
||||
|
||||
@ -54,7 +54,7 @@ typedef struct {
|
||||
} NPCProximity;
|
||||
|
||||
struct AISpells_Struct {
|
||||
uint16 type; // 0 = never, must be one (and only one) of the defined values
|
||||
uint32 type; // 0 = never, must be one (and only one) of the defined values
|
||||
uint16 spellid; // <= 0 = no spell
|
||||
int16 manacost; // -1 = use spdat, -2 = no cast time
|
||||
uint32 time_cancast; // when we can cast this spell next
|
||||
@ -377,7 +377,7 @@ public:
|
||||
void NPCSlotTexture(uint8 slot, uint16 texture); // Sets new material values for slots
|
||||
|
||||
uint32 GetAdventureTemplate() const { return adventure_template_id; }
|
||||
void AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint16 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust);
|
||||
void AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint32 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust);
|
||||
void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max);
|
||||
void RemoveSpellFromNPCList(int16 spell_id);
|
||||
Timer *GetRefaceTimer() const { return reface_timer; }
|
||||
@ -453,7 +453,7 @@ protected:
|
||||
uint32* pDontCastBefore_casting_spell;
|
||||
std::vector<AISpells_Struct> AIspells;
|
||||
bool HasAISpell;
|
||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes);
|
||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes);
|
||||
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
AISpellsVar_Struct AISpellVar;
|
||||
int16 GetFocusEffect(focusType type, uint16 spell_id);
|
||||
|
||||
@ -43,6 +43,8 @@ Raid::Raid(uint32 raidID)
|
||||
memset(leadername, 0, 64);
|
||||
locked = false;
|
||||
LootType = 4;
|
||||
|
||||
m_autohatermgr.SetOwner(nullptr, nullptr, this);
|
||||
}
|
||||
|
||||
Raid::Raid(Client* nLeader)
|
||||
@ -60,6 +62,8 @@ Raid::Raid(Client* nLeader)
|
||||
strn0cpy(leadername, nLeader->GetName(), 64);
|
||||
locked = false;
|
||||
LootType = 4;
|
||||
|
||||
m_autohatermgr.SetOwner(nullptr, nullptr, this);
|
||||
}
|
||||
|
||||
Raid::~Raid()
|
||||
@ -121,6 +125,26 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo
|
||||
c->SetRaidGrouped(true);
|
||||
SendRaidMOTD(c);
|
||||
|
||||
// xtarget shit ..........
|
||||
if (group == RAID_GROUPLESS) {
|
||||
if (rleader) {
|
||||
GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr());
|
||||
c->GetXTargetAutoMgr()->clear();
|
||||
c->SetXTargetAutoMgr(GetXTargetAutoMgr());
|
||||
} else {
|
||||
if (!c->GetXTargetAutoMgr()->empty()) {
|
||||
GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr());
|
||||
c->GetXTargetAutoMgr()->clear();
|
||||
c->RemoveAutoXTargets();
|
||||
}
|
||||
|
||||
c->SetXTargetAutoMgr(GetXTargetAutoMgr());
|
||||
|
||||
if (!c->GetXTargetAutoMgr()->empty())
|
||||
c->SetDirtyAutoHaters();
|
||||
}
|
||||
}
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct));
|
||||
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
@ -143,8 +167,10 @@ void Raid::RemoveMember(const char *characterName)
|
||||
LearnMembers();
|
||||
VerifyRaid();
|
||||
|
||||
if(client)
|
||||
if(client) {
|
||||
client->SetRaidGrouped(false);
|
||||
client->LeaveRaidXTargets(this);
|
||||
}
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct));
|
||||
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
@ -1672,3 +1698,11 @@ void Raid::CheckGroupMentor(uint32 group_id, Client *c)
|
||||
group_mentor[group_id].mentoree = c;
|
||||
}
|
||||
|
||||
void Raid::SetDirtyAutoHaters()
|
||||
{
|
||||
for (int i = 0; i < MAX_RAID_MEMBERS; ++i)
|
||||
if (members[i].member)
|
||||
members[i].member->SetDirtyAutoHaters();
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
|
||||
#include "../common/types.h"
|
||||
#include "groups.h"
|
||||
#include "xtargetautohaters.h"
|
||||
|
||||
class Client;
|
||||
class EQApplicationPacket;
|
||||
@ -230,6 +231,9 @@ public:
|
||||
inline int GetMentorPercent(uint32 group_id) { return group_mentor[group_id].mentor_percent; }
|
||||
inline Client *GetMentoree(uint32 group_id) { return group_mentor[group_id].mentoree; }
|
||||
|
||||
void SetDirtyAutoHaters();
|
||||
inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; }
|
||||
|
||||
RaidMember members[MAX_RAID_MEMBERS];
|
||||
char leadername[64];
|
||||
protected:
|
||||
@ -244,6 +248,8 @@ protected:
|
||||
GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS];
|
||||
|
||||
GroupMentor group_mentor[MAX_RAID_GROUPS];
|
||||
|
||||
XTargetAutoHaters m_autohatermgr;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@ -895,6 +895,10 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
Inviter->CastToClient()->SendGroupLeaderChangePacket(Inviter->GetName());
|
||||
Inviter->CastToClient()->SendGroupJoinAcknowledge();
|
||||
}
|
||||
|
||||
group->GetXTargetAutoMgr()->merge(*Inviter->CastToClient()->GetXTargetAutoMgr());
|
||||
Inviter->CastToClient()->GetXTargetAutoMgr()->clear();
|
||||
Inviter->CastToClient()->SetXTargetAutoMgr(group->GetXTargetAutoMgr());
|
||||
}
|
||||
|
||||
if(!group)
|
||||
@ -1007,7 +1011,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
group->SetNPCMarker(NPCMarkerName);
|
||||
group->SetGroupAAs(&GLAA);
|
||||
group->SetGroupMentor(mentor_percent, mentoree_name);
|
||||
|
||||
client->JoinGroupXTargets(group);
|
||||
}
|
||||
}
|
||||
else if (client->GetMerc())
|
||||
@ -1107,6 +1111,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
r->SendRaidRemoveAll(rga->playername);
|
||||
Client *rem = entity_list.GetClientByName(rga->playername);
|
||||
if(rem){
|
||||
rem->LeaveRaidXTargets(r);
|
||||
r->SendRaidDisband(rem);
|
||||
}
|
||||
r->LearnMembers();
|
||||
|
||||
112
zone/xtargetautohaters.cpp
Normal file
112
zone/xtargetautohaters.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include "xtargetautohaters.h"
|
||||
#include "mob.h"
|
||||
#include "client.h"
|
||||
#include "raids.h"
|
||||
#include "groups.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
void XTargetAutoHaters::increment_count(Mob *in)
|
||||
{
|
||||
assert(in != nullptr);
|
||||
auto it = std::find_if(m_haters.begin(), m_haters.end(),
|
||||
[&in](const HatersCount &c) { return c.spawn_id == in->GetID(); });
|
||||
|
||||
// we are on the list, we just need to increment the count
|
||||
if (it != m_haters.end()) {
|
||||
it->count++;
|
||||
return;
|
||||
}
|
||||
// We are not on the list
|
||||
HatersCount c;
|
||||
c.spawn_id = in->GetID();
|
||||
c.count = 1;
|
||||
|
||||
m_haters.push_back(c);
|
||||
// trigger event on owner
|
||||
if (m_client)
|
||||
m_client->SetDirtyAutoHaters();
|
||||
else if (m_group)
|
||||
m_group->SetDirtyAutoHaters();
|
||||
else if (m_raid)
|
||||
m_raid->SetDirtyAutoHaters();
|
||||
}
|
||||
|
||||
void XTargetAutoHaters::decrement_count(Mob *in)
|
||||
{
|
||||
assert(in != nullptr);
|
||||
auto it = std::find_if(m_haters.begin(), m_haters.end(),
|
||||
[&in](const HatersCount &c) { return c.spawn_id == in->GetID(); });
|
||||
|
||||
// we are not on the list ... shouldn't happen
|
||||
if (it == m_haters.end())
|
||||
return;
|
||||
it->count--;
|
||||
if (it->count == 0) {
|
||||
m_haters.erase(it);
|
||||
if (m_client)
|
||||
m_client->SetDirtyAutoHaters();
|
||||
else if (m_group)
|
||||
m_group->SetDirtyAutoHaters();
|
||||
else if (m_raid)
|
||||
m_raid->SetDirtyAutoHaters();
|
||||
}
|
||||
}
|
||||
|
||||
void XTargetAutoHaters::merge(XTargetAutoHaters &other)
|
||||
{
|
||||
bool trigger = false;
|
||||
for (auto &e : other.m_haters) {
|
||||
auto it = std::find_if(m_haters.begin(), m_haters.end(),
|
||||
[&e](const HatersCount &c) { return e.spawn_id == c.spawn_id; });
|
||||
if (it != m_haters.end()) {
|
||||
it->count += e.count;
|
||||
continue;
|
||||
}
|
||||
m_haters.push_back(e);
|
||||
trigger = true;
|
||||
}
|
||||
|
||||
if (trigger) {
|
||||
if (m_client)
|
||||
m_client->SetDirtyAutoHaters();
|
||||
else if (m_group)
|
||||
m_group->SetDirtyAutoHaters();
|
||||
else if (m_raid)
|
||||
m_raid->SetDirtyAutoHaters();
|
||||
}
|
||||
}
|
||||
|
||||
// demerge this from other. other belongs to group/raid you just left
|
||||
void XTargetAutoHaters::demerge(XTargetAutoHaters &other)
|
||||
{
|
||||
bool trigger = false;
|
||||
for (auto &e : m_haters) {
|
||||
auto it = std::find_if(other.m_haters.begin(), other.m_haters.end(),
|
||||
[&e](const HatersCount &c) { return e.spawn_id == c.spawn_id; });
|
||||
if (it != other.m_haters.end()) {
|
||||
it->count -= e.count;
|
||||
if (it->count == 0) {
|
||||
trigger = true;
|
||||
other.m_haters.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (trigger) {
|
||||
if (other.m_client)
|
||||
other.m_client->SetDirtyAutoHaters();
|
||||
else if (other.m_group)
|
||||
other.m_group->SetDirtyAutoHaters();
|
||||
else if (other.m_raid)
|
||||
other.m_raid->SetDirtyAutoHaters();
|
||||
}
|
||||
}
|
||||
|
||||
bool XTargetAutoHaters::contains_mob(int spawn_id)
|
||||
{
|
||||
auto it = std::find_if(m_haters.begin(), m_haters.end(),
|
||||
[spawn_id](const HatersCount &c) { return c.spawn_id == spawn_id; });
|
||||
return it != m_haters.end();
|
||||
}
|
||||
|
||||
48
zone/xtargetautohaters.h
Normal file
48
zone/xtargetautohaters.h
Normal file
@ -0,0 +1,48 @@
|
||||
#ifndef XTARGETAUTOHATERS_H
|
||||
#define XTARGETAUTOHATERS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Mob;
|
||||
class Client;
|
||||
class Group;
|
||||
class Raid;
|
||||
|
||||
class XTargetAutoHaters {
|
||||
struct HatersCount {
|
||||
int spawn_id;
|
||||
int count;
|
||||
};
|
||||
public:
|
||||
XTargetAutoHaters() : m_client(nullptr), m_group(nullptr), m_raid(nullptr) {}
|
||||
XTargetAutoHaters(Client *co, Group *go, Raid *ro) : m_client(co), m_group(go), m_raid(ro) {}
|
||||
~XTargetAutoHaters() {}
|
||||
|
||||
void merge(XTargetAutoHaters &other);
|
||||
void demerge(XTargetAutoHaters &other);
|
||||
void increment_count(Mob *in);
|
||||
void decrement_count(Mob *in);
|
||||
|
||||
bool contains_mob(int spawn_id);
|
||||
|
||||
inline const std::vector<HatersCount> &get_list() { return m_haters; }
|
||||
inline void SetOwner(Client *c, Group *g, Raid *r) {m_client = c; m_group = g; m_raid = r; }
|
||||
inline void clear() { m_haters.clear(); }
|
||||
inline bool empty() { return m_haters.empty(); }
|
||||
|
||||
private:
|
||||
/* This will contain all of the mobs that are possible to fill in an autohater
|
||||
* slot. This keeps track of ALL MOBS for a client or group or raid
|
||||
* This list needs to be merged when you join group/raid/etc
|
||||
*/
|
||||
std::vector<HatersCount> m_haters;
|
||||
|
||||
// So this is the object that owns us ... only 1 shouldn't be null
|
||||
Client *m_client;
|
||||
Group *m_group;
|
||||
Raid *m_raid;
|
||||
};
|
||||
|
||||
|
||||
#endif /* !XTARGETAUTOHATERS_H */
|
||||
|
||||
@ -45,7 +45,7 @@ struct wplist {
|
||||
#pragma pack(1)
|
||||
struct DBnpcspells_entries_Struct {
|
||||
int16 spellid;
|
||||
uint16 type;
|
||||
uint32 type;
|
||||
uint8 minlevel;
|
||||
uint8 maxlevel;
|
||||
int16 manacost;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user