diff --git a/changelog.txt b/changelog.txt index 53d48a83d..e42a60de8 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,12 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 02/25/2017 == +Uleat: Implemented rule-based node pathing for bots + - This currently applies to out-of-combat following movement and blocked los in-combat movement + - The default is set to 'true' (use node pathing) + - If you want to disable node pathing, apply the optional sql '2017_02_25_bots_use_pathing_rule.sql' file located in the utils/sql/git/bots/optional sub-directory. This will apply a 'false' rule..but, it can be changed as desired + - This helps with bot movement..but, there are still issues... + == 02/23/2017 == Uleat: Moved bot spell casting chance values into database - this will allow admins to tailor their bots without having to rebuild server code - Each entry uses a 4-dimensional identifier: [spell type index][class index][stance index][conditional index] diff --git a/common/ruletypes.h b/common/ruletypes.h index 20430735a..b5c0b0b99 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -558,6 +558,7 @@ RULE_BOOL(Bots, PreferNoManaCommandSpells, true) // Give sorting priority to new RULE_BOOL(Bots, QuestableSpawnLimit, false) // Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl RULE_BOOL(Bots, QuestableSpells, false) // Anita Thrall's (Anita_Thrall.pl) Bot Spell Scriber quests. RULE_INT(Bots, SpawnLimit, 71) // Number of bots a character can have spawned at one time, You + 71 bots is a 12 group pseudo-raid (bots are not raidable at this time) +RULE_BOOL(Bots, UsePathing, true) // Bots will use node pathing when moving RULE_BOOL(Bots, BotGroupXP, false) // Determines whether client gets xp for bots outside their group. RULE_BOOL(Bots, BotBardUseOutOfCombatSongs, true) // Determines whether bard bots use additional out of combat songs (optional script) RULE_BOOL(Bots, BotLevelsWithOwner, false) // Auto-updates spawned bots as owner levels/de-levels (false is original behavior) diff --git a/utils/sql/git/bots/optional/2017_02_25_bots_use_pathing_rule.sql b/utils/sql/git/bots/optional/2017_02_25_bots_use_pathing_rule.sql new file mode 100644 index 000000000..bf367d435 --- /dev/null +++ b/utils/sql/git/bots/optional/2017_02_25_bots_use_pathing_rule.sql @@ -0,0 +1 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:UsePathing', 'false', 'Bots will use node pathing when moving'); diff --git a/zone/bot.cpp b/zone/bot.cpp index 98d622742..2ae8f169c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -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)); @@ -2065,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()) @@ -2229,13 +2231,23 @@ void Bot::AI_Process() { return; } else if (!CheckLosFN(GetTarget())) { - if (!IsRooted()) { + 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; } - + return; } @@ -2516,33 +2528,52 @@ 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 following... + // Shows up really bad over long distances when movement bonuses are involved + if (cur_dist > GetFollowDistance()) { + if (RuleB(Bots, UsePathing) && zone->pathing) { + if (cur_dist <= GetFollowDistance() + 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 > GetFollowDistance() + BOT_FOLLOW_DISTANCE_CRITICAL) + speed = ((float)speed * 1.25f); // sprint mod (1/4 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 <= GetFollowDistance() + BOT_FOLLOW_DISTANCE_WALK) { + CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), GetWalkspeed()); + } + else { + int speed = GetRunspeed(); + if (cur_dist > GetFollowDistance() + BOT_FOLLOW_DISTANCE_CRITICAL) + speed = ((float)speed * 1.25f); // sprint mod (1/4 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; } else { if (moved) { @@ -2550,9 +2581,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(); } } diff --git a/zone/bot.h b/zone/bot.h index dd22f1498..095ef6720 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -38,7 +38,10 @@ #include -#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 625 // as DSq value (25 units) +#define BOT_FOLLOW_DISTANCE_CRITICAL 22500 // as DSq value (150 units) extern WorldServer worldserver; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index ef995ce69..5e15b0f08 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -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; } diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index cd2d33b3a..3ccc9726e 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -378,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; @@ -515,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())