mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-12 01:11:29 +00:00
Merge remote-tracking branch 'origin/master' into eqstream
This commit is contained in:
commit
0d1e63c92a
@ -1,5 +1,32 @@
|
||||
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)..so, consider disabling it if cpu use is too high
|
||||
- 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...
|
||||
Uleat: Implemented rule-based position update packet with movement timer check for bots
|
||||
- This currently only applies to out-of-combat movement
|
||||
- The default is set to 'false' (original behavior) to help save bandwidth (each bot will send an update packet every 1/10th of a second when enabled)
|
||||
- If you want to enable the position update packet, apply the optional sql '2017_02_25_bots_update_position_with_timer_rule.sql' file located in the utils/sql/git/bots/optional sub-directory. This will apply a 'true' rule..but, it can be changed as desired
|
||||
- This appears to help with/eliminate rubber banding
|
||||
|
||||
== 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]
|
||||
- [spell type index] is not the SpellType_## bit value..use SpellType_##Index instead
|
||||
- [class index] values of 0-15 are valid and determined by subtracting 1 from the actual class value
|
||||
- [stance index] is a direct correlation (0-6)
|
||||
- the [conditional index] is currently predicated on 2 compounded boolean states:
|
||||
- not primary healer/not primary slower: 0
|
||||
- primary healer/not primary slower: 1
|
||||
- not primary healer/ primary slower: 2
|
||||
- primary healer/primary slower: 3
|
||||
- Valid `value` entries are 0-100..though, the field accepts up to 255... Anything above 100 is clamped to 100 upon loading, however
|
||||
- Not all conditions are currently coded and changing a field may not produce any results
|
||||
- The 'default' database values will be changed and tweaked as bot spell code modifications occur
|
||||
|
||||
== 02/20/2017 ==
|
||||
Uleat: Reworked bard bot spell twisting and updated their spell (song) list
|
||||
Uleat: Added ability to shift to pre-combat song buffing by selecting a non-pet npc target, eliminating the need to mix all bard buff songs together
|
||||
|
||||
@ -558,6 +558,8 @@ 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, UpdatePositionWithTimer, false) // Sends a position update with every positive movement timer check
|
||||
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)
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9105
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9010
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9011
|
||||
#else
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
||||
#endif
|
||||
|
||||
5
utils/sql/data_tables.txt
Normal file
5
utils/sql/data_tables.txt
Normal file
@ -0,0 +1,5 @@
|
||||
command_settings
|
||||
launcher
|
||||
rule_sets
|
||||
rule_values
|
||||
variables
|
||||
@ -9,6 +9,7 @@
|
||||
9008|2016_06_28_bots_inventory_charges_update.sql|SELECT * FROM `information_schema`.`COLUMNS` isc WHERE isc.`TABLE_SCHEMA` = DATABASE() AND isc.`TABLE_NAME` = 'bot_inventories' AND isc.`COLUMN_NAME` = 'inst_charges' AND isc.`DATA_TYPE` = 'tinyint'|not_empty|
|
||||
9009|2017_02_15_bots_bot_spells_entries.sql|SELECT `id` FROM `npc_spells_entries` WHERE `npc_spells_id` >= 701 AND `npc_spells_id` <= 712|not_empty|
|
||||
9010|2017_02_20_bots_bard_spell_update.sql|SELECT * FROM `bot_spells_entries` WHERE `npc_spells_id` = 711 AND (`type` & 0xFFFF0000) = 0xFFFF0000|empty|
|
||||
9011|2017_02_23_bots_spell_casting_chances.sql|SHOW TABLES LIKE 'bot_spell_casting_chances'|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@ -0,0 +1 @@
|
||||
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Bots:UpdatePositionWithTimer', 'true', 'Sends a position update with every positive movement timer check');
|
||||
@ -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');
|
||||
11257
utils/sql/git/bots/required/2017_02_23_bots_spell_casting_chances.sql
Normal file
11257
utils/sql/git/bots/required/2017_02_23_bots_spell_casting_chances.sql
Normal file
File diff suppressed because it is too large
Load Diff
100
utils/sql/system_tables.txt
Normal file
100
utils/sql/system_tables.txt
Normal file
@ -0,0 +1,100 @@
|
||||
aa_ability
|
||||
aa_actions
|
||||
aa_effects
|
||||
aa_required_level_cost
|
||||
aa_ranks
|
||||
aa_rank_effects
|
||||
aa_rank_prereqs
|
||||
activities
|
||||
adventure_template
|
||||
adventure_template_entry
|
||||
adventure_template_entry_flavor
|
||||
altadv_vars
|
||||
alternate_currency
|
||||
base_data
|
||||
blocked_spells
|
||||
books
|
||||
char_create_combinations
|
||||
char_create_point_allocations
|
||||
class_skill
|
||||
damageshieldtypes
|
||||
doors
|
||||
faction_list
|
||||
faction_list_mod
|
||||
fear_hints
|
||||
fishing
|
||||
forage
|
||||
goallists
|
||||
graveyard
|
||||
grid
|
||||
grid_entries
|
||||
ground_spawns
|
||||
horses
|
||||
instance_list
|
||||
items
|
||||
ldon_trap_templates
|
||||
ldon_trap_entries
|
||||
level_exp_mods
|
||||
lootdrop
|
||||
lootdrop_entries
|
||||
loottable
|
||||
loottable_entries
|
||||
merc_armorinfo
|
||||
merc_weaponinfo
|
||||
merc_stats
|
||||
merc_merchant_entries
|
||||
merc_merchant_template_entries
|
||||
merc_merchant_templates
|
||||
merc_stance_entries
|
||||
merc_templates
|
||||
merc_npc_types
|
||||
merc_name_types
|
||||
merc_subtypes
|
||||
merc_types
|
||||
merc_spell_list_entries
|
||||
merc_spell_lists
|
||||
merc_buffs
|
||||
mercs
|
||||
merc_inventory
|
||||
merchantlist
|
||||
npc_emotes
|
||||
npc_faction
|
||||
npc_faction_entries
|
||||
npc_spells
|
||||
npc_spells_entries
|
||||
npc_spells_effects
|
||||
npc_spells_effects_entries
|
||||
npc_types
|
||||
npc_types_metadata
|
||||
npc_types_tint
|
||||
object
|
||||
pets
|
||||
pets_equipmentset
|
||||
pets_equipmentset_entries
|
||||
proximities
|
||||
races
|
||||
saylink
|
||||
skill_caps
|
||||
spawn2
|
||||
spawn_conditions
|
||||
spawn_condition_values
|
||||
spawn_events
|
||||
spawnentry
|
||||
spawngroup
|
||||
spells_new
|
||||
start_zones
|
||||
starting_items
|
||||
tasks
|
||||
tasksets
|
||||
titles
|
||||
tradeskill_recipe
|
||||
tradeskill_recipe_entries
|
||||
traps
|
||||
tribute_levels
|
||||
tributes
|
||||
veteran_reward_templates
|
||||
zone
|
||||
zone_points
|
||||
zone_server
|
||||
zone_state_dump
|
||||
zoneserver_auth
|
||||
93
utils/sql/user_tables.txt
Normal file
93
utils/sql/user_tables.txt
Normal file
@ -0,0 +1,93 @@
|
||||
aa_timers
|
||||
account
|
||||
account_flags
|
||||
account_ip
|
||||
account_rewards
|
||||
adventure_details
|
||||
adventure_members
|
||||
adventure_stats
|
||||
Banned_IPs
|
||||
bugs
|
||||
buyer
|
||||
char_recipe_list
|
||||
character_data
|
||||
character_currency
|
||||
character_alternate_abilities
|
||||
character_bind
|
||||
character_corpses
|
||||
character_corpse_items
|
||||
character_languages
|
||||
character_lookup
|
||||
character_skills
|
||||
character_spells
|
||||
character_memmed_spells
|
||||
character_disciplines
|
||||
character_material
|
||||
character_tribute
|
||||
character_bandolier
|
||||
character_potionbelt
|
||||
character_inspect_messages
|
||||
character_leadership_abilities
|
||||
character_activities
|
||||
character_alt_currency
|
||||
character_backup
|
||||
character_buffs
|
||||
character_enabledtasks
|
||||
character_pet_buffs
|
||||
character_pet_info
|
||||
character_pet_inventory
|
||||
character_tasks
|
||||
chatchannels
|
||||
completed_tasks
|
||||
discovered_items
|
||||
eventlog
|
||||
faction_values
|
||||
friends
|
||||
gm_ips
|
||||
group_id
|
||||
group_leaders
|
||||
guilds
|
||||
guild_bank
|
||||
guild_ranks
|
||||
guild_relations
|
||||
guild_members
|
||||
hackers
|
||||
instance_list_player
|
||||
inventory
|
||||
item_tick
|
||||
keyring
|
||||
launcher_zones
|
||||
lfguild
|
||||
mail
|
||||
merchantlist_temp
|
||||
name_filter
|
||||
object_contents
|
||||
petitions
|
||||
player_titlesets
|
||||
qs_player_move_record
|
||||
qs_player_move_record_entries
|
||||
qs_player_npc_kill_record
|
||||
qs_player_npc_kill_record_entries
|
||||
qs_player_speech
|
||||
qs_player_trade_record
|
||||
qs_player_trade_record_entries
|
||||
qs_merchant_transaction_record
|
||||
qs_merchant_transaction_record_entries
|
||||
qs_player_delete_record
|
||||
qs_player_delete_record_entries
|
||||
qs_player_handin_record
|
||||
qs_player_handin_record_entries
|
||||
qs_player_aa_rate_hourly
|
||||
qs_player_events
|
||||
quest_globals
|
||||
raid_details
|
||||
raid_leaders
|
||||
raid_members
|
||||
reports
|
||||
respawn_times
|
||||
sharedbank
|
||||
spell_globals
|
||||
timers
|
||||
trader
|
||||
trader_audit
|
||||
zone_flags
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
266
zone/bot.cpp
266
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));
|
||||
@ -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() {
|
||||
|
||||
22
zone/bot.h
22
zone/bot.h
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
2953
zone/botspellsai.cpp
2953
zone/botspellsai.cpp
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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';
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user