Compare commits

...

26 Commits

Author SHA1 Message Date
Chris Miles d3ca636a70 [Release] 22.4.5 (#3022) 2023-03-03 17:09:39 -06:00
Vayle 01855d40df [Bug Fix] Fix log messages when players join channel (#2992)
* Fix log message when players join channels

* Formatting

* More formatting

* Update CurrentPlayerChannels to use vector of channel names

* Put log statement back in place

* Remove channel limit in db query

* Formatting tweak
2023-03-03 11:54:56 -06:00
Paul Coene 748602b04e [Bug Fix] Fix an issue where EVENT_TIMER timers would not be cleaned up after zone (#3018)
* [Bug Fix] Dangling client timers fixed.

* Remove all timers from mob in destructor instead of in QuestMgr::Process
2023-03-03 11:54:14 -06:00
Alex King a97a9a0d1c [Bug Fix] Fix npcfeature and playerfeature (#3017) 2023-03-01 20:42:37 -05:00
Aeadoin a78c754c0e [Cleanup] Remove unused iterator from LoadCharacterDisciplines (#3012) 2023-03-01 20:35:28 -05:00
Alex King ef214f91e9 [Bug Fix] Fix DoAnim quest method default speed (#3016)
# Notes
- Sets default speed to `0` which makes the animations run at normal speed instead of `1` that makes them run is slow motion.
2023-03-01 17:47:27 -05:00
Aeadoin 04a74df0b2 [Bots] Add additional Heroic Sta/Wis/Int bonuses for Bots. (#3013) 2023-03-01 10:58:04 -05:00
Chris Miles c15bfe12eb [Fix] Fix issue where quest saylink responses would occur before the NPC's response (#3010)
* [Fix] Fix issue where quest saylink responses would occur before the NPC's response

* Update client_packet.cpp

* Revert "[Fix] Fix issue where quest saylink responses would occur before the NPC's response"

This reverts commit a09e1bbbe9.
2023-02-28 21:27:05 -05:00
Alex King 5702f7bcd1 [Quest API] Add IsFindable() and IsTrackable() to Perl/Lua (#2996)
# Perl
- Add `$mob->IsFindable()`.
- Add `$mob->IsTrackable()`.

# Lua
- Add `mob:IsFindable()`.
- Add `mob:IsTrackable()`.

# Notes
- Allows operators to see if a mob is findable or trackable.
2023-02-28 21:26:11 -05:00
Alex King 9a5bf53e11 [Quest API] Add IsUnderwaterOnly() to Perl/Lua (#2995)
# Perl
- Add `$npc->IsUnderwaterOnly()`.

# Lua
- Add `npc:IsUnderwaterOnly()`.

# Notes
- Allows operators to chec k if an NPC is underwater only.
2023-02-28 21:13:43 -05:00
Alex King 69c6a7b89a [Quest API] Add IsBerserk() to Perl/Lua (#2997)
* [Quest API] Add IsBerserk() to Perl/Lua

# Perl
- Add `$client->IsBerserk()`.

# Lua
- Add `client:IsBerserk()`.
- Remove `mob:IsBerserk()` to move to client.

# Notes
- Allows operators to check if a client is berserk.

* Move to Mob.

* Update lua_client.cpp
2023-02-28 20:31:20 -05:00
Aeadoin 2f0dbc5d15 [Bug Fix] Fix for Discipline Loading from Database causing issues with slot_ids (#3008)
* [Bug Fix] Fix for Discipline Loading from Database causing issues with slot_ids

* cleanup per comments
2023-02-28 16:55:22 -05:00
Aeadoin 93c79817cd [Crash] Fix crash in CheckTradeskillLoreConflict (#3009) 2023-02-27 19:24:05 -06:00
Aeadoin 3296287d70 [Bug Fix] Fix for Lore Components where component is returned. (#3005)
* [Bug Fix] Fix for Lore Components where component is returned.

* Refactor, and take into account loregroups above 0 properly

* Update tradeskills.cpp

* formatting for suggestions.

* commenting, update formatting.

---------

Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com>
2023-02-27 12:36:35 -06:00
Alex King 774a7fa779 [Cleanup] Delete unused zone/skills.h (#3007)
# Notes
- This is unused.
2023-02-26 21:35:49 -05:00
Alex King 1ff4541a9f [Cleanup] Remove NumberOfAvailableTitles() from titles.h (#3006)
# Notes
- This is unused.
2023-02-26 21:35:43 -05:00
Alex King 3448758c03 [Quest API] Add HasSpecialAbilities() to Perl/Lua (#2994)
* [Quest API] Add HasSpecialAbilities() to Perl/Lua

# Perl
- Add `$mob->HasSpecialAbilities()`.

# Lua
- Add `mob:HasSpecialAbilities()`

# Notes
- Allows operators to check if a mob has special abilities

* Move to NPC.

* Update lua_mob.cpp
2023-02-26 21:35:10 -05:00
Alex King ff4ccfa98f [Quest API] Add GetDefaultRaceSize() to Perl/Lua (#2993)
# Perl
- Add `$mob->GetDefaultRaceSize()`.

# Lua
- Add `mob:GetDefaultRaceSize()`.

# Notes
- Allows operators to get the default race size of a race if they want to use it in a script.
2023-02-26 21:35:03 -05:00
Aeadoin d2c3c14ae0 [Bots] Cleanup AI_IdleCastCheck Logic (#3004)
* [Bots] Cleanup AI_IdleCastCheck Logic

* cleanup logic
2023-02-25 20:16:09 -05:00
Alex King a470931fdd [Cleanup] Remove class EGNode from mob.h (#3003)
# Notes
- This is unused.
2023-02-25 20:10:23 -05:00
Alex King 078db3460d [Cleanup] Remove ReturnItemPacket from client.h/inventory.cpp (#3002)
# Notes
- This is unused.
2023-02-25 20:10:08 -05:00
Alex King 0980a780d0 [Cleanup] Remove GetDamageMultiplier() from client.h (#3001)
# Notes
- This is unused.
2023-02-25 19:50:42 -05:00
Alex King 7f01bb509c [Cleanup] Remove DumpPacketProfile() from client.h (#3000)
# Notes
- This is unused.
2023-02-25 19:38:09 -05:00
Alex King 4bb189cbf4 [Cleanup] Remove GetCombinedAC_TEST() from client.h (#2999)
# Notes
- This is unused.
2023-02-25 19:38:03 -05:00
Alex King b03e8ff0fb [Cleanup] Remove unused ClientFactory in client.h (#2998)
# Notes
- This is unused.
2023-02-25 19:37:57 -05:00
Aeadoin 6179b7481e [Bug Fix] Account for bad data in Tradeskill Recipe Entries (#2991) 2023-02-25 10:52:17 -06:00
30 changed files with 267 additions and 242 deletions
+42
View File
@@ -1,3 +1,45 @@
## [22.4.5] - 03/03/2023
### Bots
* Add additional Heroic Sta/Wis/Int bonuses for Bots. ([#3013](https://github.com/EQEmu/Server/pull/3013)) @Aeadoin 2023-03-01
* Cleanup AI_IdleCastCheck Logic ([#3004](https://github.com/EQEmu/Server/pull/3004)) @Aeadoin 2023-02-26
### Code
* Delete unused zone/skills.h ([#3007](https://github.com/EQEmu/Server/pull/3007)) @Kinglykrab 2023-02-27
* Remove DumpPacketProfile() from client.h ([#3000](https://github.com/EQEmu/Server/pull/3000)) @Kinglykrab 2023-02-26
* Remove GetCombinedAC_TEST() from client.h ([#2999](https://github.com/EQEmu/Server/pull/2999)) @Kinglykrab 2023-02-26
* Remove GetDamageMultiplier() from client.h ([#3001](https://github.com/EQEmu/Server/pull/3001)) @Kinglykrab 2023-02-26
* Remove NumberOfAvailableTitles() from titles.h ([#3006](https://github.com/EQEmu/Server/pull/3006)) @Kinglykrab 2023-02-27
* Remove ReturnItemPacket from client.h/inventory.cpp ([#3002](https://github.com/EQEmu/Server/pull/3002)) @Kinglykrab 2023-02-26
* Remove class EGNode from mob.h ([#3003](https://github.com/EQEmu/Server/pull/3003)) @Kinglykrab 2023-02-26
* Remove unused ClientFactory in client.h ([#2998](https://github.com/EQEmu/Server/pull/2998)) @Kinglykrab 2023-02-26
* Remove unused iterator from LoadCharacterDisciplines ([#3012](https://github.com/EQEmu/Server/pull/3012)) @Aeadoin 2023-03-02
### Crash
* Fix crash in CheckTradeskillLoreConflict ([#3009](https://github.com/EQEmu/Server/pull/3009)) @Aeadoin 2023-02-28
### Fixes
* Account for bad data in Tradeskill Recipe Entries ([#2991](https://github.com/EQEmu/Server/pull/2991)) @Aeadoin 2023-02-25
* Fix DoAnim quest method default speed ([#3016](https://github.com/EQEmu/Server/pull/3016)) @Kinglykrab 2023-03-01
* Fix an issue where EVENT_TIMER timers would not be cleaned up after zone ([#3018](https://github.com/EQEmu/Server/pull/3018)) @noudess 2023-03-03
* Fix for Discipline Loading from Database causing issues with slot_ids ([#3008](https://github.com/EQEmu/Server/pull/3008)) @Aeadoin 2023-02-28
* Fix for Lore Components where component is returned. ([#3005](https://github.com/EQEmu/Server/pull/3005)) @Aeadoin 2023-02-27
* Fix issue where quest saylink responses would occur before the NPC's response ([#3010](https://github.com/EQEmu/Server/pull/3010)) @Akkadius 2023-03-01
* Fix log messages when players join channel ([#2992](https://github.com/EQEmu/Server/pull/2992)) @Valorith 2023-03-03
* Fix npcfeature and playerfeature ([#3017](https://github.com/EQEmu/Server/pull/3017)) @Kinglykrab 2023-03-02
### Quest API
* Add GetDefaultRaceSize() to Perl/Lua ([#2993](https://github.com/EQEmu/Server/pull/2993)) @Kinglykrab 2023-02-27
* Add HasSpecialAbilities() to Perl/Lua ([#2994](https://github.com/EQEmu/Server/pull/2994)) @Kinglykrab 2023-02-27
* Add IsBerserk() to Perl/Lua ([#2997](https://github.com/EQEmu/Server/pull/2997)) @Kinglykrab 2023-03-01
* Add IsFindable() and IsTrackable() to Perl/Lua ([#2996](https://github.com/EQEmu/Server/pull/2996)) @Kinglykrab 2023-03-01
* Add IsUnderwaterOnly() to Perl/Lua ([#2995](https://github.com/EQEmu/Server/pull/2995)) @Kinglykrab 2023-03-01
## [22.4.4] - 02/24/2023
### Bots
+1 -1
View File
@@ -25,7 +25,7 @@
// Build variables
// these get injected during the build pipeline
#define CURRENT_VERSION "22.4.4-dev" // always append -dev to the current version for custom-builds
#define CURRENT_VERSION "22.4.5-dev" // always append -dev to the current version for custom-builds
#define LOGIN_VERSION "0.8.0"
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "eqemu-server",
"version": "22.4.4",
"version": "22.4.5",
"repository": {
"type": "git",
"url": "https://github.com/EQEmu/Server.git"
+5 -5
View File
@@ -48,10 +48,10 @@ ChatChannel::ChatChannel(std::string inName, std::string inOwner, std::string in
m_moderated = false;
LogDebug(
"New ChatChannel created: Name: [{}], Owner: [{}], Password: [{}], MinStatus: [{}]",
m_name.c_str(),
m_owner.c_str(),
m_password.c_str(),
"New ChatChannel created: Name: [{}] Owner: [{}] Password: [{}] MinStatus: [{}]",
m_name,
m_owner,
m_password,
m_minimum_status
);
@@ -667,7 +667,7 @@ ChatChannel *ChatChannelList::RemoveClientFromChannel(const std::string& in_chan
}
LogDebug("Client [{}] removed from channel [{}]. Channel is owned by {}. Command directed: {}", c->GetName(), channel_name, required_channel->GetOwnerName(), command_directed);
if (c->GetName() == required_channel->GetOwnerName() && command_directed) { // Check if the client that is leaving is the the channel owner
if (c->GetName() == required_channel->GetOwnerName() && command_directed) { // Check if the client that is leaving is the channel owner
LogDebug("Owner left the channel [{}], removing channel from database...", channel_name);
database.DeleteChatChannel(channel_name); // Remove the channel from the database.
LogDebug("Flagging [{}] channel as temporary...", channel_name);
+4 -1
View File
@@ -793,7 +793,10 @@ void Clientlist::ProcessOPMailCommand(Client *c, std::string command_string, boo
case CommandJoin:
if (!command_directed) {
//Append saved channels to params
parameters = parameters + ", " + database.CurrentPlayerChannels(c->GetName());
const auto saved_channels = database.CurrentPlayerChannels(c->GetName());
if (!saved_channels.empty()) {
parameters += fmt::format(", {}", Strings::Join(saved_channels, ", "));
}
parameters = RemoveDuplicateChannels(parameters);
}
c->JoinChannels(parameters, command_directed);
+10 -9
View File
@@ -336,16 +336,17 @@ void UCSDatabase::DeleteChatChannel(const std::string& channel_name)
LogInfo("Deleting channel [{}] from the database.", channel_name);
}
std::string UCSDatabase::CurrentPlayerChannels(const std::string& player_name) {
int current_player_channel_count = CurrentPlayerChannelCount(player_name);
if (current_player_channel_count == 0) {
return "";
std::vector<std::string> UCSDatabase::CurrentPlayerChannels(const std::string& player_name) {
auto rows = ChatchannelsRepository::GetWhere(*this, fmt::format("`owner` = '{}'", Strings::Escape(player_name)));
if (rows.empty()) {
return {};
}
const auto rquery = fmt::format("SELECT GROUP_CONCAT(`name` SEPARATOR ', ') FROM chatchannels WHERE `owner` = '{}'; ", Strings::Escape(player_name));
auto results = QueryDatabase(rquery);
auto row = results.begin();
std::string channels = row[0];
LogDebug("Player [{}] has the following permanent channels saved to the database: [{}].", player_name, channels);
std::vector<std::string> channels = {};
channels.reserve(rows.size());
for (auto &e: rows) {
channels.emplace_back(e.name);
}
LogDebug("Player [{}] has the following [{}] permanent channels saved to the database: [{}].", player_name, rows.size(), Strings::Join(channels, ", "));
return channels;
}
+1 -1
View File
@@ -52,7 +52,7 @@ public:
void SaveChatChannel(const std::string& channel_name, const std::string& channel_owner, const std::string& channel_password, const uint16& min_status);
void DeleteChatChannel(const std::string& channel_name);
int CurrentPlayerChannelCount(const std::string& player_name);
std::string CurrentPlayerChannels(const std::string& player_name);
std::vector<std::string> CurrentPlayerChannels(const std::string& player_name);
void GetAccountStatus(Client *c);
void SetChannelPassword(const std::string& channel_name, const std::string& password);
void SetChannelOwner(const std::string& channel_name, const std::string& owner);
-1
View File
@@ -255,7 +255,6 @@ SET(zone_headers
quest_parser_collection.h
raids.h
raycast_mesh.h
skills.h
shared_task_zone_messaging.h
spawn2.cpp
spawn2.h
+6
View File
@@ -2470,6 +2470,7 @@ void Bot::AI_Process()
}
// We also need a leash owner and follow mob (subset of primary AI criteria)
bot_group->VerifyGroup();
Client* leash_owner = (bot_group->GetLeader() && bot_group->GetLeader()->IsClient() ? bot_group->GetLeader()->CastToClient() : bot_owner);
if (!leash_owner) {
return;
@@ -6133,8 +6134,11 @@ void Bot::ProcessBotOwnerRefDelete(Mob* botOwner) {
int64 Bot::CalcMaxMana() {
switch(GetCasterClass()) {
case 'I':
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement());
max_mana += (GetHeroicINT() * 10);
case 'W': {
max_mana = (GenerateBaseManaPoints() + itembonuses.Mana + spellbonuses.Mana + GroupLeadershipAAManaEnhancement());
max_mana += (GetHeroicWIS() * 10);
break;
}
case 'N': {
@@ -7084,6 +7088,7 @@ int32 Bot::LevelRegen() {
int64 Bot::CalcHPRegen() {
int32 regen = (LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen);
regen += GetHeroicSTA() / 20;
regen += (aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration());
regen = ((regen * RuleI(Character, HPRegenMultiplier)) / 100);
return regen;
@@ -7155,6 +7160,7 @@ int64 Bot::CalcMaxHP() {
int32 bot_hp = 0;
uint32 nd = 10000;
bot_hp += (GenerateBaseHitPoints() + itembonuses.HP);
bot_hp += (GetHeroicSTA() * 10);
nd += aabonuses.MaxHP;
bot_hp = ((float)bot_hp * (float)nd / (float)10000);
bot_hp += (spellbonuses.HP + aabonuses.HP);
+46 -27
View File
@@ -1303,8 +1303,7 @@ bool Bot::AI_IdleCastCheck() {
if (HasGroup() && GetGroup()->GetLeader() && GetGroup()->GetLeader()->IsClient()) {
test_against = GetGroup()->GetLeader()->CastToClient();
}
else if (GetOwner() && GetOwner()->IsClient()) {
} else if (GetOwner() && GetOwner()->IsClient()) {
test_against = GetOwner()->CastToClient();
}
@@ -1317,17 +1316,12 @@ bool Bot::AI_IdleCastCheck() {
// Healers WITHOUT pets will check if a heal is needed before buffing.
case CLERIC:
case PALADIN:
case RANGER:
case MONK:
case ROGUE:
case WARRIOR:
case BERSERKER: {
case RANGER: {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
if (!AICastSpell(this, 100, SpellType_Buff)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
//
}
}
}
@@ -1337,13 +1331,46 @@ bool Bot::AI_IdleCastCheck() {
result = true;
break;
}
case MONK:
case ROGUE:
case WARRIOR:
case BERSERKER: {
if (!AICastSpell(this, 100, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Heal)) {
if (!AICastSpell(this, 100, SpellType_Buff)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
}
}
}
}
result = true;
break;
}
// Pets class will first cast their pet, then buffs
case DRUID:
case MAGICIAN:
case SHADOWKNIGHT:
case SHAMAN:
case NECROMANCER:
case ENCHANTER:
case ENCHANTER: {
if (!AICastSpell(this, 100, SpellType_Pet)) {
if (!AICastSpell(this, 100, SpellType_Cure)) {
if (!AICastSpell(GetPet(), 100, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Buff)) {
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
}
}
}
}
}
}
result = true;
break;
}
case DRUID:
case SHAMAN:
case BEASTLORD: {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Pet)) {
@@ -1365,16 +1392,12 @@ bool Bot::AI_IdleCastCheck() {
}
case WIZARD: { // This can eventually be move into the BEASTLORD case handler once pre-combat is fully implemented
if (pre_combat) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Pet)) {
if (!AICastSpell(this, 100, SpellType_Pet)) {
if (!AICastSpell(this, 100, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
if (!AICastSpell(this, 100, SpellType_Buff)) {
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_PreCombatBuff)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
}
}
if (!AICastSpell(this, 100, SpellType_Buff)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_PreCombatBuff)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
}
}
}
@@ -1383,15 +1406,11 @@ bool Bot::AI_IdleCastCheck() {
}
}
else {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Cure)) {
if (!AICastSpell(this, 100, SpellType_Pet)) {
if (!AICastSpell(this, 100, SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
if (!AICastSpell(this, 100, SpellType_Buff)) {
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
}
}
if (!AICastSpell(this, 100, SpellType_Buff)) {
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
}
}
}
-12
View File
@@ -218,11 +218,6 @@ struct ClientReward
uint32 amount;
};
class ClientFactory {
public:
Client *MakeClient(std::shared_ptr<EQStreamInterface> ieqs);
};
class Client : public Mob
{
public:
@@ -343,8 +338,6 @@ public:
bool HasRecipeLearned(uint32 recipe_id);
bool CanIncreaseTradeskill(EQ::skills::SkillType tradeskill);
EQApplicationPacket* ReturnItemPacket(int16 slot_id, const EQ::ItemInstance* inst, ItemPacketType packet_type);
bool GetRevoked() const { return revoked; }
void SetRevoked(bool rev) { revoked = rev; }
inline uint32 GetIP() const { return ip; }
@@ -697,7 +690,6 @@ public:
void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity, bool quest = false);
void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp);
int32 GetRawItemAC();
uint16 GetCombinedAC_TEST();
inline uint32 LSAccountID() const { return lsaccountid; }
inline uint32 GetWID() const { return WID; }
@@ -851,9 +843,6 @@ public:
inline void SetBecomeNPC(bool flag) { npcflag = flag; }
inline void SetBecomeNPCLevel(uint8 level) { npclevel = level; }
EQStreamInterface* Connection() { return eqs; }
#ifdef PACKET_PROFILER
void DumpPacketProfile() { if(eqs) eqs->DumpPacketProfile(); }
#endif
uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; // returns item id
uint32 GetEquipmentColor(uint8 material_slot) const;
virtual void UpdateEquipmentLight() { m_Light.Type[EQ::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQ::lightsource::LightEquipment] = EQ::lightsource::TypeToLevel(m_Light.Type[EQ::lightsource::LightEquipment]); }
@@ -1595,7 +1584,6 @@ public:
void SetAccountFlag(std::string flag, std::string val);
std::string GetAccountFlag(std::string flag);
void SetGMStatus(int16 new_status);
float GetDamageMultiplier(EQ::skills::SkillType how_long_has_this_been_missing);
void Consume(const EQ::ItemData *item, uint8 type, int16 slot, bool auto_consume);
void PlayMP3(const char* fname);
void ExpeditionSay(const char *str, int ExpID);
+2 -2
View File
@@ -8737,12 +8737,12 @@ void Client::Handle_OP_ItemLinkClick(const EQApplicationPacket *app)
}
if (!response.empty()) {
ChannelMessageReceived(ChatChannel_Say, 0, 100, response.c_str(), nullptr, true);
if (!silentsaylink) {
Message(Chat::LightGray, "You say, '%s'", response.c_str());
}
ChannelMessageReceived(ChatChannel_Say, 0, 100, response.c_str(), nullptr, true);
return;
}
else {
+2 -2
View File
@@ -1063,12 +1063,12 @@ void Perl__playertexture(int texture_id)
quest_manager.playertexture(texture_id);
}
void Perl__playerfeature(char* feature, int value)
void Perl__playerfeature(const char* feature, int value)
{
quest_manager.playerfeature(feature, value);
}
void Perl__npcfeature(char* feature, int value)
void Perl__npcfeature(const char* feature, int value)
{
quest_manager.npcfeature(feature, value);
}
-25
View File
@@ -3376,31 +3376,6 @@ void Client::SendItemPacket(int16 slot_id, const EQ::ItemInstance* inst, ItemPac
FastQueuePacket(&outapp);
}
EQApplicationPacket* Client::ReturnItemPacket(int16 slot_id, const EQ::ItemInstance* inst, ItemPacketType packet_type)
{
if (!inst)
return nullptr;
// Serialize item into |-delimited string
std::string packet = inst->Serialize(slot_id);
EmuOpcode opcode = OP_Unknown;
EQApplicationPacket* outapp = nullptr;
BulkItemPacket_Struct* itempacket = nullptr;
// Construct packet
opcode = OP_ItemPacket;
outapp = new EQApplicationPacket(opcode, packet.length()+1);
itempacket = (BulkItemPacket_Struct*)outapp->pBuffer;
memcpy(itempacket->SerializedItem, packet.c_str(), packet.length());
#if EQDEBUG >= 9
DumpPacket(outapp);
#endif
return outapp;
}
static int16 BandolierSlotToWeaponSlot(int BandolierSlot)
{
switch (BandolierSlot)
-1
View File
@@ -3049,7 +3049,6 @@ bool Lua_Client::IsAutoFireEnabled()
{
Lua_Safe_Call_Bool();
return self->AutoFireEnabled();
}
luabind::scope lua_register_client() {
+19
View File
@@ -2823,6 +2823,22 @@ Lua_HateList Lua_Mob::GetHateListBots(uint32 distance) {
return ret;
}
bool Lua_Mob::IsFindable() {
Lua_Safe_Call_Bool();
return self->IsFindable();
}
bool Lua_Mob::IsTrackable() {
Lua_Safe_Call_Bool();
return self->IsTrackable();
}
float Lua_Mob::GetDefaultRaceSize() {
Lua_Safe_Call_Real();
return self->GetDefaultRaceSize();
}
luabind::scope lua_register_mob() {
return luabind::class_<Lua_Mob, Lua_Entity>("Mob")
.def(luabind::constructor<>())
@@ -3022,6 +3038,7 @@ luabind::scope lua_register_mob() {
.def("GetDEX", &Lua_Mob::GetDEX)
.def("GetDR", &Lua_Mob::GetDR)
.def("GetDamageAmount", (uint32(Lua_Mob::*)(Lua_Mob))&Lua_Mob::GetDamageAmount)
.def("GetDefaultRaceSize", &Lua_Mob::GetDefaultRaceSize)
.def("GetDeity", &Lua_Mob::GetDeity)
.def("GetDisplayAC", &Lua_Mob::GetDisplayAC)
.def("GetDrakkinDetails", &Lua_Mob::GetDrakkinDetails)
@@ -3164,6 +3181,7 @@ luabind::scope lua_register_mob() {
.def("IsEngaged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEngaged)
.def("IsEnraged", (bool(Lua_Mob::*)(void))&Lua_Mob::IsEnraged)
.def("IsFeared", (bool(Lua_Mob::*)(void))&Lua_Mob::IsFeared)
.def("IsFindable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsFindable)
.def("IsHorse", &Lua_Mob::IsHorse)
.def("IsImmuneToSpell", (bool(Lua_Mob::*)(int,Lua_Mob))&Lua_Mob::IsImmuneToSpell)
.def("IsInvisible", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsInvisible)
@@ -3179,6 +3197,7 @@ luabind::scope lua_register_mob() {
.def("IsStunned", (bool(Lua_Mob::*)(void))&Lua_Mob::IsStunned)
.def("IsTargetable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsTargetable)
.def("IsTargeted", &Lua_Mob::IsTargeted)
.def("IsTrackable", (bool(Lua_Mob::*)(void))&Lua_Mob::IsTrackable)
.def("IsWarriorClass", &Lua_Mob::IsWarriorClass)
.def("Kill", (void(Lua_Mob::*)(void))&Lua_Mob::Kill)
.def("Mesmerize", (void(Lua_Mob::*)(void))&Lua_Mob::Mesmerize)
+3
View File
@@ -513,6 +513,9 @@ public:
void CopyHateList(Lua_Mob to);
bool IsAttackAllowed(Lua_Mob target);
bool IsAttackAllowed(Lua_Mob target, bool is_spell_attack);
bool IsFindable();
bool IsTrackable();
float GetDefaultRaceSize();
};
#endif
+11
View File
@@ -767,6 +767,16 @@ void Lua_NPC::ScaleNPC(uint8 npc_level, bool override_special_abilities)
self->ScaleNPC(npc_level, true, override_special_abilities);
}
bool Lua_NPC::IsUnderwaterOnly() {
Lua_Safe_Call_Bool();
return self->IsUnderwaterOnly();
}
bool Lua_NPC::HasSpecialAbilities() {
Lua_Safe_Call_Bool();
return self->HasSpecialAbilities();
}
luabind::scope lua_register_npc() {
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
.def(luabind::constructor<>())
@@ -860,6 +870,7 @@ luabind::scope lua_register_npc() {
.def("IsRaidTarget", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRaidTarget)
.def("IsRareSpawn", (bool(Lua_NPC::*)(void))&Lua_NPC::IsRareSpawn)
.def("IsTaunting", (bool(Lua_NPC::*)(void))&Lua_NPC::IsTaunting)
.def("IsUnderwaterOnly", (bool(Lua_NPC::*)(void))&Lua_NPC::IsUnderwaterOnly)
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop)
.def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop)
.def("ModifyNPCStat", (void(Lua_NPC::*)(std::string,std::string))&Lua_NPC::ModifyNPCStat)
+2
View File
@@ -174,6 +174,8 @@ public:
void SetLDoNTrapDetected(bool is_detected);
void ScaleNPC(uint8 npc_level);
void ScaleNPC(uint8 npc_level, bool override_special_abilities);
bool IsUnderwaterOnly();
bool HasSpecialAbilities();
};
#endif
+3 -1
View File
@@ -518,7 +518,9 @@ Mob::Mob(
}
Mob::~Mob()
{
{
quest_manager.stopalltimers(this);
mMovementManager->RemoveMob(this);
AI_Stop();
-1
View File
@@ -42,7 +42,6 @@ char* strn0cpy(char* dest, const char* source, uint32 size);
#define MAX_SPECIAL_ATTACK_PARAMS 8
class EGNode;
class Client;
class EQApplicationPacket;
class Group;
+24
View File
@@ -2786,6 +2786,26 @@ Bot* Perl_Mob_GetHateRandomBot(Mob* self) // @categories Hate and Aggro
return self->GetHateRandomBot();
}
bool Perl_Mob_IsFindable(Mob* self) // @categories Script Utility
{
return self->IsFindable();
}
bool Perl_Mob_IsTrackable(Mob* self) // @categories Script Utility
{
return self->IsTrackable();
}
bool Perl_Mob_IsBerserk(Mob* self) // @categories Script Utility
{
return self->IsBerserk();
}
float Perl_Mob_GetDefaultRaceSize(Mob* self) // @categories Script Utility
{
return self->GetDefaultRaceSize();
}
void perl_register_mob()
{
perl::interpreter perl(PERL_GET_THX);
@@ -2970,6 +2990,7 @@ void perl_register_mob()
package.add("GetClassName", &Perl_Mob_GetClassName);
package.add("GetCleanName", &Perl_Mob_GetCleanName);
package.add("GetCorruption", &Perl_Mob_GetCorruption);
package.add("GetDefaultRaceSize", &Perl_Mob_GetDefaultRaceSize);
package.add("GetDEX", &Perl_Mob_GetDEX);
package.add("GetDR", &Perl_Mob_GetDR);
package.add("GetDamageAmount", &Perl_Mob_GetDamageAmount);
@@ -3112,6 +3133,7 @@ void perl_register_mob()
package.add("IsAttackAllowed", (bool(*)(Mob*, Mob*, bool))&Perl_Mob_IsAttackAllowed);
package.add("IsBeacon", &Perl_Mob_IsBeacon);
package.add("IsBeneficialAllowed", &Perl_Mob_IsBeneficialAllowed);
package.add("IsBerserk", &Perl_Mob_IsBerserk);
package.add("IsBlind", &Perl_Mob_IsBlind);
package.add("IsBot", &Perl_Mob_IsBot);
package.add("IsCasting", &Perl_Mob_IsCasting);
@@ -3122,6 +3144,7 @@ void perl_register_mob()
package.add("IsEngaged", &Perl_Mob_IsEngaged);
package.add("IsEnraged", &Perl_Mob_IsEnraged);
package.add("IsFeared", &Perl_Mob_IsFeared);
package.add("IsFindable", &Perl_Mob_IsFindable);
package.add("IsHorse", &Perl_Mob_IsHorse);
package.add("IsImmuneToSpell", &Perl_Mob_IsImmuneToSpell);
package.add("IsInvisible", (bool(*)(Mob*))&Perl_Mob_IsInvisible);
@@ -3142,6 +3165,7 @@ void perl_register_mob()
package.add("IsStunned", &Perl_Mob_IsStunned);
package.add("IsTargetable", &Perl_Mob_IsTargetable);
package.add("IsTargeted", &Perl_Mob_IsTargeted);
package.add("IsTrackable", &Perl_Mob_IsTrackable);
package.add("IsTrap", &Perl_Mob_IsTrap);
package.add("IsWarriorClass", &Perl_Mob_IsWarriorClass);
package.add("Kill", &Perl_Mob_Kill);
+12
View File
@@ -765,6 +765,16 @@ void Perl_NPC_ScaleNPC(NPC* self, uint8 npc_level, bool override_special_abiliti
return self->ScaleNPC(npc_level, override_special_abilities);
}
bool Perl_NPC_IsUnderwaterOnly(NPC* self) // @categories Script Utility
{
return self->IsUnderwaterOnly();
}
bool Perl_NPC_HasSpecialAbilities(NPC* self) // @categories Script Utility
{
return self->HasSpecialAbilities();
}
void perl_register_npc()
{
perl::interpreter perl(PERL_GET_THX);
@@ -852,6 +862,7 @@ void perl_register_npc()
package.add("GetSwarmTarget", &Perl_NPC_GetSwarmTarget);
package.add("GetWaypointMax", &Perl_NPC_GetWaypointMax);
package.add("HasAISpellEffect", &Perl_NPC_HasAISpellEffect);
package.add("HasSpecialAbilities", &Perl_NPC_HasSpecialAbilities);
package.add("HasItem", &Perl_NPC_HasItem);
package.add("IsAnimal", &Perl_NPC_IsAnimal);
package.add("IsGuarding", &Perl_NPC_IsGuarding);
@@ -862,6 +873,7 @@ void perl_register_npc()
package.add("IsRaidTarget", &Perl_NPC_IsRaidTarget);
package.add("IsRareSpawn", &Perl_NPC_IsRareSpawn);
package.add("IsTaunting", &Perl_NPC_IsTaunting);
package.add("IsUnderwaterOnly", (bool(*)(NPC*))&Perl_NPC_IsUnderwaterOnly);
package.add("MerchantCloseShop", &Perl_NPC_MerchantCloseShop);
package.add("MerchantOpenShop", &Perl_NPC_MerchantOpenShop);
package.add("ModifyNPCStat", &Perl_NPC_ModifyNPCStat);
+3 -3
View File
@@ -88,7 +88,7 @@ void QuestManager::Process() {
end = QTimerList.end();
while (cur != end) {
if (cur->Timer_.Enabled() && cur->Timer_.Check()) {
if (cur->mob && entity_list.IsMobInZone(cur->mob)) {
if (cur->mob) {
if (cur->mob->IsNPC()) {
if (parse->HasQuestSub(cur->mob->GetNPCTypeID(), EVENT_TIMER)) {
parse->EventNPC(EVENT_TIMER, cur->mob->CastToNPC(), nullptr, cur->name, 0);
@@ -2130,7 +2130,7 @@ void QuestManager::playertexture(int newtexture)
initiator->SendIllusionPacket(initiator->GetRace(), 0xFF, newtexture);
}
void QuestManager::playerfeature(char *feature, int setting)
void QuestManager::playerfeature(const char* feature, int setting)
{
QuestManagerCurrentQuestVars();
uint16 Race = initiator->GetRace();
@@ -2187,7 +2187,7 @@ void QuestManager::playerfeature(char *feature, int setting)
DrakkinHeritage, DrakkinTattoo, DrakkinDetails, Size);
}
void QuestManager::npcfeature(char *feature, int setting)
void QuestManager::npcfeature(const char* feature, int setting)
{
QuestManagerCurrentQuestVars();
uint16 Race = owner->GetRace();
+3 -3
View File
@@ -140,7 +140,7 @@ public:
void movepc(int zone_id, float x, float y, float z, float heading);
void gmmove(float x, float y, float z);
void movegrp(int zoneid, float x, float y, float z);
void doanim(int animation_id, int animation_speed = 1, bool ackreq = true, eqFilterType filter = FilterNone);
void doanim(int animation_id, int animation_speed = 0, bool ackreq = true, eqFilterType filter = FilterNone);
void addskill(int skill_id, int value);
void setlanguage(int skill_id, int value);
void setskill(int skill_id, int value);
@@ -208,8 +208,8 @@ public:
void playergender(int gender_id);
void playersize(int newsize);
void playertexture(int newtexture);
void playerfeature(char *feature, int setting);
void npcfeature(char *feature, int setting);
void playerfeature(const char* feature, int setting);
void npcfeature(const char* feature, int setting);
void popup(const char *title, const char *text, uint32 popupid, uint32 buttons, uint32 Duration);
void taskselector(const std::vector<int>& tasks, bool ignore_cooldown = false);
void tasksetselector(int tasksettid, bool ignore_cooldown = false);
-101
View File
@@ -1,101 +0,0 @@
/* EQEMu: Everquest Server Emulator
Copyright (C) 2001-2002 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
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 SKILLS_H
#define SKILLS_H
#define HIGHEST_SKILL_UNUSED 74
// Correct Skill Numbers as of 4-14-2002
#define _1H_BLUNT_UNUSED 0
#define _1H_SLASHING_UNUSED 1
#define _2H_BLUNT_UNUSED 2
#define _2H_SLASHING_UNUSED 3
#define ABJURE_UNUSED 4
#define ALTERATION_UNUSED 5
#define APPLY_POISON_UNUSED 6
#define ARCHERY_UNUSED 7
#define BACKSTAB_UNUSED 8
#define BIND_WOUND_UNUSED 9
#define BASH_UNUSED 10
#define BLOCKSKILL_UNUSED 11
#define BRASS_INSTRUMENTS_UNUSED 12
#define CHANNELING_UNUSED 13
#define CONJURATION_UNUSED 14
#define DEFENSE_UNUSED 15
#define DISARM_UNUSED 16
#define DISARM_TRAPS_UNUSED 17
#define DIVINATION_UNUSED 18
#define DODGE_UNUSED 19
#define DOUBLE_ATTACK_UNUSED 20
#define DRAGON_PUNCH_UNUSED 21
#define DUAL_WIELD_UNUSED 22
#define EAGLE_STRIKE_UNUSED 23
#define EVOCATION_UNUSED 24
#define FEIGN_DEATH_UNUSED 25
#define FLYING_KICK_UNUSED 26
#define FORAGE_UNUSED 27
#define HAND_TO_HAND_UNUSED 28
#define HIDE_UNUSED 29
#define KICK_UNUSED 30
#define MEDITATE_UNUSED 31
#define MEND_UNUSED 32
#define OFFENSE_UNUSED 33
#define PARRY_UNUSED 34
#define PICK_LOCK_UNUSED 35
#define PIERCING_UNUSED 36
#define RIPOSTE_UNUSED 37
#define ROUND_KICK_UNUSED 38
#define SAFE_FALL_UNUSED 39
#define SENSE_HEADING_UNUSED 40
#define SINGING_UNUSED 41
#define SNEAK_UNUSED 42
#define SPECIALIZE_ABJURE_UNUSED 43
#define SPECIALIZE_ALTERATION_UNUSED 44
#define SPECIALIZE_CONJURATION_UNUSED 45
#define SPECIALIZE_DIVINATION_UNUSED 46
#define SPECIALIZE_EVOCATION_UNUSED 47
#define PICK_POCKETS_UNUSED 48
#define STRINGED_INSTRUMENTS_UNUSED 49
#define SWIMMING_UNUSED 50
#define THROWING_UNUSED 51
#define TIGER_CLAW_UNUSED 52
#define TRACKING_UNUSED 53
#define WIND_INSTRUMENTS_UNUSED 54
#define FISHING_UNUSED 55
#define MAKE_POISON_UNUSED 56
#define TINKERING_UNUSED 57
#define RESEARCH_UNUSED 58
#define ALCHEMY_UNUSED 59
#define BAKING_UNUSED 60
#define TAILORING_UNUSED 61
#define SENSE_TRAPS_UNUSED 62
#define BLACKSMITHING_UNUSED 63
#define FLETCHING_UNUSED 64
#define BREWING_UNUSED 65
#define ALCOHOL_TOLERANCE_UNUSED 66
#define BEGGING_UNUSED 67
#define JEWELRY_MAKING_UNUSED 68
#define POTTERY_UNUSED 69
#define PERCUSSION_INSTRUMENTS_UNUSED 70
#define INTIMIDATION_UNUSED 71
#define BERSERKING_UNUSED 72
#define TAUNT_UNUSED 73
#define FRENZY_UNUSED 74
#endif
-12
View File
@@ -93,18 +93,6 @@ EQApplicationPacket *TitleManager::MakeTitlesPacket(Client *client)
return(outapp);
}
int TitleManager::NumberOfAvailableTitles(Client *client)
{
int count = 0;
for (const auto& title : titles) {
if (IsClientEligibleForTitle(client, title)) {
++count;
}
}
return count;
}
std::string TitleManager::GetPrefix(int title_id)
{
if (!title_id) {
-1
View File
@@ -51,7 +51,6 @@ public:
EQApplicationPacket *MakeTitlesPacket(Client *client);
std::string GetPrefix(int title_id);
std::string GetSuffix(int title_id);
int NumberOfAvailableTitles(Client *client);
bool IsClientEligibleForTitle(Client *client, TitleEntry title);
bool IsNewAATitleAvailable(int aa_points, int class_id);
bool IsNewTradeSkillTitleAvailable(int skill_id, int skill_value);
+50 -19
View File
@@ -1865,32 +1865,63 @@ bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
bool Client::CheckTradeskillLoreConflict(int32 recipe_id)
{
const auto& recipe_entries = TradeskillRecipeEntriesRepository::GetWhere(
content_db,
fmt::format(
"recipe_id = {} ORDER BY id ASC",
recipe_id
)
auto recipe_entries = TradeskillRecipeEntriesRepository::GetWhere(
content_db,
fmt::format(
"recipe_id = {} ORDER BY componentcount DESC",
recipe_id
)
);
if (recipe_entries.empty()) {
return false;
}
for (auto& e : recipe_entries) {
auto item_inst = database.GetItem(e.item_id);
if (item_inst) {
if (item_inst->LoreGroup == 0 || e.componentcount > 0 || e.iscontainer) {
continue;
// validate which items from the recipe we will call CheckLoreConflict on
for (const auto &tre : recipe_entries) {
if (tre.item_id) {
auto tre_inst = database.GetItem(tre.item_id);
// To compare items we iterate against each item in the recipe that have a loregroup.
for (auto &tre_update_item : recipe_entries) {
bool fi_is_valid = tre_update_item.item_id && tre_inst && tre_inst->LoreGroup != 0;
if (fi_is_valid) {
auto tre_update_item_inst = database.GetItem(tre_update_item.item_id);
bool ei_is_valid = tre_update_item_inst && tre_update_item_inst->LoreGroup != 0;
if (ei_is_valid) {
bool unique_lore_group_match = tre_inst->LoreGroup > 0 && tre_inst->LoreGroup == tre_update_item_inst->LoreGroup;
bool component_count_is_valid = tre_update_item.componentcount == 0 && tre.componentcount > 0;
// If the recipe item is a component, and matches a unique lore group (> 0) or the item_id matches another entry in the recipe
// zero out the item_id, this will prevent us from doing a lore check inadvertently where
// the item is a component, and returned on success, fail, salvage.
// or uses an item that is part of a unique loregroup that returns an item of the same unique loregroup
if (ei_is_valid && (tre_update_item.item_id == tre.item_id || unique_lore_group_match) && component_count_is_valid) {
tre_update_item.item_id = 0;
}
}
}
}
if (CheckLoreConflict(item_inst)) {
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemData);
linker.SetItemData(item_inst);
auto item_link = linker.GenerateLink();
MessageString(Chat::Red, TRADESKILL_COMBINE_LORE, item_link.c_str());
return true;
if (tre_inst) {
if (tre_inst->LoreGroup == 0 || tre.componentcount > 0 || tre.iscontainer) {
continue;
}
if (CheckLoreConflict(tre_inst)) {
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemData);
linker.SetItemData(tre_inst);
auto item_link = linker.GenerateLink();
MessageString(Chat::Red, TRADESKILL_COMBINE_LORE, item_link.c_str());
return true;
}
}
}
}
return false;
}
}
+17 -13
View File
@@ -13,6 +13,7 @@
#include "zonedb.h"
#include "aura.h"
#include "../common/repositories/criteria/content_filter_criteria.h"
#include "../common/repositories/character_disciplines_repository.h"
#include "../common/repositories/npc_types_repository.h"
#include <ctime>
@@ -814,22 +815,25 @@ bool ZoneDatabase::LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_
}
bool ZoneDatabase::LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp){
std::string query = StringFormat(
"SELECT "
"disc_id "
"FROM "
"`character_disciplines`"
"WHERE `id` = %u ORDER BY `slot_id`", character_id);
auto results = database.QueryDatabase(query);
int i = 0;
auto character_disciplines = CharacterDisciplinesRepository::GetWhere(
database, fmt::format(
"`id` = {} ORDER BY `slot_id`",
character_id
)
);
if (character_disciplines.empty()) {
return false;
}
/* Initialize Disciplines */
memset(pp->disciplines.values, 0, (sizeof(pp->disciplines.values[0]) * MAX_PP_DISCIPLINES));
for (auto& row = results.begin(); row != results.end(); ++row) {
if (i < MAX_PP_DISCIPLINES)
pp->disciplines.values[i] = atoi(row[0]);
++i;
}
for (auto& row : character_disciplines) {
if (row.slot_id < MAX_PP_DISCIPLINES && IsValidSpell(row.disc_id)) {
pp->disciplines.values[row.slot_id] = row.disc_id;
}
}
return true;
}