mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 12:41:30 +00:00
[Bots] Add Basic Bot Raiding Functionality (#2782)
* Fix for GENERIC_9_STRINGS * Add Bot Heal Message Display Creates a new rule to display Bot heal messages to the Bot Owner * 2021-03-25 11L04pm Spell and Heal Rule added to allow for Bot spell and heal damage to be sent to the Bot Owner's Group. Also added a check to remove duplicate message for #damage on self. * Update .gitignore * BOT work Added BOT logging damage/heals to owner Added BOT message to owner for harmony fails Made var Critical global to remove duplicate crit messages Added a NULL check to Mob:GetCleanname() * Bot Group Work Fixed botid=charid spawn on zone issue Added a group_list update on zone to refresh from database to fix a dangling pointer to a Bot object that was camped but was previously in a group within the zone being entered. Modified Bot::ProcessBotGroupInvite to use the client of the bot when doing the Bot initialization so that a leader can invite another owner's Bot * Jan 4 Basic structure in place for Raid::AddBot though not working * Basement Jan 5 * End of day Jan 5 Working Raid Invite to a Bot. * Update to Client::QueuePacket to not attempt to send a packet to a BoT. Not clean, but a broad solution. * Updated Raid::VerifyRaid * Some Bot Raid working * Before VS Crash * Use Case 1, 2, 3,4,7 working. Need to fix 5, 6, 8 * Work on usecase 5 * A few more use cases working * New work on Raid invite with a invitor having a group * Bot Raid inviting working for all use cases * A few changes * end of day jan 10 * Jan 11 * end of day Jan 11 * Bot Invite/Accept cleanup * Start of moving raid bot functions to their own methods * More bot raid changes * More raid spell work * end of day Jan 16 * spawn work * Spawn on login working * End of Day Jan 18 * Raid leader and mana/hp updates fixed * Spell Tracking * Issue with Bot Death in raid when casted upon. 1741 raid.cpp * Bot Death fixed and few other crashes * Working on botgroup removal * Bot Disbanding Work 90% * Looks like BOTs are working * Fixed a bot crash * bug tracing on entity list mismatch * safe_delete resoves problem. No to track down leak * seems to be working * Memory corruption found - sending packets to BoTs using Client class * added Raid::IsRaidMemberBot() * Update p_raid_instance * g3 * Final - Bot Raid Working * Fixed IsRaidMemberBot to remove memory leak Fixed altcombat crash though RaidMainAssist (428) needs fixing * add RaidMember.IsBot * Repaired IsBot function to be more preformant. Now works on standard performance machine * Fixed Bard AE Target Spells Removed assert for buffs * updated based on Feb 2022 master updates * Added bot_db_updates and version increment * Cleanup of bot raid work and inclusion of bot_raid in cmake * Delete .gitignore * Revert "Delete .gitignore" This reverts commit 8523658d3bacdc068bcafaa652d2100afecddfc2. * Fixed a packet issue * Merged upstream/master Merged upstream/master and removed ifdef BOTS as per recent dev approach for BOTS. Functionality is there, compiles and tests ok. A few problems to be resolved though this is a good baseline. * Added sql update for raid_members to add isbot * Updated Bot Follow Function Bot will now follow the Group Leader if IsClient, otherwise follows the Bot Owner * Updates to Bot Raid System When camping a client, remove them from the raid. If they are leader, place leadership to the next client. Update a few crash checks in bot_raid.cpp * [BOTS] Added RuleB Enabled checks and updated base repo for raid_members Updated several RuleB(Bots, Enabled) checks Updated the base repo to be autogenerated. Raid functionality should work with a non-bots enabled database. * Few quick updates * Updates Corrected a number of comments. Compiled and tested against bot and non-bot database though requires the isbot column in raid_members for both. Moved the db update out of the bot stream to make bot check code easier. * Formatting and other small updates * A few more RuleB(Bots, Enabled) additions * Fix issue with conflict of bot ID versus character ID. * Delete CMakeSettings.json * Comment Updates and other Several updates including - fixed comments from PR - added id to raid_members and unique index on name to avoid botid and charid conflicts - updated a few raid functions for iterators - reordered several raid operations to ensure send leader packet to be the last item to ensure proper updating on the client - update sql to use Replace instead of Insert for botid conflicting with charid * Exploit fix for Raid Bots Added item from @Nite to disallow spawning or camping bots if Raid is engaged. Avoids abusive situations. * Initial Commit * fix Raid Window after zoning The raid window was not fully updating for clients not in the zone. * Cleanup * Update Fixed comments * Resolve crash for MOTD Fixed a crash situation sending a raid MOTD to BOTS. * Update ruletypes.h * Updated to resolve a few recent comments Fixed some comments within attack.cpp * fix sql query * update repository * prevent duplicate entries in raid after group invite, and cleanup * fixes for botgroups not following, and add already in raid messages. * fix messagestring * fixes * Cleanup, and resolving issues with disbanding * refactoring * more cleanup/fixing. * fixes for removing from ground in raid * Refactoring/fixing multiple clients * fix for compiling * Bugs from refactoring fixed * Testing completed, cleaning up unwanted items/duplicate code. * Cleaned up AICastSpell * fix typos * Refactoring * Adding Raid checks to AI_Process/cleanup * Fix a typo Was getting a SQL error on BOT spawn. Fixed typo. * fix for crash * Fixed crash when inviting player, more refactoring * AI_Process Refactoring work * More Refactoring/fixes for follow * Finish Refactoring AI_Process * cleanup * cleanup * cleanup * cleanup * fix melee attack loop * fix for leashowner. * fix for leashowner. * Bots persist in raid after client death/LD/Camp * Fix Bot Groups when zoning after death. * Fix Bots in group following after client death * remove unnecessary query * Allow Raid members to invite Bots if owner is in raid. cleanup * optimization of raid verification * remove this * Code Cleanup * formatting * formatting * formatting * fix for macro * add return for TryClassAttacks * fix query * fix for crash * restrict camping/spawn in combat. * Fix other crash issue. * update learnmembers to use Strings::To, cleanup magic numbers * fix for merge. --------- Co-authored-by: Kinglykrab <kinglykrab@gmail.com> Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com> Co-authored-by: Aeadoin <109764533+Aeadoin@users.noreply.github.com>
This commit is contained in:
parent
e778041198
commit
45da8cab61
@ -16,11 +16,14 @@
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
|
||||
class BaseRaidMembersRepository {
|
||||
public:
|
||||
struct RaidMembers {
|
||||
uint64_t id;
|
||||
int32_t raidid;
|
||||
int32_t charid;
|
||||
int32_t bot_id;
|
||||
uint32_t groupid;
|
||||
int8_t _class;
|
||||
int8_t level;
|
||||
@ -32,14 +35,16 @@ public:
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("charid");
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"raidid",
|
||||
"charid",
|
||||
"bot_id",
|
||||
"groupid",
|
||||
"_class",
|
||||
"level",
|
||||
@ -53,8 +58,10 @@ public:
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"raidid",
|
||||
"charid",
|
||||
"bot_id",
|
||||
"groupid",
|
||||
"_class",
|
||||
"level",
|
||||
@ -102,8 +109,10 @@ public:
|
||||
{
|
||||
RaidMembers e{};
|
||||
|
||||
e.id = 0;
|
||||
e.raidid = 0;
|
||||
e.charid = 0;
|
||||
e.bot_id = 0;
|
||||
e.groupid = 0;
|
||||
e._class = 0;
|
||||
e.level = 0;
|
||||
@ -121,7 +130,7 @@ public:
|
||||
)
|
||||
{
|
||||
for (auto &raid_members : raid_memberss) {
|
||||
if (raid_members.charid == raid_members_id) {
|
||||
if (raid_members.id == raid_members_id) {
|
||||
return raid_members;
|
||||
}
|
||||
}
|
||||
@ -136,8 +145,9 @@ public:
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
raid_members_id
|
||||
)
|
||||
);
|
||||
@ -146,15 +156,17 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
RaidMembers e{};
|
||||
|
||||
e.raidid = static_cast<int32_t>(atoi(row[0]));
|
||||
e.charid = static_cast<int32_t>(atoi(row[1]));
|
||||
e.groupid = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e._class = static_cast<int8_t>(atoi(row[3]));
|
||||
e.level = static_cast<int8_t>(atoi(row[4]));
|
||||
e.name = row[5] ? row[5] : "";
|
||||
e.isgroupleader = static_cast<int8_t>(atoi(row[6]));
|
||||
e.israidleader = static_cast<int8_t>(atoi(row[7]));
|
||||
e.islooter = static_cast<int8_t>(atoi(row[8]));
|
||||
e.id = strtoull(row[0], nullptr, 10);
|
||||
e.raidid = static_cast<int32_t>(atoi(row[1]));
|
||||
e.charid = static_cast<int32_t>(atoi(row[2]));
|
||||
e.bot_id = static_cast<int32_t>(atoi(row[3]));
|
||||
e.groupid = static_cast<uint32_t>(strtoul(row[4], nullptr, 10));
|
||||
e._class = static_cast<int8_t>(atoi(row[5]));
|
||||
e.level = static_cast<int8_t>(atoi(row[6]));
|
||||
e.name = row[7] ? row[7] : "";
|
||||
e.isgroupleader = static_cast<int8_t>(atoi(row[8]));
|
||||
e.israidleader = static_cast<int8_t>(atoi(row[9]));
|
||||
e.islooter = static_cast<int8_t>(atoi(row[10]));
|
||||
|
||||
return e;
|
||||
}
|
||||
@ -188,15 +200,16 @@ public:
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.raidid));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.charid));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.groupid));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e._class));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.level));
|
||||
v.push_back(columns[5] + " = '" + Strings::Escape(e.name) + "'");
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.isgroupleader));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.israidleader));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.islooter));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.raidid));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.charid));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.bot_id));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.groupid));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e._class));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.level));
|
||||
v.push_back(columns[7] + " = '" + Strings::Escape(e.name) + "'");
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.isgroupleader));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.israidleader));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.islooter));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@ -204,7 +217,7 @@ public:
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.charid
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
@ -218,8 +231,10 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.raidid));
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.groupid));
|
||||
v.push_back(std::to_string(e._class));
|
||||
v.push_back(std::to_string(e.level));
|
||||
@ -237,7 +252,7 @@ public:
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.charid = results.LastInsertedID();
|
||||
e.id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
@ -256,8 +271,10 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.raidid));
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.groupid));
|
||||
v.push_back(std::to_string(e._class));
|
||||
v.push_back(std::to_string(e.level));
|
||||
@ -298,15 +315,17 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
RaidMembers e{};
|
||||
|
||||
e.raidid = static_cast<int32_t>(atoi(row[0]));
|
||||
e.charid = static_cast<int32_t>(atoi(row[1]));
|
||||
e.groupid = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e._class = static_cast<int8_t>(atoi(row[3]));
|
||||
e.level = static_cast<int8_t>(atoi(row[4]));
|
||||
e.name = row[5] ? row[5] : "";
|
||||
e.isgroupleader = static_cast<int8_t>(atoi(row[6]));
|
||||
e.israidleader = static_cast<int8_t>(atoi(row[7]));
|
||||
e.islooter = static_cast<int8_t>(atoi(row[8]));
|
||||
e.id = strtoull(row[0], nullptr, 10);
|
||||
e.raidid = static_cast<int32_t>(atoi(row[1]));
|
||||
e.charid = static_cast<int32_t>(atoi(row[2]));
|
||||
e.bot_id = static_cast<int32_t>(atoi(row[3]));
|
||||
e.groupid = static_cast<uint32_t>(strtoul(row[4], nullptr, 10));
|
||||
e._class = static_cast<int8_t>(atoi(row[5]));
|
||||
e.level = static_cast<int8_t>(atoi(row[6]));
|
||||
e.name = row[7] ? row[7] : "";
|
||||
e.isgroupleader = static_cast<int8_t>(atoi(row[8]));
|
||||
e.israidleader = static_cast<int8_t>(atoi(row[9]));
|
||||
e.islooter = static_cast<int8_t>(atoi(row[10]));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@ -331,15 +350,17 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
RaidMembers e{};
|
||||
|
||||
e.raidid = static_cast<int32_t>(atoi(row[0]));
|
||||
e.charid = static_cast<int32_t>(atoi(row[1]));
|
||||
e.groupid = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e._class = static_cast<int8_t>(atoi(row[3]));
|
||||
e.level = static_cast<int8_t>(atoi(row[4]));
|
||||
e.name = row[5] ? row[5] : "";
|
||||
e.isgroupleader = static_cast<int8_t>(atoi(row[6]));
|
||||
e.israidleader = static_cast<int8_t>(atoi(row[7]));
|
||||
e.islooter = static_cast<int8_t>(atoi(row[8]));
|
||||
e.id = strtoull(row[0], nullptr, 10);
|
||||
e.raidid = static_cast<int32_t>(atoi(row[1]));
|
||||
e.charid = static_cast<int32_t>(atoi(row[2]));
|
||||
e.bot_id = static_cast<int32_t>(atoi(row[3]));
|
||||
e.groupid = static_cast<uint32_t>(strtoul(row[4], nullptr, 10));
|
||||
e._class = static_cast<int8_t>(atoi(row[5]));
|
||||
e.level = static_cast<int8_t>(atoi(row[6]));
|
||||
e.name = row[7] ? row[7] : "";
|
||||
e.isgroupleader = static_cast<int8_t>(atoi(row[8]));
|
||||
e.israidleader = static_cast<int8_t>(atoi(row[9]));
|
||||
e.islooter = static_cast<int8_t>(atoi(row[10]));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
@ -99,8 +99,9 @@ public:
|
||||
}
|
||||
}
|
||||
~Seperator() {
|
||||
for (int i=0; i<=maxargnum; i++)
|
||||
for (int i=0; i<=maxargnum; i++) {
|
||||
safe_delete_array(arg[i]);
|
||||
}
|
||||
safe_delete_array(arg);
|
||||
safe_delete_array(argplus);
|
||||
safe_delete_array(msg);
|
||||
|
||||
@ -42,7 +42,8 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9224
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9225
|
||||
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9038
|
||||
|
||||
#endif
|
||||
|
||||
@ -478,6 +478,7 @@
|
||||
9222|2023_02_28_npc_scaling_zone_list_version_list.sql|SHOW COLUMNS FROM `npc_scale_global_base` LIKE 'zone_id_list'|empty|
|
||||
9223|2023_03_04_npc_scale_global_base_heroic_strikethrough.sql|SHOW COLUMNS FROM `npc_scale_global_base` LIKE 'heroic_strikethrough'|empty|
|
||||
9224|2023_03_08_npc_scale_global_base_avoidance.sql|SHOW COLUMNS FROM `npc_scale_global_base` LIKE 'hp_regen_per_second'|empty|
|
||||
9225|2023_01_21_bots_raid_members.sql|SHOW COLUMNS FROM `raid_members` LIKE 'botid'|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
4
utils/sql/git/required/2023_01_21_bots_raid_members.sql
Normal file
4
utils/sql/git/required/2023_01_21_bots_raid_members.sql
Normal file
@ -0,0 +1,4 @@
|
||||
DROP INDEX IF EXISTS `PRIMARY` ON `raid_members`;
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS `UNIQUE` ON `raid_members`(`name`);
|
||||
ALTER TABLE `raid_members` ADD COLUMN `bot_id` int(4) NOT NULL DEFAULT 0 AFTER `charid`;
|
||||
ALTER TABLE `raid_members` ADD COLUMN `id` BIGINT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT FIRST;
|
||||
@ -120,8 +120,9 @@ ClientListEntry::~ClientListEntry()
|
||||
Camp(); // updates zoneserver's numplayers
|
||||
client_list.RemoveCLEReferances(this);
|
||||
}
|
||||
for (auto &elem : tell_queue)
|
||||
safe_delete_array(elem);
|
||||
for (auto& elem: tell_queue) {
|
||||
safe_delete_array(elem)
|
||||
};
|
||||
tell_queue.clear();
|
||||
}
|
||||
|
||||
@ -282,8 +283,9 @@ void ClientListEntry::ClearVars(bool iAll)
|
||||
pLFG = 0;
|
||||
gm = 0;
|
||||
pClientVersion = 0;
|
||||
for (auto &elem : tell_queue)
|
||||
safe_delete_array(elem);
|
||||
for (auto& elem: tell_queue) {
|
||||
safe_delete_array(elem)
|
||||
};
|
||||
tell_queue.clear();
|
||||
}
|
||||
|
||||
|
||||
@ -11,6 +11,7 @@ SET(zone_sources
|
||||
beacon.cpp
|
||||
bonuses.cpp
|
||||
bot.cpp
|
||||
bot_raid.cpp
|
||||
bot_command.cpp
|
||||
bot_database.cpp
|
||||
botspellsai.cpp
|
||||
|
||||
@ -3829,11 +3829,11 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
|
||||
} //end `if there is some damage being done and theres anattacker person involved`
|
||||
|
||||
Mob *pet = GetPet();
|
||||
// pets that have GHold will never automatically add NPCs
|
||||
// pets that have Hold and no Focus will add NPCs if they're engaged
|
||||
// pets that have Hold and Focus will not add NPCs
|
||||
if (
|
||||
Mob* pet = GetPet();
|
||||
pet &&
|
||||
!pet->IsFamiliar() &&
|
||||
!pet->GetSpecialAbility(IMMUNE_AGGRO) &&
|
||||
@ -3844,26 +3844,25 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
attacker != this &&
|
||||
!attacker->IsCorpse() &&
|
||||
!pet->IsGHeld() &&
|
||||
!attacker->IsTrap()
|
||||
!attacker->IsTrap() &&
|
||||
!pet->IsHeld()
|
||||
) {
|
||||
if (!pet->IsHeld()) {
|
||||
LogAggro("Sending pet [{}] into battle due to attack", pet->GetName());
|
||||
if (IsClient()) {
|
||||
// if pet was sitting his new mode is follow
|
||||
// following after the battle (live verified)
|
||||
if (pet->GetPetOrder() == SPO_Sit) {
|
||||
pet->SetPetOrder(SPO_Follow);
|
||||
}
|
||||
|
||||
// fix GUI sit button to be unpressed and stop sitting regen
|
||||
CastToClient()->SetPetCommandState(PET_BUTTON_SIT, 0);
|
||||
pet->SetAppearance(eaStanding);
|
||||
LogAggro("Sending pet [{}] into battle due to attack", pet->GetName());
|
||||
if (IsClient()) {
|
||||
// if pet was sitting his new mode is follow
|
||||
// following after the battle (live verified)
|
||||
if (pet->GetPetOrder() == SPO_Sit) {
|
||||
pet->SetPetOrder(SPO_Follow);
|
||||
}
|
||||
|
||||
pet->AddToHateList(attacker, 1, 0, true, false, false, spell_id);
|
||||
pet->SetTarget(attacker);
|
||||
MessageString(Chat::NPCQuestSay, PET_ATTACKING, pet->GetCleanName(), attacker->GetCleanName());
|
||||
// fix GUI sit button to be unpressed and stop sitting regen
|
||||
CastToClient()->SetPetCommandState(PET_BUTTON_SIT, 0);
|
||||
pet->SetAppearance(eaStanding);
|
||||
}
|
||||
|
||||
pet->AddToHateList(attacker, 1, 0, true, false, false, spell_id);
|
||||
pet->SetTarget(attacker);
|
||||
MessageString(Chat::NPCQuestSay, PET_ATTACKING, pet->GetCleanName(), attacker->GetCleanName());
|
||||
}
|
||||
|
||||
if (GetTempPetCount()) {
|
||||
@ -3884,9 +3883,18 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
else {
|
||||
int64 origdmg = damage;
|
||||
damage = AffectMagicalDamage(damage, spell_id, iBuffTic, attacker);
|
||||
if (origdmg != damage && attacker && attacker->IsClient()) {
|
||||
if (attacker->CastToClient()->GetFilter(FilterDamageShields) != FilterHide)
|
||||
attacker->Message(Chat::Yellow, "The Spellshield absorbed %d of %d points of damage", origdmg - damage, origdmg);
|
||||
if (
|
||||
origdmg != damage &&
|
||||
attacker &&
|
||||
attacker->IsClient() &&
|
||||
attacker->CastToClient()->GetFilter(FilterDamageShields) != FilterHide
|
||||
) {
|
||||
attacker->Message(
|
||||
Chat::Yellow,
|
||||
"The Spellshield absorbed %d of %d points of damage",
|
||||
origdmg - damage,
|
||||
origdmg
|
||||
);
|
||||
}
|
||||
if (damage == 0 && attacker && origdmg != damage && IsClient()) {
|
||||
//Kayen: Probably need to add a filter for this - Not sure if this msg is correct but there should be a message for spell negate/runes.
|
||||
@ -4212,7 +4220,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
//Note: if players can become pets, they will not receive damage messages of their own
|
||||
//this was done to simplify the code here (since we can only effectively skip one mob on queue)
|
||||
eqFilterType filter;
|
||||
Mob *skip = attacker;
|
||||
Mob* skip = attacker;
|
||||
if (attacker && attacker->GetOwnerID()) {
|
||||
//attacker is a pet, let pet owners see their pet's damage
|
||||
Mob* owner = attacker->GetOwner();
|
||||
@ -4239,6 +4247,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
owner->CastToClient()->QueuePacket(outapp, true, CLIENT_CONNECTED, filter);
|
||||
}
|
||||
}
|
||||
|
||||
skip = owner;
|
||||
}
|
||||
else {
|
||||
@ -4385,11 +4394,11 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
spells[spell_id].name /* Message4 */
|
||||
);
|
||||
}
|
||||
} //end packet sending
|
||||
}
|
||||
|
||||
}
|
||||
} //end packet sending
|
||||
|
||||
void Mob::HealDamage(uint64 amount, Mob *caster, uint16 spell_id)
|
||||
void Mob::HealDamage(uint64 amount, Mob* caster, uint16 spell_id)
|
||||
{
|
||||
int64 maxhp = GetMaxHP();
|
||||
int64 curhp = GetHP();
|
||||
@ -4403,16 +4412,17 @@ void Mob::HealDamage(uint64 amount, Mob *caster, uint16 spell_id)
|
||||
if (acthealed > 100) {
|
||||
if (caster) {
|
||||
if (IsBuffSpell(spell_id)) { // hots
|
||||
// message to caster
|
||||
if (caster->IsClient() && caster == this) {
|
||||
if (caster->CastToClient()->ClientVersionBit() & EQ::versions::maskSoFAndLater)
|
||||
// message to caster
|
||||
if ((caster->IsClient() && caster == this)) {
|
||||
if (caster->CastToClient()->ClientVersionBit() & EQ::versions::maskSoFAndLater) {
|
||||
FilteredMessageString(caster, Chat::NonMelee, FilterHealOverTime,
|
||||
HOT_HEAL_SELF, itoa(acthealed), spells[spell_id].name);
|
||||
}
|
||||
else
|
||||
FilteredMessageString(caster, Chat::NonMelee, FilterHealOverTime,
|
||||
YOU_HEALED, GetCleanName(), itoa(acthealed));
|
||||
}
|
||||
else if (caster->IsClient() && caster != this) {
|
||||
else if ((caster->IsClient() && caster != this)) {
|
||||
if (caster->CastToClient()->ClientVersionBit() & EQ::versions::maskSoFAndLater)
|
||||
caster->FilteredMessageString(caster, Chat::NonMelee, FilterHealOverTime,
|
||||
HOT_HEAL_OTHER, GetCleanName(), itoa(acthealed),
|
||||
@ -4421,6 +4431,7 @@ void Mob::HealDamage(uint64 amount, Mob *caster, uint16 spell_id)
|
||||
caster->FilteredMessageString(caster, Chat::NonMelee, FilterHealOverTime,
|
||||
YOU_HEAL, GetCleanName(), itoa(acthealed));
|
||||
}
|
||||
|
||||
// message to target
|
||||
if (IsClient() && caster != this) {
|
||||
if (CastToClient()->ClientVersionBit() & EQ::versions::maskSoFAndLater)
|
||||
@ -4435,7 +4446,7 @@ void Mob::HealDamage(uint64 amount, Mob *caster, uint16 spell_id)
|
||||
else { // normal heals
|
||||
FilteredMessageString(caster, Chat::NonMelee, FilterSpellDamage,
|
||||
YOU_HEALED, caster->GetCleanName(), itoa(acthealed));
|
||||
if (caster != this)
|
||||
|
||||
caster->FilteredMessageString(caster, Chat::NonMelee, FilterSpellDamage,
|
||||
YOU_HEAL, GetCleanName(), itoa(acthealed));
|
||||
}
|
||||
@ -4446,10 +4457,12 @@ void Mob::HealDamage(uint64 amount, Mob *caster, uint16 spell_id)
|
||||
}
|
||||
|
||||
if (curhp < maxhp) {
|
||||
if ((curhp + amount) > maxhp)
|
||||
if ((curhp + amount) > maxhp) {
|
||||
curhp = maxhp;
|
||||
else
|
||||
}
|
||||
else {
|
||||
curhp += amount;
|
||||
}
|
||||
SetHP(curhp);
|
||||
|
||||
SendHPUpdate();
|
||||
|
||||
4513
zone/bot.cpp
4513
zone/bot.cpp
File diff suppressed because it is too large
Load Diff
283
zone/bot.h
283
zone/bot.h
@ -33,6 +33,7 @@
|
||||
#include "../common/global_define.h"
|
||||
#include "guild_mgr.h"
|
||||
#include "worldserver.h"
|
||||
#include "raids.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
@ -42,9 +43,6 @@ constexpr uint32 BOT_FOLLOW_DISTANCE_WALK = 1000; // as DSq value (~31.623 units
|
||||
|
||||
constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds
|
||||
|
||||
//constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MIN = 5000; // 5 seconds
|
||||
//constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MAX = 20000; // 20 seconds
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
constexpr int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this
|
||||
@ -53,8 +51,6 @@ constexpr int MaxDisciplineTimer = 10;
|
||||
constexpr int DisciplineReuseStart = MaxSpellTimer + 1;
|
||||
constexpr int MaxTimer = MaxSpellTimer + MaxDisciplineTimer;
|
||||
|
||||
|
||||
|
||||
// nHSND negative Healer/Slower/Nuker/Doter
|
||||
// pH positive Healer
|
||||
// pS positive Slower
|
||||
@ -141,13 +137,13 @@ public:
|
||||
void Damage(Mob* from, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1,
|
||||
bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) override;
|
||||
|
||||
bool HasRaid() final { return (GetRaid() ? true : false); }
|
||||
bool HasGroup() final { return (GetGroup() ? true : false); }
|
||||
Raid* GetRaid() final { return entity_list.GetRaidByMob(this); }
|
||||
bool HasRaid() final { return GetRaid() != nullptr; }
|
||||
bool HasGroup() final { return GetGroup() != nullptr; }
|
||||
Raid* GetRaid() final { return entity_list.GetRaidByBot(this); }
|
||||
Group* GetGroup() final { return entity_list.GetGroupByMob(this); }
|
||||
|
||||
// Common, but informal "interfaces" with Client object
|
||||
uint32 CharacterID() { return GetBotID(); } // Just returns the Bot Id
|
||||
uint32 CharacterID() const { return GetBotID(); }
|
||||
inline bool IsInAGuild() const { return (_guildId != GUILD_NONE && _guildId != 0); }
|
||||
inline bool IsInGuild(uint32 in_gid) const { return (in_gid == _guildId && IsInAGuild()); }
|
||||
inline uint32 GuildID() const { return _guildId; }
|
||||
@ -173,20 +169,19 @@ public:
|
||||
int GetHandToHandDamage(void) override;
|
||||
bool TryFinishingBlow(Mob *defender, int64 &damage) override;
|
||||
void DoRiposte(Mob* defender) override;
|
||||
inline int32 GetATK() const override { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(EQ::skills::SkillOffense)) * 9 / 10); }
|
||||
inline int32 GetATK() { return ATK + itembonuses.ATK + spellbonuses.ATK + ((GetSTR() + GetSkill(EQ::skills::SkillOffense)) * 9 / 10); }
|
||||
inline int32 GetATKBonus() const override { return itembonuses.ATK + spellbonuses.ATK; }
|
||||
uint32 GetTotalATK();
|
||||
uint32 GetATKRating();
|
||||
uint16 GetPrimarySkillValue();
|
||||
uint16 MaxSkill(EQ::skills::SkillType skillid, uint16 class_, uint16 level) const;
|
||||
inline uint16 MaxSkill(EQ::skills::SkillType skillid) const { return MaxSkill(skillid, GetClass(), GetLevel()); }
|
||||
inline uint16 MaxSkill(EQ::skills::SkillType skillid) { return MaxSkill(skillid, GetClass(), GetLevel()); }
|
||||
int GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target = nullptr) override;
|
||||
void DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance = false);
|
||||
void TryBackstab(Mob *other,int ReuseTime = 10) override;
|
||||
void RogueBackstab(Mob* other, bool min_damage = false, int ReuseTime = 10) override;
|
||||
void RogueAssassinate(Mob* other) override;
|
||||
void DoClassAttacks(Mob *target, bool IsRiposte=false);
|
||||
bool CanDoSpecialAttack(Mob *other);
|
||||
void CalcBonuses() override;
|
||||
void CalcItemBonuses(StatBonuses* newbon);
|
||||
void AddItemBonuses(const EQ::ItemInstance *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false, int rec_override = 0);
|
||||
@ -197,7 +192,7 @@ public:
|
||||
bool IsNPC() const override { return false; }
|
||||
Mob* GetOwner() override;
|
||||
Mob* GetOwnerOrSelf() override;
|
||||
inline bool HasOwner() override { return (GetBotOwner() ? true : false); }
|
||||
inline bool HasOwner() override { return GetBotOwner() != nullptr; }
|
||||
int64 CalcMaxMana() override;
|
||||
void SetAttackTimer() override;
|
||||
uint64 GetClassHPFactor();
|
||||
@ -215,36 +210,35 @@ public:
|
||||
void Stand();
|
||||
bool IsSitting() const override;
|
||||
bool IsStanding();
|
||||
int GetWalkspeed() const override { return (int)((float)_GetWalkSpeed() * 1.785714285f); } // 1.25 / 0.7 = 1.7857142857142857142857142857143
|
||||
int GetRunspeed() const override { return (int)((float)_GetRunSpeed() * 1.785714285f); }
|
||||
int GetWalkspeed() { return (int)((float)_GetWalkSpeed() * 1.785714285f); } // 1.25 / 0.7 = 1.7857142857142857142857142857143
|
||||
int GetRunspeed() { return (int)((float)_GetRunSpeed() * 1.785714285f); }
|
||||
void WalkTo(float x, float y, float z) override;
|
||||
void RunTo(float x, float y, float z) override;
|
||||
void StopMoving() override;
|
||||
void StopMoving(float new_heading) override;
|
||||
//bool GetCombatJitterFlag() { return m_combat_jitter_flag; }
|
||||
bool GetGuardFlag() { return m_guard_flag; }
|
||||
|
||||
bool GetGuardFlag() const { return m_guard_flag; }
|
||||
void SetGuardFlag(bool flag = true) { m_guard_flag = flag; }
|
||||
bool GetHoldFlag() { return m_hold_flag; }
|
||||
bool GetHoldFlag() const { return m_hold_flag; }
|
||||
void SetHoldFlag(bool flag = true) { m_hold_flag = flag; }
|
||||
bool GetAttackFlag() { return m_attack_flag; }
|
||||
bool GetAttackFlag() const { return m_attack_flag; }
|
||||
void SetAttackFlag(bool flag = true) { m_attack_flag = flag; }
|
||||
bool GetAttackingFlag() { return m_attacking_flag; }
|
||||
bool GetPullFlag() { return m_pull_flag; }
|
||||
bool GetAttackingFlag() const { return m_attacking_flag; }
|
||||
bool GetPullFlag() const { return m_pull_flag; }
|
||||
void SetPullFlag(bool flag = true) { m_pull_flag = flag; }
|
||||
bool GetPullingFlag() { return m_pulling_flag; }
|
||||
bool GetReturningFlag() { return m_returning_flag; }
|
||||
bool GetPullingFlag() const { return m_pulling_flag; }
|
||||
bool GetReturningFlag() const { return m_returning_flag; }
|
||||
bool UseDiscipline(uint32 spell_id, uint32 target);
|
||||
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets);
|
||||
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid);
|
||||
uint8 GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid);
|
||||
bool GetNeedsCured(Mob *tar);
|
||||
bool GetNeedsHateRedux(Mob *tar);
|
||||
bool HasOrMayGetAggro();
|
||||
void SetDefaultBotStance();
|
||||
void SetSurname(std::string bot_surname);
|
||||
void SetTitle(std::string bot_title);
|
||||
void SetSuffix(std::string bot_suffix);
|
||||
std::string GetSurname() { return _surname; }
|
||||
std::string GetTitle() { return _title; }
|
||||
std::string GetSuffix() { return _suffix; }
|
||||
void SetSurname(std::string_view bot_surname);
|
||||
void SetTitle(std::string_view bot_title);
|
||||
void SetSuffix(std::string_view bot_suffix);
|
||||
std::string GetSurname() const { return _surname; }
|
||||
std::string GetTitle() const { return _title; }
|
||||
std::string GetSuffix() const { return _suffix; }
|
||||
inline virtual int32 GetMaxStat();
|
||||
inline virtual int32 GetMaxResist();
|
||||
inline virtual int32 GetMaxSTR();
|
||||
@ -282,19 +276,18 @@ public:
|
||||
uint32 CalcCurrentWeight();
|
||||
int GroupLeadershipAAHealthEnhancement();
|
||||
int GroupLeadershipAAManaEnhancement();
|
||||
int GroupLeadershipAAHealthRegeneration();
|
||||
int GroupLeadershipAAHealthRegeneration();
|
||||
int GroupLeadershipAAOffenseEnhancement();
|
||||
void CalcRestState();
|
||||
int64 CalcMaxEndurance(); //This calculates the maximum endurance we can have
|
||||
int64 CalcBaseEndurance(); //Calculates Base End
|
||||
int64 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen()
|
||||
int64 GetEndurance() const {return cur_end;} //This gets our current endurance
|
||||
int64 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call
|
||||
int64 CalcEnduranceRegenCap();
|
||||
inline uint8 GetEndurancePercent() { return (uint8)((float)cur_end / (float)max_end * 100.0f); }
|
||||
void SetEndurance(int32 newEnd); //This sets the current endurance to the new value
|
||||
void DoEnduranceRegen(); //This Regenerates endurance
|
||||
void DoEnduranceUpkeep(); //does the endurance upkeep
|
||||
int64 CalcMaxEndurance();
|
||||
int64 CalcBaseEndurance();
|
||||
int64 CalcEnduranceRegen();
|
||||
int64 GetEndurance() const override {return cur_end;}
|
||||
int64 GetMaxEndurance() const override {return max_end;}
|
||||
int64 CalcEnduranceRegenCap();
|
||||
inline uint8 GetEndurancePercent() override { return (uint8)((float)cur_end / (float)max_end * 100.0f); }
|
||||
void SetEndurance(int32 newEnd) override;
|
||||
void DoEnduranceUpkeep();
|
||||
|
||||
bool AI_AddBotSpells(uint32 bot_spell_id);
|
||||
void AddSpellToBotList(
|
||||
@ -330,19 +323,20 @@ public:
|
||||
);
|
||||
|
||||
void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot);
|
||||
|
||||
// AI Methods
|
||||
bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes);
|
||||
bool AI_EngagedCastCheck() override;
|
||||
bool AI_PursueCastCheck() override;
|
||||
bool AI_IdleCastCheck() override;
|
||||
bool AIHealRotation(Mob* tar, bool useFastHeals);
|
||||
bool GetPauseAI() { return _pauseAI; }
|
||||
bool GetPauseAI() const { return _pauseAI; }
|
||||
void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; }
|
||||
uint8 GetStopMeleeLevel() { return _stopMeleeLevel; }
|
||||
uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; }
|
||||
void SetStopMeleeLevel(uint8 level);
|
||||
void SetGuardMode();
|
||||
void SetHoldMode();
|
||||
uint32 GetBotCasterRange() { return m_bot_caster_range; }
|
||||
uint32 GetBotCasterRange() const { return m_bot_caster_range; }
|
||||
bool IsValidSpellRange(uint16 spell_id, Mob const* tar);
|
||||
|
||||
// Bot AI Methods
|
||||
@ -375,7 +369,7 @@ public:
|
||||
bool IsImmuneToSpell(uint16 spell_id, Mob *caster) override;
|
||||
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot);
|
||||
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1,
|
||||
uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
|
||||
uint32* oSpellWillFinish = nullptr, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
|
||||
inline int64 GetFocusEffect(focusType type, uint16 spell_id, Mob *caster = nullptr, bool from_buff_tic = false) override
|
||||
{ return Mob::GetFocusEffect(type, spell_id, caster, from_buff_tic); }
|
||||
inline bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false,
|
||||
@ -389,40 +383,51 @@ public:
|
||||
|
||||
bool GetBotOwnerDataBuckets();
|
||||
bool GetBotDataBuckets();
|
||||
bool CheckDataBucket(std::string bucket_name, std::string bucket_value, uint8 bucket_comparison);
|
||||
bool CheckDataBucket(const std::string& bucket_name, const std::string& bucket_value, uint8 bucket_comparison);
|
||||
|
||||
// Bot Equipment & Inventory Class Methods
|
||||
void BotTradeAddItem(const EQ::ItemInstance* inst, uint16 slot_id, std::string* error_message, bool save_to_database = true);
|
||||
void EquipBot(std::string* error_message);
|
||||
bool CheckLoreConflict(const EQ::ItemData* item);
|
||||
void UpdateEquipmentLight() override { m_Light.Type[EQ::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQ::lightsource::LightEquipment] = EQ::lightsource::TypeToLevel(m_Light.Type[EQ::lightsource::LightEquipment]); }
|
||||
inline EQ::InventoryProfile& GetInv() { return m_inv; }
|
||||
void UpdateEquipmentLight() override
|
||||
{
|
||||
m_Light.Type[EQ::lightsource::LightEquipment] = m_inv.FindBrightestLightType();
|
||||
m_Light.Level[EQ::lightsource::LightEquipment] = EQ::lightsource::TypeToLevel(m_Light.Type[EQ::lightsource::LightEquipment]);
|
||||
}
|
||||
|
||||
inline EQ::InventoryProfile& GetInv() override { return m_inv; }
|
||||
|
||||
// Static Class Methods
|
||||
//static void DestroyBotRaidObjects(Client* client); // Can be removed after bot raids are dumped
|
||||
static Bot* LoadBot(uint32 botID);
|
||||
static uint32 SpawnedBotCount(const uint32 owner_id, uint8 class_id = NO_CLASS);
|
||||
static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp);
|
||||
//static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* error_message);
|
||||
|
||||
static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined);
|
||||
static Bot* GetBotByBotClientOwnerAndBotName(Client* c, std::string botName);
|
||||
static void ProcessBotGroupInvite(Client* c, std::string botName);
|
||||
static void ProcessBotGroupDisband(Client* c, std::string botName);
|
||||
static Bot* GetBotByBotClientOwnerAndBotName(Client* c, const std::string& botName);
|
||||
static void ProcessBotGroupInvite(Client* c, std::string const& botName);
|
||||
static void ProcessBotGroupDisband(Client* c, const std::string& botName);
|
||||
static void BotOrderCampAll(Client* c, uint8 class_id = NO_CLASS);
|
||||
static void ProcessBotInspectionRequest(Bot* inspectedBot, Client* client);
|
||||
static void LoadAndSpawnAllZonedBots(Client* bot_owner);
|
||||
static bool GroupHasBot(Group* group);
|
||||
static Bot* GetFirstBotInGroup(Group* group);
|
||||
static void ProcessClientZoneChange(Client* botOwner);
|
||||
static void ProcessBotOwnerRefDelete(Mob* botOwner); // Removes a Client* reference when the Client object is destroyed
|
||||
static void ProcessGuildInvite(Client* guildOfficer, Bot* botToGuild); // Processes a client's request to guild a bot
|
||||
static bool ProcessGuildRemoval(Client* guildOfficer, std::string botName); // Processes a client's request to deguild a bot
|
||||
static void ProcessBotOwnerRefDelete(Mob* botOwner); // Removes a Client* reference when the Client object is destroyed
|
||||
static int32 GetSpellRecastTimer(Bot *caster, int timer_index);
|
||||
static bool CheckSpellRecastTimers(Bot *caster, int SpellIndex);
|
||||
static int32 GetDisciplineRecastTimer(Bot *caster, int timer_index);
|
||||
static bool CheckDisciplineRecastTimers(Bot *caster, int timer_index);
|
||||
static uint32 GetDisciplineRemainingTime(Bot *caster, int timer_index);
|
||||
|
||||
//Raid methods
|
||||
static void ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite = false);
|
||||
static void RemoveBotFromRaid(Bot* bot);
|
||||
inline void SetDirtyAutoHaters() { m_dirtyautohaters = true; }
|
||||
static void CreateBotRaid(Mob* invitee, Client* invitor, bool group_invite, Raid* raid);
|
||||
static void
|
||||
ProcessBotGroupAdd(Group* group, Raid* raid, Client* client = nullptr, bool new_raid = false, bool initial = false);
|
||||
|
||||
|
||||
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, uint32 spellType);
|
||||
@ -452,8 +457,8 @@ public:
|
||||
static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target);
|
||||
|
||||
static NPCType *CreateDefaultNPCTypeStructForBot(
|
||||
std::string botName,
|
||||
std::string botLastName,
|
||||
const std::string& botName,
|
||||
const std::string& botLastName,
|
||||
uint8 botLevel,
|
||||
uint16 botRace,
|
||||
uint8 botClass,
|
||||
@ -467,8 +472,8 @@ public:
|
||||
|
||||
// "GET" Class Methods
|
||||
uint32 GetBotID() const { return _botID; }
|
||||
uint32 GetBotOwnerCharacterID() { return _botOwnerCharacterID; }
|
||||
uint32 GetBotSpellID() { return npc_spells_id; }
|
||||
uint32 GetBotOwnerCharacterID() const { return _botOwnerCharacterID; }
|
||||
uint32 GetBotSpellID() const { return npc_spells_id; }
|
||||
Mob* GetBotOwner() { return this->_botOwner; }
|
||||
uint32 GetBotArcheryRange();
|
||||
EQ::ItemInstance* GetBotItem(uint16 slot_id);
|
||||
@ -486,18 +491,12 @@ public:
|
||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||
bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; }
|
||||
float GetBotCasterMaxRange(float melee_distance_max);
|
||||
bool IsGroupHealer() { return m_CastingRoles.GroupHealer; }
|
||||
bool IsGroupSlower() { return m_CastingRoles.GroupSlower; }
|
||||
bool IsGroupNuker() { return m_CastingRoles.GroupNuker; }
|
||||
bool IsGroupDoter() { return m_CastingRoles.GroupDoter; }
|
||||
bool IsGroupHealer() const { return m_CastingRoles.GroupHealer; }
|
||||
bool IsGroupSlower() const { return m_CastingRoles.GroupSlower; }
|
||||
bool IsGroupNuker() const { return m_CastingRoles.GroupNuker; }
|
||||
bool IsGroupDoter() const { return m_CastingRoles.GroupDoter; }
|
||||
static void UpdateGroupCastingRoles(const Group* group, bool disband = false);
|
||||
|
||||
//bool IsRaidHealer() { return m_CastingRoles.RaidHealer; }
|
||||
//bool IsRaidSlower() { return m_CastingRoles.RaidSlower; }
|
||||
//bool IsRaidNuker() { return m_CastingRoles.RaidNuker; }
|
||||
//bool IsRaidDoter() { return m_CastingRoles.RaidDoter; }
|
||||
//static void UpdateRaidCastingRoles(const Raid* raid, bool disband = false);
|
||||
|
||||
bool IsBotCaster() { return IsCasterClass(GetClass()); }
|
||||
bool IsBotHybrid() { return IsHybridClass(GetClass()); }
|
||||
bool IsBotINTCaster() { return IsINTCasterClass(GetClass()); }
|
||||
@ -506,7 +505,6 @@ public:
|
||||
bool IsBotFighter() { return IsFighterClass(GetClass()); }
|
||||
bool IsBotNonSpellFighter() { return IsNonSpellFighterClass(GetClass()); }
|
||||
uint8 GetBotClass() { return GetClass(); }
|
||||
bool CanHeal();
|
||||
int GetRawACNoShield(int &shield_ac);
|
||||
|
||||
// new heal rotation code
|
||||
@ -532,8 +530,8 @@ public:
|
||||
|
||||
std::shared_ptr<HealRotation>* MemberOfHealRotation() { return &m_member_of_heal_rotation; }
|
||||
|
||||
bool GetAltOutOfCombatBehavior() { return _altoutofcombatbehavior;}
|
||||
bool GetShowHelm() { return _showhelm; }
|
||||
bool GetAltOutOfCombatBehavior() const { return _altoutofcombatbehavior;}
|
||||
bool GetShowHelm() const { return _showhelm; }
|
||||
inline int32 GetSTR() const override { return STR; }
|
||||
inline int32 GetSTA() const override { return STA; }
|
||||
inline int32 GetDEX() const override { return DEX; }
|
||||
@ -609,7 +607,6 @@ public:
|
||||
void SetBotCharmer(bool c) { _botCharmer = c; }
|
||||
void SetPetChooser(bool p) { _petChooser = p; }
|
||||
void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; }
|
||||
// void SetBotOwnerCharacterID(uint32 botOwnerCharacterID) { _botOwnerCharacterID = botOwnerCharacterID; }
|
||||
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == RANGER ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
|
||||
void SetBotStance(EQ::constants::StanceType botStance) {
|
||||
if (botStance >= EQ::constants::stancePassive && botStance <= EQ::constants::stanceBurnAE)
|
||||
@ -650,7 +647,7 @@ public:
|
||||
void SetBotEnforceSpellSetting(bool enforcespellsettings, bool save = false);
|
||||
bool GetBotEnforceSpellSetting() const { return m_enforce_spell_settings; }
|
||||
|
||||
static void SpawnBotGroupByName(Client* c, std::string botgroup_name, uint32 leader_id);
|
||||
static void SpawnBotGroupByName(Client* c, const std::string& botgroup_name, uint32 leader_id);
|
||||
|
||||
std::string CreateSayLink(Client* botOwner, const char* message, const char* name);
|
||||
|
||||
@ -663,8 +660,8 @@ public:
|
||||
// Publicized private functions
|
||||
static NPCType *FillNPCTypeStruct(
|
||||
uint32 botSpellsID,
|
||||
std::string botName,
|
||||
std::string botLastName,
|
||||
const std::string& botName,
|
||||
const std::string& botLastName,
|
||||
uint8 botLevel,
|
||||
uint16 botRace,
|
||||
uint8 botClass,
|
||||
@ -722,43 +719,116 @@ public:
|
||||
// New accessors for BotDatabase access
|
||||
bool DeleteBot();
|
||||
uint32* GetTimers() { return timers; }
|
||||
uint32 GetLastZoneID() { return _lastZoneId; }
|
||||
int32 GetBaseAC() { return _baseAC; }
|
||||
int32 GetBaseATK() { return _baseATK; }
|
||||
int32 GetBaseSTR() { return _baseSTR; }
|
||||
int32 GetBaseSTA() { return _baseSTA; }
|
||||
int32 GetBaseCHA() { return _baseCHA; }
|
||||
int32 GetBaseDEX() { return _baseDEX; }
|
||||
int32 GetBaseINT() { return _baseINT; }
|
||||
int32 GetBaseAGI() { return _baseAGI; }
|
||||
int32 GetBaseWIS() { return _baseWIS; }
|
||||
int32 GetBaseFR() { return _baseFR; }
|
||||
int32 GetBaseCR() { return _baseCR; }
|
||||
int32 GetBaseMR() { return _baseMR; }
|
||||
int32 GetBasePR() { return _basePR; }
|
||||
int32 GetBaseDR() { return _baseDR; }
|
||||
int32 GetBaseCorrup() { return _baseCorrup; }
|
||||
uint32 GetLastZoneID() const { return _lastZoneId; }
|
||||
int32 GetBaseAC() const { return _baseAC; }
|
||||
int32 GetBaseATK() const { return _baseATK; }
|
||||
int32 GetBaseSTR() const { return _baseSTR; }
|
||||
int32 GetBaseSTA() const { return _baseSTA; }
|
||||
int32 GetBaseCHA() const { return _baseCHA; }
|
||||
int32 GetBaseDEX() const { return _baseDEX; }
|
||||
int32 GetBaseINT() const { return _baseINT; }
|
||||
int32 GetBaseAGI() const { return _baseAGI; }
|
||||
int32 GetBaseWIS() const { return _baseWIS; }
|
||||
int32 GetBaseFR() const { return _baseFR; }
|
||||
int32 GetBaseCR() const { return _baseCR; }
|
||||
int32 GetBaseMR() const { return _baseMR; }
|
||||
int32 GetBasePR() const { return _basePR; }
|
||||
int32 GetBaseDR() const { return _baseDR; }
|
||||
int32 GetBaseCorrup() const { return _baseCorrup; }
|
||||
|
||||
void Signal(int signal_id);
|
||||
void SendPayload(int payload_id, std::string payload_value = std::string());
|
||||
void OwnerMessage(std::string message);
|
||||
void OwnerMessage(const std::string& message);
|
||||
|
||||
//Raid additions
|
||||
Raid* p_raid_instance;
|
||||
|
||||
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND];
|
||||
|
||||
bool BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid);
|
||||
bool BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid);
|
||||
bool BotCastRoot(Mob* tar, uint8 botLevel, uint32 iSpellTypes, BotSpell& botSpell, const bool& checked_los);
|
||||
bool BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass);
|
||||
bool BotCastEscape(Mob*& tar, uint8 botClass, BotSpell& botSpell, uint32 iSpellTypes);
|
||||
bool BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los);
|
||||
bool BotCastDispel(Mob* tar, BotSpell& botSpell, uint32 iSpellTypes, const bool& checked_los);
|
||||
bool BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell);
|
||||
bool BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass);
|
||||
bool BotCastLifetap(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes);
|
||||
bool BotCastSnare(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes);
|
||||
bool BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const bool& checked_los);
|
||||
bool BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los, Raid* raid);
|
||||
bool BotCastDebuff(Mob* tar, uint8 botLevel, BotSpell& botSpell, bool checked_los);
|
||||
bool BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, Raid* raid);
|
||||
bool BotCastHateReduction(Mob* tar, uint8 botLevel, const BotSpell& botSpell);
|
||||
bool BotCastCombatSong(Mob* tar, uint8 botLevel);
|
||||
bool BotCastSong(Mob* tar, uint8 botLevel);
|
||||
|
||||
bool CheckIfIncapacitated();
|
||||
bool IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid);
|
||||
|
||||
Client* SetLeashOwner(Client* bot_owner, Group* bot_group, Raid* raid, uint32 r_group) const;
|
||||
Mob* SetFollowMob(Client* leash_owner);
|
||||
|
||||
Mob* GetBotTarget(Client* bot_owner);
|
||||
void AcquireBotTarget(Group* bot_group, Raid* raid, Client* leash_owner, float leash_distance);
|
||||
void SetBotTarget(Client* bot_owner, Raid* raid, Group* bot_group, Client* leash_owner, float lo_distance, float leash_distance, bool bo_alt_combat);
|
||||
void SetLeashOwnerTarget(Client* leash_owner, Client* bot_owner, float lo_distance, float leash_distance);
|
||||
void SetOwnerTarget(Client* bot_owner);
|
||||
void SetBotGroupTarget(const Client* bot_owner, Client* leash_owner, float lo_distance, float leash_distance, Mob* const& bg_member, Mob* bgm_target);
|
||||
bool IsValidTarget(Client* bot_owner, Client* leash_owner, float lo_distance, float leash_distance, bool bo_alt_combat, Mob* tar, float tar_distance);
|
||||
|
||||
bool PullingFlagChecks(Client* bot_owner);
|
||||
bool ReturningFlagChecks(Client* bot_owner, float fm_distance);
|
||||
void BotPullerProcess(Client* bot_owner, Raid* raid);
|
||||
|
||||
|
||||
// Movement Methods
|
||||
void CalcMeleeDistances(
|
||||
const Mob* tar,
|
||||
const EQ::ItemInstance* const& p_item,
|
||||
const EQ::ItemInstance* const& s_item,
|
||||
bool behind_mob,
|
||||
bool backstab_weapon,
|
||||
float& melee_distance_max,
|
||||
float& melee_distance
|
||||
) const;
|
||||
|
||||
// Combat Checks
|
||||
void SetBerserkState();
|
||||
bool CheckIfCasting(float fm_distance);
|
||||
void HealRotationChecks();
|
||||
void CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item);
|
||||
|
||||
// Try Combat Methods
|
||||
bool TryEvade(Mob* tar);
|
||||
bool TryFacingTarget(Mob* tar);
|
||||
bool TryRangedAttack(Mob* tar);
|
||||
bool TryClassAttacks(Mob* tar);
|
||||
bool TryPrimaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* p_item);
|
||||
bool TrySecondaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* s_item);
|
||||
bool TryPursueTarget(float leash_distance, glm::vec3& Goal);
|
||||
bool TryMeditate();
|
||||
bool TryAutoDefend(Client* bot_owner, float leash_distance);
|
||||
bool TryIdleChecks(float fm_distance);
|
||||
bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal);
|
||||
bool TryBardMovementCasts();
|
||||
void SetRangerCombatWeapon(bool atArcheryRange);
|
||||
|
||||
// Public "Refactor" Methods
|
||||
static bool CheckSpawnConditions(Client* c);
|
||||
|
||||
protected:
|
||||
void PetAIProcess();
|
||||
void BotMeditate(bool isSitting);
|
||||
bool CheckBotDoubleAttack(bool Triple = false);
|
||||
void PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client);
|
||||
bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0) override;
|
||||
bool AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = nullptr) override;
|
||||
|
||||
BotCastingRoles& GetCastingRoles() { return m_CastingRoles; }
|
||||
void SetGroupHealer(bool flag = true) { m_CastingRoles.GroupHealer = flag; }
|
||||
void SetGroupSlower(bool flag = true) { m_CastingRoles.GroupSlower = flag; }
|
||||
void SetGroupNuker(bool flag = true) { m_CastingRoles.GroupNuker = flag; }
|
||||
void SetGroupDoter(bool flag = true) { m_CastingRoles.GroupDoter = flag; }
|
||||
//void SetRaidHealer(bool flag = true) { m_CastingRoles.RaidHealer = flag; }
|
||||
//void SetRaidSlower(bool flag = true) { m_CastingRoles.RaidSlower = flag; }
|
||||
//void SetRaidNuker(bool flag = true) { m_CastingRoles.RaidNuker = flag; }
|
||||
//void SetRaidDoter(bool flag = true) { m_CastingRoles.RaidDoter = flag; }
|
||||
std::deque<int> bot_signal_q;
|
||||
|
||||
std::vector<BotSpells_Struct> AIBot_spells;
|
||||
@ -801,8 +871,7 @@ private:
|
||||
Timer m_evade_timer; // can be moved to pTimers at some point
|
||||
Timer m_alt_combat_hate_timer;
|
||||
Timer m_auto_defend_timer;
|
||||
//Timer m_combat_jitter_timer;
|
||||
//bool m_combat_jitter_flag;
|
||||
bool m_dirtyautohaters;
|
||||
bool m_guard_flag;
|
||||
bool m_hold_flag;
|
||||
bool m_attack_flag;
|
||||
@ -849,15 +918,12 @@ private:
|
||||
|
||||
// Class Methods
|
||||
void LoadAAs();
|
||||
int32 acmod();
|
||||
void GenerateBaseStats();
|
||||
void GenerateAppearance();
|
||||
void GenerateArmorClass();
|
||||
int32 GenerateBaseHitPoints();
|
||||
int32 GenerateBaseManaPoints();
|
||||
void GenerateSpecialAttacks();
|
||||
void SetBotID(uint32 botID);
|
||||
//void SetCombatJitterFlag(bool flag = true) { m_combat_jitter_flag = flag; }
|
||||
void SetAttackingFlag(bool flag = true) { m_attacking_flag = flag; }
|
||||
void SetPullingFlag(bool flag = true) { m_pulling_flag = flag; }
|
||||
void SetReturningFlag(bool flag = true) { m_returning_flag = flag; }
|
||||
@ -870,9 +936,6 @@ private:
|
||||
bool LoadPet(); // Load and spawn bot pet if there is one
|
||||
bool SavePet(); // Save and depop bot pet if there is one
|
||||
bool DeletePet();
|
||||
|
||||
public:
|
||||
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND];
|
||||
};
|
||||
|
||||
bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 iSpellID);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
287
zone/bot_raid.cpp
Normal file
287
zone/bot_raid.cpp
Normal file
@ -0,0 +1,287 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "bot.h"
|
||||
#include "object.h"
|
||||
#include "raids.h"
|
||||
#include "doors.h"
|
||||
#include "quest_parser_collection.h"
|
||||
#include "../common/data_verification.h"
|
||||
|
||||
extern volatile bool is_zone_loaded;
|
||||
extern bool Critical;
|
||||
|
||||
std::vector<RaidMember> Raid::GetRaidGroupMembers(uint32 gid)
|
||||
{
|
||||
std::vector<RaidMember> raid_group_members;
|
||||
raid_group_members.clear();
|
||||
|
||||
for (int i = 0; i < MAX_RAID_MEMBERS; ++i)
|
||||
{
|
||||
if (members[i].member && members[i].GroupNumber == gid)
|
||||
{
|
||||
raid_group_members.push_back(members[i]);
|
||||
}
|
||||
}
|
||||
return raid_group_members;
|
||||
}
|
||||
|
||||
// Returns Bot members that are in the raid
|
||||
// passing in owner will return all Bots that have the same owner.
|
||||
std::vector<Bot*> Raid::GetRaidBotMembers(uint32 owner)
|
||||
{
|
||||
std::vector<Bot*> raid_members_bots;
|
||||
raid_members_bots.clear();
|
||||
|
||||
for (int i = 0; i < MAX_RAID_MEMBERS; i++) {
|
||||
if (
|
||||
members[i].member &&
|
||||
members[i].member->IsBot()
|
||||
) {
|
||||
auto b_member = members[i].member->CastToBot();
|
||||
if (owner && b_member->GetBotOwnerCharacterID() == owner) {
|
||||
raid_members_bots.emplace_back(b_member);
|
||||
} else if (!owner) {
|
||||
raid_members_bots.emplace_back(b_member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return raid_members_bots;
|
||||
}
|
||||
|
||||
// Returns Bot members that are in the group specified
|
||||
// passing in owner will return only Bots that have the same owner.
|
||||
std::vector<Bot*> Raid::GetRaidGroupBotMembers(uint32 gid)
|
||||
{
|
||||
std::vector<Bot*> raid_members_bots;
|
||||
raid_members_bots.clear();
|
||||
|
||||
for (int i = 0; i < MAX_RAID_MEMBERS; i++) {
|
||||
if (
|
||||
members[i].member &&
|
||||
members[i].member->IsBot() &&
|
||||
members[i].GroupNumber == gid
|
||||
) {
|
||||
auto b_member = members[i].member->CastToBot();
|
||||
raid_members_bots.emplace_back(b_member);
|
||||
raid_members_bots.emplace_back(b_member);
|
||||
}
|
||||
}
|
||||
|
||||
return raid_members_bots;
|
||||
}
|
||||
|
||||
void Raid::HandleBotGroupDisband(uint32 owner, uint32 gid)
|
||||
{
|
||||
auto raid_members_bots = gid != RAID_GROUPLESS ? GetRaidGroupBotMembers(gid) : GetRaidBotMembers(owner);
|
||||
|
||||
// If any of the bots are a group leader then re-create the botgroup on disband, dropping any clients
|
||||
for (auto& bot_iter: raid_members_bots) {
|
||||
|
||||
// Remove the entire BOT group in this case
|
||||
if (
|
||||
bot_iter &&
|
||||
gid != RAID_GROUPLESS &&
|
||||
IsRaidMember(bot_iter->GetName()) &&
|
||||
IsGroupLeader(bot_iter->GetName())
|
||||
) {
|
||||
auto r_group_members = GetRaidGroupMembers(GetGroup(bot_iter->GetName()));
|
||||
auto group_inst = new Group(bot_iter);
|
||||
entity_list.AddGroup(group_inst);
|
||||
database.SetGroupID(bot_iter->GetCleanName(), group_inst->GetID(), bot_iter->GetBotID());
|
||||
database.SetGroupLeaderName(group_inst->GetID(), bot_iter->GetName());
|
||||
|
||||
for (auto member_iter: r_group_members) {
|
||||
if (member_iter.member->IsBot()) {
|
||||
auto b_member = member_iter.member->CastToBot();
|
||||
if (strcmp(b_member->GetName(), bot_iter->GetName()) == 0) {
|
||||
bot_iter->SetFollowID(owner);
|
||||
} else {
|
||||
Bot::AddBotToGroup(b_member, group_inst);
|
||||
}
|
||||
Bot::RemoveBotFromRaid(b_member);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Bot::RemoveBotFromRaid(bot_iter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8 Bot::GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid) {
|
||||
|
||||
if (raid) {
|
||||
uint32 r_group = raid->GetGroup(GetName());
|
||||
auto raid_group_members = raid->GetRaidGroupMembers(r_group);
|
||||
|
||||
for (auto& m: raid_group_members) {
|
||||
if (m.member && !m.member->qglobal) {
|
||||
if (m.member->GetHPRatio() <= hpr) {
|
||||
need_healed++;
|
||||
}
|
||||
|
||||
if (includePets) {
|
||||
if (m.member->GetPet() && m.member->GetPet()->GetHPRatio() <= hpr) {
|
||||
need_healed++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return need_healed;
|
||||
}
|
||||
|
||||
void Bot::ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite) {
|
||||
|
||||
if (!invitee || !invitor) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (invitee->IsBot() &&
|
||||
invitor->HasRaid() &&
|
||||
invitee->GetOwner()->HasRaid() &&
|
||||
invitor->GetRaid()->IsRaidMember(invitee->GetOwner()->GetName())
|
||||
) {
|
||||
// If the Bot Owner is in our raid we need to be able to invite their Bots
|
||||
}
|
||||
else if (invitee->IsBot() && (invitee->CastToBot()->GetBotOwnerCharacterID() != invitor->CharacterID())) {
|
||||
invitor->Message(
|
||||
Chat::Red,
|
||||
fmt::format(
|
||||
"{} is not your Bot. You can only invite your own Bots, or Bots that belong to a Raid member.",
|
||||
invitee->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
Raid* raid = entity_list.GetRaidByClient(invitor);
|
||||
|
||||
Bot::CreateBotRaid(invitee, invitor, group_invite, raid);
|
||||
}
|
||||
|
||||
void Bot::CreateBotRaid(Mob* invitee, Client* invitor, bool group_invite, Raid* raid) {
|
||||
|
||||
Group* g_invitee = invitee->GetGroup();
|
||||
Group* g_invitor = invitor->GetGroup();
|
||||
|
||||
if (g_invitee && invitor->IsClient()) {
|
||||
if (!g_invitee->IsLeader(invitee)) {
|
||||
invitor->Message(
|
||||
Chat::Red,
|
||||
fmt::format(
|
||||
"You can only invite group leaders or ungrouped bots. Try {} instead.",
|
||||
g_invitee->GetLeader()->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool new_raid = false;
|
||||
if (!raid) {
|
||||
new_raid = true;
|
||||
raid = new Raid(invitor);
|
||||
entity_list.AddRaid(raid);
|
||||
raid->SetRaidDetails();
|
||||
}
|
||||
|
||||
// Add Invitor to new raid
|
||||
if (new_raid) {
|
||||
if (g_invitor) {
|
||||
ProcessBotGroupAdd(g_invitor, raid, nullptr, true, true);
|
||||
} else {
|
||||
raid->SendRaidCreate(invitor);
|
||||
raid->AddMember(invitor, 0xFFFFFFFF, true, false, true);
|
||||
raid->SendMakeLeaderPacketTo(invitor->GetName(), invitor);
|
||||
if (raid->IsLocked()) {
|
||||
raid->SendRaidLockTo(invitor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add Bot Group or Client Bot Group to raid
|
||||
if (g_invitee) {
|
||||
ProcessBotGroupAdd(g_invitee, raid);
|
||||
|
||||
// Add individual client to raid
|
||||
} else if (invitee->IsClient()) {
|
||||
ProcessBotGroupAdd(g_invitee, raid, invitee->CastToClient());
|
||||
|
||||
// Add individual bot to raid
|
||||
} else {
|
||||
auto gid = raid->GetGroup(invitor->GetName());
|
||||
auto b = invitee->CastToBot();
|
||||
|
||||
// gives us a choice to either invite directly into the clients Raid Group, or just into the Raid
|
||||
if (group_invite && raid->GroupCount(gid) < MAX_GROUP_MEMBERS) {
|
||||
raid->AddBot(b, gid);
|
||||
} else {
|
||||
raid->AddBot(b);
|
||||
}
|
||||
|
||||
if (new_raid) {
|
||||
invitee->SetFollowID(invitor->GetID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::ProcessBotGroupAdd(Group* group, Raid* raid, Client* client, bool new_raid, bool initial) {
|
||||
|
||||
uint32 raid_free_group_id = raid->GetFreeGroup();
|
||||
if (group) {
|
||||
for (int x = 0; x < MAX_GROUP_MEMBERS; x++) {
|
||||
if (group->members[x]) {
|
||||
Client* c = nullptr;
|
||||
Bot* b = nullptr;
|
||||
|
||||
if (group->members[x] && group->members[x]->IsBot()) {
|
||||
b = group->members[x]->CastToBot();
|
||||
raid->AddBot(b, raid_free_group_id, false, x == 0, false);
|
||||
} else if (group->members[x] && group->members[x]->IsClient()) {
|
||||
c = group->members[x]->CastToClient();
|
||||
raid->SendRaidCreate(c);
|
||||
raid->AddMember(
|
||||
c,
|
||||
raid_free_group_id,
|
||||
new_raid,
|
||||
x == 0,
|
||||
false
|
||||
);
|
||||
raid->SendMakeLeaderPacketTo(raid->leadername, c);
|
||||
raid->SendBulkRaid(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
group->JoinRaidXTarget(raid, initial);
|
||||
group->DisbandGroup(true);
|
||||
} else if (client) {
|
||||
raid->SendRaidCreate(client);
|
||||
raid->AddMember(client, RAID_GROUPLESS, false, false, true);
|
||||
raid->SendMakeLeaderPacketTo(raid->leadername, client);
|
||||
raid->SendBulkRaid(client);
|
||||
if (raid->IsLocked()) {
|
||||
raid->SendRaidLockTo(client);
|
||||
}
|
||||
}
|
||||
|
||||
raid->GroupUpdate(raid_free_group_id);
|
||||
}
|
||||
|
||||
|
||||
41
zone/bot_raid.h
Normal file
41
zone/bot_raid.h
Normal file
@ -0,0 +1,41 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.org)
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; version 2 of the License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef BOT_RAID_H
|
||||
#define BOT_RAID_H
|
||||
|
||||
|
||||
#include "bot_structs.h"
|
||||
#include "mob.h"
|
||||
#include "client.h"
|
||||
#include "pets.h"
|
||||
#include "heal_rotation.h"
|
||||
#include "groups.h"
|
||||
#include "corpse.h"
|
||||
#include "zonedb.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "string_ids.h"
|
||||
#include "../common/misc_functions.h"
|
||||
#include "../common/global_define.h"
|
||||
#include "guild_mgr.h"
|
||||
#include "worldserver.h"
|
||||
#include "raids.h"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#endif // BOT_RAID_H
|
||||
2456
zone/botspellsai.cpp
2456
zone/botspellsai.cpp
File diff suppressed because it is too large
Load Diff
@ -807,16 +807,17 @@ void Client::QueuePacket(const EQApplicationPacket* app, bool ack_req, CLIENT_CO
|
||||
AddPacket(app, ack_req);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// if the program doesnt care about the status or if the status isnt what we requested
|
||||
if (required_state != CLIENT_CONNECTINGALL && client_state != required_state)
|
||||
{
|
||||
// todo: save packets for later use
|
||||
AddPacket(app, ack_req);
|
||||
}
|
||||
else
|
||||
if(eqs)
|
||||
eqs->QueuePacket(app, ack_req);
|
||||
else if (eqs)
|
||||
{
|
||||
eqs->QueuePacket(app, ack_req);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::FastQueuePacket(EQApplicationPacket** app, bool ack_req, CLIENT_CONN_STATUS required_state) {
|
||||
@ -827,7 +828,7 @@ void Client::FastQueuePacket(EQApplicationPacket** app, bool ack_req, CLIENT_CON
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if(eqs)
|
||||
if(eqs)
|
||||
eqs->FastQueuePacket((EQApplicationPacket **)app, ack_req);
|
||||
else if (app && (*app))
|
||||
delete *app;
|
||||
@ -1330,6 +1331,7 @@ void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num
|
||||
|
||||
cm->chan_num = chan_num;
|
||||
strcpy(&cm->message[0], buffer);
|
||||
|
||||
QueuePacket(&app);
|
||||
|
||||
bool senderCanTrainSelf = RuleB(Client, SelfLanguageLearning);
|
||||
|
||||
@ -2047,6 +2047,7 @@ private:
|
||||
bool CanTradeFVNoDropItem();
|
||||
void SendMobPositions();
|
||||
void PlayerTradeEventLog(Trade *t, Trade *t2);
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -51,7 +51,7 @@ int32 Client::GetMaxStat() const
|
||||
else {
|
||||
base = 330;
|
||||
}
|
||||
return (base);
|
||||
return base;
|
||||
}
|
||||
|
||||
int32 Client::GetMaxResist() const
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -194,6 +194,9 @@ bool Client::Process() {
|
||||
}
|
||||
|
||||
if (camp_timer.Check()) {
|
||||
Raid* raid = entity_list.GetRaidByClient(this);
|
||||
if (raid)
|
||||
raid->RemoveMember(this->GetName());
|
||||
LeaveGroup();
|
||||
Save();
|
||||
if (GetMerc())
|
||||
|
||||
@ -204,7 +204,7 @@ void command_pvp(Client *c, const Seperator *sep);
|
||||
void command_qglobal(Client *c, const Seperator *sep);
|
||||
void command_questerrors(Client *c, const Seperator *sep);
|
||||
void command_race(Client *c, const Seperator *sep);
|
||||
void command_raidloot(Client *c, const Seperator *sep);
|
||||
void command_raidloot(Client* c, const Seperator* sep);
|
||||
void command_randomfeatures(Client *c, const Seperator *sep);
|
||||
void command_refreshgroup(Client *c, const Seperator *sep);
|
||||
void command_reload(Client *c, const Seperator *sep);
|
||||
|
||||
@ -389,9 +389,7 @@ void EntityList::GroupProcess()
|
||||
for (auto &group : group_list)
|
||||
group->Process();
|
||||
|
||||
#if EQDEBUG >= 5
|
||||
CheckGroupList (__FILE__, __LINE__);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void EntityList::QueueToGroupsForNPCHealthAA(Mob *sender, const EQApplicationPacket *app)
|
||||
@ -609,9 +607,7 @@ void EntityList::AddGroup(Group *group)
|
||||
}
|
||||
|
||||
AddGroup(group, gid);
|
||||
#if EQDEBUG >= 5
|
||||
CheckGroupList (__FILE__, __LINE__);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void EntityList::AddGroup(Group *group, uint32 gid)
|
||||
@ -2115,9 +2111,6 @@ Group *EntityList::GetGroupByMob(Mob *mob)
|
||||
return *iterator;
|
||||
++iterator;
|
||||
}
|
||||
#if EQDEBUG >= 5
|
||||
CheckGroupList (__FILE__, __LINE__);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -2132,9 +2125,6 @@ Group *EntityList::GetGroupByLeaderName(const char *leader)
|
||||
return *iterator;
|
||||
++iterator;
|
||||
}
|
||||
#if EQDEBUG >= 5
|
||||
CheckGroupList (__FILE__, __LINE__);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -2149,9 +2139,6 @@ Group *EntityList::GetGroupByID(uint32 group_id)
|
||||
return *iterator;
|
||||
++iterator;
|
||||
}
|
||||
#if EQDEBUG >= 5
|
||||
CheckGroupList (__FILE__, __LINE__);
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -2170,13 +2157,12 @@ Group *EntityList::GetGroupByClient(Client *client)
|
||||
iterator = group_list.begin();
|
||||
|
||||
while (iterator != group_list.end()) {
|
||||
if ((*iterator)->IsGroupMember(client->CastToMob()))
|
||||
if ((*iterator)->IsGroupMember(client->CastToMob())) {
|
||||
return *iterator;
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
#if EQDEBUG >= 5
|
||||
CheckGroupList (__FILE__, __LINE__);
|
||||
#endif
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -2187,9 +2173,9 @@ Raid *EntityList::GetRaidByLeaderName(const char *leader)
|
||||
iterator = raid_list.begin();
|
||||
|
||||
while (iterator != raid_list.end()) {
|
||||
if ((*iterator)->GetLeader())
|
||||
if(strcmp((*iterator)->GetLeader()->GetName(), leader) == 0)
|
||||
return *iterator;
|
||||
if ((*iterator)->GetLeader() && strcmp((*iterator)->GetLeader()->GetName(), leader) == 0) {
|
||||
return *iterator;
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
return nullptr;
|
||||
@ -2209,22 +2195,20 @@ Raid *EntityList::GetRaidByID(uint32 id)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Raid *EntityList::GetRaidByClient(Client* client)
|
||||
Raid* EntityList::GetRaidByClient(Client* client)
|
||||
{
|
||||
if (client->p_raid_instance) {
|
||||
return client->p_raid_instance;
|
||||
}
|
||||
|
||||
std::list<Raid *>::iterator iterator;
|
||||
std::list<Raid*>::iterator iterator;
|
||||
iterator = raid_list.begin();
|
||||
|
||||
while (iterator != raid_list.end()) {
|
||||
for (auto &member : (*iterator)->members) {
|
||||
if (member.member) {
|
||||
if (member.member == client) {
|
||||
client->p_raid_instance = *iterator;
|
||||
return *iterator;
|
||||
}
|
||||
for (const auto& member : (*iterator)->members) {
|
||||
if (member.member && member.member == client) {
|
||||
client->p_raid_instance = *iterator;
|
||||
return *iterator;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2233,6 +2217,49 @@ Raid *EntityList::GetRaidByClient(Client* client)
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
Raid* EntityList::GetRaidByBotName(const char* name)
|
||||
{
|
||||
std::list<RaidMember> rm;
|
||||
auto GetMembersWithNames = [&rm](Raid const* r) -> std::list<RaidMember> {
|
||||
for (const auto& m : r->members) {
|
||||
if (strlen(m.membername) > 0)
|
||||
rm.push_back(m);
|
||||
}
|
||||
return rm;
|
||||
};
|
||||
|
||||
for (const auto& r : raid_list) {
|
||||
for (const auto& m : GetMembersWithNames(r)) {
|
||||
if (strcmp(m.membername, name) == 0) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Raid* EntityList::GetRaidByBot(const Bot* bot)
|
||||
{
|
||||
std::list<RaidMember> rm;
|
||||
auto GetMembersWhoAreBots = [&rm](Raid* r) -> std::list<RaidMember> {
|
||||
for (auto const& m : r->members) {
|
||||
if (m.IsBot) {
|
||||
rm.push_back(m);
|
||||
}
|
||||
}
|
||||
return rm;
|
||||
};
|
||||
|
||||
for (const auto& r : raid_list) {
|
||||
for (const auto& m : GetMembersWhoAreBots(r)) {
|
||||
if (m.member->CastToBot() == bot) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
Raid *EntityList::GetRaidByMob(Mob *mob)
|
||||
{
|
||||
@ -2658,6 +2685,12 @@ void EntityList::RemoveAllNPCs()
|
||||
npc_limit_list.clear();
|
||||
}
|
||||
|
||||
void EntityList::RemoveAllBots()
|
||||
{
|
||||
// doesn't clear the data
|
||||
bot_list.clear();
|
||||
}
|
||||
|
||||
void EntityList::RemoveAllMercs()
|
||||
{
|
||||
// doesn't clear the data
|
||||
@ -2671,9 +2704,6 @@ void EntityList::RemoveAllGroups()
|
||||
group_list.pop_front();
|
||||
safe_delete(group);
|
||||
}
|
||||
#if EQDEBUG >= 5
|
||||
CheckGroupList (__FILE__, __LINE__);
|
||||
#endif
|
||||
}
|
||||
|
||||
void EntityList::RemoveAllRaids()
|
||||
@ -3081,6 +3111,8 @@ void EntityList::Clear()
|
||||
{
|
||||
RemoveAllClients();
|
||||
entity_list.RemoveAllTraps(); //we can have child npcs so we go first
|
||||
entity_list.RemoveAllMercs();
|
||||
entity_list.RemoveAllBots();
|
||||
entity_list.RemoveAllNPCs();
|
||||
entity_list.RemoveAllMobs();
|
||||
entity_list.RemoveAllCorpses();
|
||||
|
||||
@ -192,7 +192,8 @@ public:
|
||||
Client* GetRandomClient(const glm::vec3& location = glm::vec3(0.f), float distance = 0, Client* exclude_client = nullptr);
|
||||
NPC* GetRandomNPC(const glm::vec3& location = glm::vec3(0.f), float distance = 0, NPC* exclude_npc = nullptr);
|
||||
Mob* GetRandomMob(const glm::vec3& location = glm::vec3(0.f), float distance = 0, Mob* exclude_mob = nullptr);
|
||||
Group *GetGroupByMob(Mob* mob);
|
||||
Group* GetGroupByMob(Mob* mob);
|
||||
Group* GetGroupByBot(Bot* bot);
|
||||
bool IsInSameGroupOrRaidGroup(Client *client1, Client *client2);
|
||||
Group *GetGroupByClient(Client* client);
|
||||
Group *GetGroupByID(uint32 id);
|
||||
@ -201,6 +202,8 @@ public:
|
||||
Raid *GetRaidByClient(Client* client);
|
||||
Raid *GetRaidByID(uint32 id);
|
||||
Raid *GetRaidByLeaderName(const char *leader);
|
||||
Raid* GetRaidByBotName(const char* name);
|
||||
Raid* GetRaidByBot(const Bot* bot);
|
||||
|
||||
Corpse *GetCorpseByOwner(Client* client);
|
||||
Corpse *GetCorpseByOwnerWithinRange(Client* client, Mob* center, int range);
|
||||
@ -304,6 +307,7 @@ public:
|
||||
void RemoveAllMobs();
|
||||
void RemoveAllClients();
|
||||
void RemoveAllNPCs();
|
||||
void RemoveAllBots();
|
||||
void RemoveAllMercs();
|
||||
void RemoveAllGroups();
|
||||
void RemoveAllCorpses();
|
||||
@ -619,7 +623,7 @@ private:
|
||||
bool RemoveBot(uint16 entityID);
|
||||
Mob* GetMobByBotID(uint32 botID);
|
||||
Bot* GetBotByBotID(uint32 botID);
|
||||
Bot* GetBotByBotName(std::string botName);
|
||||
Bot* GetBotByBotName(std::string_view botName);
|
||||
Client* GetBotOwnerByBotEntityID(uint32 entity_id);
|
||||
Client* GetBotOwnerByBotID(const uint32 bot_id);
|
||||
std::list<Bot*> GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID);
|
||||
|
||||
@ -1200,7 +1200,7 @@ void Raid::SplitExp(const uint64 exp, Mob* other) {
|
||||
}
|
||||
|
||||
for (const auto& m : members) {
|
||||
if (m.member) {
|
||||
if (m.member && !m.IsBot) {
|
||||
const int32 diff = m.member->GetLevel() - highest_level;
|
||||
int32 max_diff = -(m.member->GetLevel() * 15 / 10 - m.member->GetLevel());
|
||||
|
||||
|
||||
@ -67,4 +67,3 @@ void command_raidloot(Client *c, const Seperator *sep)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
but WITHOUT ANY WARRANTY except by those people which sell it, which
|
||||
are required to give you total support for your newly bought product;
|
||||
without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
@ -26,6 +26,7 @@
|
||||
#include "worldserver.h"
|
||||
#include "string_ids.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "../common/repositories/group_id_repository.h"
|
||||
|
||||
extern EntityList entity_list;
|
||||
extern WorldServer worldserver;
|
||||
@ -246,7 +247,7 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte
|
||||
uint32 i = 0;
|
||||
for (i = 0; i < MAX_GROUP_MEMBERS; ++i)
|
||||
{
|
||||
if(!strcasecmp(membername[i], NewMemberName))
|
||||
if (!strcasecmp(membername[i], NewMemberName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -495,7 +496,7 @@ void Group::SendEndurancePacketFrom(Mob* member)
|
||||
|
||||
//updates a group member's client pointer when they zone in
|
||||
//if the group was in the zone already
|
||||
bool Group::UpdatePlayer(Mob* update){
|
||||
bool Group::UpdatePlayer(Mob* update) {
|
||||
|
||||
if (!update)
|
||||
return false;
|
||||
@ -1138,29 +1139,30 @@ void Group::TeleportGroup(Mob* sender, uint32 zoneID, uint16 instance_id, float
|
||||
}
|
||||
|
||||
bool Group::LearnMembers() {
|
||||
std::string query = StringFormat("SELECT name FROM group_id WHERE groupid = %lu", (unsigned long)GetID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
if (results.RowCount() == 0) {
|
||||
auto rows = GroupIdRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"groupid = {}",
|
||||
GetID()
|
||||
)
|
||||
);
|
||||
|
||||
if (rows.empty()) {
|
||||
LogError(
|
||||
"Error getting group members for group [{}]: [{}]",
|
||||
(unsigned long) GetID(),
|
||||
results.ErrorMessage().c_str()
|
||||
"Error getting group members for group [{}]",
|
||||
GetID()
|
||||
);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int memberIndex = 0;
|
||||
for(auto row = results.begin(); row != results.end(); ++row) {
|
||||
if(!row[0])
|
||||
for (const auto& member : rows) {
|
||||
if (member.name.empty()) {
|
||||
continue;
|
||||
|
||||
}
|
||||
members[memberIndex] = nullptr;
|
||||
strn0cpy(membername[memberIndex], row[0], 64);
|
||||
|
||||
strn0cpy(membername[memberIndex], member.name.c_str(), 64);
|
||||
memberIndex++;
|
||||
}
|
||||
|
||||
@ -1174,36 +1176,23 @@ void Group::VerifyGroup() {
|
||||
Only called every once in a while (on member re-join for now).
|
||||
*/
|
||||
|
||||
uint32 i;
|
||||
for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
|
||||
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) {
|
||||
if (membername[i][0] == '\0') {
|
||||
#if EQDEBUG >= 7
|
||||
LogDebug("Group [{}]: Verify [{}]: Empty.\n", (unsigned long)GetID(), i);
|
||||
#endif
|
||||
members[i] = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
Mob *them = entity_list.GetMob(membername[i]);
|
||||
if(them == nullptr && members[i] != nullptr) { //they aren't in zone
|
||||
#if EQDEBUG >= 6
|
||||
LogDebug("Member of group [{}] named [{}] has disappeared!!", (unsigned long)GetID(), membername[i]);
|
||||
#endif
|
||||
membername[i][0] = '\0';
|
||||
members[i] = nullptr;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(them != nullptr && members[i] != them) { //our pointer is out of date... not so good.
|
||||
#if EQDEBUG >= 5
|
||||
LogDebug("Member of group [{}] named [{}] had an out of date pointer!!", (unsigned long)GetID(), membername[i]);
|
||||
#endif
|
||||
members[i] = them;
|
||||
continue;
|
||||
}
|
||||
#if EQDEBUG >= 8
|
||||
LogDebug("Member of group [{}] named [{}] is valid", (unsigned long)GetID(), membername[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -2497,4 +2486,4 @@ bool Group::IsLeader(const char* name) {
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -85,6 +85,7 @@ volatile bool RunLoops = true;
|
||||
#endif
|
||||
|
||||
extern volatile bool is_zone_loaded;
|
||||
extern bool Critical = false;
|
||||
|
||||
#include "zone_event_scheduler.h"
|
||||
#include "../common/file.h"
|
||||
|
||||
@ -3999,7 +3999,7 @@ void Mob::QuestJournalledSay(Client *QuestInitiator, const char *str, Journal::O
|
||||
|
||||
const char *Mob::GetCleanName()
|
||||
{
|
||||
if (!strlen(clean_name)) {
|
||||
if (!strlen(clean_name)) {
|
||||
CleanMobName(GetName(), clean_name);
|
||||
}
|
||||
|
||||
|
||||
@ -1833,6 +1833,8 @@ private:
|
||||
EQ::InventoryProfile m_inv;
|
||||
std::shared_ptr<HealRotation> m_target_of_heal_rotation;
|
||||
bool m_manual_follow;
|
||||
|
||||
void DoSpellInterrupt(uint16 spell_id, int32 mana_cost, int my_curmana);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@ -369,7 +369,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates
|
||||
return false;
|
||||
}
|
||||
|
||||
bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) {
|
||||
bool NPC::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) {
|
||||
LogAI("spellid [{}] tar [{}] mana [{}] Name [{}]", AIspells[i].spellid, tar->GetName(), mana_cost, spells[AIspells[i].spellid].name);
|
||||
casting_spell_AIindex = i;
|
||||
|
||||
|
||||
@ -589,7 +589,7 @@ protected:
|
||||
std::vector<AISpells_Struct> AIspells;
|
||||
bool HasAISpell;
|
||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates = false);
|
||||
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
virtual bool AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
AISpellsVar_Struct AISpellVar;
|
||||
int64 GetFocusEffect(focusType type, uint16 spell_id, Mob *caster = nullptr, bool from_buff_tic = false) override;
|
||||
uint16 innate_proc_spell_id;
|
||||
|
||||
528
zone/raids.cpp
528
zone/raids.cpp
@ -26,6 +26,7 @@
|
||||
#include "mob.h"
|
||||
#include "raids.h"
|
||||
#include "string_ids.h"
|
||||
#include "bot.h"
|
||||
|
||||
#include "worldserver.h"
|
||||
|
||||
@ -94,41 +95,57 @@ bool Raid::Process()
|
||||
}
|
||||
|
||||
void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bool looter){
|
||||
if(!c)
|
||||
if (!c) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string query = StringFormat("INSERT INTO raid_members SET raidid = %lu, charid = %lu, "
|
||||
"groupid = %lu, _class = %d, level = %d, name = '%s', "
|
||||
"isgroupleader = %d, israidleader = %d, islooter = %d",
|
||||
(unsigned long)GetID(), (unsigned long)c->CharacterID(),
|
||||
(unsigned long)group, c->GetClass(), c->GetLevel(),
|
||||
c->GetName(), groupleader, rleader, looter);
|
||||
auto results = database.QueryDatabase(query);
|
||||
const auto query = fmt::format(
|
||||
"REPLACE INTO raid_members SET raidid = {}, charid = {}, bot_id = 0, "
|
||||
"groupid = {}, _class = {}, level = {}, name = '{}', "
|
||||
"isgroupleader = {}, israidleader = {}, islooter = {}",
|
||||
GetID(),
|
||||
c->CharacterID(),
|
||||
group,
|
||||
c->GetClass(),
|
||||
c->GetLevel(),
|
||||
c->GetName(),
|
||||
groupleader,
|
||||
rleader,
|
||||
looter
|
||||
);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
if(!results.Success()) {
|
||||
LogError("Error inserting into raid members: [{}]", results.ErrorMessage().c_str());
|
||||
}
|
||||
|
||||
LearnMembers();
|
||||
VerifyRaid();
|
||||
|
||||
if (rleader) {
|
||||
database.SetRaidGroupLeaderInfo(group, GetID());
|
||||
UpdateRaidAAs();
|
||||
} else if (rleader) {
|
||||
database.SetRaidGroupLeaderInfo(RAID_GROUPLESS, GetID());
|
||||
UpdateRaidAAs();
|
||||
}
|
||||
|
||||
if (group != RAID_GROUPLESS && groupleader) {
|
||||
database.SetRaidGroupLeaderInfo(group, GetID());
|
||||
UpdateGroupAAs(group);
|
||||
}
|
||||
if(group < 12)
|
||||
|
||||
if (group < MAX_RAID_GROUPS) {
|
||||
GroupUpdate(group);
|
||||
else // get raid AAs, GroupUpdate will handles it otherwise
|
||||
} else { // get raid AAs, GroupUpdate will handles it otherwise
|
||||
SendGroupLeadershipAA(c, RAID_GROUPLESS);
|
||||
}
|
||||
|
||||
SendRaidAddAll(c->GetName());
|
||||
|
||||
c->SetRaidGrouped(true);
|
||||
SendRaidMOTD(c);
|
||||
|
||||
// xtarget shit ..........
|
||||
if (group == RAID_GROUPLESS) {
|
||||
if (rleader) {
|
||||
GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr());
|
||||
@ -148,54 +165,115 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo
|
||||
}
|
||||
}
|
||||
|
||||
Raid *raid_update = nullptr;
|
||||
raid_update = c->GetRaid();
|
||||
auto* raid_update = c->GetRaid();
|
||||
if (raid_update) {
|
||||
raid_update->SendHPManaEndPacketsTo(c);
|
||||
raid_update->SendHPManaEndPacketsFrom(c);
|
||||
}
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct));
|
||||
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
strn0cpy(rga->playername, c->GetName(), 64);
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
auto* rga = (ServerRaidGeneralAction_Struct*) pack->pBuffer;
|
||||
strn0cpy(rga->playername, c->GetName(), sizeof(rga->playername));
|
||||
rga->rid = GetID();
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
rga->instance_id = zone->GetInstanceID();
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Raid::AddBot(Bot* b, uint32 group, bool rleader, bool groupleader, bool looter) {
|
||||
if (!b) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto query = fmt::format(
|
||||
"REPLACE INTO raid_members SET raidid = {}, "
|
||||
"charid = 0, bot_id = {}, groupid = {}, _class = {}, level = {}, name = '{}', "
|
||||
"isgroupleader = {}, israidleader = {}, islooter = {}",
|
||||
GetID(),
|
||||
b->GetBotID(),
|
||||
group,
|
||||
b->GetClass(),
|
||||
b->GetLevel(),
|
||||
b->GetName(),
|
||||
groupleader,
|
||||
rleader,
|
||||
looter
|
||||
);
|
||||
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
LearnMembers();
|
||||
VerifyRaid();
|
||||
|
||||
if (group < MAX_RAID_GROUPS) {
|
||||
GroupUpdate(group);
|
||||
} else { // get raid AAs, GroupUpdate will handle it otherwise
|
||||
SendGroupLeadershipAA(b->GetOwner()->CastToClient(), RAID_GROUPLESS);
|
||||
}
|
||||
|
||||
SendRaidAddAll(b->GetName());
|
||||
|
||||
b->SetRaidGrouped(true);
|
||||
b->p_raid_instance = this;
|
||||
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct));
|
||||
auto* rga = (ServerRaidGeneralAction_Struct*) pack->pBuffer;
|
||||
strn0cpy(rga->playername, b->GetName(), sizeof(rga->playername));
|
||||
rga->rid = GetID();
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
rga->instance_id = zone->GetInstanceID();
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
|
||||
void Raid::RemoveMember(const char *characterName)
|
||||
{
|
||||
std::string query = StringFormat("DELETE FROM raid_members where name='%s'", characterName);
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
Client *client = entity_list.GetClientByName(characterName);
|
||||
auto* b = entity_list.GetBotByBotName(characterName);
|
||||
auto* c = entity_list.GetClientByName(characterName);
|
||||
|
||||
if (RuleB(Bots, Enabled) && b) {
|
||||
b = entity_list.GetBotByBotName(characterName);
|
||||
b->SetFollowID(b->GetOwner()->CastToClient()->GetID());
|
||||
b->SetTarget(nullptr);
|
||||
b->SetRaidGrouped(false);
|
||||
}
|
||||
|
||||
disbandCheck = true;
|
||||
SendRaidRemoveAll(characterName);
|
||||
SendRaidDisband(client);
|
||||
SendRaidDisband(c);
|
||||
LearnMembers();
|
||||
VerifyRaid();
|
||||
|
||||
if(client) {
|
||||
client->SetRaidGrouped(false);
|
||||
client->LeaveRaidXTargets(this);
|
||||
client->p_raid_instance = nullptr;
|
||||
if (c) {
|
||||
c->SetRaidGrouped(false);
|
||||
c->LeaveRaidXTargets(this);
|
||||
c->p_raid_instance = nullptr;
|
||||
}
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct));
|
||||
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
auto* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
rga->instance_id = zone->GetInstanceID();
|
||||
strn0cpy(rga->playername, characterName, 64);
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
strn0cpy(rga->playername, characterName, sizeof(rga->playername));
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void Raid::DisbandRaid()
|
||||
{
|
||||
std::string query = StringFormat("DELETE FROM raid_members WHERE raidid = %lu", (unsigned long)GetID());
|
||||
const auto query = fmt::format(
|
||||
"DELETE FROM raid_members WHERE raidid = {}",
|
||||
GetID()
|
||||
);
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
LearnMembers();
|
||||
@ -203,11 +281,11 @@ void Raid::DisbandRaid()
|
||||
SendRaidDisbandAll();
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidDisband, sizeof(ServerRaidGeneralAction_Struct));
|
||||
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
strn0cpy(rga->playername, " ", 64);
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
auto* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
rga->instance_id = zone->GetInstanceID();
|
||||
strn0cpy(rga->playername, " ", sizeof(rga->playername));
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
|
||||
@ -216,19 +294,23 @@ void Raid::DisbandRaid()
|
||||
|
||||
void Raid::MoveMember(const char *name, uint32 newGroup)
|
||||
{
|
||||
std::string query = StringFormat("UPDATE raid_members SET groupid = %lu WHERE name = '%s'",
|
||||
(unsigned long)newGroup, name);
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
const auto query = fmt::format(
|
||||
"UPDATE raid_members SET groupid = {} WHERE name = '{}'",
|
||||
newGroup,
|
||||
name
|
||||
);
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
LearnMembers();
|
||||
VerifyRaid();
|
||||
SendRaidMoveAll(name);
|
||||
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct));
|
||||
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
strn0cpy(rga->playername, name, 64);
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
auto* rga = (ServerRaidGeneralAction_Struct*) pack->pBuffer;
|
||||
strn0cpy(rga->playername, name, sizeof(rga->playername));
|
||||
rga->rid = GetID();
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
rga->instance_id = zone->GetInstanceID();
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
@ -236,18 +318,21 @@ void Raid::MoveMember(const char *name, uint32 newGroup)
|
||||
|
||||
void Raid::SetGroupLeader(const char *who, bool glFlag)
|
||||
{
|
||||
std::string query = StringFormat("UPDATE raid_members SET isgroupleader = %lu WHERE name = '%s'",
|
||||
(unsigned long)glFlag, who);
|
||||
auto results = database.QueryDatabase(query);
|
||||
const auto query = fmt::format(
|
||||
"UPDATE raid_members SET isgroupleader = {} WHERE name = '{}'",
|
||||
glFlag,
|
||||
who
|
||||
);
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
LearnMembers();
|
||||
VerifyRaid();
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidGroupLeader, sizeof(ServerRaidGeneralAction_Struct));
|
||||
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
strn0cpy(rga->playername, who, 64);
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
auto* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
|
||||
strn0cpy(rga->playername, who, sizeof(rga->playername));
|
||||
rga->rid = GetID();
|
||||
rga->zoneid = zone->GetZoneID();
|
||||
rga->instance_id = zone->GetInstanceID();
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
@ -406,13 +491,13 @@ uint32 Raid::GetFreeGroup()
|
||||
return x;
|
||||
}
|
||||
//if we get to here then there were no free groups so we added the group as free floating members.
|
||||
return 0xFFFFFFFF;
|
||||
return RAID_GROUPLESS;
|
||||
}
|
||||
|
||||
uint8 Raid::GroupCount(uint32 gid)
|
||||
{
|
||||
uint8 count = 0;
|
||||
if(gid < 12)
|
||||
if(gid < MAX_RAID_GROUPS)
|
||||
{
|
||||
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
|
||||
{
|
||||
@ -453,7 +538,7 @@ uint32 Raid::GetGroup(const char *name)
|
||||
if(strcmp(members[x].membername, name) == 0)
|
||||
return members[x].GroupNumber;
|
||||
}
|
||||
return 0xFFFFFFFF;
|
||||
return RAID_GROUPLESS;
|
||||
}
|
||||
|
||||
uint32 Raid::GetGroup(Client *c)
|
||||
@ -463,7 +548,7 @@ uint32 Raid::GetGroup(Client *c)
|
||||
if(members[x].member == c)
|
||||
return members[x].GroupNumber;
|
||||
}
|
||||
return 0xFFFFFFFF;
|
||||
return RAID_GROUPLESS;
|
||||
}
|
||||
|
||||
void Raid::RaidSay(const char *msg, Client *c, uint8 language, uint8 lang_skill)
|
||||
@ -474,7 +559,7 @@ void Raid::RaidSay(const char *msg, Client *c, uint8 language, uint8 lang_skill)
|
||||
auto pack = new ServerPacket(ServerOP_RaidSay, sizeof(ServerRaidMessage_Struct) + strlen(msg) + 1);
|
||||
ServerRaidMessage_Struct *rga = (ServerRaidMessage_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
rga->gid = 0xFFFFFFFF;
|
||||
rga->gid = RAID_GROUPLESS;
|
||||
rga->language = language;
|
||||
rga->lang_skill = lang_skill;
|
||||
strn0cpy(rga->from, c->GetName(), 64);
|
||||
@ -992,19 +1077,21 @@ void Raid::SendRaidAdd(const char *who, Client *to)
|
||||
if(!to)
|
||||
return;
|
||||
|
||||
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
|
||||
std::vector<RaidMember> rm = GetMembers();
|
||||
|
||||
for (const auto& m : rm)
|
||||
{
|
||||
if(strcmp(members[x].membername, who) == 0)
|
||||
if (strcmp(m.membername, who) == 0)
|
||||
{
|
||||
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidAddMember_Struct));
|
||||
RaidAddMember_Struct *ram = (RaidAddMember_Struct*)outapp->pBuffer;
|
||||
RaidAddMember_Struct* ram = (RaidAddMember_Struct*)outapp->pBuffer;
|
||||
ram->raidGen.action = raidAdd;
|
||||
ram->raidGen.parameter = members[x].GroupNumber;
|
||||
strn0cpy(ram->raidGen.leader_name, members[x].membername, 64);
|
||||
strn0cpy(ram->raidGen.player_name, members[x].membername, 64);
|
||||
ram->_class = members[x]._class;
|
||||
ram->level = members[x].level;
|
||||
ram->isGroupLeader = members[x].IsGroupLeader;
|
||||
ram->raidGen.parameter = m.GroupNumber;
|
||||
strn0cpy(ram->raidGen.leader_name, m.membername, 64);
|
||||
strn0cpy(ram->raidGen.player_name, m.membername, 64);
|
||||
ram->_class = m._class;
|
||||
ram->level = m.level;
|
||||
ram->isGroupLeader = m.IsGroupLeader;
|
||||
to->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
@ -1014,19 +1101,21 @@ void Raid::SendRaidAdd(const char *who, Client *to)
|
||||
|
||||
void Raid::SendRaidAddAll(const char *who)
|
||||
{
|
||||
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
|
||||
{
|
||||
if(strcmp(members[x].membername, who) == 0)
|
||||
std::vector<RaidMember> rm = GetMembers();
|
||||
|
||||
for (const auto& m : rm) {
|
||||
if (strcmp(m.membername, who) == 0)
|
||||
{
|
||||
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidAddMember_Struct));
|
||||
RaidAddMember_Struct *ram = (RaidAddMember_Struct*)outapp->pBuffer;
|
||||
RaidAddMember_Struct* ram = (RaidAddMember_Struct*)outapp->pBuffer;
|
||||
ram->raidGen.action = raidAdd;
|
||||
ram->raidGen.parameter = members[x].GroupNumber;
|
||||
strcpy(ram->raidGen.leader_name, members[x].membername);
|
||||
strcpy(ram->raidGen.player_name, members[x].membername);
|
||||
ram->_class = members[x]._class;
|
||||
ram->level = members[x].level;
|
||||
ram->isGroupLeader = members[x].IsGroupLeader;
|
||||
ram->raidGen.parameter = m.GroupNumber;
|
||||
strcpy(ram->raidGen.leader_name, m.membername);
|
||||
strcpy(ram->raidGen.player_name, m.membername);
|
||||
ram->isGroupLeader = m.IsGroupLeader;
|
||||
ram->_class = m._class;
|
||||
ram->level = m.level;
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
@ -1129,12 +1218,13 @@ void Raid::SendRaidMoveAll(const char* who)
|
||||
SendRaidRemoveAll(who);
|
||||
if(c)
|
||||
SendRaidCreate(c);
|
||||
SendMakeLeaderPacket(leadername);
|
||||
SendRaidAddAll(who);
|
||||
if(c){
|
||||
SendBulkRaid(c);
|
||||
if(IsLocked()) { SendRaidLockTo(c); }
|
||||
}
|
||||
SendRaidAddAll(who);
|
||||
SendMakeLeaderPacket(leadername);
|
||||
|
||||
}
|
||||
|
||||
void Raid::SendBulkRaid(Client *to)
|
||||
@ -1144,7 +1234,7 @@ void Raid::SendBulkRaid(Client *to)
|
||||
|
||||
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
|
||||
{
|
||||
if(strlen(members[x].membername) > 0 && (strcmp(members[x].membername, to->GetName()) != 0)) //don't send ourself
|
||||
if (strlen(members[x].membername) > 0 && (strcmp(members[x].membername, to->GetName()) != 0)) //don't send ourself
|
||||
{
|
||||
SendRaidAdd(members[x].membername, to);
|
||||
}
|
||||
@ -1155,7 +1245,7 @@ void Raid::QueuePacket(const EQApplicationPacket *app, bool ack_req)
|
||||
{
|
||||
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
|
||||
{
|
||||
if(members[x].member)
|
||||
if(members[x].member && !members[x].IsBot)
|
||||
{
|
||||
members[x].member->QueuePacket(app, ack_req);
|
||||
}
|
||||
@ -1164,6 +1254,11 @@ void Raid::QueuePacket(const EQApplicationPacket *app, bool ack_req)
|
||||
|
||||
void Raid::SendMakeLeaderPacket(const char *who) //30
|
||||
{
|
||||
|
||||
if (RuleB(Bots, Enabled) && entity_list.GetBotByBotName(who) && members[GetPlayerIndex(who)].IsBot) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct));
|
||||
RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer;
|
||||
rg->action = raidMakeLeader;
|
||||
@ -1176,8 +1271,13 @@ void Raid::SendMakeLeaderPacket(const char *who) //30
|
||||
|
||||
void Raid::SendMakeLeaderPacketTo(const char *who, Client *to)
|
||||
{
|
||||
if(!to)
|
||||
if (!to) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (RuleB(Bots, Enabled) && members[GetPlayerIndex(who)].IsBot) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct));
|
||||
RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer;
|
||||
@ -1206,8 +1306,13 @@ void Raid::SendMakeGroupLeaderPacketTo(const char *who, Client *to)
|
||||
|
||||
void Raid::SendGroupUpdate(Client *to)
|
||||
{
|
||||
if(!to)
|
||||
if (!to) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (RuleB(Bots, Enabled) && members[GetPlayerIndex(to)].IsBot) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate2_Struct));
|
||||
GroupUpdate2_Struct* gu = (GroupUpdate2_Struct*)outapp->pBuffer;
|
||||
@ -1249,13 +1354,14 @@ void Raid::SendGroupUpdate(Client *to)
|
||||
|
||||
void Raid::GroupUpdate(uint32 gid, bool initial)
|
||||
{
|
||||
if(gid > 11) //ungrouped member doesn't need grouping.
|
||||
if(gid > 11) {//ungrouped member doesn't need grouping.
|
||||
return;
|
||||
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
|
||||
}
|
||||
for (int x = 0; x < MAX_RAID_MEMBERS; x++)
|
||||
{
|
||||
if(strlen(members[x].membername) > 0){
|
||||
if(members[x].GroupNumber == gid){
|
||||
if(members[x].member) {
|
||||
if (members[x].member) {
|
||||
SendGroupUpdate(members[x].member);
|
||||
SendGroupLeadershipAA(members[x].member, gid);
|
||||
}
|
||||
@ -1362,6 +1468,10 @@ void Raid::SendRaidMOTD(Client *c)
|
||||
if (!c || motd.empty())
|
||||
return;
|
||||
|
||||
if (members[GetPlayerIndex(c)].IsBot) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size = motd.size() + 1;
|
||||
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidMOTD_Struct) + size);
|
||||
RaidMOTD_Struct *rmotd = (RaidMOTD_Struct *)outapp->pBuffer;
|
||||
@ -1397,6 +1507,10 @@ void Raid::SendRaidMOTDToWorld()
|
||||
|
||||
void Raid::SendGroupLeadershipAA(Client *c, uint32 gid)
|
||||
{
|
||||
if (RuleB(Bots, Enabled) && members[GetPlayerIndex(c)].IsBot) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct));
|
||||
RaidLeadershipUpdate_Struct *rlaa = (RaidLeadershipUpdate_Struct *)outapp->pBuffer;
|
||||
rlaa->action = raidSetLeaderAbilities;
|
||||
@ -1411,18 +1525,23 @@ void Raid::SendGroupLeadershipAA(Client *c, uint32 gid)
|
||||
|
||||
void Raid::SendGroupLeadershipAA(uint32 gid)
|
||||
{
|
||||
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++)
|
||||
if (members[i].member && members[i].GroupNumber == gid)
|
||||
SendGroupLeadershipAA(members[i].member, gid);
|
||||
for (const auto& m : members) {
|
||||
if (m.member && m.GroupNumber == gid && !m.IsBot) {
|
||||
SendGroupLeadershipAA(m.member, gid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Raid::SendAllRaidLeadershipAA()
|
||||
{
|
||||
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++)
|
||||
if (members[i].member)
|
||||
SendGroupLeadershipAA(members[i].member, members[i].GroupNumber);
|
||||
for (const auto& m : members) {
|
||||
if (m.member && !m.IsBot) {
|
||||
SendGroupLeadershipAA(m.member, m.GroupNumber);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Raid::LockRaid(bool lockFlag)
|
||||
{
|
||||
std::string query = StringFormat("UPDATE raid_details SET locked = %d WHERE raidid = %lu",
|
||||
@ -1486,70 +1605,84 @@ void Raid::SaveRaidMOTD()
|
||||
|
||||
bool Raid::LearnMembers()
|
||||
{
|
||||
memset(members, 0, (sizeof(RaidMember)*MAX_RAID_MEMBERS));
|
||||
memset(members, 0, (sizeof(RaidMember) * MAX_RAID_MEMBERS));
|
||||
|
||||
std::string query = StringFormat("SELECT name, groupid, _class, level, "
|
||||
"isgroupleader, israidleader, islooter "
|
||||
"FROM raid_members WHERE raidid = %lu",
|
||||
(unsigned long)GetID());
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
const auto query = fmt::format(
|
||||
"SELECT name, groupid, _class, level, "
|
||||
"isgroupleader, israidleader, islooter, bot_id "
|
||||
"FROM raid_members WHERE raidid = {} ORDER BY groupid",
|
||||
GetID()
|
||||
);
|
||||
|
||||
if(results.RowCount() == 0) {
|
||||
LogError("Error getting raid members for raid [{}]: [{}]", (unsigned long)GetID(), results.ErrorMessage().c_str());
|
||||
disbandCheck = true;
|
||||
return false;
|
||||
}
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = 0;
|
||||
for(auto row = results.begin(); row != results.end(); ++row) {
|
||||
if(!row[0])
|
||||
continue;
|
||||
if (!results.RowCount()) {
|
||||
LogError("Error getting raid members for raid [{}]: [{}]", GetID(), results.ErrorMessage());
|
||||
disbandCheck = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
members[index].member = nullptr;
|
||||
strn0cpy(members[index].membername, row[0], 64);
|
||||
uint32 groupNum = Strings::ToUnsignedInt(row[1]);
|
||||
if(groupNum > 11)
|
||||
members[index].GroupNumber = 0xFFFFFFFF;
|
||||
else
|
||||
members[index].GroupNumber = groupNum;
|
||||
int index = 0;
|
||||
for (auto row: results) {
|
||||
if (!row[0]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
members[index]._class = Strings::ToInt(row[2]);
|
||||
members[index].level = Strings::ToInt(row[3]);
|
||||
members[index].IsGroupLeader = Strings::ToInt(row[4]);
|
||||
members[index].IsRaidLeader = Strings::ToInt(row[5]);
|
||||
members[index].IsLooter = Strings::ToInt(row[6]);
|
||||
++index;
|
||||
}
|
||||
members[index].member = nullptr;
|
||||
strn0cpy(members[index].membername, row[0], sizeof(members[index].membername));
|
||||
uint32 group_id = Strings::ToUnsignedInt(row[1]);
|
||||
|
||||
if (group_id > 11) {
|
||||
members[index].GroupNumber = RAID_GROUPLESS;
|
||||
} else {
|
||||
members[index].GroupNumber = group_id;
|
||||
}
|
||||
|
||||
members[index]._class = Strings::ToUnsignedInt(row[2]);
|
||||
members[index].level = Strings::ToUnsignedInt(row[3]);
|
||||
members[index].IsGroupLeader = Strings::ToBool(row[4]);
|
||||
members[index].IsRaidLeader = Strings::ToBool(row[5]);
|
||||
members[index].IsLooter = Strings::ToBool(row[6]);
|
||||
members[index].IsBot = Strings::ToBool(row[7]) > 0;
|
||||
++index;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Raid::VerifyRaid()
|
||||
{
|
||||
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
|
||||
{
|
||||
if(strlen(members[x].membername) == 0){
|
||||
members[x].member = nullptr;
|
||||
}
|
||||
else{
|
||||
Client *c = entity_list.GetClientByName(members[x].membername);
|
||||
if(c){
|
||||
members[x].member = c;
|
||||
}
|
||||
else{
|
||||
members[x].member = nullptr;
|
||||
for (auto& m : members) {
|
||||
if(strlen(m.membername) == 0){
|
||||
m.member = nullptr;
|
||||
} else {
|
||||
auto* c = entity_list.GetClientByName(m.membername);
|
||||
auto* b = entity_list.GetBotByBotName(m.membername);
|
||||
|
||||
if (c) {
|
||||
m.member = c;
|
||||
m.IsBot = false;
|
||||
} else if(RuleB(Bots, Enabled) && b){
|
||||
//Raid requires client* we are forcing it here to be a BOT. Care is needed here as any client function that
|
||||
//does not exist within the Bot Class will likely corrupt memory for the member object. Good practice to test the IsBot
|
||||
//attribute before calling a client function or casting to client.
|
||||
b = entity_list.GetBotByBotName(m.membername);
|
||||
m.member = b->CastToClient();
|
||||
m.IsBot = true; //Used to identify those members who are Bots
|
||||
} else {
|
||||
m.member = nullptr;
|
||||
m.IsBot = false;
|
||||
}
|
||||
}
|
||||
if(members[x].IsRaidLeader){
|
||||
if(strlen(members[x].membername) > 0){
|
||||
SetLeader(members[x].member);
|
||||
strn0cpy(leadername, members[x].membername, 64);
|
||||
}
|
||||
else
|
||||
{
|
||||
//should never happen, but maybe it is?
|
||||
|
||||
if (m.IsRaidLeader) {
|
||||
if (strlen(m.membername) > 0){
|
||||
SetLeader(m.member);
|
||||
strn0cpy(leadername, m.membername, sizeof(leadername));
|
||||
} else {
|
||||
SetLeader(nullptr);
|
||||
}
|
||||
}
|
||||
@ -1577,7 +1710,7 @@ void Raid::MemberZoned(Client *c)
|
||||
}
|
||||
}
|
||||
|
||||
if (gid < 12 && group_mentor[gid].mentoree == c)
|
||||
if (gid < MAX_RAID_GROUPS && group_mentor[gid].mentoree == c)
|
||||
group_mentor[gid].mentoree = nullptr;
|
||||
}
|
||||
|
||||
@ -1592,7 +1725,7 @@ void Raid::SendHPManaEndPacketsTo(Client *client)
|
||||
EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct));
|
||||
|
||||
for(int x = 0; x < MAX_RAID_MEMBERS; x++) {
|
||||
if(members[x].member) {
|
||||
if(members[x].member && !members[x].IsBot) {
|
||||
if((members[x].member != client) && (members[x].GroupNumber == group_id)) {
|
||||
|
||||
members[x].member->CreateHPPacket(&hp_packet);
|
||||
@ -1634,7 +1767,7 @@ void Raid::SendHPManaEndPacketsFrom(Mob *mob)
|
||||
mob->CreateHPPacket(&hpapp);
|
||||
|
||||
for(int x = 0; x < MAX_RAID_MEMBERS; x++) {
|
||||
if(members[x].member) {
|
||||
if(members[x].member && !members[x].IsBot) {
|
||||
if(!mob->IsClient() || ((members[x].member != mob->CastToClient()) && (members[x].GroupNumber == group_id))) {
|
||||
members[x].member->QueuePacket(&hpapp, false);
|
||||
if (members[x].member->ClientVersion() >= EQ::versions::ClientVersion::SoD) {
|
||||
@ -1667,7 +1800,7 @@ void Raid::SendManaPacketFrom(Mob *mob)
|
||||
EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct));
|
||||
|
||||
for (int x = 0; x < MAX_RAID_MEMBERS; x++) {
|
||||
if (members[x].member) {
|
||||
if (members[x].member && !members[x].IsBot) {
|
||||
if (!mob->IsClient() || ((members[x].member != mob->CastToClient()) && (members[x].GroupNumber == group_id))) {
|
||||
if (members[x].member->ClientVersion() >= EQ::versions::ClientVersion::SoD) {
|
||||
outapp.SetOpcode(OP_MobManaUpdate);
|
||||
@ -1694,7 +1827,7 @@ void Raid::SendEndurancePacketFrom(Mob *mob)
|
||||
EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct));
|
||||
|
||||
for (int x = 0; x < MAX_RAID_MEMBERS; x++) {
|
||||
if (members[x].member) {
|
||||
if (members[x].member && !members[x].IsBot) {
|
||||
if (!mob->IsClient() || ((members[x].member != mob->CastToClient()) && (members[x].GroupNumber == group_id))) {
|
||||
if (members[x].member->ClientVersion() >= EQ::versions::ClientVersion::SoD) {
|
||||
outapp.SetOpcode(OP_MobEnduranceUpdate);
|
||||
@ -1801,9 +1934,14 @@ void Raid::CheckGroupMentor(uint32 group_id, Client *c)
|
||||
void Raid::SetDirtyAutoHaters()
|
||||
{
|
||||
for (int i = 0; i < MAX_RAID_MEMBERS; ++i)
|
||||
if (members[i].member)
|
||||
if (members[i].member && members[i].IsBot)
|
||||
{
|
||||
members[i].member->CastToBot()->SetDirtyAutoHaters();
|
||||
}
|
||||
else if (members[i].member && !members[i].IsBot)
|
||||
{
|
||||
members[i].member->SetDirtyAutoHaters();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void Raid::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_required /*= true*/, bool ignore_sender /*= true*/, float distance /*= 0*/, bool group_only /*= true*/) {
|
||||
@ -1812,22 +1950,25 @@ void Raid::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_re
|
||||
uint32 group_id = GetGroup(sender->CastToClient());
|
||||
|
||||
/* If this is a group only packet and we're not in a group -- return */
|
||||
if (group_id == 0xFFFFFFFF && group_only)
|
||||
if (group_id == RAID_GROUPLESS && group_only)
|
||||
return;
|
||||
|
||||
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) {
|
||||
if (!members[i].member)
|
||||
if (!members[i].member) {
|
||||
continue;
|
||||
|
||||
if (!members[i].member->IsClient())
|
||||
}
|
||||
if (!members[i].member->IsClient()) {
|
||||
continue;
|
||||
|
||||
if (ignore_sender && members[i].member == sender)
|
||||
}
|
||||
if (members[i].IsBot) {
|
||||
continue;
|
||||
|
||||
if (group_only && members[i].GroupNumber != group_id)
|
||||
}
|
||||
if (ignore_sender && members[i].member == sender) {
|
||||
continue;
|
||||
|
||||
}
|
||||
if (group_only && members[i].GroupNumber != group_id) {
|
||||
continue;
|
||||
}
|
||||
/* If we don't have a distance requirement - send to all members */
|
||||
if (distance == 0) {
|
||||
members[i].member->CastToClient()->QueuePacket(app, ack_required);
|
||||
@ -1884,3 +2025,84 @@ bool Raid::DoesAnyMemberHaveExpeditionLockout(
|
||||
return Expedition::HasLockoutByCharacterName(raid_member.membername, expedition_name, event_name);
|
||||
});
|
||||
}
|
||||
|
||||
Mob* Raid::GetRaidMainAssistOneByName(const char* name)
|
||||
{
|
||||
Raid* raid = entity_list.GetRaidByBotName(name);
|
||||
std::vector<RaidMember> raid_members = raid->GetMembers();
|
||||
|
||||
for (RaidMember iter : raid_members)
|
||||
{
|
||||
if (iter.IsRaidMainAssistOne) {
|
||||
return iter.member->CastToMob();
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
bool Raid::IsEngaged() {
|
||||
|
||||
std::vector<RaidMember> rm = GetMembers();
|
||||
for (const auto& m : rm) {
|
||||
if (m.member && !m.IsBot && (m.member->IsEngaged() || m.member->GetAggroCount() > 0)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void Raid::RaidGroupSay(const char* msg, const char* from, uint8 language, uint8 lang_skill)
|
||||
{
|
||||
if (!from)
|
||||
return;
|
||||
|
||||
uint32 groupToUse = GetGroup(from);
|
||||
|
||||
if (groupToUse > 11)
|
||||
return;
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidGroupSay, sizeof(ServerRaidMessage_Struct) + strlen(msg) + 1);
|
||||
ServerRaidMessage_Struct* rga = (ServerRaidMessage_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
rga->gid = groupToUse;
|
||||
rga->language = language;
|
||||
rga->lang_skill = lang_skill;
|
||||
strn0cpy(rga->from, from, 64);
|
||||
|
||||
strcpy(rga->message, msg); // this is safe because we are allocating enough space for the entire msg above
|
||||
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
void Raid::RaidSay(const char* msg, const char* from, uint8 language, uint8 lang_skill)
|
||||
{
|
||||
if (!from)
|
||||
return;
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_RaidSay, sizeof(ServerRaidMessage_Struct) + strlen(msg) + 1);
|
||||
ServerRaidMessage_Struct* rga = (ServerRaidMessage_Struct*)pack->pBuffer;
|
||||
rga->rid = GetID();
|
||||
rga->gid = RAID_GROUPLESS;
|
||||
rga->language = language;
|
||||
rga->lang_skill = lang_skill;
|
||||
strn0cpy(rga->from, from, 64);
|
||||
|
||||
strcpy(rga->message, msg);
|
||||
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void Raid::SetNewRaidLeader(uint32 i)
|
||||
{
|
||||
if (members[i].IsRaidLeader) {
|
||||
for (int x = 0; x < MAX_RAID_MEMBERS; x++) {
|
||||
if (!members[x].IsBot) {
|
||||
if (strlen(members[x].membername) > 0 && strcmp(members[x].membername, members[i].membername) != 0) {
|
||||
SetRaidLeader(members[i].membername, members[x].membername);
|
||||
UpdateRaidAAs();
|
||||
SendAllRaidLeadershipAA();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
zone/raids.h
12
zone/raids.h
@ -88,6 +88,8 @@ struct RaidMember{
|
||||
bool IsGroupLeader;
|
||||
bool IsRaidLeader;
|
||||
bool IsLooter;
|
||||
bool IsBot = false;
|
||||
bool IsRaidMainAssistOne = false;
|
||||
};
|
||||
|
||||
struct GroupMentor {
|
||||
@ -113,6 +115,11 @@ public:
|
||||
bool IsRaid() { return true; }
|
||||
|
||||
void AddMember(Client *c, uint32 group = 0xFFFFFFFF, bool rleader=false, bool groupleader=false, bool looter=false);
|
||||
void AddBot(Bot* b, uint32 group = 0xFFFFFFFF, bool rleader=false, bool groupleader=false, bool looter=false);
|
||||
void RaidGroupSay(const char* msg, const char* from, uint8 language, uint8 lang_skill);
|
||||
void RaidSay(const char* msg, const char* from, uint8 language, uint8 lang_skill);
|
||||
bool IsEngaged();
|
||||
Mob* GetRaidMainAssistOneByName(const char* name);
|
||||
void RemoveMember(const char *c);
|
||||
void DisbandRaid();
|
||||
void MoveMember(const char *name, uint32 newGroup);
|
||||
@ -124,6 +131,7 @@ public:
|
||||
bool IsRaidMember(const char* name);
|
||||
bool IsRaidMember(Client *c);
|
||||
void UpdateLevel(const char *name, int newLevel);
|
||||
void SetNewRaidLeader(uint32 i);
|
||||
|
||||
uint32 GetFreeGroup();
|
||||
uint8 GroupCount(uint32 gid);
|
||||
@ -244,6 +252,10 @@ public:
|
||||
bool DoesAnyMemberHaveExpeditionLockout(const std::string& expedition_name, const std::string& event_name, int max_check_count = 0);
|
||||
|
||||
std::vector<RaidMember> GetMembers() const;
|
||||
std::vector<RaidMember> GetRaidGroupMembers(uint32 gid);
|
||||
std::vector<Bot*> GetRaidGroupBotMembers(uint32 gid);
|
||||
std::vector<Bot*> GetRaidBotMembers(uint32 owner = 0);
|
||||
void HandleBotGroupDisband(uint32 owner, uint32 gid = RAID_GROUPLESS);
|
||||
|
||||
RaidMember members[MAX_RAID_MEMBERS];
|
||||
char leadername[64];
|
||||
|
||||
@ -430,26 +430,16 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
// If you're at full mana, let it cast even if you dont have enough mana
|
||||
|
||||
// we calculated this above, now enforce it
|
||||
if (mana_cost > 0 && slot != CastingSlot::Item) {
|
||||
if (mana_cost > 0 && slot != CastingSlot::Item || (IsBot() && !CastToBot()->IsBotNonSpellFighter())) {
|
||||
int my_curmana = GetMana();
|
||||
int my_maxmana = GetMaxMana();
|
||||
if (my_curmana < mana_cost) {// not enough mana
|
||||
//this is a special case for NPCs with no mana...
|
||||
if (IsNPC() && my_curmana == my_maxmana){
|
||||
if (IsNPC() && my_curmana == my_maxmana) {
|
||||
mana_cost = 0;
|
||||
} else {
|
||||
//The client will prevent spell casting if insufficient mana, this is only for serverside enforcement.
|
||||
LogSpells("Spell Error not enough mana spell=[{}] mymana=[{}] cost=[{}]\n", spell_id, my_curmana, mana_cost);
|
||||
if (IsClient()) {
|
||||
//clients produce messages... npcs should not for this case
|
||||
MessageString(Chat::Red, INSUFFICIENT_MANA);
|
||||
InterruptSpell();
|
||||
} else {
|
||||
InterruptSpell(0, 0, 0); //the 0 args should cause no messages
|
||||
}
|
||||
ZeroCastingVars();
|
||||
return(false);
|
||||
}
|
||||
DoSpellInterrupt(spell_id, mana_cost, my_curmana);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -475,7 +465,7 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
// cast time is 0, just finish it right now and be done with it
|
||||
if(cast_time == 0) {
|
||||
CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust); //
|
||||
return(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ok we know it has a cast time so we can start the timer now
|
||||
@ -501,7 +491,20 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
|
||||
}
|
||||
}
|
||||
|
||||
return(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mob::DoSpellInterrupt(uint16 spell_id, int32 mana_cost, int my_curmana) {
|
||||
//The client will prevent spell casting if insufficient mana, this is only for serverside enforcement.
|
||||
LogSpells("Spell Error not enough mana spell=[{}] mymana=[{}] cost=[{}]\n", spell_id, my_curmana, mana_cost);
|
||||
if (IsClient()) {
|
||||
//clients produce messages... npcs should not for this case
|
||||
MessageString(Chat::Red, INSUFFICIENT_MANA);
|
||||
InterruptSpell();
|
||||
} else {
|
||||
InterruptSpell(0, 0, 0); //the 0 args should cause no messages
|
||||
}
|
||||
ZeroCastingVars();
|
||||
}
|
||||
|
||||
void Mob::SendBeginCast(uint16 spell_id, uint32 casttime)
|
||||
@ -4153,7 +4156,6 @@ bool Mob::SpellOnTarget(
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (spelltar->IsClient()){
|
||||
spelltar->CastToClient()->BreakSneakWhenCastOn(this, false);
|
||||
spelltar->CastToClient()->BreakFeignDeathWhenCastOn(false);
|
||||
|
||||
@ -362,6 +362,7 @@
|
||||
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
|
||||
#define PETITION_DELETED 5054 //Your petition was successfully deleted.
|
||||
#define ALREADY_IN_RAID 5060 //%1 is already in a raid.
|
||||
#define ALREADY_IN_YOUR_RAID 5077 //%1 is already in your raid.
|
||||
#define GAIN_RAIDEXP 5085 //You gained raid experience!
|
||||
#define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there.
|
||||
#define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure.
|
||||
|
||||
@ -1500,7 +1500,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
if (strcmp(rmsg->from, r->members[x].member->GetName()) != 0)
|
||||
{
|
||||
if (r->members[x].GroupNumber == rmsg->gid) {
|
||||
if (r->members[x].member->GetFilter(FilterGroupChat) != 0)
|
||||
if (!r->members[x].IsBot && r->members[x].member->GetFilter(FilterGroupChat) != 0)
|
||||
{
|
||||
r->members[x].member->ChannelMessageSend(rmsg->from, r->members[x].member->GetName(), ChatChannel_Group, rmsg->language, rmsg->lang_skill, rmsg->message);
|
||||
}
|
||||
@ -1524,7 +1524,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
if (r->members[x].member) {
|
||||
if (strcmp(rmsg->from, r->members[x].member->GetName()) != 0)
|
||||
{
|
||||
if (r->members[x].member->GetFilter(FilterGroupChat) != 0)
|
||||
if (!r->members[x].IsBot && r->members[x].member->GetFilter(FilterGroupChat) != 0)
|
||||
{
|
||||
r->members[x].member->ChannelMessageSend(rmsg->from, r->members[x].member->GetName(), ChatChannel_Raid, rmsg->language, rmsg->lang_skill, rmsg->message);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user