mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-16 22:58:34 +00:00
Merge remote-tracking branch 'origin/master' into eqstream
This commit is contained in:
+1
-1
@@ -253,7 +253,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
//sometimes if a client has some lag while zoning into a dangerous place while either invis or a GM
|
||||
//they will aggro mobs even though it's supposed to be impossible, to lets make sure we've finished connecting
|
||||
if (mob->IsClient()) {
|
||||
if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn())
|
||||
if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn() || mob->CastToClient()->zoning)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1564,7 +1564,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk
|
||||
}
|
||||
}
|
||||
|
||||
entity_list.RemoveFromTargets(this);
|
||||
entity_list.RemoveFromTargets(this, true);
|
||||
hate_list.RemoveEntFromHateList(this);
|
||||
RemoveAutoXTargets();
|
||||
|
||||
|
||||
+187
-79
@@ -77,7 +77,7 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
|
||||
SetShowHelm(true);
|
||||
SetPauseAI(false);
|
||||
rest_timer.Disable();
|
||||
SetFollowDistance(BOT_DEFAULT_FOLLOW_DISTANCE);
|
||||
SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT);
|
||||
// Do this once and only in this constructor
|
||||
GenerateAppearance();
|
||||
GenerateBaseStats();
|
||||
@@ -151,7 +151,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
SetPauseAI(false);
|
||||
|
||||
rest_timer.Disable();
|
||||
SetFollowDistance(BOT_DEFAULT_FOLLOW_DISTANCE);
|
||||
SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT);
|
||||
strcpy(this->name, this->GetCleanName());
|
||||
|
||||
memset(&_botInspectMessage, 0, sizeof(InspectMessage_Struct));
|
||||
@@ -176,6 +176,9 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
if (GetClass() == ROGUE)
|
||||
evade_timer.Start();
|
||||
|
||||
m_CastingRoles.GroupHealer = false;
|
||||
m_CastingRoles.GroupSlower = false;
|
||||
|
||||
GenerateBaseStats();
|
||||
|
||||
if (!botdb.LoadTimers(this) && bot_owner)
|
||||
@@ -2062,6 +2065,8 @@ float Bot::GetMaxMeleeRangeToTarget(Mob* target) {
|
||||
|
||||
// AI Processing for the Bot object
|
||||
void Bot::AI_Process() {
|
||||
// TODO: Need to add root checks to all movement code
|
||||
|
||||
if (!IsAIControlled())
|
||||
return;
|
||||
if (GetPauseAI())
|
||||
@@ -2214,7 +2219,7 @@ void Bot::AI_Process() {
|
||||
// Let's check if we have a los with our target.
|
||||
// If we don't, our hate_list is wiped.
|
||||
// Else, it was causing the bot to aggro behind wall etc... causing massive trains.
|
||||
if(!CheckLosFN(GetTarget()) || GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) {
|
||||
if(GetTarget()->IsMezzed() || !IsAttackAllowed(GetTarget())) {
|
||||
WipeHateList();
|
||||
if(IsMoving()) {
|
||||
SetHeading(0);
|
||||
@@ -2225,6 +2230,26 @@ void Bot::AI_Process() {
|
||||
}
|
||||
return;
|
||||
}
|
||||
else if (!CheckLosFN(GetTarget())) {
|
||||
if (RuleB(Bots, UsePathing) && zone->pathing) {
|
||||
bool WaypointChanged, NodeReached;
|
||||
|
||||
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
|
||||
GetRunspeed(), WaypointChanged, NodeReached);
|
||||
|
||||
if (WaypointChanged)
|
||||
tar_ndx = 20;
|
||||
|
||||
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed());
|
||||
}
|
||||
else {
|
||||
Mob* follow = entity_list.GetMob(GetFollowID());
|
||||
if (follow)
|
||||
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), GetRunspeed());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
|
||||
SendAddPlayerState(PlayerState::Aggressive);
|
||||
@@ -2339,6 +2364,7 @@ void Bot::AI_Process() {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Test RuleB(Bots, UpdatePositionWithTimer)
|
||||
if(IsMoving())
|
||||
SendPosUpdate();
|
||||
else
|
||||
@@ -2503,33 +2529,60 @@ void Bot::AI_Process() {
|
||||
}
|
||||
}
|
||||
else if(AI_movement_timer->Check()) {
|
||||
// something is wrong with bot movement spell bonuses - based on logging..
|
||||
// ..this code won't need to be so complex once fixed...
|
||||
int speed = GetRunspeed();
|
||||
if (cur_dist < GetFollowDistance() + 2000) {
|
||||
speed = GetWalkspeed();
|
||||
}
|
||||
else if (cur_dist >= GetFollowDistance() + 10000) { // 100
|
||||
if (cur_dist >= 22500) { // 150
|
||||
auto leader = follow;
|
||||
while (leader->GetFollowID()) {
|
||||
leader = entity_list.GetMob(leader->GetFollowID());
|
||||
if (!leader || leader == this)
|
||||
break;
|
||||
if (leader->GetRunspeed() > speed)
|
||||
speed = leader->GetRunspeed();
|
||||
if (leader->IsClient())
|
||||
break;
|
||||
// Something is still wrong with bot the follow code...
|
||||
// Shows up really bad over long distances when movement bonuses are involved
|
||||
// The flip-side is that too much speed adversely affects node pathing...
|
||||
if (cur_dist > GetFollowDistance()) {
|
||||
if (RuleB(Bots, UsePathing) && zone->pathing) {
|
||||
if (cur_dist <= BOT_FOLLOW_DISTANCE_WALK) {
|
||||
bool WaypointChanged, NodeReached;
|
||||
|
||||
glm::vec3 Goal = UpdatePath(follow->GetX(), follow->GetY(), follow->GetZ(),
|
||||
GetWalkspeed(), WaypointChanged, NodeReached);
|
||||
|
||||
if (WaypointChanged)
|
||||
tar_ndx = 20;
|
||||
|
||||
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetWalkspeed());
|
||||
}
|
||||
else {
|
||||
int speed = GetRunspeed();
|
||||
if (cur_dist > BOT_FOLLOW_DISTANCE_CRITICAL)
|
||||
speed = ((float)speed * 1.333f); // sprint mod (1/3 boost)
|
||||
|
||||
bool WaypointChanged, NodeReached;
|
||||
|
||||
glm::vec3 Goal = UpdatePath(follow->GetX(), follow->GetY(), follow->GetZ(),
|
||||
speed, WaypointChanged, NodeReached);
|
||||
|
||||
if (WaypointChanged)
|
||||
tar_ndx = 20;
|
||||
|
||||
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, speed);
|
||||
}
|
||||
}
|
||||
speed = (float)speed * (1.0f + ((float)speed * 0.03125f)); // 1/32 - special bot sprint mod
|
||||
}
|
||||
else {
|
||||
if (cur_dist <= BOT_FOLLOW_DISTANCE_WALK) {
|
||||
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), GetWalkspeed());
|
||||
}
|
||||
else {
|
||||
int speed = GetRunspeed();
|
||||
if (cur_dist > BOT_FOLLOW_DISTANCE_CRITICAL)
|
||||
speed = ((float)speed * 1.333f); // sprint mod (1/3 boost)
|
||||
|
||||
if (cur_dist > GetFollowDistance()) {
|
||||
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
|
||||
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
|
||||
}
|
||||
}
|
||||
|
||||
if (rest_timer.Enabled())
|
||||
rest_timer.Disable();
|
||||
return;
|
||||
|
||||
if (RuleB(Bots, UpdatePositionWithTimer)) { // this helps with rubber-banding effect
|
||||
if (IsMoving())
|
||||
SendPosUpdate();
|
||||
//else
|
||||
// SendPosition(); // enabled - no discernable difference..disabled - saves on no movement packets
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (moved) {
|
||||
@@ -2537,9 +2590,14 @@ void Bot::AI_Process() {
|
||||
SetCurrentSpeed(0);
|
||||
}
|
||||
}
|
||||
|
||||
if (GetClass() == BARD && GetBotStance() != BotStancePassive && !spellend_timer.Enabled() && AI_think_timer->Check())
|
||||
AI_IdleCastCheck();
|
||||
|
||||
return;
|
||||
}
|
||||
else if (IsMoving()) {
|
||||
if (GetBotStance() != BotStancePassive && GetClass() == BARD && !spellend_timer.Enabled() && AI_think_timer->Check()) {
|
||||
if (GetClass() == BARD && GetBotStance() != BotStancePassive && !spellend_timer.Enabled() && AI_think_timer->Check()) {
|
||||
AI_IdleCastCheck();
|
||||
}
|
||||
}
|
||||
@@ -6835,66 +6893,116 @@ bool Bot::IsBotCasterCombatRange(Mob *target) {
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bot::IsGroupPrimaryHealer() {
|
||||
bool result = false;
|
||||
uint8 botclass = GetClass();
|
||||
if(HasGroup()) {
|
||||
Group *g = GetGroup();
|
||||
switch(botclass) {
|
||||
case CLERIC: {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
case DRUID: {
|
||||
result = GroupHasClericClass(g) ? false : true;
|
||||
break;
|
||||
}
|
||||
case SHAMAN: {
|
||||
result = (GroupHasClericClass(g) || GroupHasDruidClass(g)) ? false : true;
|
||||
break;
|
||||
}
|
||||
case PALADIN:
|
||||
case RANGER:
|
||||
case BEASTLORD: {
|
||||
result = GroupHasPriestClass(g) ? false : true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
void Bot::UpdateGroupCastingRoles(const Group* group, bool disband)
|
||||
{
|
||||
if (!group)
|
||||
return;
|
||||
|
||||
for (auto iter : group->members) {
|
||||
if (!iter)
|
||||
continue;
|
||||
|
||||
if (iter->IsBot()) {
|
||||
iter->CastToBot()->SetGroupHealer(false);
|
||||
iter->CastToBot()->SetGroupSlower(false);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
if (disband)
|
||||
return;
|
||||
|
||||
bool Bot::IsGroupPrimarySlower() {
|
||||
bool result = false;
|
||||
uint8 botclass = GetClass();
|
||||
if(HasGroup()) {
|
||||
Group *g = GetGroup();
|
||||
switch(botclass) {
|
||||
case SHAMAN: {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
case ENCHANTER: {
|
||||
result = GroupHasShamanClass(g) ? false : true;
|
||||
break;
|
||||
}
|
||||
case BEASTLORD: {
|
||||
result = (GroupHasShamanClass(g) || GroupHasEnchanterClass(g)) ? false : true;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
Mob* healer = nullptr;
|
||||
Mob* slower = nullptr;
|
||||
|
||||
for (auto iter : group->members) {
|
||||
if (!iter)
|
||||
continue;
|
||||
|
||||
switch (iter->GetClass()) {
|
||||
case CLERIC:
|
||||
if (!healer)
|
||||
healer = iter;
|
||||
else
|
||||
switch (healer->GetClass()) {
|
||||
case CLERIC:
|
||||
break;
|
||||
default:
|
||||
healer = iter;
|
||||
}
|
||||
|
||||
break;
|
||||
case DRUID:
|
||||
if (!healer)
|
||||
healer = iter;
|
||||
else
|
||||
switch (healer->GetClass()) {
|
||||
case CLERIC:
|
||||
case DRUID:
|
||||
break;
|
||||
default:
|
||||
healer = iter;
|
||||
}
|
||||
break;
|
||||
case SHAMAN:
|
||||
if (!healer)
|
||||
healer = iter;
|
||||
else
|
||||
switch (healer->GetClass()) {
|
||||
case CLERIC:
|
||||
case DRUID:
|
||||
case SHAMAN:
|
||||
break;
|
||||
default:
|
||||
healer = iter;
|
||||
}
|
||||
break;
|
||||
case PALADIN:
|
||||
case RANGER:
|
||||
case BEASTLORD:
|
||||
if (!healer)
|
||||
healer = iter;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (iter->GetClass()) {
|
||||
case SHAMAN:
|
||||
if (!slower)
|
||||
slower = iter;
|
||||
else
|
||||
switch (slower->GetClass()) {
|
||||
case SHAMAN:
|
||||
break;
|
||||
default:
|
||||
slower = iter;
|
||||
}
|
||||
break;
|
||||
case ENCHANTER:
|
||||
if (!slower)
|
||||
slower = iter;
|
||||
else
|
||||
switch (slower->GetClass()) {
|
||||
case SHAMAN:
|
||||
case ENCHANTER:
|
||||
break;
|
||||
default:
|
||||
slower = iter;
|
||||
}
|
||||
break;
|
||||
case BEASTLORD:
|
||||
if (!slower)
|
||||
slower = iter;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
if (healer && healer->IsBot())
|
||||
healer->CastToBot()->SetGroupHealer();
|
||||
if (slower && slower->IsBot())
|
||||
slower->CastToBot()->SetGroupSlower();
|
||||
}
|
||||
|
||||
bool Bot::CanHeal() {
|
||||
|
||||
+19
-3
@@ -38,7 +38,10 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#define BOT_DEFAULT_FOLLOW_DISTANCE 184
|
||||
#define BOT_FOLLOW_DISTANCE_DEFAULT 184 // as DSq value (~13.565 units)
|
||||
#define BOT_FOLLOW_DISTANCE_DEFAULT_MAX 2500 // as DSq value (50 units)
|
||||
#define BOT_FOLLOW_DISTANCE_WALK 400 // as DSq value (20 units)
|
||||
#define BOT_FOLLOW_DISTANCE_CRITICAL 22500 // as DSq value (150 units)
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
@@ -132,6 +135,10 @@ enum SpellTypeIndex {
|
||||
MaxSpellTypes
|
||||
};
|
||||
|
||||
// negative Healer/Slower, positive Healer, postive Slower, positive Healer/Slower
|
||||
enum BotCastingChanceConditional : uint8 { nHS = 0, pH, pS, pHS, cntHS = 4 };
|
||||
|
||||
|
||||
class Bot : public NPC {
|
||||
friend class Mob;
|
||||
public:
|
||||
@@ -472,8 +479,11 @@ public:
|
||||
BotRoleType GetBotRole() { return _botRole; }
|
||||
BotStanceType GetBotStance() { return _botStance; }
|
||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||
bool IsGroupPrimaryHealer();
|
||||
bool IsGroupPrimarySlower();
|
||||
|
||||
bool IsGroupHealer() { return m_CastingRoles.GroupHealer; }
|
||||
bool IsGroupSlower() { return m_CastingRoles.GroupSlower; }
|
||||
static void UpdateGroupCastingRoles(const Group* group, bool disband = false);
|
||||
|
||||
bool IsBotCaster() { return IsCasterClass(GetClass()); }
|
||||
bool IsBotINTCaster() { return IsINTCasterClass(GetClass()); }
|
||||
bool IsBotWISCaster() { return IsWISCasterClass(GetClass()); }
|
||||
@@ -637,6 +647,10 @@ protected:
|
||||
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
virtual float GetMaxMeleeRangeToTarget(Mob* target);
|
||||
|
||||
BotCastingRoles& GetCastingRoles() { return m_CastingRoles; }
|
||||
void SetGroupHealer(bool flag = true) { m_CastingRoles.GroupHealer = flag; }
|
||||
void SetGroupSlower(bool flag = true) { m_CastingRoles.GroupSlower = flag; }
|
||||
|
||||
private:
|
||||
// Class Members
|
||||
uint32 _botID;
|
||||
@@ -676,6 +690,8 @@ private:
|
||||
|
||||
Timer evade_timer;
|
||||
|
||||
BotCastingRoles m_CastingRoles;
|
||||
|
||||
std::shared_ptr<HealRotation> m_member_of_heal_rotation;
|
||||
|
||||
std::map<uint32, BotAA> botAAs;
|
||||
|
||||
@@ -4550,7 +4550,7 @@ void bot_subcommand_bot_follow_distance(Client *c, const Seperator *sep)
|
||||
}
|
||||
const int ab_mask = ActionableBots::ABM_NoFilter;
|
||||
|
||||
uint32 bfd = BOT_DEFAULT_FOLLOW_DISTANCE;
|
||||
uint32 bfd = BOT_FOLLOW_DISTANCE_DEFAULT;
|
||||
bool set_flag = false;
|
||||
int ab_arg = 2;
|
||||
|
||||
@@ -4561,6 +4561,10 @@ void bot_subcommand_bot_follow_distance(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
bfd = atoi(sep->arg[2]);
|
||||
if (bfd < 1)
|
||||
bfd = 1;
|
||||
if (bfd > BOT_FOLLOW_DISTANCE_DEFAULT_MAX)
|
||||
bfd = BOT_FOLLOW_DISTANCE_DEFAULT_MAX;
|
||||
set_flag = true;
|
||||
ab_arg = 3;
|
||||
}
|
||||
|
||||
+65
-2
@@ -82,6 +82,50 @@ bool BotDatabase::LoadBotCommandSettings(std::map<std::string, std::pair<uint8,
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8 spell_casting_chances[MaxSpellTypes][PLAYER_CLASS_COUNT][MaxStances][cntHS];
|
||||
|
||||
bool BotDatabase::LoadBotSpellCastingChances()
|
||||
{
|
||||
memset(spell_casting_chances, 0, sizeof(spell_casting_chances));
|
||||
|
||||
query =
|
||||
"SELECT"
|
||||
" `spell_type_index`,"
|
||||
" `class_index`,"
|
||||
" `stance_index`,"
|
||||
" `conditional_index`,"
|
||||
" `value` "
|
||||
"FROM"
|
||||
" `bot_spell_casting_chances` "
|
||||
"WHERE"
|
||||
" `value` != '0'";
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
uint8 spell_type_index = atoi(row[0]);
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
continue;
|
||||
uint8 class_index = atoi(row[1]);
|
||||
if (class_index >= PLAYER_CLASS_COUNT)
|
||||
continue;
|
||||
uint8 stance_index = atoi(row[2]);
|
||||
if (stance_index >= MaxStances)
|
||||
continue;
|
||||
uint8 conditional_index = atoi(row[3]);
|
||||
if (conditional_index >= cntHS)
|
||||
continue;
|
||||
uint8 value = atoi(row[4]);
|
||||
if (value > 100)
|
||||
value = 100;
|
||||
spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* Bot functions */
|
||||
bool BotDatabase::QueryNameAvailablity(const std::string& bot_name, bool& available_flag)
|
||||
@@ -334,7 +378,13 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
|
||||
loaded_bot = new Bot(bot_id, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct);
|
||||
if (loaded_bot) {
|
||||
loaded_bot->SetShowHelm((atoi(row[43]) > 0 ? true : false));
|
||||
loaded_bot->SetFollowDistance(atoi(row[44]));
|
||||
uint32 bfd = atoi(row[44]);
|
||||
if (bfd < 1)
|
||||
bfd = 1;
|
||||
if (bfd > BOT_FOLLOW_DISTANCE_DEFAULT_MAX)
|
||||
bfd = BOT_FOLLOW_DISTANCE_DEFAULT_MAX;
|
||||
loaded_bot->SetFollowDistance(bfd);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -471,7 +521,7 @@ bool BotDatabase::SaveNewBot(Bot* bot_inst, uint32& bot_id)
|
||||
bot_inst->GetPR(),
|
||||
bot_inst->GetDR(),
|
||||
bot_inst->GetCorrup(),
|
||||
BOT_DEFAULT_FOLLOW_DISTANCE
|
||||
BOT_FOLLOW_DISTANCE_DEFAULT
|
||||
);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
@@ -2711,6 +2761,19 @@ bool BotDatabase::DeleteAllHealRotations(const uint32 owner_id)
|
||||
|
||||
|
||||
/* Bot miscellaneous functions */
|
||||
uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index)
|
||||
{
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
return 0;
|
||||
if (class_index >= PLAYER_CLASS_COUNT)
|
||||
return 0;
|
||||
if (stance_index >= MaxStances)
|
||||
return 0;
|
||||
if (conditional_index >= cntHS)
|
||||
return 0;
|
||||
|
||||
return spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
|
||||
}
|
||||
|
||||
|
||||
/* fail::Bot functions */
|
||||
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
bool Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port);
|
||||
|
||||
bool LoadBotCommandSettings(std::map<std::string, std::pair<uint8, std::vector<std::string>>> &bot_command_settings);
|
||||
bool LoadBotSpellCastingChances();
|
||||
|
||||
|
||||
/* Bot functions */
|
||||
@@ -183,6 +184,7 @@ public:
|
||||
bool DeleteAllHealRotations(const uint32 owner_id);
|
||||
|
||||
/* Bot miscellaneous functions */
|
||||
uint8 GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index);
|
||||
|
||||
|
||||
class fail {
|
||||
|
||||
@@ -60,6 +60,11 @@ struct BotSpell_wPriority : public BotSpell {
|
||||
uint8 Priority;
|
||||
};
|
||||
|
||||
struct BotCastingRoles {
|
||||
bool GroupHealer;
|
||||
bool GroupSlower;
|
||||
};
|
||||
|
||||
struct BotAA {
|
||||
uint32 aa_id;
|
||||
uint8 req_level;
|
||||
|
||||
+4
-2949
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -8902,7 +8902,7 @@ void Client::ProcessAggroMeter()
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Group3, 0))
|
||||
add_entry(AggroMeter::AT_Group3);
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Group4, 0))
|
||||
add_entry(AggroMeter::AT_Group5);
|
||||
add_entry(AggroMeter::AT_Group4);
|
||||
if (m_aggrometer.set_pct(AggroMeter::AT_Group5, 0))
|
||||
add_entry(AggroMeter::AT_Group5);
|
||||
}
|
||||
|
||||
+11
-7
@@ -6505,6 +6505,16 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app)
|
||||
if (!group) //We must recheck this here.. incase the final bot disbanded the party..otherwise we crash
|
||||
return;
|
||||
#endif
|
||||
Mob* memberToDisband = GetTarget();
|
||||
|
||||
if (!memberToDisband)
|
||||
memberToDisband = entity_list.GetMob(gd->name2);
|
||||
|
||||
if (memberToDisband) {
|
||||
auto group2 = memberToDisband->GetGroup();
|
||||
if (group2 != group) // they're not in our group!
|
||||
memberToDisband = this;
|
||||
}
|
||||
|
||||
if (group->GroupCount() < 3)
|
||||
{
|
||||
@@ -6526,7 +6536,7 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app)
|
||||
GetMerc()->Suspend();
|
||||
}
|
||||
}
|
||||
else if (group->IsLeader(this) && GetTarget() == this)
|
||||
else if (group->IsLeader(this) && (GetTarget() == this || memberToDisband == this))
|
||||
{
|
||||
LeaveGroup();
|
||||
if (GetMerc() && !GetMerc()->IsSuspended())
|
||||
@@ -6536,12 +6546,6 @@ void Client::Handle_OP_GroupDisband(const EQApplicationPacket *app)
|
||||
}
|
||||
else
|
||||
{
|
||||
Mob* memberToDisband = nullptr;
|
||||
memberToDisband = GetTarget();
|
||||
|
||||
if (!memberToDisband)
|
||||
memberToDisband = entity_list.GetMob(gd->name2);
|
||||
|
||||
if (memberToDisband)
|
||||
{
|
||||
if (group->IsLeader(this))
|
||||
|
||||
+1
-1
@@ -796,7 +796,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
|
||||
continue;
|
||||
if (dist_targ < min_range2) //make sure they are in range
|
||||
continue;
|
||||
if (isnpc && curmob->IsNPC()) { //check npc->npc casting
|
||||
if (isnpc && curmob->IsNPC() && spells[spell_id].targettype != ST_AreaNPCOnly) { //check npc->npc casting
|
||||
FACTION_VALUE f = curmob->GetReverseFactionCon(caster);
|
||||
if (bad) {
|
||||
//affect mobs that are on our hate list, or
|
||||
|
||||
@@ -347,6 +347,10 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte
|
||||
|
||||
safe_delete(outapp);
|
||||
|
||||
#ifdef BOTS
|
||||
Bot::UpdateGroupCastingRoles(this);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -481,6 +485,10 @@ bool Group::UpdatePlayer(Mob* update){
|
||||
if (update->IsClient() && !mentoree && mentoree_name.length() && !mentoree_name.compare(update->GetName()))
|
||||
mentoree = update->CastToClient();
|
||||
|
||||
#ifdef BOTS
|
||||
Bot::UpdateGroupCastingRoles(this);
|
||||
#endif
|
||||
|
||||
return updateSuccess;
|
||||
}
|
||||
|
||||
@@ -513,6 +521,10 @@ void Group::MemberZoned(Mob* removemob) {
|
||||
|
||||
if (removemob->IsClient() && removemob == mentoree)
|
||||
mentoree = nullptr;
|
||||
|
||||
#ifdef BOTS
|
||||
Bot::UpdateGroupCastingRoles(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Group::SendGroupJoinOOZ(Mob* NewMember) {
|
||||
@@ -581,6 +593,16 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: fix this shit
|
||||
// okay, so there is code below that tries to handle this. It does not.
|
||||
// So instead of figuring it out now, lets just disband the group so the client doesn't
|
||||
// sit there with a broken group and there isn't any group leader shuffling going on
|
||||
// since the code below doesn't work.
|
||||
if (oldmember == GetLeader()) {
|
||||
DisbandGroup();
|
||||
return true;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++)
|
||||
{
|
||||
if (members[i] == oldmember)
|
||||
@@ -721,6 +743,10 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender)
|
||||
ClearAllNPCMarks();
|
||||
}
|
||||
|
||||
#ifdef BOTS
|
||||
Bot::UpdateGroupCastingRoles(this);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -864,6 +890,10 @@ uint32 Group::GetTotalGroupDamage(Mob* other) {
|
||||
}
|
||||
|
||||
void Group::DisbandGroup(bool joinraid) {
|
||||
#ifdef BOTS
|
||||
Bot::UpdateGroupCastingRoles(this, true);
|
||||
#endif
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate_Struct));
|
||||
|
||||
GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer;
|
||||
|
||||
@@ -371,6 +371,10 @@ int main(int argc, char** argv) {
|
||||
Log.Out(Logs::General, Logs::Error, "Bot command loading FAILED");
|
||||
else
|
||||
Log.Out(Logs::General, Logs::Zone_Server, "%d bot commands loaded", botretval);
|
||||
|
||||
Log.Out(Logs::General, Logs::Zone_Server, "Loading bot spell casting chances");
|
||||
if (!botdb.LoadBotSpellCastingChances())
|
||||
Log.Out(Logs::General, Logs::Error, "Bot spell casting chances loading FAILED");
|
||||
#endif
|
||||
|
||||
if(RuleB(TaskSystem, EnableTaskSystem)) {
|
||||
|
||||
@@ -304,6 +304,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
|
||||
// 2 - `s Warder
|
||||
// 3 - Random name if client, `s pet for others
|
||||
// 4 - Keep DB name
|
||||
// 5 - `s ward
|
||||
|
||||
|
||||
if (petname != nullptr) {
|
||||
@@ -325,6 +326,10 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
|
||||
// Keep the DB name
|
||||
} else if (record.petnaming == 3 && IsClient()) {
|
||||
strcpy(npc_type->name, GetRandPetName());
|
||||
} else if (record.petnaming == 5 && IsClient()) {
|
||||
strcpy(npc_type->name, this->GetName());
|
||||
npc_type->name[24] = '\0';
|
||||
strcat(npc_type->name, "`s_ward");
|
||||
} else {
|
||||
strcpy(npc_type->name, this->GetCleanName());
|
||||
npc_type->name[25] = '\0';
|
||||
|
||||
Reference in New Issue
Block a user