Activation of the new 'Bots' command system

This commit is contained in:
Uleat
2016-03-24 18:50:31 -04:00
parent 747895cbe5
commit b327da7092
48 changed files with 12821 additions and 5616 deletions
+6
View File
@@ -8,6 +8,8 @@ SET(zone_sources
beacon.cpp
bonuses.cpp
bot.cpp
bot_command.cpp
bot_database.cpp
botspellsai.cpp
client.cpp
client_mods.cpp
@@ -30,6 +32,7 @@ SET(zone_sources
guild.cpp
guild_mgr.cpp
hate_list.cpp
heal_rotation.cpp
horse.cpp
inventory.cpp
loottables.cpp
@@ -131,6 +134,8 @@ SET(zone_headers
basic_functions.h
beacon.h
bot.h
bot_command.h
bot_database.h
bot_structs.h
client.h
client_packet.h
@@ -149,6 +154,7 @@ SET(zone_headers
groups.h
guild_mgr.h
hate_list.h
heal_rotation.h
horse.h
lua_bit.h
lua_client.h
+2 -2
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2004 EQEMu Development Team (http://eqemulator.net)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
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
@@ -1428,7 +1428,7 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
}
}
auto race = GetArrayRace(GetBaseRace());
auto race = GetPlayerRaceValue(GetBaseRace());
race = race > 16 ? 1 : race;
if(!(ability->races & (1 << (race - 1)))) {
return false;
+472 -4773
View File
File diff suppressed because it is too large Load Diff
+132 -89
View File
@@ -1,3 +1,21 @@
/* 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_H
#define BOT_H
@@ -7,9 +25,11 @@
#include "mob.h"
#include "client.h"
#include "pets.h"
#include "heal_rotation.h"
#include "groups.h"
#include "corpse.h"
#include "zonedb.h"
#include "bot_database.h"
#include "string_ids.h"
#include "../common/misc_functions.h"
#include "../common/global_define.h"
@@ -29,8 +49,6 @@ const int DisciplineReuseStart = MaxSpellTimer + 1;
const int MaxTimer = MaxSpellTimer + MaxDisciplineTimer;
const int MaxStances = 7;
const int MaxSpellTypes = 16;
const int MaxHealRotationMembers = 6;
const int MaxHealRotationTargets = 3;
enum BotStanceType {
BotStancePassive,
@@ -39,9 +57,58 @@ enum BotStanceType {
BotStanceReactive,
BotStanceAggressive,
BotStanceBurn,
BotStanceBurnAE
BotStanceBurnAE,
BotStanceUnknown
};
#define BOT_STANCE_COUNT 8
#define VALIDBOTSTANCE(x) ((x >= (int)BotStancePassive && x <= (int)BotStanceBurnAE) ? ((BotStanceType)x) : (BotStanceUnknown))
static const std::string bot_stance_name[BOT_STANCE_COUNT] = {
"Passive", // 0
"Balanced", // 1
"Efficient", // 2
"Reactive", // 3
"Aggressive", // 4
"Burn", // 5
"BurnAE", // 6
"Unknown" // 7
};
static const char* GetBotStanceName(int stance_id) { return bot_stance_name[VALIDBOTSTANCE(stance_id)].c_str(); }
#define VALIDBOTEQUIPSLOT(x) ((x >= EmuConstants::EQUIPMENT_BEGIN && x <= EmuConstants::EQUIPMENT_END) ? (x) : ((x == MainPowerSource) ? (22) : (23)))
static std::string bot_equip_slot_name[EmuConstants::EQUIPMENT_SIZE + 2] =
{
"Charm", // MainCharm
"Left Ear", // MainEar1
"Head", // MainHead
"Face", // MainFace
"Right Ear", // MainEar2
"Neck", // MainNeck
"Shoulders", // MainShoulders
"Arms", // MainArms
"Back", // MainBack
"Left Wrist", // MainWrist1
"Right Wrist", // MainWrist2
"Range", // MainRange
"Hands", // MainHands
"Primary Hand", // MainPrimary
"Secondary Hand", // MainSecondary
"Left Finger", // MainFinger1
"Right Finger", // MainFinger2
"Chest", // MainChest
"Legs", // MainLegs
"Feet", // MainFeet
"Waist", // MainWaist
"Ammo", // MainAmmo
"Power Source", // 22 (MainPowerSource = 9999)
"Unknown"
};
static const char* GetBotEquipSlotName(int slot_id) { return bot_equip_slot_name[VALIDBOTEQUIPSLOT(slot_id)].c_str(); }
enum SpellTypeIndex {
SpellType_NukeIndex,
SpellType_HealIndex,
@@ -110,7 +177,7 @@ public:
BotRoleRaidHealer
};
enum EqExpansions {
enum EqExpansions { // expansions are off..EQ should be '0'
ExpansionNone,
ExpansionEQ,
ExpansionRoK,
@@ -156,8 +223,10 @@ public:
// Class Methods
bool IsValidRaceClassCombo();
static bool IsValidRaceClassCombo(uint16 r, uint8 c);
bool IsValidName();
static bool IsBotNameAvailable(char *botName, std::string* errorMessage);
static bool IsValidName(std::string& name);
static bool IsBotNameAvailable(const char *botName, std::string* errorMessage);
bool DeleteBot(std::string* errorMessage);
void Spawn(Client* botCharacterOwner, std::string* errorMessage);
virtual void SetLevel(uint8 in_level, bool command = false);
@@ -230,18 +299,7 @@ public:
bool HasOrMayGetAggro();
void SetDefaultBotStance();
void CalcChanceToCast();
void CreateHealRotation( Mob* target, uint32 timer = 10000 );
bool AddHealRotationMember( Bot* healer );
bool RemoveHealRotationMember( Bot* healer );
bool AddHealRotationTarget( Mob* target );
//bool AddHealRotationTarget( const char *targetName, int index);
bool AddHealRotationTarget( Mob* target, int index);
bool RemoveHealRotationTarget( Mob* target );
bool RemoveHealRotationTarget( int index);
void NotifyNextHealRotationMember( bool notifyNow = false );
void ClearHealRotationLeader() { _healRotationLeader = 0; }
void ClearHealRotationMembers();
void ClearHealRotationTargets();
inline virtual int32 GetMaxStat();
inline virtual int32 GetMaxResist();
inline virtual int32 GetMaxSTR();
@@ -299,6 +357,9 @@ public:
virtual bool AI_PursueCastCheck();
virtual bool AI_IdleCastCheck();
bool AIHealRotation(Mob* tar, bool useFastHeals);
bool GetPauseAI() { return _pauseAI; }
void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; }
// Mob AI Virtual Override Methods
virtual void AI_Process();
@@ -322,15 +383,6 @@ public:
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, uint16 slot = USE_ITEM_SPELL_SLOT, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
// Bot Action Command Methods
bool MesmerizeTarget(Mob* target);
bool Bot_Command_Resist(int resisttype, int level);
bool Bot_Command_DireTarget(int diretype, Mob *target);
bool Bot_Command_CharmTarget(int charmtype, Mob *target);
bool Bot_Command_CalmTarget(Mob *target);
bool Bot_Command_RezzTarget(Mob *target);
bool Bot_Command_Cure(int curetype, int level);
// Bot Equipment & Inventory Class Methods
void BotTradeSwapItem(Client* client, int16 lootSlot, const ItemInst* inst, const ItemInst* inst_swap, uint32 equipableSlots, std::string* errorMessage, bool swap = true);
void BotTradeAddItem(uint32 id, const ItemInst* inst, int16 charges, uint32 equipableSlots, uint16 lootSlot, std::string* errorMessage, bool addToDb = true);
@@ -339,20 +391,11 @@ public:
uint32 GetEquipmentColor(uint8 material_slot) const;
virtual void UpdateEquipmentLight() { m_Light.Type.Equipment = m_inv.FindBrightestLightType(); m_Light.Level.Equipment = m_Light.TypeToLevel(m_Light.Type.Equipment); }
// Static Class Methods
static void SaveBotGroup(Group* botGroup, std::string botGroupName, std::string* errorMessage);
static void DeleteBotGroup(std::string botGroupName, std::string* errorMessage);
static std::list<BotGroup> LoadBotGroup(std::string botGroupName, std::string* errorMessage);
static uint32 CanLoadBotGroup(uint32 botOwnerCharacterId, std::string botGroupName, std::string* errorMessage);
static uint32 GetBotGroupIdByBotGroupName(std::string botGroupName, std::string* errorMessage);
static uint32 GetBotGroupLeaderIdByBotGroupName(std::string botGroupName);
static std::list<BotGroupList> GetBotGroupListByBotOwnerCharacterId(uint32 botOwnerCharacterId, std::string* errorMessage);
static bool DoesBotGroupNameExist(std::string botGroupName);
// Static Class Methods
//static void DestroyBotRaidObjects(Client* client); // Can be removed after bot raids are dumped
static uint32 GetBotIDByBotName(std::string botName);
static Bot* LoadBot(uint32 botID, std::string* errorMessage);
static std::list<BotsAvailableList> GetBotList(uint32 botOwnerCharacterID, std::string* errorMessage);
static void ProcessBotCommands(Client *c, const Seperator *sep);
static std::list<SpawnedBotsList> ListSpawnedBots(uint32 characterID, std::string* errorMessage);
static uint32 SpawnedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage);
static uint32 CreatedBotCount(uint32 botOwnerCharacterID, std::string* errorMessage);
@@ -363,15 +406,10 @@ public:
static std::string ClassIdToString(uint16 classId);
static std::string RaceIdToString(uint16 raceId);
static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined);
static void BotGroupOrderFollow(Group* group, Client* client);
static void BotGroupOrderGuard(Group* group, Client* client);
static void BotGroupOrderAttack(Group* group, Mob* target, Client* client);
static void BotGroupSummon(Group* group, Client* client);
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 void BotOrderCampAll(Client* c);
static void BotHealRotationsClear( Client* c );
static void ProcessBotInspectionRequest(Bot* inspectedBot, Client* client);
static std::list<uint32> GetGroupedBotsByGroupId(uint32 groupId, std::string* errorMessage);
static void LoadAndSpawnAllZonedBots(Client* botOwner);
@@ -411,13 +449,10 @@ public:
static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target);
static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target);
static NPCType CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender);
static std::list<Bot*> GetBotsInHealRotation( Bot* leader );
// Static Bot Group Methods
static bool AddBotToGroup(Bot* bot, Group* group);
static bool RemoveBotFromGroup(Bot* bot, Group* group);
static bool BotGroupCreate(std::string botGroupLeaderName);
static bool BotGroupCreate(Bot* botGroupLeader);
static bool GroupHasClass(Group* group, uint8 classId);
static bool GroupHasClericClass(Group* group) { return GroupHasClass(group, CLERIC); }
static bool GroupHasDruidClass(Group* group) { return GroupHasClass(group, DRUID); }
@@ -445,26 +480,38 @@ public:
uint8 GetChanceToCastBySpellType(uint16 spellType);
bool IsGroupPrimaryHealer();
bool IsGroupPrimarySlower();
bool IsBotCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN || GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); }
bool IsBotINTCaster() { return (GetClass() == NECROMANCER || GetClass() == WIZARD || GetClass() == MAGICIAN || GetClass() == ENCHANTER); }
bool IsBotWISCaster() { return (GetClass() == CLERIC || GetClass() == DRUID || GetClass() == SHAMAN); }
bool IsBotCaster() { return IsCasterClass(GetClass()); }
bool IsBotINTCaster() { return IsINTCasterClass(GetClass()); }
bool IsBotWISCaster() { return IsWISCasterClass(GetClass()); }
bool CanHeal();
int GetRawACNoShield(int &shield_ac);
bool GetHasBeenSummoned() { return _hasBeenSummoned; }
const glm::vec3 GetPreSummonLocation() const { return m_PreSummonLocation; }
bool GetInHealRotation() { return _isInHealRotation; }
bool GetHealRotationActive() { return (GetInHealRotation() && _isHealRotationActive); }
bool GetHealRotationUseFastHeals() { return _healRotationUseFastHeals; }
bool GetHasHealedThisCycle() { return _hasHealedThisCycle; }
Mob* GetHealRotationTarget();
Mob* GetHealRotationTarget(uint8 index);
Bot* GetHealRotationLeader();
Bot* GetNextHealRotationMember();
Bot* GetPrevHealRotationMember();
uint8 GetNumHealRotationMembers () { return _numHealRotationMembers; }
uint32 GetHealRotationNextHealTime() { return _healRotationNextHeal; }
uint32 GetHealRotationTimer () { return _healRotationTimer; }
bool GetBardUseOutOfCombatSongs() { return _bardUseOutOfCombatSongs;}
// new heal rotation code
bool CreateHealRotation(uint32 cycle_duration_ms = 5000, bool fast_heals = false, bool adaptive_targeting = false, bool casting_override = false);
bool DestroyHealRotation();
bool JoinHealRotationMemberPool(std::shared_ptr<HealRotation>* heal_rotation);
bool LeaveHealRotationMemberPool();
bool IsHealRotationMember() { return (m_member_of_heal_rotation.use_count() && m_member_of_heal_rotation.get()); }
bool UseHealRotationFastHeals();
bool UseHealRotationAdaptiveTargeting();
bool IsHealRotationActive();
bool IsHealRotationReady();
bool IsHealRotationCaster();
bool HealRotationPokeTarget();
Mob* HealRotationTarget();
bool AdvanceHealRotation(bool use_interval = true);
bool IsMyHealRotationSet();
bool AmICastingForHealRotation();
void SetMyCastingForHealRotation(bool flag = true);
std::shared_ptr<HealRotation>* MemberOfHealRotation() { return &m_member_of_heal_rotation; }
bool GetAltOutOfCombatBehavior() { return _altoutofcombatbehavior;}
bool GetShowHelm() { return _showhelm; }
inline virtual int32 GetAC() const { return AC; }
inline virtual int32 GetSTR() const { return STR; }
@@ -532,34 +579,41 @@ public:
// void SetBotOwnerCharacterID(uint32 botOwnerCharacterID) { _botOwnerCharacterID = botOwnerCharacterID; }
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == RANGER ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
void SetBotRole(BotRoleType botRole) { _botRole = botRole; }
void SetBotStance(BotStanceType botStance) { _botStance = botStance; }
void SetBotStance(BotStanceType botStance) { _botStance = ((botStance != BotStanceUnknown) ? (botStance) : (BotStancePassive)); }
void SetSpellRecastTimer(int timer_index, int32 recast_delay);
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
void SetHasBeenSummoned(bool s);
void SetPreSummonLocation(const glm::vec3& location) { m_PreSummonLocation = location; }
void SetInHealRotation( bool inRotation ) { _isInHealRotation = inRotation; }
void SetHealRotationActive( bool isActive ) { _isHealRotationActive = isActive; }
void SetHealRotationUseFastHeals( bool useFastHeals ) { _healRotationUseFastHeals = useFastHeals; }
void SetHasHealedThisCycle( bool hasHealed ) { _hasHealedThisCycle = hasHealed; }
void SetHealRotationLeader( Bot* leader );
void SetNextHealRotationMember( Bot* healer );
void SetPrevHealRotationMember( Bot* healer );
void SetHealRotationNextHealTime( uint32 nextHealTime ) { _healRotationNextHeal = nextHealTime; }
void SetHealRotationTimer( uint32 timer ) { _healRotationTimer = timer; }
void SetNumHealRotationMembers( uint8 numMembers ) { _numHealRotationMembers = numMembers; }
void SetBardUseOutOfCombatSongs(bool useOutOfCombatSongs) { _bardUseOutOfCombatSongs = useOutOfCombatSongs;}
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
void SetShowHelm(bool showhelm) { _showhelm = showhelm; }
void SetBeardColor(uint8 value) { beardcolor = value; }
void SetBeard(uint8 value) { beard = value; }
void SetEyeColor1(uint8 value) { eyecolor1 = value; }
void SetEyeColor2(uint8 value) { eyecolor2 = value; }
void SetLuclinFace(uint8 value) { luclinface = value; }
void SetHairColor(uint8 value) { haircolor = value; }
void SetHairStyle(uint8 value) { hairstyle = value; }
void SetDrakkinDetails(uint32 value) { drakkin_details = value; }
void SetDrakkinHeritage(uint32 value) { drakkin_heritage = value; }
void SetDrakkinTattoo(uint32 value) { drakkin_tattoo = value; }
bool DyeArmor(int16 slot_id, uint32 rgb, bool all_flag = false, bool save_flag = true);
std::string CreateSayLink(Client* botOwner, const char* message, const char* name);
// Class Destructors
virtual ~Bot();
// Publicized protected/private functions
virtual void BotRangedAttack(Mob* other); // protected
uint32 GetBotItemsCount(std::string* errorMessage); // private
void BotRemoveEquipItem(int slot); // private
void RemoveBotItemBySlot(uint32 slotID, std::string* errorMessage); // private
protected:
virtual void PetAIProcess();
static NPCType FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack);
virtual void BotMeditate(bool isSitting);
virtual void BotRangedAttack(Mob* other);
virtual bool CheckBotDoubleAttack(bool Triple = false);
virtual int32 GetBotFocusEffect(BotfocusType bottype, uint16 spell_id);
virtual int32 CalcBotFocusEffect(BotfocusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false);
@@ -607,22 +661,14 @@ private:
bool _hasBeenSummoned;
glm::vec3 m_PreSummonLocation;
uint8 _spellCastingChances[MaxStances][MaxSpellTypes];
bool _isInHealRotation;
bool _isHealRotationActive;
bool _healRotationUseFastHeals;
bool _hasHealedThisCycle;
uint32 _healRotationTimer;
uint32 _healRotationNextHeal;
//char _healRotationTargets[MaxHealRotationTargets][64];
uint16 _healRotationTargets[MaxHealRotationTargets];
uint32 _healRotationLeader;
uint32 _healRotationMemberNext;
uint32 _healRotationMemberPrev;
uint8 _numHealRotationMembers;
std::shared_ptr<HealRotation> m_member_of_heal_rotation;
std::map<uint32, BotAA> botAAs;
InspectMessage_Struct _botInspectMessage;
bool _bardUseOutOfCombatSongs;
bool _altoutofcombatbehavior;
bool _showhelm;
bool _pauseAI;
// Private "base stats" Members
int32 _baseMR;
@@ -656,12 +702,9 @@ private:
// Private "Inventory" Methods
void GetBotItems(std::string* errorMessage, Inventory &inv);
void BotRemoveEquipItem(int slot);
void BotAddEquipItem(int slot, uint32 id);
uint32 GetBotItemBySlot(uint32 slotID);
void RemoveBotItemBySlot(uint32 slotID, std::string* errorMessage);
void SetBotItemInSlot(uint32 slotID, uint32 itemID, const ItemInst* inst, std::string* errorMessage);
uint32 GetBotItemsCount(std::string* errorMessage);
uint32 GetTotalPlayTime();
void SaveBuffs(); // Saves existing buffs to the database to persist zoning and camping
void LoadBuffs(); // Retrieves saved buffs from the database on spawning
+7312
View File
File diff suppressed because it is too large Load Diff
+667
View File
@@ -0,0 +1,667 @@
/* 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_COMMAND_H
#define BOT_COMMAND_H
class Client;
class Seperator;
#include "../common/types.h"
#include "bot.h"
class BCEnum
{
public:
typedef enum SpellType {
SpT_None = 0,
SpT_BindAffinity,
SpT_Charm,
SpT_Cure,
SpT_Depart,
SpT_Escape,
SpT_Identify,
SpT_Invisibility,
SpT_Levitation,
SpT_Lull,
SpT_Mesmerize,
SpT_MovementSpeed,
SpT_Resistance,
SpT_Resurrect,
SpT_Rune,
SpT_SendHome,
SpT_Size,
SpT_Stance,
SpT_SummonCorpse,
SpT_WaterBreathing
} SpType;
static const int SpellTypeFirst = SpT_BindAffinity;
static const int SpellTypeLast = SpT_WaterBreathing;
typedef enum TargetType {
TT_None = 0,
TT_Corpse,
TT_Self,
TT_Animal,
TT_Undead,
TT_Summoned,
TT_Plant,
TT_Single,
TT_GroupV1,
TT_GroupV2,
TT_AECaster,
TT_AEBard,
TT_AETarget
} TType;
static const int TargetTypeFirst = TT_Corpse;
static const int TargetTypeLast = TT_AETarget;
static const int TargetTypeCount = 13;
typedef enum TargetMask {
TM_None = 0,
TM_Corpse = 1,
TM_Self = 2,
TM_Animal = 4,
TM_Undead = 8,
TM_Summoned = 16,
TM_Plant = 32,
TM_Single = 124, // currently, 2^6 + 2^{2..5}) -or- (64+32+16+8+4)
TM_GroupV1 = 128,
TM_GroupV2 = 256,
TM_AECaster = 512,
TM_AEBard = 1024,
TM_AETarget = 2048
} TMask;
typedef enum AppearanceFailType {
AFT_None = 0,
AFT_Value,
AFT_GenderRace,
AFT_Race
} AFType;
typedef enum AilmentType {
AT_None = 0,
AT_Blindness, // SE: 20
AT_Disease, // SE: 35
AT_Poison, // SE: 36
AT_Curse, // SE: 116
AT_Corruption // SE: 369
} AType;
static const int AilmentTypeCount = 5;
typedef enum InvisibilityType {
IT_None = 0,
IT_Animal,
IT_Undead,
IT_Living,
IT_See
} IType;
typedef enum ResistanceType {
RT_None = 0,
RT_Fire, // SE: 46
RT_Cold, // SE: 47
RT_Poison, // SE: 48
RT_Disease, // SE: 49
RT_Magic, // SE: 50
RT_Corruption // SE: 370
} RType;
static const int ResistanceTypeCount = 6;
typedef enum SizeType {
SzT_None = 0,
SzT_Enlarge,
SzT_Reduce
} SzType;
typedef enum StanceType {
StT_None = 0,
StT_Aggressive,
StT_Defensive
} StType;
static std::string SpellTypeEnumToString(BCEnum::SpType spell_type) {
switch (spell_type) {
case SpT_BindAffinity:
return "SpT_BindAffinity";
case SpT_Charm:
return "SpT_Charm";
case SpT_Cure:
return "SpT_Cure";
case SpT_Depart:
return "SpT_Depart";
case SpT_Escape:
return "SpT_Escape";
case SpT_Identify:
return "SpT_Identify";
case SpT_Invisibility:
return "SpT_Invisibility";
case SpT_Levitation:
return "SpT_Levitation";
case SpT_Lull:
return "SpT_Lull";
case SpT_Mesmerize:
return "SpT_Mesmerize";
case SpT_MovementSpeed:
return "SpT_MovementSpeed";
case SpT_Resistance:
return "SpT_Resistance";
case SpT_Resurrect:
return "SpT_Resurrect";
case SpT_Rune:
return "SpT_Rune";
case SpT_SendHome:
return "SpT_SendHome";
case SpT_Size:
return "SpT_Size";
case SpT_Stance:
return "SpT_Stance";
case SpT_SummonCorpse:
return "SpT_SummonCorpse";
case SpT_WaterBreathing:
return "SpT_WaterBreathing";
default:
return "SpT_None";
}
}
static std::string TargetTypeEnumToString(BCEnum::TType target_type) {
switch (target_type) {
case TT_Self:
return "TT_Self";
case TT_Animal:
return "TT_Animal";
case TT_Undead:
return "TT_Undead";
case TT_Summoned:
return "TT_Summoned";
case TT_Plant:
return "TT_Plant";
case TT_Single:
return "TT_Single";
case TT_GroupV1:
return "TT_GroupV1";
case TT_GroupV2:
return "TT_GroupV2";
case TT_AECaster:
return "TT_AECaster";
case TT_AEBard:
return "TT_AEBard";
case TT_AETarget:
return "TT_AETarget";
case TT_Corpse:
return "TT_Corpse";
default:
return "TT_None";
}
}
};
class STBaseEntry;
class STCharmEntry;
class STCureEntry;
class STDepartEntry;
class STEscapeEntry;
class STInvisibilityEntry;
class STMovementSpeedEntry;
class STResistanceEntry;
class STResurrectEntry;
class STSendHomeEntry;
class STSizeEntry;
class STStanceEntry;
class STBaseEntry
{
protected:
BCEnum::SpType m_bcst;
public:
int spell_id;
uint8 spell_level;
uint8 caster_class;
BCEnum::TType target_type;
// A non-polymorphic constructor requires an appropriate, non-'ST_None' BCEnum::SType
STBaseEntry(BCEnum::SpType init_bcst = BCEnum::SpT_None) {
spell_id = 0;
spell_level = 255;
caster_class = 255;
target_type = BCEnum::TT_None;
m_bcst = init_bcst;
}
STBaseEntry(STBaseEntry* prototype) {
spell_id = prototype->spell_id;
spell_level = 255;
caster_class = 255;
target_type = prototype->target_type;
m_bcst = prototype->BCST();
}
virtual ~STBaseEntry() { return; };
BCEnum::SpType BCST() { return m_bcst; }
virtual bool IsDerived() { return false; }
bool IsCharm() const { return (m_bcst == BCEnum::SpT_Charm); }
bool IsCure() const { return (m_bcst == BCEnum::SpT_Cure); }
bool IsDepart() const { return (m_bcst == BCEnum::SpT_Depart); }
bool IsEscape() const { return (m_bcst == BCEnum::SpT_Escape); }
bool IsInvisibility() const { return (m_bcst == BCEnum::SpT_Invisibility); }
bool IsMovementSpeed() const { return (m_bcst == BCEnum::SpT_MovementSpeed); }
bool IsResistance() const { return (m_bcst == BCEnum::SpT_Resistance); }
bool IsResurrect() const { return (m_bcst == BCEnum::SpT_Resurrect); }
bool IsSendHome() const { return (m_bcst == BCEnum::SpT_SendHome); }
bool IsSize() const { return (m_bcst == BCEnum::SpT_Size); }
bool IsStance() const { return (m_bcst == BCEnum::SpT_Stance); }
virtual STCharmEntry* SafeCastToCharm() { return nullptr; }
virtual STCureEntry* SafeCastToCure() { return nullptr; }
virtual STDepartEntry* SafeCastToDepart() { return nullptr; }
virtual STEscapeEntry* SafeCastToEscape() { return nullptr; }
virtual STInvisibilityEntry* SafeCastToInvisibility() { return nullptr; }
virtual STMovementSpeedEntry* SafeCastToMovementSpeed() { return nullptr; }
virtual STResistanceEntry* SafeCastToResistance() { return nullptr; }
virtual STResurrectEntry* SafeCastToResurrect() { return nullptr; }
virtual STSendHomeEntry* SafeCastToSendHome() { return nullptr; }
virtual STSizeEntry* SafeCastToSize() { return nullptr; }
virtual STStanceEntry* SafeCastToStance() { return nullptr; }
};
class STCharmEntry : public STBaseEntry
{
public:
bool dire;
STCharmEntry() {
m_bcst = BCEnum::SpT_Charm;
dire = false;
}
STCharmEntry(STCharmEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_Charm;
dire = prototype->dire;
}
virtual ~STCharmEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STCharmEntry* SafeCastToCharm() { return ((m_bcst == BCEnum::SpT_Charm) ? (static_cast<STCharmEntry*>(this)) : (nullptr)); }
};
class STCureEntry : public STBaseEntry
{
public:
int cure_value[BCEnum::AilmentTypeCount];
int cure_total;
STCureEntry() {
m_bcst = BCEnum::SpT_Cure;
memset(&cure_value, 0, (sizeof(int) * BCEnum::AilmentTypeCount));
cure_total = 0;
}
STCureEntry(STCureEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_Cure;
memcpy(&cure_value, prototype->cure_value, (sizeof(int) * BCEnum::AilmentTypeCount));
cure_total = prototype->cure_total;
}
virtual ~STCureEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STCureEntry* SafeCastToCure() { return ((m_bcst == BCEnum::SpT_Cure) ? (static_cast<STCureEntry*>(this)) : (nullptr)); }
};
class STDepartEntry : public STBaseEntry
{
public:
bool single;
std::string long_name;
STDepartEntry() {
m_bcst = BCEnum::SpT_Depart;
single = false;
long_name.clear();
}
STDepartEntry(STDepartEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_Depart;
single = prototype->single;
long_name = prototype->long_name;
}
virtual ~STDepartEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STDepartEntry* SafeCastToDepart() { return ((m_bcst == BCEnum::SpT_Depart) ? (static_cast<STDepartEntry*>(this)) : (nullptr)); }
};
class STEscapeEntry : public STBaseEntry
{
public:
bool lesser;
STEscapeEntry() {
m_bcst = BCEnum::SpT_Escape;
lesser = false;
}
STEscapeEntry(STEscapeEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_Escape;
lesser = prototype->lesser;
}
virtual ~STEscapeEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STEscapeEntry* SafeCastToEscape() { return ((m_bcst == BCEnum::SpT_Escape) ? (static_cast<STEscapeEntry*>(this)) : (nullptr)); }
};
class STInvisibilityEntry : public STBaseEntry
{
public:
BCEnum::IType invis_type;
STInvisibilityEntry() {
m_bcst = BCEnum::SpT_Invisibility;
invis_type = BCEnum::IT_None;
}
STInvisibilityEntry(STInvisibilityEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_Invisibility;
invis_type = prototype->invis_type;
}
virtual ~STInvisibilityEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STInvisibilityEntry* SafeCastToInvisibility() { return ((m_bcst == BCEnum::SpT_Invisibility) ? (static_cast<STInvisibilityEntry*>(this)) : (nullptr)); }
};
class STMovementSpeedEntry : public STBaseEntry
{
public:
bool group;
STMovementSpeedEntry() {
m_bcst = BCEnum::SpT_MovementSpeed;
group = false;
}
STMovementSpeedEntry(STMovementSpeedEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_MovementSpeed;
group = prototype->group;
}
virtual ~STMovementSpeedEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STMovementSpeedEntry* SafeCastToMovementSpeed() { return ((m_bcst == BCEnum::SpT_MovementSpeed) ? (static_cast<STMovementSpeedEntry*>(this)) : (nullptr)); }
};
class STResistanceEntry : public STBaseEntry
{
public:
int resist_value[BCEnum::ResistanceTypeCount];
int resist_total;
STResistanceEntry() {
m_bcst = BCEnum::SpT_Resistance;
memset(&resist_value, 0, (sizeof(int) * BCEnum::ResistanceTypeCount));
resist_total = 0;
}
STResistanceEntry(STResistanceEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_Resistance;
memcpy(&resist_value, prototype->resist_value, (sizeof(int) * BCEnum::ResistanceTypeCount));
resist_total = prototype->resist_total;
}
virtual ~STResistanceEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STResistanceEntry* SafeCastToResistance() { return ((m_bcst == BCEnum::SpT_Resistance) ? (static_cast<STResistanceEntry*>(this)) : (nullptr)); }
};
class STResurrectEntry : public STBaseEntry
{
public:
bool aoe;
STResurrectEntry() {
m_bcst = BCEnum::SpT_Resurrect;
aoe = false;
}
STResurrectEntry(STResurrectEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_Resurrect;
aoe = prototype->aoe;
}
virtual ~STResurrectEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STResurrectEntry* SafeCastToResurrect() { return ((m_bcst == BCEnum::SpT_Resurrect) ? (static_cast<STResurrectEntry*>(this)) : (nullptr)); }
};
class STSendHomeEntry : public STBaseEntry
{
public:
bool group;
STSendHomeEntry() {
m_bcst = BCEnum::SpT_SendHome;
group = false;
}
STSendHomeEntry(STSendHomeEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_SendHome;
group = prototype->group;
}
virtual ~STSendHomeEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STSendHomeEntry* SafeCastToSendHome() { return ((m_bcst == BCEnum::SpT_SendHome) ? (static_cast<STSendHomeEntry*>(this)) : (nullptr)); }
};
class STSizeEntry : public STBaseEntry
{
public:
BCEnum::SzType size_type;
STSizeEntry() {
m_bcst = BCEnum::SpT_Size;
size_type = BCEnum::SzT_None;
}
STSizeEntry(STSizeEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_Size;
size_type = prototype->size_type;
}
virtual ~STSizeEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STSizeEntry* SafeCastToSize() { return ((m_bcst == BCEnum::SpT_Size) ? (static_cast<STSizeEntry*>(this)) : (nullptr)); }
};
class STStanceEntry : public STBaseEntry {
public:
BCEnum::StType stance_type;
STStanceEntry() {
m_bcst = BCEnum::SpT_Stance;
stance_type = BCEnum::StT_None;
}
STStanceEntry(STStanceEntry* prototype) : STBaseEntry(prototype) {
m_bcst = BCEnum::SpT_Stance;
stance_type = prototype->stance_type;
}
virtual ~STStanceEntry() { return; };
virtual bool IsDerived() { return true; }
virtual STStanceEntry* SafeCastToStance() { return ((m_bcst == BCEnum::SpT_Stance) ? (static_cast<STStanceEntry*>(this)) : (nullptr)); }
};
typedef std::list<STBaseEntry*> bcst_list;
typedef std::map<BCEnum::SpType, bcst_list> bcst_map;
typedef std::map<BCEnum::SpType, std::string> bcst_required_bot_classes_map;
typedef std::map<BCEnum::SpType, std::map<uint8, std::string>> bcst_required_bot_classes_map_by_class;
typedef std::map<uint8, uint8> bcst_levels;
typedef std::map<BCEnum::SpType, bcst_levels> bcst_levels_map;
#define BOT_COMMAND_CHAR '^'
typedef void (*BotCmdFuncPtr)(Client *,const Seperator *);
typedef struct {
int access;
const char *desc; // description of bot command
BotCmdFuncPtr function; // null means perl function
} BotCommandRecord;
extern int (*bot_command_dispatch)(Client *,char const*);
extern int bot_command_count; // number of bot commands loaded
// the bot command system:
int bot_command_init(void);
void bot_command_deinit(void);
int bot_command_add(std::string bot_command_name, const char *desc, int access, BotCmdFuncPtr function);
int bot_command_not_avail(Client *c, const char *message);
int bot_command_real_dispatch(Client *c, char const *message);
void bot_command_log_command(Client *c, const char *message);
// bot commands
void bot_command_actionable(Client *c, const Seperator *sep);
void bot_command_aggressive(Client *c, const Seperator *sep);
void bot_command_attack(Client *c, const Seperator *sep);
void bot_command_bind_affinity(Client *c, const Seperator *sep);
void bot_command_bot(Client *c, const Seperator *sep);
void bot_command_botgroup(Client *c, const Seperator *sep);
void bot_command_charm(Client *c, const Seperator *sep);
void bot_command_cure(Client *c, const Seperator *sep);
void bot_command_defensive(Client *c, const Seperator *sep);
void bot_command_depart(Client *c, const Seperator *sep);
void bot_command_escape(Client *c, const Seperator *sep);
void bot_command_find_aliases(Client *c, const Seperator *sep);
void bot_command_follow(Client *c, const Seperator *sep);
void bot_command_guard(Client *c, const Seperator *sep);
void bot_command_heal_rotation(Client *c, const Seperator *sep);
void bot_command_help(Client *c, const Seperator *sep);
void bot_command_hold(Client *c, const Seperator *sep);
void bot_command_identify(Client *c, const Seperator *sep);
void bot_command_inventory(Client *c, const Seperator *sep);
void bot_command_invisibility(Client *c, const Seperator *sep);
void bot_command_levitation(Client *c, const Seperator *sep);
void bot_command_lull(Client *c, const Seperator *sep);
void bot_command_mesmerize(Client *c, const Seperator *sep);
void bot_command_movement_speed(Client *c, const Seperator *sep);
void bot_command_pet(Client *c, const Seperator *sep);
void bot_command_pick_lock(Client *c, const Seperator *sep);
void bot_command_pull(Client *c, const Seperator *sep);
void bot_command_release(Client *c, const Seperator *sep);
void bot_command_resistance(Client *c, const Seperator *sep);
void bot_command_resurrect(Client *c, const Seperator *sep);
void bot_command_rune(Client *c, const Seperator *sep);
void bot_command_send_home(Client *c, const Seperator *sep);
void bot_command_size(Client *c, const Seperator *sep);
void bot_command_summon_corpse(Client *c, const Seperator *sep);
void bot_command_taunt(Client *c, const Seperator *sep);
void bot_command_track(Client *c, const Seperator *sep);
void bot_command_water_breathing(Client *c, const Seperator *sep);
// bot subcommands
void bot_subcommand_bot_appearance(Client *c, const Seperator *sep);
void bot_subcommand_bot_beard_color(Client *c, const Seperator *sep);
void bot_subcommand_bot_beard_style(Client *c, const Seperator *sep);
void bot_subcommand_bot_camp(Client *c, const Seperator *sep);
void bot_subcommand_bot_clone(Client *c, const Seperator *sep);
void bot_subcommand_bot_create(Client *c, const Seperator *sep);
void bot_subcommand_bot_delete(Client *c, const Seperator *sep);
void bot_subcommand_bot_details(Client *c, const Seperator *sep);
void bot_subcommand_bot_dye_armor(Client *c, const Seperator *sep);
void bot_subcommand_bot_eyes(Client *c, const Seperator *sep);
void bot_subcommand_bot_face(Client *c, const Seperator *sep);
void bot_subcommand_bot_follow_distance(Client *c, const Seperator *sep);
void bot_subcommand_bot_hair_color(Client *c, const Seperator *sep);
void bot_subcommand_bot_hairstyle(Client *c, const Seperator *sep);
void bot_subcommand_bot_heritage(Client *c, const Seperator *sep);
void bot_subcommand_bot_inspect_message(Client *c, const Seperator *sep);
void bot_subcommand_bot_list(Client *c, const Seperator *sep);
void bot_subcommand_bot_out_of_combat(Client *c, const Seperator *sep);
void bot_subcommand_bot_report(Client *c, const Seperator *sep);
void bot_subcommand_bot_spawn(Client *c, const Seperator *sep);
void bot_subcommand_bot_stance(Client *c, const Seperator *sep);
void bot_subcommand_bot_summon(Client *c, const Seperator *sep);
void bot_subcommand_bot_tattoo(Client *c, const Seperator *sep);
void bot_subcommand_bot_toggle_archer(Client *c, const Seperator *sep);
void bot_subcommand_bot_toggle_helm(Client *c, const Seperator *sep);
void bot_subcommand_bot_update(Client *c, const Seperator *sep);
void bot_subcommand_bot_woad(Client *c, const Seperator *sep);
void bot_subcommand_botgroup_add_member(Client *c, const Seperator *sep);
void bot_subcommand_botgroup_create(Client *c, const Seperator *sep);
void bot_subcommand_botgroup_delete(Client *c, const Seperator *sep);
void bot_subcommand_botgroup_list(Client *c, const Seperator *sep);
void bot_subcommand_botgroup_load(Client *c, const Seperator *sep);
void bot_subcommand_botgroup_remove_member(Client *c, const Seperator *sep);
void bot_subcommand_circle(Client *c, const Seperator *sep);
void bot_subcommand_evacuate(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_adaptive_targeting(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_add_member(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_add_target(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_adjust_critical(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_adjust_safe(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_casting_override(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_change_interval(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_clear_targets(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_create(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_fast_heals(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_list(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_remove_member(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_remove_target(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_reset_limits(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_start(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_stop(Client *c, const Seperator *sep);
void bot_subcommand_inventory_give(Client *c, const Seperator *sep);
void bot_subcommand_inventory_list(Client *c, const Seperator *sep);
void bot_subcommand_inventory_remove(Client *c, const Seperator *sep);
void bot_subcommand_pet_remove(Client *c, const Seperator *sep);
void bot_subcommand_pet_set_type(Client *c, const Seperator *sep);
void bot_subcommand_portal(Client *c, const Seperator *sep);
void bot_subcommand_succor(Client *c, const Seperator *sep);
// bot command helpers
bool helper_bot_appearance_fail(Client *bot_owner, Bot *my_bot, BCEnum::AFType fail_type, const char* type_desc);
void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot);
void helper_bot_appearance_form_update(Bot *my_bot);
uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_class, uint16 bot_race, uint8 bot_gender);
void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot);
bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast = true, uint32* dont_root_before = nullptr);
bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command);
void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_bot, bcst_list* local_list, bool single_flag = false);
bool helper_is_help_or_usage(const char* arg);
bool helper_no_available_bots(Client *bot_owner, Bot *my_bot = nullptr);
void helper_send_available_subcommands(Client *bot_owner, const char* command_simile, const std::list<const char*>& subcommand_list);
void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class = 0);
bool helper_spell_check_fail(STBaseEntry* local_entry);
bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type);
#endif
+732
View File
@@ -0,0 +1,732 @@
/* 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 "../common/global_define.h"
#include "../common/rulesys.h"
#include "../common/string_util.h"
#include "../common/eqemu_logsys.h"
#include "bot_database.h"
#include "bot.h"
BotDatabase botdb;
BotDatabase::BotDatabase()
{
}
BotDatabase::BotDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port)
{
Connect(host, user, passwd, database, port);
}
BotDatabase::~BotDatabase()
{
}
bool BotDatabase::Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port) {
uint32 errnum = 0;
char errbuf[MYSQL_ERRMSG_SIZE];
if (!Open(host, user, passwd, database, port, &errnum, errbuf)) {
Log.Out(Logs::General, Logs::Error, "Failed to connect to bot database: Error: %s", errbuf);
return false;
}
else {
Log.Out(Logs::General, Logs::Status, "Using bot database '%s' at %s:%d", database, host, port);
return true;
}
}
bool BotDatabase::GetCommandSettings(std::map<std::string, std::pair<uint8, std::vector<std::string>>> &bot_command_settings)
{
bot_command_settings.clear();
std::string query = "SELECT `bot_command`, `access`, `aliases` FROM `bot_command_settings`";
auto results = QueryDatabase(query);
if (!results.Success())
return false;
for (auto row = results.begin(); row != results.end(); ++row) {
bot_command_settings[row[0]].first = atoi(row[1]);
if (row[2][0] == 0)
continue;
auto aliases = SplitString(row[2], '|');
for (auto iter : aliases) {
if (!iter.empty())
bot_command_settings[row[0]].second.push_back(iter);
}
}
return true;
}
// Bot command functions
bool BotDatabase::GetInspectMessage(uint32 bot_id, InspectMessage_Struct* message)
{
std::string query = StringFormat("SELECT `inspect_message` FROM `bot_inspect_messages` WHERE `bot_id` = %i LIMIT 1", bot_id);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
auto row = results.begin();
memset(message, '\0', sizeof(InspectMessage_Struct));
for (auto row = results.begin(); row != results.end(); ++row) {
memcpy(message, row[0], sizeof(InspectMessage_Struct));
}
return true;
}
bool BotDatabase::SetInspectMessage(uint32 bot_id, const InspectMessage_Struct* message)
{
std::string query = StringFormat("REPLACE INTO `bot_inspect_messages` (bot_id, inspect_message) VALUES (%u, '%s')", bot_id, EscapeString(message->text).c_str());
auto results = QueryDatabase(query);
return results.Success();
}
bool BotDatabase::SetAllInspectMessages(uint32 owner_id, const InspectMessage_Struct* message)
{
std::string query = StringFormat(
"UPDATE `bot_inspect_messages`"
" SET `inspect_message` = '%s'"
" WHERE `bot_id`"
" IN (SELECT `bot_id` FROM `bot_data` WHERE `owner_id` = '%u')",
EscapeString(message->text).c_str(), owner_id
);
auto results = QueryDatabase(query);
return results.Success();
}
bool BotDatabase::SetAllArmorColorBySlot(uint32 owner_id, int16 slot_id, uint32 rgb_value)
{
if (!owner_id)
return false;
std::string query = StringFormat(
"UPDATE `bot_inventories` bi"
" INNER JOIN `bot_data` bd"
" ON bd.`owner_id` = '%u'"
" SET bi.`inst_color` = '%u'"
" WHERE bi.`bot_id` = bd.`bot_id`"
" AND bi.`slot_id` IN (%u, %u, %u, %u, %u, %u, %u, %u)"
" AND bi.`slot_id` = '%i'",
owner_id,
rgb_value,
MainHead, MainChest, MainArms, MainWrist1, MainWrist2, MainHands, MainLegs, MainFeet,
slot_id
);
auto results = QueryDatabase(query);
return results.Success();
}
bool BotDatabase::SetAllArmorColors(uint32 owner_id, uint32 rgb_value)
{
if (!owner_id)
return false;
std::string query = StringFormat(
"UPDATE `bot_inventories` bi"
" INNER JOIN `bot_data` bd"
" ON bd.`owner_id` = '%u'"
" SET bi.`inst_color` = '%u'"
" WHERE bi.`bot_id` = bd.`bot_id`"
" AND bi.`slot_id` IN (%u, %u, %u, %u, %u, %u, %u, %u)",
owner_id,
rgb_value,
MainHead, MainChest, MainArms, MainWrist1, MainWrist2, MainHands, MainLegs, MainFeet
);
auto results = QueryDatabase(query);
return results.Success();
}
bool BotDatabase::SetHelmAppearance(uint32 owner_id, uint32 bot_id, bool show_flag)
{
if (!owner_id || !bot_id)
return false;
std::string query = StringFormat(
"UPDATE `bot_data`"
" SET `show_helm` = '%u'"
" WHERE `owner_id` = '%u'"
" AND `bot_id` = '%u'",
(show_flag ? 1 : 0),
owner_id,
bot_id
);
auto results = QueryDatabase(query);
return results.Success();
}
bool BotDatabase::SetAllHelmAppearances(uint32 owner_id, bool show_flag)
{
if (!owner_id)
return false;
std::string query = StringFormat(
"UPDATE `bot_data`"
" SET `show_helm` = '%u'"
" WHERE `owner_id` = '%u'",
(show_flag ? 1 : 0),
owner_id
);
auto results = QueryDatabase(query);
return results.Success();
}
bool BotDatabase::ToggleHelmAppearance(uint32 owner_id, uint32 bot_id)
{
if (!owner_id || !bot_id)
return false;
std::string query = StringFormat(
"UPDATE `bot_data`"
" SET `show_helm` = (`show_helm` XOR '1')"
" WHERE `owner_id` = '%u'"
" AND `bot_id` = '%u'",
owner_id,
bot_id
);
auto results = QueryDatabase(query);
return results.Success();
}
bool BotDatabase::ToggleAllHelmAppearances(uint32 owner_id)
{
if (!owner_id)
return false;
std::string query = StringFormat(
"UPDATE `bot_data`"
" SET `show_helm` = (`show_helm` XOR '1')"
" WHERE `owner_id` = '%u'",
owner_id
);
auto results = QueryDatabase(query);
return results.Success();
}
bool BotDatabase::SetFollowDistance(uint32 owner_id, uint32 bot_id, uint32 follow_distance)
{
if (!owner_id || !bot_id || !follow_distance)
return false;
std::string query = StringFormat(
"UPDATE `bot_data`"
" SET `follow_distance` = '%u'"
" WHERE `owner_id` = '%u'"
" AND `bot_id` = '%u'",
follow_distance,
owner_id,
bot_id
);
auto results = QueryDatabase(query);
return results.Success();
}
bool BotDatabase::SetAllFollowDistances(uint32 owner_id, uint32 follow_distance)
{
if (!owner_id || !follow_distance)
return false;
std::string query = StringFormat(
"UPDATE `bot_data`"
" SET `follow_distance` = '%u'"
" WHERE `owner_id` = '%u'",
follow_distance,
owner_id
);
auto results = QueryDatabase(query);
return results.Success();
}
uint32 BotDatabase::Clone(uint32 owner_id, uint32 bot_id, const char* clone_name)
{
if (!owner_id || !bot_id || !clone_name)
return 0;
std::string data_query = StringFormat(
"INSERT INTO `bot_data`"
" ("
"`owner_id`,"
" `spells_id`,"
" `name`,"
" `last_name`,"
" `title`,"
" `suffix`,"
" `zone_id`,"
" `gender`,"
" `race`,"
" `class`,"
" `level`,"
" `deity`,"
" `creation_day`,"
" `last_spawn`,"
" `time_spawned`,"
" `size`,"
" `face`,"
" `hair_color`,"
" `hair_style`,"
" `beard`,"
" `beard_color`,"
" `eye_color_1`,"
" `eye_color_2`,"
" `drakkin_heritage`,"
" `drakkin_tattoo`,"
" `drakkin_details`,"
" `ac`,"
" `atk`,"
" `hp`,"
" `mana`,"
" `str`,"
" `sta`,"
" `cha`,"
" `dex`,"
" `int`,"
" `agi`,"
" `wis`,"
" `fire`,"
" `cold`,"
" `magic`,"
" `poison`,"
" `disease`,"
" `corruption`,"
" `show_helm`,"
" `follow_distance`"
")"
" SELECT"
" bd.`owner_id`,"
" bd.`spells_id`,"
" '%s',"
" '',"
" bd.`title`,"
" bd.`suffix`,"
" bd.`zone_id`,"
" bd.`gender`,"
" bd.`race`,"
" bd.`class`,"
" bd.`level`,"
" bd.`deity`,"
" UNIX_TIMESTAMP(),"
" UNIX_TIMESTAMP(),"
" '0',"
" bd.`size`,"
" bd.`face`,"
" bd.`hair_color`,"
" bd.`hair_style`,"
" bd.`beard`,"
" bd.`beard_color`,"
" bd.`eye_color_1`,"
" bd.`eye_color_2`,"
" bd.`drakkin_heritage`,"
" bd.`drakkin_tattoo`,"
" bd.`drakkin_details`,"
" bd.`ac`,"
" bd.`atk`,"
" bd.`hp`,"
" bd.`mana`,"
" bd.`str`,"
" bd.`sta`,"
" bd.`cha`,"
" bd.`dex`,"
" bd.`int`,"
" bd.`agi`,"
" bd.`wis`,"
" bd.`fire`,"
" bd.`cold`,"
" bd.`magic`,"
" bd.`poison`,"
" bd.`disease`,"
" bd.`corruption`,"
" bd.`show_helm`,"
" bd.`follow_distance`"
" FROM `bot_data` bd"
" WHERE"
" bd.`owner_id` = '%u'"
" AND"
" bd.`bot_id` = '%u'",
clone_name,
owner_id,
bot_id
);
auto results = QueryDatabase(data_query);
if (!results.Success())
return 0;
return results.LastInsertedID();
}
bool BotDatabase::CloneInventory(uint32 owner_id, uint32 bot_id, uint32 clone_id)
{
if (!owner_id || !bot_id || !clone_id)
return false;
std::string inv_query = StringFormat(
"INSERT INTO `bot_inventories`"
" ("
"bot_id,"
" `slot_id`,"
" `item_id`,"
" `inst_charges`,"
" `inst_color`,"
" `inst_no_drop`,"
" `inst_custom_data`,"
" `ornament_icon`,"
" `ornament_id_file`,"
" `ornament_hero_model`,"
" `augment_1`,"
" `augment_2`,"
" `augment_3`,"
" `augment_4`,"
" `augment_5`,"
" `augment_6`"
")"
" SELECT"
" '%u' bot_id,"
" bi.`slot_id`,"
" bi.`item_id`,"
" bi.`inst_charges`,"
" bi.`inst_color`,"
" bi.`inst_no_drop`,"
" bi.`inst_custom_data`,"
" bi.`ornament_icon`,"
" bi.`ornament_id_file`,"
" bi.`ornament_hero_model`,"
" bi.`augment_1`,"
" bi.`augment_2`,"
" bi.`augment_3`,"
" bi.`augment_4`,"
" bi.`augment_5`,"
" bi.`augment_6`"
" FROM `bot_inventories` bi"
" WHERE"
" bi.`bot_id` = '%u'"
" AND"
" '%u' = (SELECT `owner_id` FROM `bot_data` WHERE `bot_id` = '%u')",
clone_id,
bot_id,
owner_id,
bot_id
);
auto results = QueryDatabase(inv_query);
return results.Success();
}
// Bot-group functions
bool BotDatabase::DoesBotGroupExist(std::string& group_name)
{
if (group_name.empty())
return false;
std::string query = StringFormat("SELECT `group_name` FROM `vw_bot_groups` WHERE `group_name` LIKE '%s' LIMIT 1", group_name.c_str());
auto results = QueryDatabase(query);
if (!results.Success() || results.RowCount() == 0)
return false;
auto row = results.begin();
if (!group_name.compare(row[0]))
return true;
return false;
}
uint32 BotDatabase::GetGroupIDByGroupName(std::string& group_name, std::string& error_message)
{
if (group_name.empty())
return 0;
std::string query = StringFormat("SELECT `groups_index` FROM `bot_groups` WHERE `group_name` = '%s'", group_name.c_str());
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
error_message = results.ErrorMessage();
return 0;
}
auto row = results.begin();
return atoi(row[0]);
}
uint32 BotDatabase::GetLeaderIDByGroupName(std::string& group_name, std::string& error_message)
{
if (group_name.empty())
return 0;
std::string query = StringFormat("SELECT `group_leader_id` FROM `bot_groups` WHERE `group_name` = '%s'", group_name.c_str());
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
error_message = results.ErrorMessage();
return 0;
}
auto row = results.begin();
return atoi(row[0]);
}
std::string BotDatabase::GetGroupNameByGroupID(uint32 group_id, std::string& error_message)
{
if (!group_id)
return std::string();
std::string query = StringFormat("SELECT `group_name` FROM `bot_groups` WHERE `groups_index` = '%u'", group_id);
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
error_message = results.ErrorMessage();
return std::string();
}
auto row = results.begin();
return std::string(row[0]);
}
std::string BotDatabase::GetGroupNameByLeaderID(uint32 leader_id, std::string& error_message)
{
if (!leader_id)
return std::string();
std::string query = StringFormat("SELECT `group_name` FROM `bot_groups` WHERE `group_leader_id` = '%u'", leader_id);
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
error_message = results.ErrorMessage();
return std::string();
}
auto row = results.begin();
return std::string(row[0]);
}
uint32 BotDatabase::GetGroupIDByLeaderID(uint32 leader_id, std::string& error_message)
{
if (!leader_id)
return 0;
std::string query = StringFormat("SELECT `groups_index` FROM `bot_groups` WHERE `group_leader_id` = '%u'", leader_id);
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
error_message = results.ErrorMessage();
return 0;
}
auto row = results.begin();
return atoi(row[0]);
}
uint32 BotDatabase::GetLeaderIDByGroupID(uint32 group_id, std::string& error_message)
{
if (!group_id)
return 0;
std::string query = StringFormat("SELECT `group_leader_id` FROM `bot_groups` WHERE `groups_index` = '%u'", group_id);
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
error_message = results.ErrorMessage();
return 0;
}
auto row = results.begin();
return atoi(row[0]);
}
uint32 BotDatabase::GetGroupIDByMemberID(uint32 member_id, std::string& error_message)
{
if (!member_id)
return 0;
std::string query = StringFormat("SELECT `groups_index` FROM `bot_group_members` WHERE `bot_id` = '%u'", member_id);
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
error_message = results.ErrorMessage();
return 0;
}
auto row = results.begin();
return atoi(row[0]);
}
bool BotDatabase::CreateBotGroup(std::string& group_name, uint32 leader_id, std::string& error_message)
{
if (group_name.empty() || !leader_id)
return false;
if (DoesBotGroupExist(group_name))
return false;
std::string query = StringFormat("INSERT INTO `bot_groups` (`group_leader_id`, `group_name`) VALUES ('%u', '%s')", leader_id, group_name.c_str());
auto results = QueryDatabase(query);
if (!results.Success()) {
error_message = results.ErrorMessage();
return false;
}
auto group_id = results.LastInsertedID();
if (!group_id)
return false;
query = StringFormat("INSERT INTO `bot_group_members` (`groups_index`, `bot_id`) VALUES ('%u', '%u')", group_id, leader_id);
results = QueryDatabase(query);
if (!results.Success()) {
error_message = results.ErrorMessage();
return false;
}
return true;
}
bool BotDatabase::DeleteBotGroup(uint32 leader_id, std::string& error_message)
{
if (!leader_id)
return false;
uint32 group_id = GetGroupIDByLeaderID(leader_id, error_message);
if (!group_id || !error_message.empty())
return false;
std::string query = StringFormat("DELETE FROM `bot_group_members` WHERE `groups_index` = '%u'", group_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
error_message = results.ErrorMessage();
return false;
}
query = StringFormat("DELETE FROM `bot_groups` WHERE `groups_index` = '%u'", group_id);
results = QueryDatabase(query);
if (!results.Success()) {
error_message = results.ErrorMessage();
return false;
}
return true;
}
bool BotDatabase::AddMemberToBotGroup(uint32 leader_id, uint32 member_id, std::string& error_message)
{
if (!leader_id || !member_id)
return false;
uint32 group_id = GetGroupIDByLeaderID(leader_id, error_message);
if (!group_id || !error_message.empty())
return false;
std::string query = StringFormat("INSERT INTO `bot_group_members` (`groups_index`, `bot_id`) VALUES ('%u', '%u')", group_id, member_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
error_message = results.ErrorMessage();
return false;
}
return true;
}
bool BotDatabase::RemoveMemberFromBotGroup(uint32 member_id, std::string& error_message)
{
if (!member_id)
return false;
if (GetGroupIDByLeaderID(member_id, error_message))
return DeleteBotGroup(member_id, error_message);
if (!error_message.empty())
return false;
std::string query = StringFormat("DELETE FROM `bot_group_members` WHERE `bot_id` = '%u'", member_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
error_message = results.ErrorMessage();
return false;
}
return true;
}
uint32 BotDatabase::GetGroupIDForLoadGroup(uint32 owner_id, std::string& group_name, std::string& error_message)
{
if (!owner_id || group_name.empty())
return 0;
std::string query = StringFormat("SELECT `groups_index`, `group_name` FROM `vw_bot_groups` WHERE `owner_id` = '%u'", owner_id);
auto results = QueryDatabase(query);
if (!results.Success() || !results.RowCount()) {
error_message = results.ErrorMessage();
return 0;
}
for (auto row = results.begin(); row != results.end(); ++row) {
if (!group_name.compare(row[1]))
return atoi(row[0]);
}
return 0;
}
std::map<uint32, std::list<uint32>> BotDatabase::LoadGroup(std::string& group_name, std::string& error_message)
{
std::map<uint32, std::list<uint32>> group_list;
if (group_name.empty())
return group_list;
uint32 group_id = GetGroupIDByGroupName(group_name, error_message);
if (!group_id || !error_message.empty())
return group_list;
std::string query = StringFormat("SELECT `bot_id` FROM `bot_group_members` WHERE `groups_index` = '%u'", group_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
error_message = results.ErrorMessage();
return group_list;
}
for (auto row = results.begin(); row != results.end(); ++row)
group_list[group_id].push_back(atoi(row[0]));
return group_list;
}
std::list<std::pair<std::string, std::string>> BotDatabase::GetGroupsListByOwnerID(uint32 owner_id, std::string& error_message)
{
std::list<std::pair<std::string, std::string>> groups_list;
if (!owner_id)
return groups_list;
std::string query = StringFormat("SELECT `group_name`, `group_leader_name` FROM `vw_bot_groups` WHERE `owner_id` = '%u'", owner_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
error_message = results.ErrorMessage();
return groups_list;
}
for (auto row = results.begin(); row != results.end(); ++row)
groups_list.push_back(std::pair<std::string, std::string>(row[0], row[1]));
return groups_list;
}
+87
View File
@@ -0,0 +1,87 @@
/* 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_DATABASE_H
#define BOT_DATABASE_H
#include "../common/dbcore.h"
#include "../common/eq_packet_structs.h"
#include <list>
#include <map>
#include <vector>
class BotDatabase : public DBcore
{
public:
BotDatabase();
BotDatabase(const char* host, const char* user, const char* passwd, const char* database, uint32 port);
virtual ~BotDatabase();
bool Connect(const char* host, const char* user, const char* passwd, const char* database, uint32 port);
bool GetCommandSettings(std::map<std::string, std::pair<uint8, std::vector<std::string>>> &bot_command_settings);
// Bot command functions
bool GetInspectMessage(uint32 bot_id, InspectMessage_Struct* message);
bool SetInspectMessage(uint32 bot_id, const InspectMessage_Struct* message);
bool SetAllInspectMessages(uint32 owner_id, const InspectMessage_Struct* message);
bool SetAllArmorColorBySlot(uint32 owner_id, int16 slot_id, uint32 rgb_value);
bool SetAllArmorColors(uint32 owner_id, uint32 rgb_value);
bool SetHelmAppearance(uint32 owner_id, uint32 bot_id, bool show_flag = true);
bool SetAllHelmAppearances(uint32 owner_id, bool show_flag = true);
bool ToggleHelmAppearance(uint32 owner_id, uint32 bot_id);
bool ToggleAllHelmAppearances(uint32 owner_id);
bool SetFollowDistance(uint32 owner_id, uint32 bot_id, uint32 follow_distance);
bool SetAllFollowDistances(uint32 owner_id, uint32 follow_distance);
uint32 Clone(uint32 owner_id, uint32 bot_id, const char* clone_name);
bool CloneInventory(uint32 owner_id, uint32 bot_id, uint32 clone_id);
// Bot-group functions
bool DoesBotGroupExist(std::string& group_name);
uint32 GetGroupIDByGroupName(std::string& group_name, std::string& error_message);
uint32 GetLeaderIDByGroupName(std::string& group_name, std::string& error_message);
std::string GetGroupNameByGroupID(uint32 group_id, std::string& error_message);
std::string GetGroupNameByLeaderID(uint32 leader_id, std::string& error_message);
uint32 GetGroupIDByLeaderID(uint32 leader_id, std::string& error_message);
uint32 GetLeaderIDByGroupID(uint32 group_id, std::string& error_message);
uint32 GetGroupIDByMemberID(uint32 member_id, std::string& error_message);
bool CreateBotGroup(std::string& group_name, uint32 leader_id, std::string& error_message);
bool DeleteBotGroup(uint32 leader_id, std::string& error_message);
bool AddMemberToBotGroup(uint32 leader_id, uint32 member_id, std::string& error_message);
bool RemoveMemberFromBotGroup(uint32 member_id, std::string& error_message);
uint32 GetGroupIDForLoadGroup(uint32 owner_id, std::string& group_name, std::string& error_message);
std::map<uint32, std::list<uint32>> LoadGroup(std::string& group_name, std::string& error_message);
std::list<std::pair<std::string, std::string>> GetGroupsListByOwnerID(uint32 owner_id, std::string& error_message);
};
extern BotDatabase botdb;
#endif
+19
View File
@@ -1,3 +1,21 @@
/* 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_STRUCTS
#define BOT_STRUCTS
@@ -13,6 +31,7 @@ struct BotsAvailableList {
uint16 BotClass;
uint8 BotLevel;
uint16 BotRace;
uint8 BotGender;
};
struct BotGroup {
+22 -4
View File
@@ -1,3 +1,21 @@
/* 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
*/
#ifdef BOTS
#include "bot.h"
@@ -1025,7 +1043,7 @@ bool Bot::AI_IdleCastCheck() {
// bard bots
if(!AICastSpell(this, 100, SpellType_Cure)) {
if(!AICastSpell(this, 100, SpellType_Heal)) {
if((!RuleB(Bots, BotBardUseOutOfCombatSongs) || !GetBardUseOutOfCombatSongs()) || !AICastSpell(this, 100, SpellType_Buff)) { // skips if rule is false
if((!RuleB(Bots, BotBardUseOutOfCombatSongs) || !GetAltOutOfCombatBehavior()) || !AICastSpell(this, 100, SpellType_Buff)) { // skips if rule is false
if(!AICastSpell(this, 100, SpellType_InCombatBuff)) { // this tries to keep some combat buffs on the group until engaged code can pick up the buffing
//
}
@@ -1261,7 +1279,7 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) {
return false;
}
if(!AI_HasSpells())
if (!AI_HasSpells())
return false;
if(tar->GetAppearance() == eaDead) {
@@ -1303,11 +1321,11 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) {
}
// If there is still no spell id, then there isn't going to be one so we are done
if(botSpell.SpellId == 0)
if (botSpell.SpellId == 0)
return false;
// Can we cast this spell on this target?
if(!(spells[botSpell.SpellId].targettype==ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this)
if (!(spells[botSpell.SpellId].targettype == ST_GroupTeleport || spells[botSpell.SpellId].targettype == ST_Target || tar == this)
&& !(tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))
return false;
+29 -3
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.org)
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
@@ -44,6 +44,9 @@ extern volatile bool RunLoops;
#include "zonedb.h"
#include "petitions.h"
#include "command.h"
#ifdef BOTS
#include "bot_command.h"
#endif
#include "string_ids.h"
#include "guild_mgr.h"
@@ -1046,6 +1049,24 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
break;
}
#ifdef BOTS
if (message[0] == BOT_COMMAND_CHAR) {
if (bot_command_dispatch(this, message) == -2) {
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
Message(13, "Bot command '%s' not recognized.", message);
}
}
else {
if (!RuleB(Chat, SuppressCommandErrors))
Message(13, "Bot command '%s' not recognized.", message);
}
}
break;
}
#endif
Mob* sender = this;
if (GetPet() && GetPet()->FindType(SE_VoiceGraft))
sender = GetPet();
@@ -3561,7 +3582,7 @@ void Client::Insight(uint32 t_id)
}
strcat(resists," to disease.");
Message(0,"Your target is a level %i %s. It appears %s and %s for its level. It seems %s",who->GetLevel(),GetEQClassName(who->GetClass(),1),dmg,hitpoints,resists);
Message(0,"Your target is a level %i %s. It appears %s and %s for its level. It seems %s",who->GetLevel(),GetClassIDName(who->GetClass(),1),dmg,hitpoints,resists);
}
void Client::GetGroupAAs(GroupLeadershipAA_Struct *into) const {
@@ -7546,9 +7567,14 @@ void Client::GarbleMessage(char *message, uint8 variance)
int delimiter_count = 0;
// Don't garble # commands
if (message[0] == '#')
if (message[0] == COMMAND_CHAR)
return;
#ifdef BOTS
if (message[0] == BOT_COMMAND_CHAR)
return;
#endif
for (size_t i = 0; i < strlen(message); i++) {
// Client expects hex values inside of a text link body
if (message[i] == delimiter) {
+3 -3
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net)
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
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
@@ -160,7 +160,7 @@ int32 Client::LevelRegen()
bool sitting = IsSitting();
bool feigned = GetFeigned();
int level = GetLevel();
bool bonus = GetRaceBitmask(GetBaseRace()) & RuleI(Character, BaseHPRegenBonusRaces);
bool bonus = GetPlayerRaceBit(GetBaseRace()) & RuleI(Character, BaseHPRegenBonusRaces);
uint8 multiplier1 = bonus ? 2 : 1;
int32 hp = 0;
//these calculations should match up with the info from Monkly Business, which was last updated ~05/2008: http://www.monkly-business.net/index.php?pageid=abilities
+1 -2
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2009 EQEMu Development Team (http://eqemulator.net)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
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
@@ -3896,7 +3896,6 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app)
{
#ifdef BOTS
// This block is necessary to clean up any bot objects owned by a Client
Bot::BotHealRotationsClear(this);
Bot::BotOrderCampAll(this);
#endif
if (IsLFP())
+33 -15
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemulator.org)
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
@@ -86,7 +86,7 @@ void command_pf(Client *c, const Seperator *message);
std::map<std::string, CommandRecord *> commandlist;
std::map<std::string, std::string> commandaliases;
//All allocated CommandRecords get put in here so they get deleted on shutdown
// All allocated CommandRecords get put in here so they get deleted on shutdown
LinkedList<CommandRecord *> cleanup_commandlist;
/*
@@ -103,9 +103,9 @@ int command_notavail(Client *c, const char *message)
return -1;
}
/*****************************************************************************/
/* the rest below here could be in a dynamically loaded module eventually */
/*****************************************************************************/
/**************************************************************************
/* the rest below here could be in a dynamically loaded module eventually *
/*************************************************************************/
/*
@@ -163,7 +163,7 @@ int command_init(void)
command_add("bind", "- Sets your targets bind spot to their current location", 200, command_bind) ||
#ifdef BOTS
command_add("bot", "- Type \"#bot help\" to the see the list of available commands for bots.", 0, command_bot) ||
command_add("bot", "- Type \"#bot help\" or \"^help\" to the see the list of available commands for bots.", 0, command_bot) ||
#endif
command_add("camerashake", "Shakes the camera on everyone's screen globally.", 80, command_camerashake) ||
@@ -9665,14 +9665,6 @@ void command_showspellslist(Client *c, const Seperator *sep)
return;
}
// All new code added to command.cpp ought to be BEFORE this comment line. Do no append code to this file below the BOTS code block.
#ifdef BOTS
// Function delegate to support the command interface for Bots with the client.
void command_bot(Client *c, const Seperator *sep) {
Bot::ProcessBotCommands(c, sep);
}
#endif
void command_raidloot(Client *c, const Seperator *sep)
{
if(!sep->arg[1][0]) {
@@ -10842,4 +10834,30 @@ void command_reloadperlexportsettings(Client *c, const Seperator *sep)
safe_delete(pack);
}
}
}
// All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block.
#ifdef BOTS
#include "bot_command.h"
// Function delegate to support the command interface for Bots with the client.
void command_bot(Client *c, const Seperator *sep)
{
std::string bot_message = sep->msg;
bot_message = bot_message.substr(bot_message.find_first_not_of("#bot"));
bot_message[0] = BOT_COMMAND_CHAR;
if (bot_command_dispatch(c, bot_message.c_str()) == -2) {
if (parse->PlayerHasQuestSub(EVENT_COMMAND)) {
int i = parse->EventPlayer(EVENT_COMMAND, c, bot_message, 0);
if (i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
c->Message(13, "Bot command '%s' not recognized.", bot_message.c_str());
}
}
else {
if (!RuleB(Chat, SuppressCommandErrors))
c->Message(13, "Bot command '%s' not recognized.", bot_message.c_str());
}
}
}
#endif
+1 -1
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
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
+5 -5
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2006 EQEMu Development Team (http://eqemulator.net)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
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
@@ -9,11 +9,11 @@
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
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifdef EMBPERL
@@ -1052,8 +1052,8 @@ void PerlembParser::ExportMobVariables(bool isPlayerQuest, bool isGlobalPlayerQu
if(mob) {
ExportVar(package_name.c_str(), "name", mob->GetName());
ExportVar(package_name.c_str(), "race", GetRaceName(mob->GetRace()));
ExportVar(package_name.c_str(), "class", GetEQClassName(mob->GetClass()));
ExportVar(package_name.c_str(), "race", GetRaceIDName(mob->GetRace()));
ExportVar(package_name.c_str(), "class", GetClassIDName(mob->GetClass()));
ExportVar(package_name.c_str(), "ulevel", mob->GetLevel());
ExportVar(package_name.c_str(), "userid", mob->GetID());
}
+17 -13
View File
@@ -1,26 +1,30 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net)
/* 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
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
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
*/
#ifdef EMBPERL
#include "../common/global_define.h"
#include "../common/eqemu_logsys.h"
#include "masterentity.h"
#include "command.h"
#ifdef BOTS
#include "bot_command.h"
#endif
#include "embperl.h"
#include "embxs.h"
+1 -2
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
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
@@ -466,7 +466,6 @@ private:
#ifdef BOTS
public:
void AddBot(Bot* newBot, bool SendSpawnPacket = true, bool dontqueue = false);
void BotPickLock(Bot* rogue);
bool RemoveBot(uint16 entityID);
Mob* GetMobByBotID(uint32 botID);
Bot* GetBotByBotID(uint32 botID);
+61 -1
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.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
@@ -15,6 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "../common/global_define.h"
#include "../common/eqemu_logsys.h"
#include "masterentity.h"
@@ -938,6 +939,41 @@ void Group::DisbandGroup() {
safe_delete(outapp);
}
void Group::GetMemberList(std::list<Mob*>& member_list, bool clear_list)
{
if (clear_list)
member_list.clear();
for (auto member_iter : members) {
if (member_iter)
member_list.push_back(member_iter);
}
}
void Group::GetClientList(std::list<Client*>& client_list, bool clear_list)
{
if (clear_list)
client_list.clear();
for (auto client_iter : members) {
if (client_iter && client_iter->IsClient())
client_list.push_back(client_iter->CastToClient());
}
}
#ifdef BOTS
void Group::GetBotList(std::list<Bot*>& bot_list, bool clear_list)
{
if (clear_list)
bot_list.clear();
for (auto bot_iter : members) {
if (bot_iter && bot_iter->IsBot())
bot_list.push_back(bot_iter->CastToBot());
}
}
#endif
bool Group::Process() {
if(disbandcheck && !GroupCount())
return false;
@@ -2294,6 +2330,30 @@ void Group::SetPuller(const char *NewPullerName)
}
}
bool Group::AmIMainTank(const char *mob_name)
{
if (!mob_name)
return false;
return !((bool)MainTankName.compare(mob_name));
}
bool Group::AmIMainAssist(const char *mob_name)
{
if (!mob_name)
return false;
return !((bool)MainTankName.compare(mob_name));
}
bool Group::AmIPuller(const char *mob_name)
{
if (!mob_name)
return false;
return !((bool)PullerName.compare(mob_name));
}
bool Group::HasRole(Mob *m, uint8 Role)
{
if(!m)
+9 -1
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.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
@@ -59,6 +59,11 @@ public:
bool DelMemberOOZ(const char *Name);
bool DelMember(Mob* oldmember,bool ignoresender = false);
void DisbandGroup();
void GetMemberList(std::list<Mob*>& member_list, bool clear_list = true);
void GetClientList(std::list<Client*>& client_list, bool clear_list = true);
#ifdef BOTS
void GetBotList(std::list<Bot*>& bot_list, bool clear_list = true);
#endif
bool IsGroupMember(Mob* client);
bool IsGroupMember(const char *Name);
bool Process();
@@ -123,6 +128,9 @@ public:
const char *GetMainTankName() { return MainTankName.c_str(); }
const char *GetMainAssistName() { return MainAssistName.c_str(); }
const char *GetPullerName() { return PullerName.c_str(); }
bool AmIMainTank(const char *mob_name);
bool AmIMainAssist(const char *mob_name);
bool AmIPuller(const char *mob_name);
void SetNPCMarker(const char *NewNPCMarkerName);
void UnMarkNPC(uint16 ID);
void SendMarkedNPCsToMember(Client *c, bool Clear = false);
+882
View File
@@ -0,0 +1,882 @@
/* 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"
#define SAFE_HP_RATIO_CLOTH 95.0f
#define SAFE_HP_RATIO_LEATHER 90.0f
#define SAFE_HP_RATIO_CHAIN 80.0f
#define SAFE_HP_RATIO_PLATE 75.0f
#define CRITICAL_HP_RATIO_CLOTH 30.0f
#define CRITICAL_HP_RATIO_LEATHER 25.0f
#define CRITICAL_HP_RATIO_CHAIN 15.0f
#define CRITICAL_HP_RATIO_PLATE 10.0f
HealRotation::HealRotation(Bot* hr_creator, uint32 interval_ms, bool fast_heals, bool adaptive_targeting, bool casting_override)
{
m_member_pool.push_back(hr_creator);
m_creation_time_ms = Timer::GetCurrentTime();
m_last_heal_time_ms = m_creation_time_ms;
m_interval_ms = ((interval_ms >= CASTING_CYCLE_MINIMUM_INTERVAL) ? (interval_ms) : (CASTING_CYCLE_MINIMUM_INTERVAL));
m_next_cast_time_ms = m_creation_time_ms;
m_next_poke_time_ms = m_creation_time_ms;
m_healing_stats_begin_ms = m_creation_time_ms;
m_fast_heals = fast_heals;
m_adaptive_targeting = adaptive_targeting;
m_casting_override = casting_override;
m_casting_target_poke = true;
m_active_heal_target = false;
ResetArmorTypeHPLimits();
m_is_active = false;
}
void HealRotation::SetIntervalMS(uint32 interval_ms)
{
if (interval_ms > CASTING_CYCLE_MAXIMUM_INTERVAL)
interval_ms = CASTING_CYCLE_MAXIMUM_INTERVAL;
else if (interval_ms < CASTING_CYCLE_MINIMUM_INTERVAL)
interval_ms = CASTING_CYCLE_MINIMUM_INTERVAL;
m_interval_ms = interval_ms;
}
void HealRotation::SetIntervalS(uint32 interval_s)
{
interval_s *= 1000;
if (interval_s > CASTING_CYCLE_MAXIMUM_INTERVAL)
interval_s = CASTING_CYCLE_MAXIMUM_INTERVAL;
else if (interval_s < CASTING_CYCLE_MINIMUM_INTERVAL)
interval_s = CASTING_CYCLE_MINIMUM_INTERVAL;
m_interval_ms = interval_s;
}
bool HealRotation::AddMemberToPool(Bot* hr_member)
{
if (!hr_member)
return false;
if (!IsMemberClass(hr_member->GetClass()))
return false;
if (m_member_pool.size() >= RuleI(Bots, HealRotationMaxMembers))
return false;
for (auto find_iter : m_member_pool) {
if (find_iter == hr_member)
return false;
}
m_member_pool.push_back(hr_member);
valid_state();
return true;
}
bool HealRotation::AddTargetToPool(Mob* hr_target)
{
if (!hr_target)
return false;
if (!valid_state())
return false;
if (!IsTargetMobType(hr_target))
return false;
if (m_target_pool.size() >= RuleI(Bots, HealRotationMaxTargets))
return false;
for (auto find_iter : m_target_pool) {
if (find_iter == hr_target)
return false;
}
m_target_pool.push_back(hr_target);
return true;
}
bool HealRotation::RemoveMemberFromPool(Bot* hr_member)
{
if (!hr_member)
return true;
for (auto member_iter : m_member_pool) {
if (member_iter != hr_member)
continue;
m_member_is_casting.erase(hr_member);
m_member_pool.remove(hr_member);
valid_state();
return true;
}
return false;
}
bool HealRotation::RemoveTargetFromPool(Mob* hr_target)
{
if (!hr_target)
return true;
if (!valid_state())
return true;
for (auto target_iter : m_target_pool) {
if (target_iter != hr_target)
continue;
m_target_healing_stats_2.erase(hr_target);
m_target_healing_stats_1.erase(hr_target);
m_target_pool.remove(hr_target);
m_casting_target_poke = false;
bias_targets();
return true;
}
return false;
}
bool HealRotation::ClearMemberPool()
{
m_is_active = false;
m_cycle_pool.clear();
m_casting_target_poke = false;
m_active_heal_target = false;
ClearTargetPool();
auto clear_list = m_member_pool;
for (auto member_iter : clear_list)
member_iter->LeaveHealRotationMemberPool();
return true;
}
bool HealRotation::ClearTargetPool()
{
m_is_active = false;
auto clear_list = m_target_pool;
for (auto target_iter : clear_list)
target_iter->LeaveHealRotationTargetPool();
m_casting_target_poke = false;
bias_targets();
return m_target_pool.empty();
}
bool HealRotation::Start()
{
m_is_active = false;
if (m_member_pool.empty() || m_target_pool.empty())
return false;
m_cycle_pool = m_member_pool;
m_is_active = true;
return true;
}
bool HealRotation::Stop()
{
m_is_active = false;
m_active_heal_target = false;
m_cycle_pool.clear();
return true;
}
Bot* HealRotation::CastingMember()
{
if (!m_is_active)
return nullptr;
if (m_cycle_pool.empty()) {
cycle_refresh();
if (m_cycle_pool.empty())
return nullptr;
}
return m_cycle_pool.front();
}
bool HealRotation::PokeCastingTarget()
{
if (!m_is_active)
return false;
uint32 current_time = Timer::GetCurrentTime();
if (current_time < m_next_poke_time_ms) {
auto hr_target = CastingTarget();
if (hr_target && hr_target->DontHealMeBefore() > current_time)
m_next_poke_time_ms = current_time;
else
return m_active_heal_target;
}
m_next_poke_time_ms = (current_time + POKE_PROPAGATION_DELAY);
if (m_healing_stats_begin_ms + HEALING_STATS_RESET_INTERVAL <= current_time)
StartNewTargetHealingStatsCycle(current_time);
m_casting_target_poke = false;
bias_targets();
return m_active_heal_target;
}
Mob* HealRotation::CastingTarget()
{
if (!m_is_active)
return nullptr;
if (!m_active_heal_target)
return nullptr;
return m_target_pool.front();
}
bool HealRotation::AdvanceRotation(bool use_interval)
{
m_cycle_pool.pop_front();
m_next_cast_time_ms = Timer::GetCurrentTime();
if (use_interval) {
m_next_poke_time_ms = m_next_cast_time_ms;
m_next_cast_time_ms += m_interval_ms;
}
else {
m_next_cast_time_ms += ADVANCE_ROTATION_MINIMUM_INTERVAL;
}
if (m_cycle_pool.empty())
cycle_refresh();
return (!m_cycle_pool.empty());
}
bool HealRotation::IsMemberInPool(Bot* hr_member)
{
if (!hr_member)
return false;
if (m_member_pool.empty())
return false;
for (auto find_iter : m_member_pool) {
if (find_iter == hr_member)
return true;
}
return false;
}
bool HealRotation::IsTargetInPool(Mob* hr_target)
{
if (!hr_target)
return false;
if (m_target_pool.empty())
return false;
for (auto find_iter : m_target_pool) {
if (find_iter == hr_target)
return true;
}
return false;
}
void HealRotation::SetMemberIsCasting(Bot* hr_member, bool flag)
{
if (!hr_member)
return;
if (!IsMemberInPool(hr_member))
return;
m_member_is_casting[hr_member] = flag;
}
bool HealRotation::MemberIsCasting(Bot* hr_member)
{
if (!hr_member)
return false;
if (m_member_is_casting.find(hr_member) == m_member_is_casting.end())
return false;
return m_member_is_casting[hr_member];
}
void HealRotation::UpdateTargetHealingStats(Mob* hr_target)
{
if (!hr_target)
return;
if (!IsTargetInPool(hr_target))
return;
m_last_heal_time_ms = Timer::GetCurrentTime();
m_target_healing_stats_1[hr_target].last_heal_time_ms = m_last_heal_time_ms;
++m_target_healing_stats_1[hr_target].heal_count;
}
void HealRotation::StartNewTargetHealingStatsCycle(uint32 current_time)
{
m_target_healing_stats_2 = m_target_healing_stats_1;
m_target_healing_stats_1.clear();
m_healing_stats_begin_ms = current_time;
}
uint32 HealRotation::HealCount(Mob* hr_target)
{
if (!hr_target)
return 0;
uint32 heal_count = 0;
if (m_target_healing_stats_1.find(hr_target) != m_target_healing_stats_1.end())
heal_count += m_target_healing_stats_1[hr_target].heal_count;
return heal_count;
}
uint32 HealRotation::ExtendedHealCount(Mob* hr_target)
{
if (!hr_target)
return 0;
uint32 heal_count = 0;
if (m_target_healing_stats_1.find(hr_target) != m_target_healing_stats_1.end())
heal_count += m_target_healing_stats_1[hr_target].heal_count;
if (m_target_healing_stats_2.find(hr_target) != m_target_healing_stats_2.end())
heal_count += m_target_healing_stats_2[hr_target].heal_count;
return heal_count;
}
float HealRotation::HealFrequency(Mob* hr_target)
{
if (!hr_target)
return 0.0f;
float time_base = 0;
uint32 heal_count = 0;
if (m_target_healing_stats_1.find(hr_target) != m_target_healing_stats_1.end()) {
heal_count += m_target_healing_stats_1[hr_target].heal_count;
time_base = (Timer::GetCurrentTime() - m_target_healing_stats_1[hr_target].last_heal_time_ms);
}
time_base /= 1000;
if (!time_base)
time_base = HEALING_STATS_RESET_INTERVAL_S;
if (heal_count)
return ((float)1 / (time_base / heal_count));
else
return ((float)1 / time_base);
}
float HealRotation::ExtendedHealFrequency(Mob* hr_target)
{
if (!hr_target)
return 0.0f;
uint32 current_time = Timer::GetCurrentTime();
uint32 heal_count = 0;
float time_base = 0;
if (m_target_healing_stats_1.find(hr_target) != m_target_healing_stats_1.end()) {
heal_count += m_target_healing_stats_1[hr_target].heal_count;
time_base = (current_time - m_target_healing_stats_1[hr_target].last_heal_time_ms + HEALING_STATS_RESET_INTERVAL);
}
if (m_target_healing_stats_2.find(hr_target) != m_target_healing_stats_2.end()) {
heal_count += m_target_healing_stats_2[hr_target].heal_count;
time_base = (current_time - m_target_healing_stats_2[hr_target].last_heal_time_ms);
}
time_base /= 1000;
if (!time_base)
time_base = (HEALING_STATS_RESET_INTERVAL_S * 2);
if (heal_count)
return ((float)1 / (time_base / heal_count));
else
return ((float)1 / time_base);
}
HealingStats* HealRotation::TargetHealingStats1(Mob* hr_target)
{
if (!hr_target)
return nullptr;
if (m_target_healing_stats_1.find(hr_target) == m_target_healing_stats_1.end())
return nullptr;
return &m_target_healing_stats_1[hr_target];
}
HealingStats* HealRotation::TargetHealingStats2(Mob* hr_target)
{
if (!hr_target)
return nullptr;
if (m_target_healing_stats_2.find(hr_target) == m_target_healing_stats_2.end())
return nullptr;
return &m_target_healing_stats_2[hr_target];
}
bool HealRotation::SetArmorTypeSafeHPRatio(uint8 armor_type, float hp_ratio)
{
if (armor_type >= ARMOR_TYPE_COUNT)
return false;
if (hp_ratio < CRITICAL_HP_RATIO_BASE || hp_ratio > SAFE_HP_RATIO_BASE)
return false;
if (hp_ratio < m_critical_hp_ratio[armor_type])
return false;
m_safe_hp_ratio[armor_type] = hp_ratio;
return true;
}
bool HealRotation::SetArmorTypeCriticalHPRatio(uint8 armor_type, float hp_ratio)
{
if (armor_type >= ARMOR_TYPE_COUNT)
return false;
if (hp_ratio < CRITICAL_HP_RATIO_BASE || hp_ratio > SAFE_HP_RATIO_BASE)
return false;
if (hp_ratio > m_safe_hp_ratio[armor_type])
return false;
m_critical_hp_ratio[armor_type] = hp_ratio;
return true;
}
float HealRotation::ArmorTypeSafeHPRatio(uint8 armor_type)
{
if (armor_type < ARMOR_TYPE_COUNT)
return m_safe_hp_ratio[armor_type];
else
return m_safe_hp_ratio[ARMOR_TYPE_UNKNOWN];
}
float HealRotation::ArmorTypeCriticalHPRatio(uint8 armor_type)
{
if (armor_type < ARMOR_TYPE_COUNT)
return m_critical_hp_ratio[armor_type];
else
return m_critical_hp_ratio[ARMOR_TYPE_UNKNOWN];
}
void HealRotation::ResetArmorTypeHPLimits()
{
m_safe_hp_ratio[ARMOR_TYPE_UNKNOWN] = SAFE_HP_RATIO_BASE;
m_safe_hp_ratio[ARMOR_TYPE_CLOTH] = SAFE_HP_RATIO_CLOTH;
m_safe_hp_ratio[ARMOR_TYPE_LEATHER] = SAFE_HP_RATIO_LEATHER;
m_safe_hp_ratio[ARMOR_TYPE_CHAIN] = SAFE_HP_RATIO_CHAIN;
m_safe_hp_ratio[ARMOR_TYPE_PLATE] = SAFE_HP_RATIO_PLATE;
m_critical_hp_ratio[ARMOR_TYPE_UNKNOWN] = CRITICAL_HP_RATIO_BASE;
m_critical_hp_ratio[ARMOR_TYPE_CLOTH] = CRITICAL_HP_RATIO_CLOTH;
m_critical_hp_ratio[ARMOR_TYPE_LEATHER] = CRITICAL_HP_RATIO_LEATHER;
m_critical_hp_ratio[ARMOR_TYPE_CHAIN] = CRITICAL_HP_RATIO_CHAIN;
m_critical_hp_ratio[ARMOR_TYPE_PLATE] = CRITICAL_HP_RATIO_PLATE;
}
bool HealRotation::valid_state()
{
m_member_pool.remove(nullptr);
m_member_pool.remove_if([](Mob* l) {return (!IsMemberClass(l->GetClass())); });
cycle_refresh();
if (m_member_pool.empty())
ClearTargetPool(); // Consumes HealRotation at this point
return (!m_member_pool.empty());
}
void HealRotation::cycle_refresh()
{
m_is_active = false;
m_cycle_pool.clear();
if (m_member_pool.empty())
return;
m_cycle_pool = m_member_pool;
m_is_active = true;
}
bool HealRotation::healable_target(bool use_class_at, bool critical_only)
{
if (m_target_pool.empty())
return false;
auto healable_target = m_target_pool.front();
if (!healable_target)
return false;
if (healable_target->DontHealMeBefore() > Timer::GetCurrentTime())
return false;
if (healable_target->GetAppearance() == eaDead)
return false;
if (use_class_at) {
if (critical_only && healable_target->GetHPRatio() > m_critical_hp_ratio[ClassArmorType(healable_target->GetClass())])
return false;
if (healable_target->GetHPRatio() > m_safe_hp_ratio[ClassArmorType(healable_target->GetClass())])
return false;
if (healable_target->IsBerserk() && (healable_target->GetClass() == WARRIOR || healable_target->GetClass() == BERSERKER)) {
if (healable_target->GetHPRatio() <= RuleI(Combat, BerserkerFrenzyEnd) && healable_target->GetHPRatio() > m_critical_hp_ratio[ClassArmorType(healable_target->GetClass())])
return false;
}
}
else {
if (critical_only && healable_target->GetHPRatio() > CRITICAL_HP_RATIO_BASE)
return false;
if (healable_target->GetHPRatio() > SAFE_HP_RATIO_BASE)
return false;
if (healable_target->IsBerserk() && (healable_target->GetClass() == WARRIOR || healable_target->GetClass() == BERSERKER)) {
if (healable_target->GetHPRatio() <= RuleI(Combat, BerserkerFrenzyEnd) && healable_target->GetHPRatio() > CRITICAL_HP_RATIO_BASE)
return false;
}
}
return true;
}
void HealRotation::bias_targets()
{
#define LT_HPRATIO(l, r) (l->GetHPRatio() < r->GetHPRatio())
#define LT_ARMTYPE(l, r) (ClassArmorType(l->GetClass()) < ClassArmorType(r->GetClass()))
#define EQ_ALIVE(l, r) (l->GetAppearance() != eaDead && r->GetAppearance() != eaDead)
#define EQ_READY(l, r, ct) (l->DontHealMeBefore() <= ct && r->DontHealMeBefore() <= ct)
#define EQ_TANK(l, r) ((l->HasGroup() && l->GetGroup()->AmIMainTank(l->GetCleanName())) && (r->HasGroup() && r->GetGroup()->AmIMainTank(r->GetCleanName())))
#define EQ_HEALER(l, r) (IsMemberClass(l->GetClass()) && IsMemberClass(r->GetClass()))
#define EQ_ARMTYPE(l, r) (ClassArmorType(l->GetClass()) == ClassArmorType(r->GetClass()))
#define EQ_ATCRIT(l, r) (l->GetHPRatio() <= (*l->TargetOfHealRotation())->ArmorTypeCriticalHPRatio(ClassArmorType(l->GetClass())) && \
r->GetHPRatio() <= (*r->TargetOfHealRotation())->ArmorTypeCriticalHPRatio(ClassArmorType(r->GetClass())))
#define EQ_ATWOUND(l, r) (l->GetHPRatio() <= (*l->TargetOfHealRotation())->ArmorTypeSafeHPRatio(ClassArmorType(l->GetClass())) && \
r->GetHPRatio() <= (*r->TargetOfHealRotation())->ArmorTypeSafeHPRatio(ClassArmorType(r->GetClass())))
#define GT_ALIVE(l, r) (l->GetAppearance() != eaDead && r->GetAppearance() == eaDead)
#define GT_READY(l, r, ct) (l->DontHealMeBefore() <= ct && r->DontHealMeBefore() > ct)
#define GT_TANK(l, r) ((l->HasGroup() && l->GetGroup()->AmIMainTank(l->GetCleanName())) && (!r->HasGroup() || !r->GetGroup()->AmIMainTank(r->GetCleanName())))
#define GT_HEALER(l, r) (IsMemberClass(l->GetClass()) && !IsMemberClass(r->GetClass()))
#define GT_HEALFREQ(l, r) (l->HealRotationHealFrequency() > r->HealRotationHealFrequency())
#define GT_HEALCNT(l, r) (l->HealRotationHealCount() > r->HealRotationHealCount())
#define GT_ATCRIT(l, r) (l->GetHPRatio() <= (*l->TargetOfHealRotation())->ArmorTypeCriticalHPRatio(ClassArmorType(l->GetClass())) && \
r->GetHPRatio() > (*r->TargetOfHealRotation())->ArmorTypeCriticalHPRatio(ClassArmorType(r->GetClass())))
#define GT_XHEALFREQ(l, r) (l->HealRotationExtendedHealFrequency() > r->HealRotationExtendedHealFrequency())
#define GT_XHEALCNT(l, r) (l->HealRotationExtendedHealCount() > r->HealRotationExtendedHealCount())
#define GT_ATWOUND(l, r) (l->GetHPRatio() <= (*l->TargetOfHealRotation())->ArmorTypeSafeHPRatio(ClassArmorType(l->GetClass())) && \
r->GetHPRatio() > (*r->TargetOfHealRotation())->ArmorTypeSafeHPRatio(ClassArmorType(r->GetClass())))
if (m_target_pool.empty()) {
m_casting_target_poke = true;
m_active_heal_target = false;
return;
}
// attempt to clear invalid target pool entries
m_target_pool.remove(nullptr);
m_target_pool.remove_if([](Mob* l) { return (!IsTargetMobType(l)); });
uint32 sort_type = 0; // debug
while (m_target_pool.size() > 1 && !m_casting_target_poke && !m_adaptive_targeting) { // standard behavior
sort_type = 1;
m_target_pool.sort([](Mob* l, Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_TANK(l, r))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && EQ_TANK(l, r) && LT_HPRATIO(l, r))
return true;
return false;
});
if (m_target_pool.front()->HasGroup() && m_target_pool.front()->GetGroup()->AmIMainTank(m_target_pool.front()->GetCleanName()) && healable_target(false))
break;
sort_type = 2;
m_target_pool.sort([](Mob* l, Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_HEALER(l, r))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && EQ_HEALER(l, r) && LT_HPRATIO(l, r))
return true;
return false;
});
if (IsMemberClass(m_target_pool.front()->GetClass()) && healable_target(false))
break;
sort_type = 3; // default
m_target_pool.sort([](const Mob* l, const Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && LT_HPRATIO(l, r))
return true;
return false;
});
break;
}
while (m_target_pool.size() > 1 && !m_casting_target_poke && m_adaptive_targeting) { // adaptive targeting behavior
sort_type = 101;
m_target_pool.sort([](Mob* l, Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_HEALFREQ(l, r))
return true;
return false;
});
if (healable_target(true, true))
break;
sort_type = 102;
m_target_pool.sort([](Mob* l, Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_HEALCNT(l, r))
return true;
return false;
});
if (healable_target(true, true))
break;
sort_type = 103;
m_target_pool.sort([](Mob* l, Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_TANK(l, r))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && EQ_TANK(l, r) && LT_HPRATIO(l, r))
return true;
return false;
});
if (m_target_pool.front()->HasGroup() && m_target_pool.front()->GetGroup()->AmIMainTank(m_target_pool.front()->GetCleanName()) && healable_target(true, true))
break;
sort_type = 104;
m_target_pool.sort([](const Mob* l, const Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_HEALER(l, r))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && EQ_HEALER(l, r) && LT_HPRATIO(l, r))
return true;
return false;
});
if (IsMemberClass(m_target_pool.front()->GetClass()) && healable_target(true, true))
break;
sort_type = 105;
m_target_pool.sort([](const Mob* l, const Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_ATCRIT(l, r))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && EQ_ATCRIT(l, r) && LT_ARMTYPE(l, r))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && EQ_ATCRIT(l, r) && EQ_ARMTYPE(l, r) && LT_HPRATIO(l, r))
return true;
return false;
});
if (healable_target(true, true))
break;
sort_type = 106;
m_target_pool.sort([](Mob* l, Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_XHEALFREQ(l, r))
return true;
return false;
});
if (healable_target(true))
break;
sort_type = 107;
m_target_pool.sort([](Mob* l, Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_XHEALCNT(l, r))
return true;
return false;
});
if (healable_target(true))
break;
sort_type = 108;
m_target_pool.sort([](const Mob* l, const Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && GT_ATWOUND(l, r))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && EQ_ATWOUND(l, r) && LT_ARMTYPE(l, r))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && EQ_ATWOUND(l, r) && EQ_ARMTYPE(l, r) && LT_HPRATIO(l, r))
return true;
return false;
});
if (healable_target())
break;
sort_type = 109; // default
m_target_pool.sort([](const Mob* l, const Mob* r) {
if (GT_ALIVE(l, r))
return true;
uint32 current_time = Timer::GetCurrentTime();
if (EQ_ALIVE(l, r) && GT_READY(l, r, current_time))
return true;
if (EQ_ALIVE(l, r) && EQ_READY(l, r, current_time) && LT_HPRATIO(l, r))
return true;
return false;
});
break;
}
m_active_heal_target = healable_target(false);
if (!m_active_heal_target)
m_active_heal_target = healable_target();
m_casting_target_poke = true;
#if (EQDEBUG >= 12)
Log.Out(Logs::General, Logs::Error, "HealRotation::bias_targets() - *** Post-processing state ***");
Log.Out(Logs::General, Logs::Error, "HealRotation Settings:");
Log.Out(Logs::General, Logs::Error, "HealRotation::m_interval_ms = %u", m_interval_ms);
Log.Out(Logs::General, Logs::Error, "HealRotation::m_next_cast_time_ms = %u (current_time: %u, time_diff: %i)", m_next_cast_time_ms, Timer::GetCurrentTime(), ((int32)Timer::GetCurrentTime() - (int32)m_next_cast_time_ms));
Log.Out(Logs::General, Logs::Error, "HealRotation::m_next_poke_time_ms = %u (current_time: %u, time_diff: %i)", m_next_poke_time_ms, Timer::GetCurrentTime(), ((int32)Timer::GetCurrentTime() - (int32)m_next_poke_time_ms));
Log.Out(Logs::General, Logs::Error, "HealRotation::m_fast_heals = %s", ((m_fast_heals) ? ("true") : ("false")));
Log.Out(Logs::General, Logs::Error, "HealRotation::m_adaptive_targeting = %s", ((m_adaptive_targeting) ? ("true") : ("false")));
Log.Out(Logs::General, Logs::Error, "HealRotation::m_casting_override = %s", ((m_casting_override) ? ("true") : ("false")));
Log.Out(Logs::General, Logs::Error, "HealRotation::m_casting_target_poke = %s", ((m_casting_target_poke) ? ("true") : ("false")));
Log.Out(Logs::General, Logs::Error, "HealRotation::m_active_heal_target = %s", ((m_active_heal_target) ? ("true") : ("false")));
Log.Out(Logs::General, Logs::Error, "HealRotation::m_is_active = %s", ((m_is_active) ? ("true") : ("false")));
Log.Out(Logs::General, Logs::Error, "HealRotation::m_member_list.size() = %i", m_member_pool.size());
Log.Out(Logs::General, Logs::Error, "HealRotation::m_cycle_list.size() = %i", m_cycle_pool.size());
Log.Out(Logs::General, Logs::Error, "HealRotation::m_target_list.size() = %i", m_target_pool.size());
if (m_member_pool.size()) { Log.Out(Logs::General, Logs::Error, "(std::shared_ptr<HealRotation>::use_count() = %i", m_member_pool.front()->MemberOfHealRotation()->use_count()); }
else { Log.Out(Logs::General, Logs::Error, "(std::shared_ptr<HealRotation>::use_count() = unknown (0)"); }
Log.Out(Logs::General, Logs::Error, "HealRotation Members:");
int member_index = 0;
for (auto mlist_iter : m_member_pool) {
if (!mlist_iter) { continue; }
Log.Out(Logs::General, Logs::Error, "(%i) %s (hrcast: %c)", (++member_index), mlist_iter->GetCleanName(), ((mlist_iter->AmICastingForHealRotation())?('T'):('F')));
}
if (!member_index) { Log.Out(Logs::General, Logs::Error, "(0) None"); }
Log.Out(Logs::General, Logs::Error, "HealRotation Cycle:");
int cycle_index = 0;
for (auto clist_iter : m_cycle_pool) {
if (!clist_iter) { continue; }
Log.Out(Logs::General, Logs::Error, "(%i) %s", (++cycle_index), clist_iter->GetCleanName());
}
if (!cycle_index) { Log.Out(Logs::General, Logs::Error, "(0) None"); }
Log.Out(Logs::General, Logs::Error, "HealRotation Targets: (sort type: %u)", sort_type);
int target_index = 0;
for (auto tlist_iter : m_target_pool) {
if (!tlist_iter) { continue; }
Log.Out(Logs::General, Logs::Error, "(%i) %s (hp: %3.1f%%, at: %u, dontheal: %c, crit(base): %c(%c), safe(base): %c(%c), hcnt(ext): %u(%u), hfreq(ext): %f(%f))",
(++target_index), tlist_iter->GetCleanName(),
tlist_iter->GetHPRatio(),
ClassArmorType(tlist_iter->GetClass()),
((tlist_iter->DontHealMeBefore() > Timer::GetCurrentTime()) ? ('T') : ('F')),
((tlist_iter->GetHPRatio()>m_critical_hp_ratio[ClassArmorType(tlist_iter->GetClass())]) ? ('F') : ('T')),
((tlist_iter->GetHPRatio()>m_critical_hp_ratio[ARMOR_TYPE_UNKNOWN]) ? ('F') : ('T')),
((tlist_iter->GetHPRatio()>m_safe_hp_ratio[ClassArmorType(tlist_iter->GetClass())]) ? ('T') : ('F')),
((tlist_iter->GetHPRatio()>m_safe_hp_ratio[ARMOR_TYPE_UNKNOWN]) ? ('T') : ('F')),
tlist_iter->HealRotationHealCount(),
tlist_iter->HealRotationExtendedHealCount(),
tlist_iter->HealRotationHealFrequency(),
tlist_iter->HealRotationExtendedHealFrequency());
}
if (!target_index) { Log.Out(Logs::General, Logs::Error, "(0) None (hp: 0.0\%, at: 0, dontheal: F, crit(base): F(F), safe(base): F(F), hcnt(ext): 0(0), hfreq(ext): 0.0(0.0))"); }
#endif
}
bool HealRotation::IsMemberClass(uint8 class_id)
{
switch (class_id) {
case CLERIC:
case DRUID:
case SHAMAN:
return true;
default:
return false;
}
}
bool HealRotation::IsTargetMobType(Mob* target_mob)
{
if (!target_mob)
return false;
if (!target_mob->IsClient() && !target_mob->IsBot() && !target_mob->IsPet())
return false;
if (target_mob->IsPet() && (!target_mob->GetOwner() || (!target_mob->GetOwner()->IsClient() && !target_mob->GetOwner()->IsBot())))
return false;
return true;
}
+148
View File
@@ -0,0 +1,148 @@
/* 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 HEAL_ROTATION_H
#define HEAL_ROTATION_H
#include "mob.h"
#define CASTING_CYCLE_MINIMUM_INTERVAL 1000
#define CASTING_CYCLE_MINIMUM_INTERVAL_S 1
#define CASTING_CYCLE_MAXIMUM_INTERVAL 30000
#define CASTING_CYCLE_MAXIMUM_INTERVAL_S 30
#define CASTING_CYCLE_DEFAULT_INTERVAL 5000
#define CASTING_CYCLE_DEFAULT_INTERVAL_S 5
#define POKE_PROPAGATION_DELAY 250
#define ADVANCE_ROTATION_MINIMUM_INTERVAL 100
#define HEALING_STATS_RESET_INTERVAL 60000
#define HEALING_STATS_RESET_INTERVAL_S 60
#define SAFE_HP_RATIO_BASE 95.0f
#define CRITICAL_HP_RATIO_BASE 10.0f
struct HealingStats
{
uint32 last_heal_time_ms;
uint32 heal_count;
};
// Both members and targets use a shared_ptr to keep track of their HealRotation instance
class HealRotation
{
public:
HealRotation(Bot* hr_creator, uint32 interval_ms = CASTING_CYCLE_DEFAULT_INTERVAL, bool fast_heals = false, bool adaptive_targeting = false, bool casting_override = false);
void SetIntervalMS(uint32 interval_ms);
void SetIntervalS(uint32 interval_s);
void SetFastHeals(bool flag) { m_fast_heals = flag; }
void SetAdaptiveTargeting(bool flag) { m_adaptive_targeting = flag; }
void SetCastingOverride(bool flag) { m_casting_override = flag; }
bool AddMemberToPool(Bot* hr_member);
bool AddTargetToPool(Mob* hr_target);
uint32 CreationTimeMS() { return m_creation_time_ms; }
uint32 LastHealTimeMS() { return m_last_heal_time_ms; }
uint32 IntervalMS() { return m_interval_ms; }
uint32 IntervalS() { return (m_interval_ms / 1000); }
bool FastHeals() { return m_fast_heals; }
bool AdaptiveTargeting() { return m_adaptive_targeting; }
bool CastingOverride() { return m_casting_override; }
bool RemoveMemberFromPool(Bot* hr_member);
bool RemoveTargetFromPool(Mob* hr_target);
bool ClearMemberPool();
bool ClearTargetPool();
bool Start();
bool Stop();
bool IsActive() { return m_is_active; }
bool CastingReady() { return (Timer::GetCurrentTime() >= m_next_cast_time_ms); }
Bot* CastingMember();
bool PokeCastingTarget();
Mob* CastingTarget();
bool AdvanceRotation(bool use_interval = true);
std::list<Bot*>* MemberList() { return &m_member_pool; }
std::list<Bot*>* CycleList() { return &m_cycle_pool; }
std::list<Mob*>* TargetList() { return &m_target_pool; }
bool IsMemberInPool(Bot* hr_member);
bool IsTargetInPool(Mob* hr_target);
void SetMemberIsCasting(Bot* hr_member, bool flag = true);
bool MemberIsCasting(Bot* hr_member);
void UpdateTargetHealingStats(Mob* hr_target);
void StartNewTargetHealingStatsCycle(uint32 current_time);
uint32 HealCount(Mob* hr_target);
uint32 ExtendedHealCount(Mob* hr_target);
float HealFrequency(Mob* hr_target);
float ExtendedHealFrequency(Mob* hr_target);
HealingStats* TargetHealingStats1(Mob* hr_target);
HealingStats* TargetHealingStats2(Mob* hr_target);
bool SetArmorTypeSafeHPRatio(uint8 armor_type, float hp_ratio);
bool SetArmorTypeCriticalHPRatio(uint8 armor_type, float hp_ratio);
float ArmorTypeSafeHPRatio(uint8 armor_type);
float ArmorTypeCriticalHPRatio(uint8 armor_type);
void ResetArmorTypeHPLimits();
static bool IsMemberClass(uint8 class_id);
static bool IsTargetMobType(Mob* target_mob);
private:
bool valid_state();
void cycle_refresh();
bool healable_target(bool use_class_at = true, bool critical_only = false);
void bias_targets();
uint32 m_creation_time_ms;
uint32 m_last_heal_time_ms;
uint32 m_interval_ms;
uint32 m_next_cast_time_ms;
uint32 m_next_poke_time_ms;
uint32 m_healing_stats_begin_ms;
bool m_fast_heals;
bool m_adaptive_targeting;
bool m_casting_override;
bool m_casting_target_poke;
bool m_active_heal_target;
bool m_is_active;
std::list<Bot*> m_member_pool;
std::list<Bot*> m_cycle_pool;
std::list<Mob*> m_target_pool;
std::map<Bot*, bool> m_member_is_casting;
std::map<Mob*, HealingStats> m_target_healing_stats_1;
std::map<Mob*, HealingStats> m_target_healing_stats_2;
float m_safe_hp_ratio[ARMOR_TYPE_COUNT];
float m_critical_hp_ratio[ARMOR_TYPE_COUNT];
friend class std::_Ref_count_obj<HealRotation>;
HealRotation(HealRotation* allocator_shunt) {};
};
#endif
+2 -5
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2003 EQEMu Development Team (http://eqemulator.net)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net)
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
@@ -2020,10 +2020,7 @@ void Client::QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call) {
void Client::DyeArmor(DyeStruct* dye){
int16 slot=0;
for (int i = EmuConstants::MATERIAL_BEGIN; i <= EmuConstants::MATERIAL_TINT_END; i++) {
if (m_pp.item_tint[i].RGB.Blue != dye->dye[i].RGB.Blue ||
m_pp.item_tint[i].RGB.Red != dye->dye[i].RGB.Red ||
m_pp.item_tint[i].RGB.Green != dye->dye[i].RGB.Green
) {
if ((m_pp.item_tint[i].Color & 0x00FFFFFF) != (dye->dye[i].Color & 0x00FFFFFF)) {
slot = m_inv.HasItem(32557, 1, invWherePersonal);
if (slot != INVALID_INDEX){
DeleteItemInInventory(slot,1,true);
+220 -154
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.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
@@ -473,6 +473,10 @@ Mob::~Mob()
safe_delete(PathingRouteUpdateTimerShort);
safe_delete(PathingRouteUpdateTimerLong);
UninitializeBuffSlots();
#ifdef BOTS
LeaveHealRotationTargetPool();
#endif
}
uint32 Mob::GetAppearanceValue(EmuAppearance iAppearance) {
@@ -1766,8 +1770,7 @@ void Mob::SendIllusionPacket(uint16 in_race, uint8 in_gender, uint8 in_texture,
bool Mob::RandomizeFeatures(bool send_illusion, bool set_variables)
{
if (IsPlayerRace(GetRace()))
{
if (IsPlayerRace(GetRace())) {
uint8 Gender = GetGender();
uint8 Texture = 0xFF;
uint8 HelmTexture = 0xFF;
@@ -1788,160 +1791,158 @@ bool Mob::RandomizeFeatures(bool send_illusion, bool set_variables)
LuclinFace = zone->random.Int(0, 7);
// Adjust all settings based on the min and max for each feature of each race and gender
switch (GetRace())
{
case 1: // Human
HairColor = zone->random.Int(0, 19);
if (Gender == 0) {
BeardColor = HairColor;
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == 1) {
HairStyle = zone->random.Int(0, 2);
}
break;
case 2: // Barbarian
HairColor = zone->random.Int(0, 19);
switch (GetRace()) {
case HUMAN:
HairColor = zone->random.Int(0, 19);
if (Gender == MALE) {
BeardColor = HairColor;
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 2);
}
break;
case BARBARIAN:
HairColor = zone->random.Int(0, 19);
LuclinFace = zone->random.Int(0, 87);
if (Gender == MALE) {
BeardColor = HairColor;
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 2);
}
break;
case ERUDITE:
if (Gender == MALE) {
BeardColor = zone->random.Int(0, 19);
Beard = zone->random.Int(0, 5);
LuclinFace = zone->random.Int(0, 57);
}
if (Gender == FEMALE) {
LuclinFace = zone->random.Int(0, 87);
if (Gender == 0) {
BeardColor = HairColor;
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == 1) {
HairStyle = zone->random.Int(0, 2);
}
break;
case 3: // Erudite
if (Gender == 0) {
BeardColor = zone->random.Int(0, 19);
Beard = zone->random.Int(0, 5);
LuclinFace = zone->random.Int(0, 57);
}
if (Gender == 1) {
LuclinFace = zone->random.Int(0, 87);
}
break;
case 4: // WoodElf
HairColor = zone->random.Int(0, 19);
if (Gender == 0) {
HairStyle = zone->random.Int(0, 3);
}
if (Gender == 1) {
HairStyle = zone->random.Int(0, 2);
}
break;
case 5: // HighElf
HairColor = zone->random.Int(0, 14);
if (Gender == 0) {
HairStyle = zone->random.Int(0, 3);
LuclinFace = zone->random.Int(0, 37);
BeardColor = HairColor;
}
if (Gender == 1) {
HairStyle = zone->random.Int(0, 2);
}
break;
case 6: // DarkElf
HairColor = zone->random.Int(13, 18);
if (Gender == 0) {
HairStyle = zone->random.Int(0, 3);
LuclinFace = zone->random.Int(0, 37);
BeardColor = HairColor;
}
if (Gender == 1) {
HairStyle = zone->random.Int(0, 2);
}
break;
case 7: // HalfElf
HairColor = zone->random.Int(0, 19);
if (Gender == 0) {
HairStyle = zone->random.Int(0, 3);
LuclinFace = zone->random.Int(0, 37);
BeardColor = HairColor;
}
if (Gender == 1) {
HairStyle = zone->random.Int(0, 2);
}
break;
case 8: // Dwarf
HairColor = zone->random.Int(0, 19);
}
break;
case WOOD_ELF:
HairColor = zone->random.Int(0, 19);
if (Gender == MALE) {
HairStyle = zone->random.Int(0, 3);
}
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 2);
}
break;
case HIGH_ELF:
HairColor = zone->random.Int(0, 14);
if (Gender == MALE) {
HairStyle = zone->random.Int(0, 3);
LuclinFace = zone->random.Int(0, 37);
BeardColor = HairColor;
if (Gender == 0) {
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == 1) {
HairStyle = zone->random.Int(0, 2);
LuclinFace = zone->random.Int(0, 17);
}
break;
case 9: // Troll
EyeColor1 = zone->random.Int(0, 10);
EyeColor2 = zone->random.Int(0, 10);
if (Gender == 1) {
HairStyle = zone->random.Int(0, 3);
HairColor = zone->random.Int(0, 23);
}
break;
case 10: // Ogre
if (Gender == 1) {
HairStyle = zone->random.Int(0, 3);
HairColor = zone->random.Int(0, 23);
}
break;
case 11: // Halfling
HairColor = zone->random.Int(0, 19);
if (Gender == 0) {
BeardColor = HairColor;
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == 1) {
HairStyle = zone->random.Int(0, 2);
}
break;
case 12: // Gnome
HairColor = zone->random.Int(0, 24);
if (Gender == 0) {
BeardColor = HairColor;
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == 1) {
HairStyle = zone->random.Int(0, 2);
}
break;
case 128: // Iksar
case 130: // VahShir
break;
case 330: // Froglok
LuclinFace = zone->random.Int(0, 9);
case 522: // Drakkin
HairColor = zone->random.Int(0, 3);
}
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 2);
}
break;
case DARK_ELF:
HairColor = zone->random.Int(13, 18);
if (Gender == MALE) {
HairStyle = zone->random.Int(0, 3);
LuclinFace = zone->random.Int(0, 37);
BeardColor = HairColor;
EyeColor1 = zone->random.Int(0, 11);
EyeColor2 = zone->random.Int(0, 11);
LuclinFace = zone->random.Int(0, 6);
DrakkinHeritage = zone->random.Int(0, 6);
DrakkinTattoo = zone->random.Int(0, 7);
DrakkinDetails = zone->random.Int(0, 7);
if (Gender == 0) {
Beard = zone->random.Int(0, 12);
HairStyle = zone->random.Int(0, 8);
}
if (Gender == 1) {
Beard = zone->random.Int(0, 3);
HairStyle = zone->random.Int(0, 7);
}
break;
default:
break;
}
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 2);
}
break;
case HALF_ELF:
HairColor = zone->random.Int(0, 19);
if (Gender == MALE) {
HairStyle = zone->random.Int(0, 3);
LuclinFace = zone->random.Int(0, 37);
BeardColor = HairColor;
}
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 2);
}
break;
case DWARF:
HairColor = zone->random.Int(0, 19);
BeardColor = HairColor;
if (Gender == MALE) {
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 2);
LuclinFace = zone->random.Int(0, 17);
}
break;
case TROLL:
EyeColor1 = zone->random.Int(0, 10);
EyeColor2 = zone->random.Int(0, 10);
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 3);
HairColor = zone->random.Int(0, 23);
}
break;
case OGRE:
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 3);
HairColor = zone->random.Int(0, 23);
}
break;
case HALFLING:
HairColor = zone->random.Int(0, 19);
if (Gender == MALE) {
BeardColor = HairColor;
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 2);
}
break;
case GNOME:
HairColor = zone->random.Int(0, 24);
if (Gender == MALE) {
BeardColor = HairColor;
HairStyle = zone->random.Int(0, 3);
Beard = zone->random.Int(0, 5);
}
if (Gender == FEMALE) {
HairStyle = zone->random.Int(0, 2);
}
break;
case IKSAR:
case VAHSHIR:
break;
case FROGLOK:
LuclinFace = zone->random.Int(0, 9);
case DRAKKIN:
HairColor = zone->random.Int(0, 3);
BeardColor = HairColor;
EyeColor1 = zone->random.Int(0, 11);
EyeColor2 = zone->random.Int(0, 11);
LuclinFace = zone->random.Int(0, 6);
DrakkinHeritage = zone->random.Int(0, 6);
DrakkinTattoo = zone->random.Int(0, 7);
DrakkinDetails = zone->random.Int(0, 7);
if (Gender == MALE) {
Beard = zone->random.Int(0, 12);
HairStyle = zone->random.Int(0, 8);
}
if (Gender == FEMALE) {
Beard = zone->random.Int(0, 3);
HairStyle = zone->random.Int(0, 7);
}
break;
default:
break;
}
if (set_variables)
{
if (set_variables) {
haircolor = HairColor;
beardcolor = BeardColor;
eyecolor1 = EyeColor1;
@@ -1954,8 +1955,7 @@ bool Mob::RandomizeFeatures(bool send_illusion, bool set_variables)
drakkin_details = DrakkinDetails;
}
if (send_illusion)
{
if (send_illusion) {
SendIllusionPacket(GetRace(), Gender, Texture, HelmTexture, HairColor, BeardColor,
EyeColor1, EyeColor2, HairStyle, LuclinFace, Beard, 0xFF, DrakkinHeritage,
DrakkinTattoo, DrakkinDetails);
@@ -5861,3 +5861,69 @@ int Mob::CheckBaneDamage(const ItemInst *item)
return damage;
}
#ifdef BOTS
bool Mob::JoinHealRotationTargetPool(std::shared_ptr<HealRotation>* heal_rotation)
{
if (IsHealRotationTarget())
return false;
if (!heal_rotation->use_count())
return false;
if (!(*heal_rotation))
return false;
if (!HealRotation::IsTargetMobType(this))
return false;
if (!(*heal_rotation)->AddTargetToPool(this))
return false;
m_target_of_heal_rotation = *heal_rotation;
return IsHealRotationTarget();
}
bool Mob::LeaveHealRotationTargetPool()
{
if (!IsHealRotationTarget()) {
m_target_of_heal_rotation.reset();
return true;
}
m_target_of_heal_rotation->RemoveTargetFromPool(this);
m_target_of_heal_rotation.reset();
return !IsHealRotationTarget();
}
uint32 Mob::HealRotationHealCount()
{
if (!IsHealRotationTarget())
return 0;
return m_target_of_heal_rotation->HealCount(this);
}
uint32 Mob::HealRotationExtendedHealCount()
{
if (!IsHealRotationTarget())
return 0;
return m_target_of_heal_rotation->ExtendedHealCount(this);
}
float Mob::HealRotationHealFrequency()
{
if (!IsHealRotationTarget())
return 0.0f;
return m_target_of_heal_rotation->HealFrequency(this);
}
float Mob::HealRotationExtendedHealFrequency()
{
if (!IsHealRotationTarget())
return 0.0f;
return m_target_of_heal_rotation->ExtendedHealFrequency(this);
}
#endif
+24 -1
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.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
@@ -29,6 +29,10 @@
#include <vector>
#include <memory>
#ifdef BOTS
#include "heal_rotation.h"
#endif
char* strn0cpy(char* dest, const char* source, uint32 size);
#define MAX_SPECIAL_ATTACK_PARAMS 8
@@ -1005,6 +1009,20 @@ public:
void DelAssistCap() { --npc_assist_cap; }
void ResetAssistCap() { npc_assist_cap = 0; }
// Bots HealRotation methods
#ifdef BOTS
bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); }
bool JoinHealRotationTargetPool(std::shared_ptr<HealRotation>* heal_rotation);
bool LeaveHealRotationTargetPool();
uint32 HealRotationHealCount();
uint32 HealRotationExtendedHealCount();
float HealRotationHealFrequency();
float HealRotationExtendedHealFrequency();
const std::shared_ptr<HealRotation>* TargetOfHealRotation() const { return &m_target_of_heal_rotation; }
#endif
protected:
void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic, int special = 0);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
@@ -1373,6 +1391,11 @@ protected:
private:
void _StopSong(); //this is not what you think it is
Mob* target;
#ifdef BOTS
std::shared_ptr<HealRotation> m_target_of_heal_rotation;
#endif
};
#endif
+29 -1
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.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
@@ -52,6 +52,10 @@
#include "zone.h"
#include "queryserv.h"
#include "command.h"
#ifdef BOTS
#include "bot_command.h"
#include "bot_database.h"
#endif
#include "zone_config.h"
#include "titles.h"
#include "guild_mgr.h"
@@ -203,6 +207,18 @@ int main(int argc, char** argv) {
return 1;
}
#ifdef BOTS
if (!botdb.Connect(
Config->DatabaseHost.c_str(),
Config->DatabaseUsername.c_str(),
Config->DatabasePassword.c_str(),
Config->DatabaseDB.c_str(),
Config->DatabasePort)) {
Log.Out(Logs::General, Logs::Error, "Cannot continue without a bots database connection.");
return 1;
}
#endif
/* Register Log System and Settings */
Log.OnLogHookCallBackZone(&Zone::GMSayHookCallBackProcess);
database.LoadLogSettings(Log.log_settings);
@@ -325,6 +341,15 @@ int main(int argc, char** argv) {
}
}
#ifdef BOTS
Log.Out(Logs::General, Logs::Zone_Server, "Loading bot commands");
int botretval = bot_command_init();
if (botretval<0)
Log.Out(Logs::General, Logs::Error, "Bot command loading FAILED");
else
Log.Out(Logs::General, Logs::Zone_Server, "%d bot commands loaded", botretval);
#endif
if(RuleB(TaskSystem, EnableTaskSystem)) {
Log.Out(Logs::General, Logs::Tasks, "[INIT] Loading Tasks");
taskmanager = new TaskManager;
@@ -524,6 +549,9 @@ int main(int argc, char** argv) {
worldserver.Disconnect();
safe_delete(taskmanager);
command_deinit();
#ifdef BOTS
bot_command_deinit();
#endif
safe_delete(parse);
Log.Out(Logs::General, Logs::Zone_Server, "Proper zone shutdown complete.");
Log.CloseFileLogs();
+2 -2
View File
@@ -1,5 +1,5 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
Copyright (C) 2001-2016 EQEMu Development Team (http://eqemu.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
@@ -1994,7 +1994,7 @@ bool WorldServer::SendVoiceMacro(Client* From, uint32 Type, char* Target, uint32
svm->Type = Type;
svm->Voice = (GetArrayRace(From->GetRace()) * 2) + From->GetGender();
svm->Voice = (GetPlayerRaceValue(From->GetRace()) * 2) + From->GetGender();
svm->MacroNumber = MacroNumber;