mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-24 09:01:29 +00:00
Merge branch 'master' of https://github.com/EQEmu/Server
This commit is contained in:
commit
ae3052fbd1
@ -1,6 +1,35 @@
|
||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
|
||||
== 2/7/2019 ==
|
||||
Uleat: Put merc and bot classes on the same stance standard (mercs)
|
||||
- Both classes will now use the same stance standard
|
||||
- Pushed stance types up to EQEmu::constants
|
||||
|
||||
== 2/4/2019 ==
|
||||
Uleat: Added command 'profanity' (aliased 'prof')
|
||||
- This is a server-based tool for redacting any language that an admin deems as profanity (socially unacceptable within their community)
|
||||
- Five options are available under this command..
|
||||
-- 'list' - shows the current list of banned words
|
||||
-- 'clear' - clears the current list of banned words
|
||||
-- 'add <word>' - adds <word> to the banned word list
|
||||
-- 'del <word>' - deletes <word> from the banned word list
|
||||
-- 'reload' - forces a reload of the banned word list
|
||||
- All actions are immediate and a world broadcast refreshes other active zones
|
||||
- The system is in stand-by when the list is empty..just add a word to the list to begin censorship
|
||||
- Redaction only occurs on genuine occurences of any banned word
|
||||
-- Banned words are replaced with a series of '*' characters
|
||||
-- Compounded words are ignored to avoid issues with allowed words containing a banned sub-string
|
||||
-- If 'test' is banned, 'testing' will not be banned .. it must be added separately
|
||||
- Extreme care should be exercised when adding words to the banned list..
|
||||
-- Quest failures and limited social interactions may alienate players if they become inhibiting
|
||||
-- System commands are allowed to be processed before redaction occurs in the 'say' channel
|
||||
- A longer list requires more clock cycles to process - so, try to keep them to the most offensible occurrences
|
||||
Uleat: Fix for bots ceasing combat when their 'follow me' mob dies
|
||||
- Bots will revert to their client leash owner (bot owner or client group leader) when their FollowID() mob is no longer valid
|
||||
- Combat will no longer be interrupted in these cases
|
||||
- Does not apply to bot owner death...
|
||||
|
||||
== 1/26/2019 ==
|
||||
Uleat: Fix for class Bot not honoring NPCType data reference
|
||||
- Fixes bots not moving on spawn/grouping issue
|
||||
|
||||
@ -55,6 +55,7 @@ SET(common_sources
|
||||
perl_eqdb.cpp
|
||||
perl_eqdb_res.cpp
|
||||
proc_launcher.cpp
|
||||
profanity_manager.cpp
|
||||
ptimer.cpp
|
||||
races.cpp
|
||||
rdtsc.cpp
|
||||
@ -181,6 +182,7 @@ SET(common_headers
|
||||
packet_functions.h
|
||||
platform.h
|
||||
proc_launcher.h
|
||||
profanity_manager.h
|
||||
profiler.h
|
||||
ptimer.h
|
||||
queue.h
|
||||
|
||||
@ -118,3 +118,37 @@ EQEmu::bug::CategoryID EQEmu::bug::CategoryNameToCategoryID(const char* category
|
||||
|
||||
return catOther;
|
||||
}
|
||||
|
||||
const char *EQEmu::constants::GetStanceName(StanceType stance_type) {
|
||||
switch (stance_type) {
|
||||
case stanceUnknown:
|
||||
return "Unknown";
|
||||
case stancePassive:
|
||||
return "Passive";
|
||||
case stanceBalanced:
|
||||
return "Balanced";
|
||||
case stanceEfficient:
|
||||
return "Efficient";
|
||||
case stanceReactive:
|
||||
return "Reactive";
|
||||
case stanceAggressive:
|
||||
return "Aggressive";
|
||||
case stanceAssist:
|
||||
return "Assist";
|
||||
case stanceBurn:
|
||||
return "Burn";
|
||||
case stanceEfficient2:
|
||||
return "Efficient2";
|
||||
case stanceBurnAE:
|
||||
return "BurnAE";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
int EQEmu::constants::ConvertStanceTypeToIndex(StanceType stance_type) {
|
||||
if (stance_type >= EQEmu::constants::stancePassive && stance_type <= EQEmu::constants::stanceBurnAE)
|
||||
return (stance_type - EQEmu::constants::stancePassive);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -203,6 +203,26 @@ namespace EQEmu
|
||||
const size_t SAY_LINK_CLOSER_SIZE = 1;
|
||||
const size_t SAY_LINK_MAXIMUM_SIZE = (SAY_LINK_OPENER_SIZE + SAY_LINK_BODY_SIZE + SAY_LINK_TEXT_SIZE + SAY_LINK_CLOSER_SIZE);
|
||||
|
||||
enum StanceType : int {
|
||||
stanceUnknown = 0,
|
||||
stancePassive,
|
||||
stanceBalanced,
|
||||
stanceEfficient,
|
||||
stanceReactive,
|
||||
stanceAggressive,
|
||||
stanceAssist,
|
||||
stanceBurn,
|
||||
stanceEfficient2,
|
||||
stanceBurnAE
|
||||
};
|
||||
|
||||
const char *GetStanceName(StanceType stance_type);
|
||||
int ConvertStanceTypeToIndex(StanceType stance_type);
|
||||
|
||||
const int STANCE_TYPE_FIRST = stancePassive;
|
||||
const int STANCE_TYPE_LAST = stanceBurnAE;
|
||||
const int STANCE_TYPE_COUNT = stanceBurnAE;
|
||||
|
||||
} /*constants*/
|
||||
|
||||
namespace profile {
|
||||
|
||||
@ -18,6 +18,7 @@
|
||||
*/
|
||||
|
||||
#include "emu_versions.h"
|
||||
#include "emu_constants.h"
|
||||
|
||||
|
||||
bool EQEmu::versions::IsValidClientVersion(ClientVersion client_version)
|
||||
@ -493,7 +494,7 @@ EQEmu::expansions::Expansion EQEmu::expansions::ConvertExpansionBitToExpansion(u
|
||||
}
|
||||
}
|
||||
|
||||
uint32 EQEmu::expansions::ConvertExpansionToExpansionMask(Expansion expansion)
|
||||
uint32 EQEmu::expansions::ConvertExpansionToExpansionsMask(Expansion expansion)
|
||||
{
|
||||
switch (expansion) {
|
||||
case Expansion::RoK:
|
||||
@ -543,57 +544,15 @@ uint32 EQEmu::expansions::ConvertExpansionToExpansionMask(Expansion expansion)
|
||||
|
||||
EQEmu::expansions::Expansion EQEmu::expansions::ConvertClientVersionToExpansion(versions::ClientVersion client_version)
|
||||
{
|
||||
switch (client_version) {
|
||||
case versions::ClientVersion::Titanium:
|
||||
return expansions::Expansion::PoR;
|
||||
case versions::ClientVersion::SoF:
|
||||
return expansions::Expansion::SoF;
|
||||
case versions::ClientVersion::SoD:
|
||||
return expansions::Expansion::SoD;
|
||||
case versions::ClientVersion::UF:
|
||||
return expansions::Expansion::UF;
|
||||
case versions::ClientVersion::RoF:
|
||||
case versions::ClientVersion::RoF2:
|
||||
return expansions::Expansion::RoF;
|
||||
default:
|
||||
return expansions::Expansion::EverQuest;
|
||||
}
|
||||
return EQEmu::constants::StaticLookup(client_version)->Expansion;
|
||||
}
|
||||
|
||||
uint32 EQEmu::expansions::ConvertClientVersionToExpansionBit(versions::ClientVersion client_version)
|
||||
{
|
||||
switch (client_version) {
|
||||
case versions::ClientVersion::Titanium:
|
||||
return expansions::bitPoR;
|
||||
case versions::ClientVersion::SoF:
|
||||
return expansions::bitSoF;
|
||||
case versions::ClientVersion::SoD:
|
||||
return expansions::bitSoD;
|
||||
case versions::ClientVersion::UF:
|
||||
return expansions::bitUF;
|
||||
case versions::ClientVersion::RoF:
|
||||
case versions::ClientVersion::RoF2:
|
||||
return expansions::bitRoF;
|
||||
default:
|
||||
return expansions::bitEverQuest;
|
||||
}
|
||||
return EQEmu::constants::StaticLookup(client_version)->ExpansionBit;
|
||||
}
|
||||
|
||||
uint32 EQEmu::expansions::ConvertClientVersionToExpansionMask(versions::ClientVersion client_version)
|
||||
uint32 EQEmu::expansions::ConvertClientVersionToExpansionsMask(versions::ClientVersion client_version)
|
||||
{
|
||||
switch (client_version) {
|
||||
case versions::ClientVersion::Titanium:
|
||||
return expansions::maskPoR;
|
||||
case versions::ClientVersion::SoF:
|
||||
return expansions::maskSoF;
|
||||
case versions::ClientVersion::SoD:
|
||||
return expansions::maskSoD;
|
||||
case versions::ClientVersion::UF:
|
||||
return expansions::maskUF;
|
||||
case versions::ClientVersion::RoF:
|
||||
case versions::ClientVersion::RoF2:
|
||||
return expansions::maskRoF;
|
||||
default:
|
||||
return expansions::maskEverQuest;
|
||||
}
|
||||
return EQEmu::constants::StaticLookup(client_version)->ExpansionsMask;
|
||||
}
|
||||
|
||||
@ -210,10 +210,10 @@ namespace EQEmu
|
||||
const char* ExpansionName(uint32 expansion_bit);
|
||||
uint32 ConvertExpansionToExpansionBit(Expansion expansion);
|
||||
Expansion ConvertExpansionBitToExpansion(uint32 expansion_bit);
|
||||
uint32 ConvertExpansionToExpansionMask(Expansion expansion);
|
||||
uint32 ConvertExpansionToExpansionsMask(Expansion expansion);
|
||||
Expansion ConvertClientVersionToExpansion(versions::ClientVersion client_version);
|
||||
uint32 ConvertClientVersionToExpansionBit(versions::ClientVersion client_version);
|
||||
uint32 ConvertClientVersionToExpansionMask(versions::ClientVersion client_version);
|
||||
uint32 ConvertClientVersionToExpansionsMask(versions::ClientVersion client_version);
|
||||
|
||||
} /*expansions*/
|
||||
|
||||
|
||||
@ -43,17 +43,17 @@ static const EQEmu::constants::LookupEntry constants_static_lookup_entries[EQEmu
|
||||
{
|
||||
/*[ClientVersion::Unknown] =*/
|
||||
EQEmu::constants::LookupEntry(
|
||||
EQEmu::expansions::Expansion::EverQuest,
|
||||
ClientUnknown::INULL,
|
||||
ClientUnknown::INULL,
|
||||
ClientUnknown::constants::EXPANSION,
|
||||
ClientUnknown::constants::EXPANSION_BIT,
|
||||
ClientUnknown::constants::EXPANSIONS_MASK,
|
||||
ClientUnknown::INULL,
|
||||
ClientUnknown::INULL
|
||||
),
|
||||
/*[ClientVersion::Client62] =*/
|
||||
EQEmu::constants::LookupEntry(
|
||||
EQEmu::expansions::Expansion::EverQuest,
|
||||
Client62::INULL,
|
||||
Client62::INULL,
|
||||
Client62::constants::EXPANSION,
|
||||
Client62::constants::EXPANSION_BIT,
|
||||
Client62::constants::EXPANSIONS_MASK,
|
||||
Client62::INULL,
|
||||
Client62::INULL
|
||||
),
|
||||
@ -1167,7 +1167,7 @@ static const EQEmu::spells::LookupEntry spells_static_lookup_entries[EQEmu::vers
|
||||
/*[ClientVersion::UF] =*/
|
||||
EQEmu::spells::LookupEntry(
|
||||
UF::spells::SPELL_ID_MAX,
|
||||
SoD::spells::SPELLBOOK_SIZE,
|
||||
UF::spells::SPELLBOOK_SIZE,
|
||||
UF::spells::SPELL_GEM_COUNT,
|
||||
UF::spells::LONG_BUFFS,
|
||||
UF::spells::SHORT_BUFFS,
|
||||
@ -1180,7 +1180,7 @@ static const EQEmu::spells::LookupEntry spells_static_lookup_entries[EQEmu::vers
|
||||
/*[ClientVersion::RoF] =*/
|
||||
EQEmu::spells::LookupEntry(
|
||||
RoF::spells::SPELL_ID_MAX,
|
||||
SoD::spells::SPELLBOOK_SIZE,
|
||||
RoF::spells::SPELLBOOK_SIZE,
|
||||
UF::spells::SPELL_GEM_COUNT, // client translators are setup to allow the max value a client supports..however, the top 4 indices are not valid in this case
|
||||
RoF::spells::LONG_BUFFS,
|
||||
RoF::spells::SHORT_BUFFS,
|
||||
@ -1193,7 +1193,7 @@ static const EQEmu::spells::LookupEntry spells_static_lookup_entries[EQEmu::vers
|
||||
/*[ClientVersion::RoF2] =*/
|
||||
EQEmu::spells::LookupEntry(
|
||||
RoF2::spells::SPELL_ID_MAX,
|
||||
SoD::spells::SPELLBOOK_SIZE,
|
||||
RoF2::spells::SPELLBOOK_SIZE,
|
||||
UF::spells::SPELL_GEM_COUNT, // client translators are setup to allow the max value a client supports..however, the top 4 indices are not valid in this case
|
||||
RoF2::spells::LONG_BUFFS,
|
||||
RoF2::spells::SHORT_BUFFS,
|
||||
|
||||
@ -243,6 +243,13 @@ namespace ClientUnknown
|
||||
const int16 IINVALID = -1;
|
||||
const int16 INULL = 0;
|
||||
|
||||
namespace constants {
|
||||
const EQEmu::expansions::Expansion EXPANSION = EQEmu::expansions::Expansion::EverQuest;
|
||||
const uint32 EXPANSION_BIT = EQEmu::expansions::bitEverQuest;
|
||||
const uint32 EXPANSIONS_MASK = EQEmu::expansions::maskEverQuest;
|
||||
|
||||
} // namespace constants
|
||||
|
||||
} /*ClientUnknown*/
|
||||
|
||||
namespace Client62
|
||||
@ -250,6 +257,13 @@ namespace Client62
|
||||
const int16 IINVALID = -1;
|
||||
const int16 INULL = 0;
|
||||
|
||||
namespace constants {
|
||||
const EQEmu::expansions::Expansion EXPANSION = EQEmu::expansions::Expansion::EverQuest;
|
||||
const uint32 EXPANSION_BIT = EQEmu::expansions::bitEverQuest;
|
||||
const uint32 EXPANSIONS_MASK = EQEmu::expansions::maskEverQuest;
|
||||
|
||||
} // namespace constants
|
||||
|
||||
} /*Client62*/
|
||||
|
||||
#endif /*COMMON_EQ_LIMITS_H*/
|
||||
|
||||
250
common/profanity_manager.cpp
Normal file
250
common/profanity_manager.cpp
Normal file
@ -0,0 +1,250 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
|
||||
Copyright (C) 2001-2019 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
|
||||
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 "profanity_manager.h"
|
||||
#include "dbcore.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
|
||||
|
||||
static std::list<std::string> profanity_list;
|
||||
static bool update_originator_flag = false;
|
||||
|
||||
bool EQEmu::ProfanityManager::LoadProfanityList(DBcore *db) {
|
||||
if (update_originator_flag == true) {
|
||||
update_originator_flag = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!load_database_entries(db))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::UpdateProfanityList(DBcore *db) {
|
||||
if (!load_database_entries(db))
|
||||
return false;
|
||||
|
||||
update_originator_flag = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::DeleteProfanityList(DBcore *db) {
|
||||
if (!clear_database_entries(db))
|
||||
return false;
|
||||
|
||||
update_originator_flag = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::AddProfanity(DBcore *db, const char *profanity) {
|
||||
if (!db || !profanity)
|
||||
return false;
|
||||
|
||||
std::string entry(profanity);
|
||||
|
||||
std::transform(entry.begin(), entry.end(), entry.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
||||
|
||||
if (check_for_existing_entry(entry.c_str()))
|
||||
return true;
|
||||
|
||||
if (entry.length() < REDACTION_LENGTH_MIN)
|
||||
return false;
|
||||
|
||||
profanity_list.push_back(entry);
|
||||
|
||||
std::string query = "REPLACE INTO `profanity_list` (`word`) VALUES ('";
|
||||
query.append(entry);
|
||||
query.append("')");
|
||||
auto results = db->QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
update_originator_flag = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::RemoveProfanity(DBcore *db, const char *profanity) {
|
||||
if (!db || !profanity)
|
||||
return false;
|
||||
|
||||
std::string entry(profanity);
|
||||
|
||||
std::transform(entry.begin(), entry.end(), entry.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
||||
|
||||
if (!check_for_existing_entry(entry.c_str()))
|
||||
return true;
|
||||
|
||||
profanity_list.remove(entry);
|
||||
|
||||
std::string query = "DELETE FROM `profanity_list` WHERE `word` LIKE '";
|
||||
query.append(entry);
|
||||
query.append("'");
|
||||
auto results = db->QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
update_originator_flag = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void EQEmu::ProfanityManager::RedactMessage(char *message) {
|
||||
if (!message)
|
||||
return;
|
||||
|
||||
std::string test_message(message);
|
||||
// hard-coded max length based on channel message buffer size (4096 bytes)..
|
||||
// ..will need to change or remove if other sources are used for redaction
|
||||
if (test_message.length() < REDACTION_LENGTH_MIN || test_message.length() >= 4096)
|
||||
return;
|
||||
|
||||
std::transform(test_message.begin(), test_message.end(), test_message.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
||||
|
||||
for (const auto &iter : profanity_list) { // consider adding textlink checks if it becomes an issue
|
||||
size_t pos = 0;
|
||||
size_t start_pos = 0;
|
||||
|
||||
while (pos != std::string::npos) {
|
||||
pos = test_message.find(iter, start_pos);
|
||||
if (pos == std::string::npos)
|
||||
continue;
|
||||
|
||||
if ((pos + iter.length()) == test_message.length() || !isalpha(test_message.at(pos + iter.length()))) {
|
||||
if (pos == 0 || !isalpha(test_message.at(pos - 1)))
|
||||
memset((message + pos), REDACTION_CHARACTER, iter.length());
|
||||
}
|
||||
|
||||
start_pos = (pos + iter.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void EQEmu::ProfanityManager::RedactMessage(std::string &message) {
|
||||
if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096)
|
||||
return;
|
||||
|
||||
std::string test_message(const_cast<const std::string&>(message));
|
||||
|
||||
std::transform(test_message.begin(), test_message.end(), test_message.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
||||
|
||||
for (const auto &iter : profanity_list) { // consider adding textlink checks if it becomes an issue
|
||||
size_t pos = 0;
|
||||
size_t start_pos = 0;
|
||||
|
||||
while (pos != std::string::npos) {
|
||||
pos = test_message.find(iter, start_pos);
|
||||
if (pos == std::string::npos)
|
||||
continue;
|
||||
|
||||
if ((pos + iter.length()) == test_message.length() || !isalpha(test_message.at(pos + iter.length()))) {
|
||||
if (pos == 0 || !isalpha(test_message.at(pos - 1)))
|
||||
message.replace(pos, iter.length(), iter.length(), REDACTION_CHARACTER);
|
||||
}
|
||||
|
||||
start_pos = (pos + iter.length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::ContainsCensoredLanguage(const char *message) {
|
||||
if (!message)
|
||||
return false;
|
||||
|
||||
return ContainsCensoredLanguage(std::string(message));
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::ContainsCensoredLanguage(const std::string &message) {
|
||||
if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096)
|
||||
return false;
|
||||
|
||||
std::string test_message(message);
|
||||
|
||||
std::transform(test_message.begin(), test_message.end(), test_message.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
||||
|
||||
for (const auto &iter : profanity_list) {
|
||||
if (test_message.find(iter) != std::string::npos)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::list<std::string> &EQEmu::ProfanityManager::GetProfanityList() {
|
||||
return profanity_list;
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::IsCensorshipActive() {
|
||||
return (profanity_list.size() != 0);
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::load_database_entries(DBcore *db) {
|
||||
if (!db)
|
||||
return false;
|
||||
|
||||
profanity_list.clear();
|
||||
|
||||
std::string query = "SELECT `word` FROM `profanity_list`";
|
||||
auto results = db->QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
if (std::strlen(row[0]) >= REDACTION_LENGTH_MIN) {
|
||||
std::string entry(row[0]);
|
||||
std::transform(entry.begin(), entry.end(), entry.begin(), [](unsigned char c) -> unsigned char { return tolower(c); });
|
||||
if (!check_for_existing_entry(entry.c_str()))
|
||||
profanity_list.push_back((std::string)entry);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::clear_database_entries(DBcore *db) {
|
||||
if (!db)
|
||||
return false;
|
||||
|
||||
profanity_list.clear();
|
||||
|
||||
std::string query = "DELETE FROM `profanity_list`";
|
||||
auto results = db->QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EQEmu::ProfanityManager::check_for_existing_entry(const char *profanity) {
|
||||
if (!profanity)
|
||||
return false;
|
||||
|
||||
for (const auto &iter : profanity_list) {
|
||||
if (iter.compare(profanity) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
62
common/profanity_manager.h
Normal file
62
common/profanity_manager.h
Normal file
@ -0,0 +1,62 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
|
||||
Copyright (C) 2001-2019 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
|
||||
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 COMMON_PROFANITY_MANAGER_H
|
||||
#define COMMON_PROFANITY_MANAGER_H
|
||||
|
||||
#include <string>
|
||||
#include <list>
|
||||
|
||||
|
||||
class DBcore;
|
||||
|
||||
namespace EQEmu
|
||||
{
|
||||
class ProfanityManager {
|
||||
public:
|
||||
static bool LoadProfanityList(DBcore *db);
|
||||
static bool UpdateProfanityList(DBcore *db);
|
||||
static bool DeleteProfanityList(DBcore *db);
|
||||
|
||||
static bool AddProfanity(DBcore *db, const char *profanity);
|
||||
static bool RemoveProfanity(DBcore *db, const char *profanity);
|
||||
|
||||
static void RedactMessage(char *message);
|
||||
static void RedactMessage(std::string &message);
|
||||
|
||||
static bool ContainsCensoredLanguage(const char *message);
|
||||
static bool ContainsCensoredLanguage(const std::string &message);
|
||||
|
||||
static const std::list<std::string> &GetProfanityList();
|
||||
|
||||
static bool IsCensorshipActive();
|
||||
|
||||
static const char REDACTION_CHARACTER = '*';
|
||||
static const int REDACTION_LENGTH_MIN = 3;
|
||||
|
||||
private:
|
||||
static bool load_database_entries(DBcore *db);
|
||||
static bool clear_database_entries(DBcore *db);
|
||||
static bool check_for_existing_entry(const char *profanity);
|
||||
|
||||
};
|
||||
|
||||
} /*EQEmu*/
|
||||
|
||||
#endif /*COMMON_PROFANITY_MANAGER_H*/
|
||||
@ -159,6 +159,7 @@
|
||||
#define ServerOP_SetWorldTime 0x200B
|
||||
#define ServerOP_GetWorldTime 0x200C
|
||||
#define ServerOP_SyncWorldTime 0x200E
|
||||
#define ServerOP_RefreshCensorship 0x200F
|
||||
|
||||
#define ServerOP_LSZoneInfo 0x3001
|
||||
#define ServerOP_LSZoneStart 0x3002
|
||||
|
||||
@ -68,16 +68,16 @@ enum SpellTypes : uint32
|
||||
SpellType_InCombatBuffSong = (1 << 18), // bard in-combat group/ae buffs
|
||||
SpellType_OutOfCombatBuffSong = (1 << 19), // bard out-of-combat group/ae buffs
|
||||
SpellType_PreCombatBuff = (1 << 20),
|
||||
SpellType_PreCombatBuffSong = (1 << 21),
|
||||
|
||||
SpellTypes_Detrimental = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow),
|
||||
SpellTypes_Beneficial = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong),
|
||||
|
||||
SpellTypes_Innate = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root),
|
||||
|
||||
SpellType_Any = 0xFFFFFFFF
|
||||
SpellType_PreCombatBuffSong = (1 << 21)
|
||||
};
|
||||
|
||||
const uint32 SPELL_TYPE_MIN = (SpellType_Nuke << 1) - 1;
|
||||
const uint32 SPELL_TYPE_MAX = (SpellType_PreCombatBuffSong << 1) - 1;
|
||||
const uint32 SPELL_TYPE_ANY = 0xFFFFFFFF;
|
||||
|
||||
const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow);
|
||||
const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong);
|
||||
const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root);
|
||||
|
||||
// These should not be used to determine spell category..
|
||||
// They are a graphical affects (effects?) index only
|
||||
|
||||
@ -30,9 +30,9 @@
|
||||
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9135
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9136
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9021
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9022
|
||||
#else
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
||||
#endif
|
||||
|
||||
@ -389,6 +389,7 @@
|
||||
9133|2018_11_25_StuckBehavior.sql|SHOW COLUMNS FROM `npc_types` LIKE 'stuck_behavior'|empty|
|
||||
9134|2019_01_04_update_global_base_scaling.sql|SELECT * FROM db_version WHERE version >= 9134|empty|
|
||||
9135|2019_01_10_multi_version_spawns.sql|SHOW COLUMNS FROM `spawn2` LIKE 'version'|contains|unsigned|
|
||||
9136|2019_02_04_profanity_command.sql|SHOW TABLES LIKE 'profanity_list'|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
9019|2018_04_12_bots_stop_melee_level.sql|SHOW COLUMNS FROM `bot_data` LIKE 'stop_melee_level'|empty|
|
||||
9020|2018_08_13_bots_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `bot_step` = 0|not_empty|
|
||||
9021|2018_10_09_bots_owner_options.sql|SHOW TABLES LIKE 'bot_owner_options'|empty|
|
||||
9022|2019_02_07_bots_stance_type_update.sql|SELECT * FROM `bot_spell_casting_chances` WHERE `spell_type_index` = '255' AND `class_id` = '255' AND `stance_index` = '0'|not_empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
-- Update `bot_stances`.`stance_id` to new values
|
||||
UPDATE `bot_stances` SET `stance_id` = '9' WHERE `stance_id` = '6';
|
||||
UPDATE `bot_stances` SET `stance_id` = '7' WHERE `stance_id` = '5';
|
||||
UPDATE `bot_stances` SET `stance_id` = (`stance_id` + 1) WHERE `stance_id` in (0,1,2,3,4);
|
||||
|
||||
-- Update `bot_spell_casting_chances`.`stance_index` to new values
|
||||
UPDATE `bot_spell_casting_chances` SET `stance_index` = '8' WHERE `stance_index` = '6';
|
||||
UPDATE `bot_spell_casting_chances` SET `stance_index` = '6' WHERE `stance_index` = '5';
|
||||
|
||||
-- Update `bot_spell_casting_chances` implicit versioning
|
||||
UPDATE `bot_spell_casting_chances` SET `stance_index` = '1' WHERE `spell_type_index` = '255' AND `class_id` = '255';
|
||||
10
utils/sql/git/required/2019_02_04_profanity_command.sql
Normal file
10
utils/sql/git/required/2019_02_04_profanity_command.sql
Normal file
@ -0,0 +1,10 @@
|
||||
DROP TABLE IF EXISTS `profanity_list`;
|
||||
|
||||
CREATE TABLE `profanity_list` (
|
||||
`word` VARCHAR(16) NOT NULL
|
||||
)
|
||||
COLLATE='latin1_swedish_ci'
|
||||
ENGINE=InnoDB
|
||||
;
|
||||
|
||||
REPLACE INTO `command_settings` VALUES ('profanity', 150, 'prof');
|
||||
@ -172,12 +172,11 @@ void Client::SendExpansionInfo() {
|
||||
auto outapp = new EQApplicationPacket(OP_ExpansionInfo, sizeof(ExpansionInfo_Struct));
|
||||
ExpansionInfo_Struct *eis = (ExpansionInfo_Struct*)outapp->pBuffer;
|
||||
|
||||
// need to rework .. not until full scope of change is accounted for, though
|
||||
if (RuleB(World, UseClientBasedExpansionSettings)) {
|
||||
eis->Expansions = EQEmu::expansions::ConvertClientVersionToExpansionMask(eqs->ClientVersion());
|
||||
eis->Expansions = EQEmu::expansions::ConvertClientVersionToExpansionsMask(eqs->ClientVersion());
|
||||
}
|
||||
else {
|
||||
eis->Expansions = RuleI(World, ExpansionSettings);
|
||||
eis->Expansions = (RuleI(World, ExpansionSettings) & EQEmu::expansions::ConvertClientVersionToExpansionsMask(eqs->ClientVersion()));
|
||||
}
|
||||
|
||||
QueuePacket(outapp);
|
||||
|
||||
@ -981,6 +981,10 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
safe_delete(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_RefreshCensorship: {
|
||||
zoneserver_list.SendPacket(pack);
|
||||
break;
|
||||
}
|
||||
case ServerOP_SetWorldTime: {
|
||||
Log(Logs::Detail, Logs::World_Server, "Received SetWorldTime");
|
||||
eqTimeOfDay* newtime = (eqTimeOfDay*)pack->pBuffer;
|
||||
|
||||
@ -4514,7 +4514,9 @@ void Mob::ApplyMeleeDamageMods(uint16 skill, int &damage, Mob *defender, ExtraAt
|
||||
if (defender->IsClient() && defender->GetClass() == WARRIOR)
|
||||
dmgbonusmod -= 5;
|
||||
// 168 defensive
|
||||
dmgbonusmod += (defender->spellbonuses.MeleeMitigationEffect + itembonuses.MeleeMitigationEffect + aabonuses.MeleeMitigationEffect);
|
||||
dmgbonusmod += (defender->spellbonuses.MeleeMitigationEffect +
|
||||
defender->itembonuses.MeleeMitigationEffect +
|
||||
defender->aabonuses.MeleeMitigationEffect);
|
||||
}
|
||||
|
||||
damage += damage * dmgbonusmod / 100;
|
||||
|
||||
88
zone/bot.cpp
88
zone/bot.cpp
@ -163,7 +163,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
if (!stance_flag && bot_owner)
|
||||
bot_owner->Message(13, "Could not locate stance for '%s'", GetCleanName());
|
||||
|
||||
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == BotStanceAggressive));
|
||||
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == EQEmu::constants::stanceAggressive));
|
||||
SetPauseAI(false);
|
||||
|
||||
rest_timer.Disable();
|
||||
@ -2013,12 +2013,20 @@ bool Bot::Process() {
|
||||
if(GetAppearance() == eaDead && GetHP() > 0)
|
||||
SetAppearance(eaStanding);
|
||||
|
||||
if (IsMoving()) {
|
||||
ping_timer.Disable();
|
||||
}
|
||||
else {
|
||||
if (!ping_timer.Enabled())
|
||||
ping_timer.Start(BOT_KEEP_ALIVE_INTERVAL);
|
||||
|
||||
if (ping_timer.Check())
|
||||
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
|
||||
}
|
||||
|
||||
if (IsStunned() || IsMezzed())
|
||||
return true;
|
||||
|
||||
if (!IsMoving() && ping_timer.Check())
|
||||
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
|
||||
|
||||
// Bot AI
|
||||
AI_Process();
|
||||
return true;
|
||||
@ -2240,10 +2248,9 @@ void Bot::AI_Process() {
|
||||
|
||||
Client* bot_owner = (GetBotOwner() && GetBotOwner()->IsClient() ? GetBotOwner()->CastToClient() : nullptr);
|
||||
Group* bot_group = GetGroup();
|
||||
Mob* follow_mob = entity_list.GetMob(GetFollowID());
|
||||
|
||||
|
||||
// Primary reasons for not processing AI
|
||||
if (!bot_owner || !bot_group || !follow_mob || !IsAIControlled())
|
||||
if (!bot_owner || !bot_group || !IsAIControlled())
|
||||
return;
|
||||
|
||||
if (bot_owner->IsDead()) {
|
||||
@ -2253,11 +2260,18 @@ void Bot::AI_Process() {
|
||||
return;
|
||||
}
|
||||
|
||||
// We also need a leash owner (subset of primary AI criteria)
|
||||
// We also need a leash owner and follow mob (subset of primary AI criteria)
|
||||
Client* leash_owner = (bot_group->GetLeader() && bot_group->GetLeader()->IsClient() ? bot_group->GetLeader()->CastToClient() : bot_owner);
|
||||
if (!leash_owner)
|
||||
return;
|
||||
|
||||
Mob* follow_mob = entity_list.GetMob(GetFollowID());
|
||||
|
||||
if (!follow_mob) {
|
||||
follow_mob = leash_owner;
|
||||
SetFollowID(leash_owner->GetID());
|
||||
}
|
||||
|
||||
// Berserk updates should occur if primary AI criteria are met
|
||||
if (GetClass() == WARRIOR || GetClass() == BERSERKER) {
|
||||
if (!berserk && GetHP() > 0 && GetHPRatio() < 30.0f) {
|
||||
@ -2752,7 +2766,7 @@ void Bot::AI_Process() {
|
||||
// we can't fight if we don't have a target, are stun/mezzed or dead..
|
||||
// Stop attacking if the target is enraged
|
||||
TEST_TARGET();
|
||||
if (GetBotStance() == BotStancePassive || (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())))
|
||||
if (GetBotStance() == EQEmu::constants::stancePassive || (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())))
|
||||
return;
|
||||
|
||||
// First, special attack per class (kick, backstab etc..)
|
||||
@ -2879,7 +2893,7 @@ void Bot::AI_Process() {
|
||||
FaceTarget(GetTarget());
|
||||
|
||||
// This is a mob that is fleeing either because it has been feared or is low on hitpoints
|
||||
if (GetBotStance() != BotStancePassive) {
|
||||
if (GetBotStance() != EQEmu::constants::stancePassive) {
|
||||
AI_PursueCastCheck(); // This appears to always return true..can't trust for success/fail
|
||||
return;
|
||||
}
|
||||
@ -2887,7 +2901,7 @@ void Bot::AI_Process() {
|
||||
} // end not in combat range
|
||||
|
||||
if (!IsMoving() && !spellend_timer.Enabled()) { // This may actually need work...
|
||||
if (GetBotStance() == BotStancePassive)
|
||||
if (GetBotStance() == EQEmu::constants::stancePassive)
|
||||
return;
|
||||
|
||||
if (GetTarget() && AI_EngagedCastCheck())
|
||||
@ -2945,7 +2959,7 @@ void Bot::AI_Process() {
|
||||
// Ok to idle
|
||||
if (fm_dist <= GetFollowDistance()) {
|
||||
if (!IsMoving() && AI_think_timer->Check() && !spellend_timer.Enabled()) {
|
||||
if (GetBotStance() != BotStancePassive) {
|
||||
if (GetBotStance() != EQEmu::constants::stancePassive) {
|
||||
if (!AI_IdleCastCheck() && !IsCasting() && GetClass() != BARD)
|
||||
BotMeditate(true);
|
||||
}
|
||||
@ -2990,7 +3004,7 @@ void Bot::AI_Process() {
|
||||
|
||||
// Basically, bard bots get a chance to cast idle spells while moving
|
||||
if (IsMoving()) {
|
||||
if (GetBotStance() != BotStancePassive) {
|
||||
if (GetBotStance() != EQEmu::constants::stancePassive) {
|
||||
if (GetClass() == BARD && !spellend_timer.Enabled() && AI_think_timer->Check()) {
|
||||
AI_IdleCastCheck();
|
||||
return;
|
||||
@ -8199,7 +8213,7 @@ bool Bot::CheckLoreConflict(const EQEmu::ItemData* item) {
|
||||
}
|
||||
|
||||
bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
|
||||
if((iSpellTypes&SpellTypes_Detrimental) != 0) {
|
||||
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
|
||||
Log(Logs::General, Logs::Error, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!");
|
||||
return false;
|
||||
}
|
||||
@ -8254,19 +8268,19 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl
|
||||
Group *g = caster->GetGroup();
|
||||
float hpRatioToHeal = 25.0f;
|
||||
switch(caster->GetBotStance()) {
|
||||
case BotStanceReactive:
|
||||
case BotStanceBalanced:
|
||||
hpRatioToHeal = 50.0f;
|
||||
break;
|
||||
case BotStanceBurn:
|
||||
case BotStanceBurnAE:
|
||||
hpRatioToHeal = 20.0f;
|
||||
break;
|
||||
case BotStanceAggressive:
|
||||
case BotStanceEfficient:
|
||||
default:
|
||||
hpRatioToHeal = 25.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceReactive:
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
hpRatioToHeal = 50.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBurn:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
hpRatioToHeal = 20.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceAggressive:
|
||||
case EQEmu::constants::stanceEfficient:
|
||||
default:
|
||||
hpRatioToHeal = 25.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
if(g) {
|
||||
@ -8805,11 +8819,11 @@ bool Bot::HasOrMayGetAggro() {
|
||||
}
|
||||
|
||||
void Bot::SetDefaultBotStance() {
|
||||
BotStanceType defaultStance = BotStanceBalanced;
|
||||
EQEmu::constants::StanceType defaultStance = EQEmu::constants::stanceBalanced;
|
||||
if (GetClass() == WARRIOR)
|
||||
defaultStance = BotStanceAggressive;
|
||||
defaultStance = EQEmu::constants::stanceAggressive;
|
||||
|
||||
_baseBotStance = BotStancePassive;
|
||||
_baseBotStance = EQEmu::constants::stancePassive;
|
||||
_botStance = defaultStance;
|
||||
}
|
||||
|
||||
@ -9082,18 +9096,6 @@ std::string Bot::CreateSayLink(Client* c, const char* message, const char* name)
|
||||
return saylink;
|
||||
}
|
||||
|
||||
void Bot::StopMoving() {
|
||||
if (!ping_timer.Enabled())
|
||||
ping_timer.Start(8000);
|
||||
|
||||
Mob::StopMoving();
|
||||
}
|
||||
|
||||
void Bot::StopMoving(float new_heading) {
|
||||
if (!ping_timer.Enabled())
|
||||
ping_timer.Start(8000);
|
||||
|
||||
Mob::StopMoving(new_heading);
|
||||
}
|
||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQEmu::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
||||
|
||||
#endif
|
||||
|
||||
152
zone/bot.h
152
zone/bot.h
@ -44,6 +44,8 @@
|
||||
|
||||
#define BOT_LEASH_DISTANCE 250000 // as DSq value (500 units)
|
||||
|
||||
#define BOT_KEEP_ALIVE_INTERVAL 5000 // 5 seconds
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
const int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this
|
||||
@ -52,91 +54,7 @@ const int MaxDisciplineTimer = 10;
|
||||
const int DisciplineReuseStart = MaxSpellTimer + 1;
|
||||
const int MaxTimer = MaxSpellTimer + MaxDisciplineTimer;
|
||||
|
||||
enum BotStanceType {
|
||||
BotStancePassive,
|
||||
BotStanceBalanced,
|
||||
BotStanceEfficient,
|
||||
BotStanceReactive,
|
||||
BotStanceAggressive,
|
||||
BotStanceBurn,
|
||||
BotStanceBurnAE,
|
||||
BotStanceUnknown,
|
||||
MaxStances = 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 >= EQEmu::invslot::EQUIPMENT_BEGIN && x <= EQEmu::invslot::EQUIPMENT_END) ? (x) : (EQEmu::invslot::EQUIPMENT_COUNT))
|
||||
|
||||
static const std::string bot_equip_slot_name[EQEmu::invslot::EQUIPMENT_COUNT + 1] =
|
||||
{
|
||||
"Charm", // slotCharm
|
||||
"Ear 1", // slotEar1
|
||||
"Head", // slotHead
|
||||
"Face", // slotFace
|
||||
"Ear 2", // slotEar2
|
||||
"Neck", // slotNeck
|
||||
"Shoulders", // slotShoulders
|
||||
"Arms", // slotArms
|
||||
"Back", // slotBack
|
||||
"Wrist 1", // slotWrist1
|
||||
"Wrist 2", // slotWrist2
|
||||
"Range", // slotRange
|
||||
"Hands", // slotHands
|
||||
"Primary", // slotPrimary
|
||||
"Secondary", // slotSecondary
|
||||
"Finger 1", // slotFinger1
|
||||
"Finger 2", // slotFinger2
|
||||
"Chest", // slotChest
|
||||
"Legs", // slotLegs
|
||||
"Feet", // slotFeet
|
||||
"Waist", // slotWaist
|
||||
"Power Source", // slotPowerSource
|
||||
"Ammo", // slotAmmo
|
||||
"Unknown"
|
||||
};
|
||||
|
||||
static const char* GetBotEquipSlotName(int slot_id) { return bot_equip_slot_name[VALIDBOTEQUIPSLOT(slot_id)].c_str(); }
|
||||
|
||||
enum SpellTypeIndex {
|
||||
SpellType_NukeIndex,
|
||||
SpellType_HealIndex,
|
||||
SpellType_RootIndex,
|
||||
SpellType_BuffIndex,
|
||||
SpellType_EscapeIndex,
|
||||
SpellType_PetIndex,
|
||||
SpellType_LifetapIndex,
|
||||
SpellType_SnareIndex,
|
||||
SpellType_DOTIndex,
|
||||
SpellType_DispelIndex,
|
||||
SpellType_InCombatBuffIndex,
|
||||
SpellType_MezIndex,
|
||||
SpellType_CharmIndex,
|
||||
SpellType_SlowIndex,
|
||||
SpellType_DebuffIndex,
|
||||
SpellType_CureIndex,
|
||||
SpellType_ResurrectIndex,
|
||||
SpellType_HateReduxIndex,
|
||||
SpellType_InCombatBuffSongIndex,
|
||||
SpellType_OutOfCombatBuffSongIndex,
|
||||
SpellType_PreCombatBuffIndex,
|
||||
SpellType_PreCombatBuffSongIndex,
|
||||
MaxSpellTypes
|
||||
};
|
||||
|
||||
// nHSND negative Healer/Slower/Nuker/Doter
|
||||
// pH positive Healer
|
||||
@ -226,29 +144,35 @@ public:
|
||||
BotRoleRaidHealer
|
||||
};
|
||||
|
||||
enum EqExpansions { // expansions are off..EQ should be '0'
|
||||
ExpansionNone,
|
||||
ExpansionEQ,
|
||||
ExpansionRoK,
|
||||
ExpansionSoV,
|
||||
ExpansionSoL,
|
||||
ExpansionPoP,
|
||||
ExpansionLoY,
|
||||
ExpansionLDoN,
|
||||
ExpansionGoD,
|
||||
ExpansionOoW,
|
||||
ExpansionDoN,
|
||||
ExpansionDoDH,
|
||||
ExpansionPoR,
|
||||
ExpansionTSS,
|
||||
ExpansionSoF,
|
||||
ExpansionSoD,
|
||||
ExpansionUF,
|
||||
ExpansionHoT,
|
||||
ExpansionVoA,
|
||||
ExpansionRoF
|
||||
enum SpellTypeIndex : uint32 {
|
||||
spellTypeIndexNuke,
|
||||
spellTypeIndexHeal,
|
||||
spellTypeIndexRoot,
|
||||
spellTypeIndexBuff,
|
||||
spellTypeIndexEscape,
|
||||
spellTypeIndexPet,
|
||||
spellTypeIndexLifetap,
|
||||
spellTypeIndexSnare,
|
||||
spellTypeIndexDot,
|
||||
spellTypeIndexDispel,
|
||||
spellTypeIndexInCombatBuff,
|
||||
spellTypeIndexMez,
|
||||
spellTypeIndexCharm,
|
||||
spellTypeIndexSlow,
|
||||
spellTypeIndexDebuff,
|
||||
spellTypeIndexCure,
|
||||
spellTypeIndexResurrect,
|
||||
spellTypeIndexHateRedux,
|
||||
spellTypeIndexInCombatBuffSong,
|
||||
spellTypeIndexOutOfCombatBuffSong,
|
||||
spellTypeIndexPreCombatBuff,
|
||||
spellTypeIndexPreCombatBuffSong
|
||||
};
|
||||
|
||||
static const uint32 SPELL_TYPE_FIRST = spellTypeIndexNuke;
|
||||
static const uint32 SPELL_TYPE_LAST = spellTypeIndexPreCombatBuffSong;
|
||||
static const uint32 SPELL_TYPE_COUNT = SPELL_TYPE_LAST + 1;
|
||||
|
||||
// Class Constructors
|
||||
Bot(NPCType *npcTypeData, Client* botOwner);
|
||||
Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData);
|
||||
@ -341,8 +265,6 @@ public:
|
||||
virtual int GetRunspeed() const { return (int)((float)_GetRunSpeed() * 1.785714f); }
|
||||
virtual void WalkTo(float x, float y, float z);
|
||||
virtual void RunTo(float x, float y, float z);
|
||||
virtual void StopMoving();
|
||||
virtual void StopMoving(float new_heading);
|
||||
bool UseDiscipline(uint32 spell_id, uint32 target);
|
||||
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets);
|
||||
bool GetNeedsCured(Mob *tar);
|
||||
@ -519,7 +441,7 @@ public:
|
||||
virtual bool IsBot() const { return true; }
|
||||
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
|
||||
BotRoleType GetBotRole() { return _botRole; }
|
||||
BotStanceType GetBotStance() { return _botStance; }
|
||||
EQEmu::constants::StanceType GetBotStance() { return _botStance; }
|
||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||
|
||||
bool IsGroupHealer() { return m_CastingRoles.GroupHealer; }
|
||||
@ -633,7 +555,12 @@ 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 != BotStanceUnknown) ? (botStance) : (BotStancePassive)); }
|
||||
void SetBotStance(EQEmu::constants::StanceType botStance) {
|
||||
if (botStance >= EQEmu::constants::stancePassive && botStance <= EQEmu::constants::stanceBurnAE)
|
||||
_botStance = botStance;
|
||||
else
|
||||
_botStance = EQEmu::constants::stancePassive;
|
||||
}
|
||||
void SetSpellRecastTimer(int timer_index, int32 recast_delay);
|
||||
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
|
||||
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
|
||||
@ -727,8 +654,8 @@ private:
|
||||
uint32 _lastZoneId;
|
||||
bool _rangerAutoWeaponSelect;
|
||||
BotRoleType _botRole;
|
||||
BotStanceType _botStance;
|
||||
BotStanceType _baseBotStance;
|
||||
EQEmu::constants::StanceType _botStance;
|
||||
EQEmu::constants::StanceType _baseBotStance;
|
||||
unsigned int RestRegenHP;
|
||||
unsigned int RestRegenMana;
|
||||
unsigned int RestRegenEndurance;
|
||||
@ -792,6 +719,9 @@ private:
|
||||
bool LoadPet(); // Load and spawn bot pet if there is one
|
||||
bool SavePet(); // Save and depop bot pet if there is one
|
||||
bool DeletePet();
|
||||
|
||||
public:
|
||||
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQEmu::constants::STANCE_TYPE_COUNT][cntHSND];
|
||||
};
|
||||
|
||||
#endif // BOTS
|
||||
|
||||
@ -4249,7 +4249,7 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
int clone_stance = BotStancePassive;
|
||||
int clone_stance = EQEmu::constants::stancePassive;
|
||||
if (!botdb.LoadStance(my_bot->GetBotID(), clone_stance))
|
||||
c->Message(m_fail, "%s for bot '%s'", BotDatabase::fail::LoadStance(), my_bot->GetCleanName());
|
||||
if (!botdb.SaveStance(clone_id, clone_stance))
|
||||
@ -5160,29 +5160,34 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
|
||||
if (helper_command_alias_fail(c, "bot_subcommand_bot_stance", sep->arg[0], "botstance"))
|
||||
return;
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(m_usage, "usage: %s [current | value: 0-6] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
|
||||
c->Message(m_usage, "usage: %s [current | value: 1-9] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
|
||||
c->Message(m_note, "value: %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s)",
|
||||
BotStancePassive, GetBotStanceName(BotStancePassive),
|
||||
BotStanceBalanced, GetBotStanceName(BotStanceBalanced),
|
||||
BotStanceEfficient, GetBotStanceName(BotStanceEfficient),
|
||||
BotStanceReactive, GetBotStanceName(BotStanceReactive),
|
||||
BotStanceAggressive, GetBotStanceName(BotStanceAggressive),
|
||||
BotStanceBurn, GetBotStanceName(BotStanceBurn),
|
||||
BotStanceBurnAE, GetBotStanceName(BotStanceBurnAE)
|
||||
EQEmu::constants::stancePassive, EQEmu::constants::GetStanceName(EQEmu::constants::stancePassive),
|
||||
EQEmu::constants::stanceBalanced, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBalanced),
|
||||
EQEmu::constants::stanceEfficient, EQEmu::constants::GetStanceName(EQEmu::constants::stanceEfficient),
|
||||
EQEmu::constants::stanceReactive, EQEmu::constants::GetStanceName(EQEmu::constants::stanceReactive),
|
||||
EQEmu::constants::stanceAggressive, EQEmu::constants::GetStanceName(EQEmu::constants::stanceAggressive),
|
||||
EQEmu::constants::stanceAssist, EQEmu::constants::GetStanceName(EQEmu::constants::stanceAssist),
|
||||
EQEmu::constants::stanceBurn, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBurn),
|
||||
EQEmu::constants::stanceEfficient2, EQEmu::constants::GetStanceName(EQEmu::constants::stanceEfficient2),
|
||||
EQEmu::constants::stanceBurnAE, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBurnAE)
|
||||
);
|
||||
return;
|
||||
}
|
||||
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
|
||||
|
||||
bool current_flag = false;
|
||||
auto bst = BotStanceUnknown;
|
||||
auto bst = EQEmu::constants::stanceUnknown;
|
||||
|
||||
if (!strcasecmp(sep->arg[1], "current"))
|
||||
current_flag = true;
|
||||
else if (sep->IsNumber(1))
|
||||
bst = VALIDBOTSTANCE(atoi(sep->arg[1]));
|
||||
else if (sep->IsNumber(1)) {
|
||||
bst = (EQEmu::constants::StanceType)atoi(sep->arg[1]);
|
||||
if (bst < EQEmu::constants::stanceUnknown || bst > EQEmu::constants::stanceBurnAE)
|
||||
bst = EQEmu::constants::stanceUnknown;
|
||||
}
|
||||
|
||||
if (!current_flag && bst == BotStanceUnknown) {
|
||||
if (!current_flag && bst == EQEmu::constants::stanceUnknown) {
|
||||
c->Message(m_fail, "A [current] argument or valid numeric [value] is required to use this command");
|
||||
return;
|
||||
}
|
||||
@ -5200,7 +5205,12 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
|
||||
bot_iter->Save();
|
||||
}
|
||||
|
||||
Bot::BotGroupSay(bot_iter, "My current stance is '%s' (%u)", GetBotStanceName(bot_iter->GetBotStance()), bot_iter->GetBotStance());
|
||||
Bot::BotGroupSay(
|
||||
bot_iter,
|
||||
"My current stance is '%s' (%i)",
|
||||
EQEmu::constants::GetStanceName(bot_iter->GetBotStance()),
|
||||
bot_iter->GetBotStance()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7220,7 +7230,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
|
||||
|
||||
inst = my_bot->CastToBot()->GetBotItem(i);
|
||||
if (!inst || !inst->GetItem()) {
|
||||
c->Message(m_message, "I need something for my %s (slot %i)", GetBotEquipSlotName(i), i);
|
||||
c->Message(m_message, "I need something for my %s (slot %i)", EQEmu::invslot::GetInvPossessionsSlotName(i), i);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -7230,7 +7240,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
linker.SetItemInst(inst);
|
||||
c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), GetBotEquipSlotName(i), i);
|
||||
c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), EQEmu::invslot::GetInvPossessionsSlotName(i), i);
|
||||
|
||||
++inventory_count;
|
||||
}
|
||||
@ -7333,14 +7343,14 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
|
||||
case EQEmu::invslot::slotWaist:
|
||||
case EQEmu::invslot::slotPowerSource:
|
||||
case EQEmu::invslot::slotAmmo:
|
||||
c->Message(m_message, "My %s is %s unequipped", GetBotEquipSlotName(slotId), ((itm) ? ("now") : ("already")));
|
||||
c->Message(m_message, "My %s is %s unequipped", EQEmu::invslot::GetInvPossessionsSlotName(slotId), ((itm) ? ("now") : ("already")));
|
||||
break;
|
||||
case EQEmu::invslot::slotShoulders:
|
||||
case EQEmu::invslot::slotArms:
|
||||
case EQEmu::invslot::slotHands:
|
||||
case EQEmu::invslot::slotLegs:
|
||||
case EQEmu::invslot::slotFeet:
|
||||
c->Message(m_message, "My %s are %s unequipped", GetBotEquipSlotName(slotId), ((itm) ? ("now") : ("already")));
|
||||
c->Message(m_message, "My %s are %s unequipped", EQEmu::invslot::GetInvPossessionsSlotName(slotId), ((itm) ? ("now") : ("already")));
|
||||
break;
|
||||
default:
|
||||
c->Message(m_fail, "I'm soo confused...");
|
||||
@ -7383,7 +7393,7 @@ void bot_subcommand_inventory_window(Client *c, const Seperator *sep)
|
||||
item = inst->GetItem();
|
||||
|
||||
window_text.append("<c \"#FFFFFF\">");
|
||||
window_text.append(GetBotEquipSlotName(i));
|
||||
window_text.append(EQEmu::invslot::GetInvPossessionsSlotName(i));
|
||||
window_text.append(": ");
|
||||
if (item) {
|
||||
//window_text.append("</c>");
|
||||
|
||||
@ -83,12 +83,8 @@ bool BotDatabase::LoadBotCommandSettings(std::map<std::string, std::pair<uint8,
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8 spell_casting_chances[MaxSpellTypes][PLAYER_CLASS_COUNT][MaxStances][cntHSND];
|
||||
|
||||
bool BotDatabase::LoadBotSpellCastingChances()
|
||||
{
|
||||
memset(spell_casting_chances, 0, sizeof(spell_casting_chances));
|
||||
|
||||
query =
|
||||
"SELECT"
|
||||
" `spell_type_index`,"
|
||||
@ -119,14 +115,14 @@ bool BotDatabase::LoadBotSpellCastingChances()
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
uint8 spell_type_index = atoi(row[0]);
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
|
||||
continue;
|
||||
uint8 class_index = atoi(row[1]);
|
||||
if (class_index < WARRIOR || class_index > BERSERKER)
|
||||
continue;
|
||||
--class_index;
|
||||
uint8 stance_index = atoi(row[2]);
|
||||
if (stance_index >= MaxStances)
|
||||
if (stance_index >= EQEmu::constants::STANCE_TYPE_COUNT)
|
||||
continue;
|
||||
|
||||
for (uint8 conditional_index = nHSND; conditional_index < cntHSND; ++conditional_index) {
|
||||
@ -136,7 +132,7 @@ bool BotDatabase::LoadBotSpellCastingChances()
|
||||
if (value > 100)
|
||||
value = 100;
|
||||
|
||||
spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
|
||||
Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -877,7 +873,7 @@ bool BotDatabase::LoadStance(Bot* bot_inst, bool& stance_flag)
|
||||
return true;
|
||||
|
||||
auto row = results.begin();
|
||||
bot_inst->SetBotStance((BotStanceType)atoi(row[0]));
|
||||
bot_inst->SetBotStance((EQEmu::constants::StanceType)atoi(row[0]));
|
||||
stance_flag = true;
|
||||
|
||||
return true;
|
||||
@ -2853,16 +2849,16 @@ bool BotDatabase::DeleteAllHealRotations(const uint32 owner_id)
|
||||
/* Bot miscellaneous functions */
|
||||
uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index) // class_index is 0-based
|
||||
{
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
|
||||
return 0;
|
||||
if (class_index >= PLAYER_CLASS_COUNT)
|
||||
return 0;
|
||||
if (stance_index >= MaxStances)
|
||||
if (stance_index >= EQEmu::constants::STANCE_TYPE_COUNT)
|
||||
return 0;
|
||||
if (conditional_index >= cntHSND)
|
||||
return 0;
|
||||
|
||||
return spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
|
||||
return Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -192,25 +192,24 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
else {
|
||||
float hpRatioToCast = 0.0f;
|
||||
|
||||
switch(this->GetBotStance())
|
||||
{
|
||||
case BotStanceEfficient:
|
||||
case BotStanceAggressive:
|
||||
hpRatioToCast = isPrimaryHealer?90.0f:50.0f;
|
||||
break;
|
||||
case BotStanceBalanced:
|
||||
hpRatioToCast = isPrimaryHealer?95.0f:75.0f;
|
||||
break;
|
||||
case BotStanceReactive:
|
||||
hpRatioToCast = isPrimaryHealer?100.0f:90.0f;
|
||||
break;
|
||||
case BotStanceBurn:
|
||||
case BotStanceBurnAE:
|
||||
hpRatioToCast = isPrimaryHealer?75.0f:25.0f;
|
||||
break;
|
||||
default:
|
||||
hpRatioToCast = isPrimaryHealer?100.0f:0.0f;
|
||||
break;
|
||||
switch(this->GetBotStance()) {
|
||||
case EQEmu::constants::stanceEfficient:
|
||||
case EQEmu::constants::stanceAggressive:
|
||||
hpRatioToCast = isPrimaryHealer?90.0f:50.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
hpRatioToCast = isPrimaryHealer?95.0f:75.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceReactive:
|
||||
hpRatioToCast = isPrimaryHealer?100.0f:90.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBurn:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
hpRatioToCast = isPrimaryHealer?75.0f:25.0f;
|
||||
break;
|
||||
default:
|
||||
hpRatioToCast = isPrimaryHealer?100.0f:0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
//If we're at specified mana % or below, don't heal as hybrid
|
||||
@ -381,23 +380,22 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
{
|
||||
float manaRatioToCast = 75.0f;
|
||||
|
||||
switch(this->GetBotStance())
|
||||
{
|
||||
case BotStanceEfficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case BotStanceBalanced:
|
||||
case BotStanceAggressive:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case BotStanceReactive:
|
||||
case BotStanceBurn:
|
||||
case BotStanceBurnAE:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
default:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
switch(this->GetBotStance()) {
|
||||
case EQEmu::constants::stanceEfficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
case EQEmu::constants::stanceAggressive:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceReactive:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
default:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
//If we're at specified mana % or below, don't rune as enchanter
|
||||
@ -461,25 +459,24 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
{
|
||||
float manaRatioToCast = 75.0f;
|
||||
|
||||
switch(this->GetBotStance())
|
||||
{
|
||||
case BotStanceEfficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case BotStanceBalanced:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case BotStanceReactive:
|
||||
case BotStanceAggressive:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
case BotStanceBurn:
|
||||
case BotStanceBurnAE:
|
||||
manaRatioToCast = 25.0f;
|
||||
break;
|
||||
default:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
switch(this->GetBotStance()) {
|
||||
case EQEmu::constants::stanceEfficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceReactive:
|
||||
case EQEmu::constants::stanceAggressive:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBurn:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
manaRatioToCast = 25.0f;
|
||||
break;
|
||||
default:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
//If we're at specified mana % or below, don't nuke as cleric or enchanter
|
||||
@ -1310,7 +1307,7 @@ bool Bot::AI_EngagedCastCheck() {
|
||||
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
||||
|
||||
uint8 botClass = GetClass();
|
||||
BotStanceType botStance = GetBotStance();
|
||||
EQEmu::constants::StanceType botStance = GetBotStance();
|
||||
bool mayGetAggro = HasOrMayGetAggro();
|
||||
|
||||
Log(Logs::Detail, Logs::AI, "Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells.");
|
||||
@ -2573,79 +2570,79 @@ bool Bot::CheckDisciplineRecastTimers(Bot *caster, int timer_index) {
|
||||
|
||||
uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
||||
{
|
||||
uint8 spell_type_index = MaxSpellTypes;
|
||||
uint8 spell_type_index = SPELL_TYPE_COUNT;
|
||||
switch (spellType) {
|
||||
case SpellType_Nuke:
|
||||
spell_type_index = SpellType_NukeIndex;
|
||||
spell_type_index = spellTypeIndexNuke;
|
||||
break;
|
||||
case SpellType_Heal:
|
||||
spell_type_index = SpellType_HealIndex;
|
||||
spell_type_index = spellTypeIndexHeal;
|
||||
break;
|
||||
case SpellType_Root:
|
||||
spell_type_index = SpellType_RootIndex;
|
||||
spell_type_index = spellTypeIndexRoot;
|
||||
break;
|
||||
case SpellType_Buff:
|
||||
spell_type_index = SpellType_BuffIndex;
|
||||
spell_type_index = spellTypeIndexBuff;
|
||||
break;
|
||||
case SpellType_Escape:
|
||||
spell_type_index = SpellType_EscapeIndex;
|
||||
spell_type_index = spellTypeIndexEscape;
|
||||
break;
|
||||
case SpellType_Pet:
|
||||
spell_type_index = SpellType_PetIndex;
|
||||
spell_type_index = spellTypeIndexPet;
|
||||
break;
|
||||
case SpellType_Lifetap:
|
||||
spell_type_index = SpellType_LifetapIndex;
|
||||
spell_type_index = spellTypeIndexLifetap;
|
||||
break;
|
||||
case SpellType_Snare:
|
||||
spell_type_index = SpellType_SnareIndex;
|
||||
spell_type_index = spellTypeIndexSnare;
|
||||
break;
|
||||
case SpellType_DOT:
|
||||
spell_type_index = SpellType_DOTIndex;
|
||||
spell_type_index = spellTypeIndexDot;
|
||||
break;
|
||||
case SpellType_Dispel:
|
||||
spell_type_index = SpellType_DispelIndex;
|
||||
spell_type_index = spellTypeIndexDispel;
|
||||
break;
|
||||
case SpellType_InCombatBuff:
|
||||
spell_type_index = SpellType_InCombatBuffIndex;
|
||||
spell_type_index = spellTypeIndexInCombatBuff;
|
||||
break;
|
||||
case SpellType_Mez:
|
||||
spell_type_index = SpellType_MezIndex;
|
||||
spell_type_index = spellTypeIndexMez;
|
||||
break;
|
||||
case SpellType_Charm:
|
||||
spell_type_index = SpellType_CharmIndex;
|
||||
spell_type_index = spellTypeIndexCharm;
|
||||
break;
|
||||
case SpellType_Slow:
|
||||
spell_type_index = SpellType_SlowIndex;
|
||||
spell_type_index = spellTypeIndexSlow;
|
||||
break;
|
||||
case SpellType_Debuff:
|
||||
spell_type_index = SpellType_DebuffIndex;
|
||||
spell_type_index = spellTypeIndexDebuff;
|
||||
break;
|
||||
case SpellType_Cure:
|
||||
spell_type_index = SpellType_CureIndex;
|
||||
spell_type_index = spellTypeIndexCure;
|
||||
break;
|
||||
case SpellType_Resurrect:
|
||||
spell_type_index = SpellType_ResurrectIndex;
|
||||
spell_type_index = spellTypeIndexResurrect;
|
||||
break;
|
||||
case SpellType_HateRedux:
|
||||
spell_type_index = SpellType_HateReduxIndex;
|
||||
spell_type_index = spellTypeIndexHateRedux;
|
||||
break;
|
||||
case SpellType_InCombatBuffSong:
|
||||
spell_type_index = SpellType_InCombatBuffSongIndex;
|
||||
spell_type_index = spellTypeIndexInCombatBuffSong;
|
||||
break;
|
||||
case SpellType_OutOfCombatBuffSong:
|
||||
spell_type_index = SpellType_OutOfCombatBuffSongIndex;
|
||||
spell_type_index = spellTypeIndexOutOfCombatBuffSong;
|
||||
break;
|
||||
case SpellType_PreCombatBuff:
|
||||
spell_type_index = SpellType_PreCombatBuffIndex;
|
||||
spell_type_index = spellTypeIndexPreCombatBuff;
|
||||
break;
|
||||
case SpellType_PreCombatBuffSong:
|
||||
spell_type_index = SpellType_PreCombatBuffSongIndex;
|
||||
spell_type_index = spellTypeIndexPreCombatBuffSong;
|
||||
break;
|
||||
default:
|
||||
spell_type_index = MaxSpellTypes;
|
||||
spell_type_index = SPELL_TYPE_COUNT;
|
||||
break;
|
||||
}
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
if (spell_type_index >= SPELL_TYPE_COUNT)
|
||||
return 0;
|
||||
|
||||
uint8 class_index = GetClass();
|
||||
@ -2653,11 +2650,13 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
||||
return 0;
|
||||
--class_index;
|
||||
|
||||
uint8 stance_index = (uint8)GetBotStance();
|
||||
if (stance_index >= MaxStances)
|
||||
EQEmu::constants::StanceType stance_type = GetBotStance();
|
||||
if (stance_type < EQEmu::constants::stancePassive || stance_type > EQEmu::constants::stanceBurnAE)
|
||||
return 0;
|
||||
|
||||
uint8 stance_index = EQEmu::constants::ConvertStanceTypeToIndex(stance_type);
|
||||
uint8 type_index = nHSND;
|
||||
|
||||
if (HasGroup()) {
|
||||
if (IsGroupHealer()/* || IsRaidHealer()*/)
|
||||
type_index |= pH;
|
||||
|
||||
@ -38,6 +38,7 @@ extern volatile bool RunLoops;
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/string_util.h"
|
||||
#include "../common/data_verification.h"
|
||||
#include "../common/profanity_manager.h"
|
||||
#include "data_bucket.h"
|
||||
#include "position.h"
|
||||
#include "net.h"
|
||||
@ -895,6 +896,10 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
language = 0; // No need for language when drunk
|
||||
}
|
||||
|
||||
// Censor the message
|
||||
if (EQEmu::ProfanityManager::IsCensorshipActive() && (chan_num != 8))
|
||||
EQEmu::ProfanityManager::RedactMessage(message);
|
||||
|
||||
switch(chan_num)
|
||||
{
|
||||
case 0: { /* Guild Chat */
|
||||
@ -1092,6 +1097,9 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
break;
|
||||
}
|
||||
|
||||
if (EQEmu::ProfanityManager::IsCensorshipActive())
|
||||
EQEmu::ProfanityManager::RedactMessage(message);
|
||||
|
||||
#ifdef BOTS
|
||||
if (message[0] == BOT_COMMAND_CHAR) {
|
||||
if (bot_command_dispatch(this, message) == -2) {
|
||||
|
||||
@ -1414,12 +1414,11 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
if (m_pp.ldon_points_tak < 0 || m_pp.ldon_points_tak > 2000000000) { m_pp.ldon_points_tak = 0; }
|
||||
if (m_pp.ldon_points_available < 0 || m_pp.ldon_points_available > 2000000000) { m_pp.ldon_points_available = 0; }
|
||||
|
||||
// need to rework .. not until full scope of change is accounted for, though
|
||||
if (RuleB(World, UseClientBasedExpansionSettings)) {
|
||||
m_pp.expansions = EQEmu::expansions::ConvertClientVersionToExpansionMask(ClientVersion());
|
||||
m_pp.expansions = EQEmu::expansions::ConvertClientVersionToExpansionsMask(ClientVersion());
|
||||
}
|
||||
else {
|
||||
m_pp.expansions = RuleI(World, ExpansionSettings);
|
||||
m_pp.expansions = (RuleI(World, ExpansionSettings) & EQEmu::expansions::ConvertClientVersionToExpansionsMask(ClientVersion()));
|
||||
}
|
||||
|
||||
if (!database.LoadAlternateAdvancement(this)) {
|
||||
@ -2813,24 +2812,19 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 ApplyPoisonSuccessResult = 0;
|
||||
ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer;
|
||||
|
||||
uint32 ApplyPoisonSuccessResult = 0;
|
||||
|
||||
const EQEmu::ItemInstance* PrimaryWeapon = GetInv().GetItem(EQEmu::invslot::slotPrimary);
|
||||
const EQEmu::ItemInstance* SecondaryWeapon = GetInv().GetItem(EQEmu::invslot::slotSecondary);
|
||||
const EQEmu::ItemInstance* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot];
|
||||
const EQEmu::ItemData* poison=PoisonItemInstance->GetItem();
|
||||
const EQEmu::ItemData* primary=nullptr;
|
||||
const EQEmu::ItemData* secondary=nullptr;
|
||||
bool IsPoison = PoisonItemInstance &&
|
||||
(poison->ItemType == EQEmu::item::ItemTypePoison);
|
||||
const EQEmu::ItemInstance* PoisonItemInstance = GetInv().GetItem(ApplyPoisonData->inventorySlot);
|
||||
|
||||
if (PrimaryWeapon) {
|
||||
primary=PrimaryWeapon->GetItem();
|
||||
}
|
||||
const EQEmu::ItemData* primary = (PrimaryWeapon ? PrimaryWeapon->GetItem() : nullptr);
|
||||
const EQEmu::ItemData* secondary = (SecondaryWeapon ? SecondaryWeapon->GetItem() : nullptr);
|
||||
const EQEmu::ItemData* poison = (PoisonItemInstance ? PoisonItemInstance->GetItem() : nullptr);
|
||||
|
||||
if (SecondaryWeapon) {
|
||||
secondary=SecondaryWeapon->GetItem();
|
||||
}
|
||||
bool IsPoison = (poison && poison->ItemType == EQEmu::item::ItemTypePoison);
|
||||
|
||||
if (IsPoison && GetClass() == ROGUE) {
|
||||
|
||||
@ -9400,7 +9394,7 @@ void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app)
|
||||
//check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance)
|
||||
if (option >= 0 && option < numStances)
|
||||
{
|
||||
merc->SetStance(mercTemplate->Stances[option]);
|
||||
merc->SetStance((EQEmu::constants::StanceType)mercTemplate->Stances[option]);
|
||||
GetMercInfo().Stance = mercTemplate->Stances[option];
|
||||
|
||||
Log(Logs::General, Logs::Mercenaries, "Set Stance: %u for %s (%s)", merc->GetStance(), merc->GetName(), GetName());
|
||||
|
||||
285
zone/command.cpp
285
zone/command.cpp
@ -54,6 +54,7 @@
|
||||
#include "../common/string_util.h"
|
||||
#include "../say_link.h"
|
||||
#include "../common/eqemu_logsys.h"
|
||||
#include "../common/profanity_manager.h"
|
||||
|
||||
#include "data_bucket.h"
|
||||
#include "command.h"
|
||||
@ -307,6 +308,7 @@ int command_init(void)
|
||||
command_add("petitioninfo", "[petition number] - Get info about a petition", 20, command_petitioninfo) ||
|
||||
command_add("pf", "- Display additional mob coordinate and wandering data", 0, command_pf) ||
|
||||
command_add("picklock", "Analog for ldon pick lock for the newer clients since we still don't have it working.", 0, command_picklock) ||
|
||||
command_add("profanity", "Manage censored language.", 150, command_profanity) ||
|
||||
|
||||
#ifdef EQPROFILE
|
||||
command_add("profiledump", "- Dump profiling info to logs", 250, command_profiledump) ||
|
||||
@ -6405,34 +6407,29 @@ void command_beardcolor(Client *c, const Seperator *sep)
|
||||
|
||||
void command_scribespells(Client *c, const Seperator *sep)
|
||||
{
|
||||
uint8 max_level, min_level;
|
||||
uint16 book_slot, curspell, count;
|
||||
Client *t=c;
|
||||
Client *t = c;
|
||||
if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
|
||||
t = c->GetTarget()->CastToClient();
|
||||
|
||||
if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
|
||||
t=c->GetTarget()->CastToClient();
|
||||
|
||||
if(!sep->arg[1][0])
|
||||
{
|
||||
if(sep->argnum < 1 || !sep->IsNumber(1)) {
|
||||
c->Message(0, "FORMAT: #scribespells <max level> <min level>");
|
||||
return;
|
||||
}
|
||||
|
||||
max_level = (uint8)atoi(sep->arg[1]);
|
||||
if (!c->GetGM() && max_level > RuleI(Character, MaxLevel))
|
||||
max_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
||||
min_level = sep->arg[2][0] ? (uint8)atoi(sep->arg[2]) : 1; //default to 1 if there isn't a 2nd argument
|
||||
if (!c->GetGM() && min_level > RuleI(Character, MaxLevel))
|
||||
min_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
||||
uint8 max_level = (uint8)atol(sep->arg[1]);
|
||||
if (!c->GetGM() && max_level > (uint8)RuleI(Character, MaxLevel))
|
||||
max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
||||
|
||||
uint8 min_level = (sep->IsNumber(2) ? (uint8)atol(sep->arg[2]) : 1); // default to 1 if there isn't a 2nd argument
|
||||
if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel))
|
||||
min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
||||
|
||||
if(max_level < 1 || min_level < 1)
|
||||
{
|
||||
if(max_level < 1 || min_level < 1) {
|
||||
c->Message(0, "ERROR: Level must be greater than 1.");
|
||||
return;
|
||||
}
|
||||
if (min_level > max_level) {
|
||||
c->Message(0, "Error: Min Level must be less than or equal to Max Level.");
|
||||
c->Message(0, "ERROR: Min Level must be less than or equal to Max Level.");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -6441,42 +6438,71 @@ void command_scribespells(Client *c, const Seperator *sep)
|
||||
c->Message(0, "Scribing spells for %s.", t->GetName());
|
||||
Log(Logs::General, Logs::Normal, "Scribe spells request for %s from %s, levels: %u -> %u", t->GetName(), c->GetName(), min_level, max_level);
|
||||
|
||||
for (
|
||||
curspell = 0,
|
||||
book_slot = t->GetNextAvailableSpellBookSlot(),
|
||||
count = 0; // ;
|
||||
curspell < SPDAT_RECORDS &&
|
||||
book_slot < EQEmu::spells::SPELLBOOK_SIZE; // ;
|
||||
curspell++,
|
||||
book_slot = t->GetNextAvailableSpellBookSlot(book_slot)
|
||||
)
|
||||
{
|
||||
if
|
||||
(
|
||||
spells[curspell].classes[WARRIOR] != 0 && // check if spell exists
|
||||
spells[curspell].classes[t->GetPP().class_-1] <= max_level && //maximum level
|
||||
spells[curspell].classes[t->GetPP().class_-1] >= min_level && //minimum level
|
||||
spells[curspell].skill != 52
|
||||
)
|
||||
{
|
||||
if (book_slot == -1) { //no more book slots
|
||||
t->Message(13, "Unable to scribe spell %s (%u) to spellbook: no more spell book slots available.", spells[curspell].name, curspell);
|
||||
if (t != c)
|
||||
c->Message(13, "Error scribing spells: %s ran out of spell book slots on spell %s (%u)", t->GetName(), spells[curspell].name, curspell);
|
||||
break;
|
||||
}
|
||||
if(!IsDiscipline(curspell) && !t->HasSpellScribed(curspell)) { //isn't a discipline & we don't already have it scribed
|
||||
t->ScribeSpell(curspell, book_slot);
|
||||
count++;
|
||||
}
|
||||
int book_slot = t->GetNextAvailableSpellBookSlot();
|
||||
int spell_id = 0;
|
||||
int count = 0;
|
||||
|
||||
for ( ; spell_id < SPDAT_RECORDS && book_slot < EQEmu::spells::SPELLBOOK_SIZE; ++spell_id) {
|
||||
if (book_slot == -1) {
|
||||
t->Message(
|
||||
13,
|
||||
"Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.",
|
||||
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
|
||||
spell_id
|
||||
);
|
||||
if (t != c)
|
||||
c->Message(
|
||||
13,
|
||||
"Error scribing spells: %s ran out of spell book slots on spell %s (%i)",
|
||||
t->GetName(),
|
||||
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
|
||||
spell_id
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
|
||||
c->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
|
||||
return;
|
||||
}
|
||||
if (book_slot < 0 || book_slot >= EQEmu::spells::SPELLBOOK_SIZE) {
|
||||
c->Message(13, "FATAL ERROR: Book slot out-of-range (slot: %i, min: 0, max: %i)", book_slot, EQEmu::spells::SPELLBOOK_SIZE);
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
|
||||
break;
|
||||
if (spells[spell_id].classes[t->GetPP().class_ - 1] > max_level) // maximum level
|
||||
break;
|
||||
if (spells[spell_id].classes[t->GetPP().class_ - 1] < min_level) // minimum level
|
||||
break;
|
||||
if (spells[spell_id].skill == 52)
|
||||
break;
|
||||
|
||||
uint16 spell_id_ = (uint16)spell_id;
|
||||
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
|
||||
c->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDiscipline(spell_id_) && !t->HasSpellScribed(spell_id)) { // isn't a discipline & we don't already have it scribed
|
||||
t->ScribeSpell(spell_id_, book_slot);
|
||||
++count;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
book_slot = t->GetNextAvailableSpellBookSlot(book_slot);
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
t->Message(0, "Successfully scribed %u spells.", count);
|
||||
t->Message(0, "Successfully scribed %i spells.", count);
|
||||
if (t != c)
|
||||
c->Message(0, "Successfully scribed %u spells for %s.", count, t->GetName());
|
||||
} else {
|
||||
c->Message(0, "Successfully scribed %i spells for %s.", count, t->GetName());
|
||||
}
|
||||
else {
|
||||
t->Message(0, "No spells scribed.");
|
||||
if (t != c)
|
||||
c->Message(0, "No spells scribed for %s.", t->GetName());
|
||||
@ -8732,28 +8758,24 @@ void command_reloadtitles(Client *c, const Seperator *sep)
|
||||
|
||||
void command_traindisc(Client *c, const Seperator *sep)
|
||||
{
|
||||
uint8 max_level, min_level;
|
||||
uint16 curspell, count;
|
||||
Client *t=c;
|
||||
Client *t = c;
|
||||
if (c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
|
||||
t = c->GetTarget()->CastToClient();
|
||||
|
||||
if(c->GetTarget() && c->GetTarget()->IsClient() && c->GetGM())
|
||||
t=c->GetTarget()->CastToClient();
|
||||
|
||||
if(!sep->arg[1][0])
|
||||
{
|
||||
if (sep->argnum < 1 || !sep->IsNumber(1)) {
|
||||
c->Message(0, "FORMAT: #traindisc <max level> <min level>");
|
||||
return;
|
||||
}
|
||||
|
||||
max_level = (uint8)atoi(sep->arg[1]);
|
||||
if (!c->GetGM() && max_level > RuleI(Character, MaxLevel))
|
||||
max_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
||||
min_level = sep->arg[2][0] ? (uint8)atoi(sep->arg[2]) : 1; //default to 1 if there isn't a 2nd argument
|
||||
if (!c->GetGM() && min_level > RuleI(Character, MaxLevel))
|
||||
min_level = RuleI(Character, MaxLevel); //default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
||||
uint8 max_level = (uint8)atol(sep->arg[1]);
|
||||
if (!c->GetGM() && max_level >(uint8)RuleI(Character, MaxLevel))
|
||||
max_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
||||
|
||||
if(max_level < 1 || min_level < 1)
|
||||
{
|
||||
uint8 min_level = (sep->IsNumber(2) ? (uint8)atol(sep->arg[2]) : 1); // default to 1 if there isn't a 2nd argument
|
||||
if (!c->GetGM() && min_level > (uint8)RuleI(Character, MaxLevel))
|
||||
min_level = (uint8)RuleI(Character, MaxLevel); // default to Character:MaxLevel if we're not a GM & it's higher than the max level
|
||||
|
||||
if(max_level < 1 || min_level < 1) {
|
||||
c->Message(0, "ERROR: Level must be greater than 1.");
|
||||
return;
|
||||
}
|
||||
@ -8767,35 +8789,58 @@ void command_traindisc(Client *c, const Seperator *sep)
|
||||
c->Message(0, "Training disciplines for %s.", t->GetName());
|
||||
Log(Logs::General, Logs::Normal, "Train disciplines request for %s from %s, levels: %u -> %u", t->GetName(), c->GetName(), min_level, max_level);
|
||||
|
||||
for(curspell = 0, count = 0; curspell < SPDAT_RECORDS; curspell++)
|
||||
{
|
||||
if
|
||||
(
|
||||
spells[curspell].classes[WARRIOR] != 0 && // check if spell exists
|
||||
spells[curspell].classes[t->GetPP().class_-1] <= max_level && //maximum level
|
||||
spells[curspell].classes[t->GetPP().class_-1] >= min_level && //minimum level
|
||||
spells[curspell].skill != 52
|
||||
)
|
||||
{
|
||||
if(IsDiscipline(curspell)){
|
||||
//we may want to come up with a function like Client::GetNextAvailableSpellBookSlot() to help speed this up a little
|
||||
for(int r = 0; r < MAX_PP_DISCIPLINES; r++) {
|
||||
if(t->GetPP().disciplines.values[r] == curspell) {
|
||||
t->Message(13, "You already know this discipline.");
|
||||
break; //continue the 1st loop
|
||||
} else if(t->GetPP().disciplines.values[r] == 0) {
|
||||
t->GetPP().disciplines.values[r] = curspell;
|
||||
database.SaveCharacterDisc(t->CharacterID(), r, curspell);
|
||||
t->SendDisciplineUpdate();
|
||||
t->Message(0, "You have learned a new discipline!");
|
||||
count++; //success counter
|
||||
break; //continue the 1st loop
|
||||
} //if we get to this point, there's already a discipline in this slot, so we continue onto the next slot
|
||||
}
|
||||
int spell_id = 0;
|
||||
int count = 0;
|
||||
|
||||
bool change = false;
|
||||
|
||||
for( ; spell_id < SPDAT_RECORDS; ++spell_id) {
|
||||
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
|
||||
c->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
|
||||
return;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
|
||||
break;
|
||||
if (spells[spell_id].classes[t->GetPP().class_ - 1] > max_level) // maximum level
|
||||
break;
|
||||
if (spells[spell_id].classes[t->GetPP().class_ - 1] < min_level) // minimum level
|
||||
break;
|
||||
if (spells[spell_id].skill == 52)
|
||||
break;
|
||||
|
||||
uint16 spell_id_ = (uint16)spell_id;
|
||||
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
|
||||
c->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsDiscipline(spell_id_))
|
||||
break;
|
||||
|
||||
for (uint32 r = 0; r < MAX_PP_DISCIPLINES; ++r) {
|
||||
if (t->GetPP().disciplines.values[r] == spell_id_) {
|
||||
t->Message(13, "You already know this discipline.");
|
||||
break; // continue the 1st loop
|
||||
}
|
||||
else if (t->GetPP().disciplines.values[r] == 0) {
|
||||
t->GetPP().disciplines.values[r] = spell_id_;
|
||||
database.SaveCharacterDisc(t->CharacterID(), r, spell_id_);
|
||||
change = true;
|
||||
t->Message(0, "You have learned a new discipline!");
|
||||
++count; // success counter
|
||||
break; // continue the 1st loop
|
||||
} // if we get to this point, there's already a discipline in this slot, so we continue onto the next slot
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (change)
|
||||
t->SendDisciplineUpdate();
|
||||
|
||||
if (count > 0) {
|
||||
t->Message(0, "Successfully trained %u disciplines.", count);
|
||||
if (t != c)
|
||||
@ -11043,6 +11088,68 @@ void command_picklock(Client *c, const Seperator *sep)
|
||||
}
|
||||
}
|
||||
|
||||
void command_profanity(Client *c, const Seperator *sep)
|
||||
{
|
||||
std::string arg1(sep->arg[1]);
|
||||
|
||||
while (true) {
|
||||
if (arg1.compare("list") == 0) {
|
||||
// do nothing
|
||||
}
|
||||
else if (arg1.compare("clear") == 0) {
|
||||
EQEmu::ProfanityManager::DeleteProfanityList(&database);
|
||||
auto pack = new ServerPacket(ServerOP_RefreshCensorship);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
else if (arg1.compare("add") == 0) {
|
||||
if (!EQEmu::ProfanityManager::AddProfanity(&database, sep->arg[2]))
|
||||
c->Message(CC_Red, "Could not add '%s' to the profanity list.", sep->arg[2]);
|
||||
auto pack = new ServerPacket(ServerOP_RefreshCensorship);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
else if (arg1.compare("del") == 0) {
|
||||
if (!EQEmu::ProfanityManager::RemoveProfanity(&database, sep->arg[2]))
|
||||
c->Message(CC_Red, "Could not delete '%s' from the profanity list.", sep->arg[2]);
|
||||
auto pack = new ServerPacket(ServerOP_RefreshCensorship);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
else if (arg1.compare("reload") == 0) {
|
||||
if (!EQEmu::ProfanityManager::UpdateProfanityList(&database))
|
||||
c->Message(CC_Red, "Could not reload the profanity list.");
|
||||
auto pack = new ServerPacket(ServerOP_RefreshCensorship);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
|
||||
std::string popup;
|
||||
const auto &list = EQEmu::ProfanityManager::GetProfanityList();
|
||||
for (const auto &iter : list) {
|
||||
popup.append(iter);
|
||||
popup.append("<br>");
|
||||
}
|
||||
if (list.empty())
|
||||
popup.append("** Censorship Inactive **<br>");
|
||||
else
|
||||
popup.append("** End of List **<br>");
|
||||
|
||||
c->SendPopupToClient("Profanity List", popup.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
c->Message(0, "Usage: #profanity [list] - shows profanity list");
|
||||
c->Message(0, "Usage: #profanity [clear] - deletes all entries");
|
||||
c->Message(0, "Usage: #profanity [add] [<word>] - adds entry");
|
||||
c->Message(0, "Usage: #profanity [del] [<word>] - deletes entry");
|
||||
c->Message(0, "Usage: #profanity [reload] - reloads profanity list");
|
||||
}
|
||||
|
||||
void command_mysql(Client *c, const Seperator *sep)
|
||||
{
|
||||
if(!sep->arg[1][0] || !sep->arg[2][0]) {
|
||||
|
||||
@ -210,6 +210,7 @@ void command_permagender(Client *c, const Seperator *sep);
|
||||
void command_permarace(Client *c, const Seperator *sep);
|
||||
void command_petitioninfo(Client *c, const Seperator *sep);
|
||||
void command_picklock(Client *c, const Seperator *sep);
|
||||
void command_profanity(Client *c, const Seperator *sep);
|
||||
|
||||
#ifdef EQPROFILE
|
||||
void command_profiledump(Client *c, const Seperator *sep);
|
||||
|
||||
@ -4178,7 +4178,7 @@ void EntityList::ZoneWho(Client *c, Who_All_Struct *Who)
|
||||
WAPP2->RankMSGID = 12315;
|
||||
else if (ClientEntry->IsBuyer())
|
||||
WAPP2->RankMSGID = 6056;
|
||||
else if (ClientEntry->Admin() >= 10)
|
||||
else if (ClientEntry->Admin() >= 10 && ClientEntry->GetGM())
|
||||
WAPP2->RankMSGID = 12312;
|
||||
else
|
||||
WAPP2->RankMSGID = 0xFFFFFFFF;
|
||||
|
||||
@ -66,7 +66,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading)
|
||||
memset(equipment, 0, sizeof(equipment));
|
||||
|
||||
SetMercID(0);
|
||||
SetStance(MercStanceBalanced);
|
||||
SetStance(EQEmu::constants::stanceBalanced);
|
||||
rest_timer.Disable();
|
||||
|
||||
if (GetClass() == ROGUE)
|
||||
@ -1908,7 +1908,7 @@ bool Merc::AI_IdleCastCheck() {
|
||||
|
||||
bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
|
||||
|
||||
if((iSpellTypes&SpellTypes_Detrimental) != 0) {
|
||||
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
|
||||
//according to live, you can buff and heal through walls...
|
||||
//now with PCs, this only applies if you can TARGET the target, but
|
||||
// according to Rogean, Live NPCs will just cast through walls/floors, no problem..
|
||||
@ -3669,13 +3669,13 @@ MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) {
|
||||
|
||||
switch(caster->GetStance())
|
||||
{
|
||||
case MercStanceBurnAE:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
initialCastChance = 50;
|
||||
break;
|
||||
case MercStanceBalanced:
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
initialCastChance = 25;
|
||||
break;
|
||||
case MercStanceBurn:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
initialCastChance = 0;
|
||||
break;
|
||||
}
|
||||
@ -3717,11 +3717,11 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) {
|
||||
|
||||
switch(caster->GetStance())
|
||||
{
|
||||
case MercStanceBurnAE:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
numTargetsCheck = 1;
|
||||
break;
|
||||
case MercStanceBalanced:
|
||||
case MercStanceBurn:
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
numTargetsCheck = 2;
|
||||
break;
|
||||
}
|
||||
@ -3769,11 +3769,11 @@ MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) {
|
||||
|
||||
switch(caster->GetStance())
|
||||
{
|
||||
case MercStanceBurnAE:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
numTargetsCheck = 2;
|
||||
break;
|
||||
case MercStanceBalanced:
|
||||
case MercStanceBurn:
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
numTargetsCheck = 3;
|
||||
break;
|
||||
}
|
||||
@ -3820,11 +3820,11 @@ MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) {
|
||||
|
||||
switch(caster->GetStance())
|
||||
{
|
||||
case MercStanceBurnAE:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
numTargetsCheck = 1;
|
||||
break;
|
||||
case MercStanceBalanced:
|
||||
case MercStanceBurn:
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
numTargetsCheck = 2;
|
||||
break;
|
||||
}
|
||||
@ -5649,7 +5649,7 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats) {
|
||||
merc->SetSuspended(false);
|
||||
SetMerc(merc);
|
||||
merc->Unsuspend(setMaxStats);
|
||||
merc->SetStance(GetMercInfo().Stance);
|
||||
merc->SetStance((EQEmu::constants::StanceType)GetMercInfo().Stance);
|
||||
|
||||
Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName());
|
||||
|
||||
|
||||
18
zone/merc.h
18
zone/merc.h
@ -30,18 +30,6 @@ namespace EQEmu
|
||||
|
||||
const int MercAISpellRange = 100; // TODO: Write a method that calcs what the merc's spell range is based on spell, equipment, AA, whatever and replace this
|
||||
|
||||
enum MercStanceType {
|
||||
MercStancePassive = 1,
|
||||
MercStanceBalanced,
|
||||
MercStanceEfficient,
|
||||
MercStanceReactive,
|
||||
MercStanceAggressive,
|
||||
MercStanceAssist,
|
||||
MercStanceBurn,
|
||||
MercStanceEfficient2,
|
||||
MercStanceBurnAE
|
||||
};
|
||||
|
||||
struct MercSpell {
|
||||
uint16 spellid; // <= 0 = no spell
|
||||
uint32 type; // 0 = never, must be one (and only one) of the defined values
|
||||
@ -175,7 +163,7 @@ public:
|
||||
uint8 GetTierID() { return _TierID; }
|
||||
uint32 GetCostFormula() { return _CostFormula; }
|
||||
uint32 GetMercNameType() { return _NameType; }
|
||||
uint32 GetStance() { return _currentStance; }
|
||||
EQEmu::constants::StanceType GetStance() { return _currentStance; }
|
||||
int GetHatedCount() { return _hatedCount; }
|
||||
|
||||
inline const uint8 GetClientVersion() const { return _OwnerClientVersion; }
|
||||
@ -265,7 +253,7 @@ public:
|
||||
void SetMercNameType( uint8 nametype ) { _NameType = nametype; }
|
||||
void SetClientVersion(uint8 clientVersion) { _OwnerClientVersion = clientVersion; }
|
||||
void SetSuspended(bool suspended) { _suspended = suspended; }
|
||||
void SetStance( uint32 stance ) { _currentStance = stance; }
|
||||
void SetStance( EQEmu::constants::StanceType stance ) { _currentStance = stance; }
|
||||
void SetHatedCount( int count ) { _hatedCount = count; }
|
||||
|
||||
void Sit();
|
||||
@ -385,7 +373,7 @@ private:
|
||||
uint8 _CostFormula;
|
||||
uint8 _NameType;
|
||||
uint8 _OwnerClientVersion;
|
||||
uint32 _currentStance;
|
||||
EQEmu::constants::StanceType _currentStance;
|
||||
|
||||
EQEmu::InventoryProfile m_inv;
|
||||
int32 max_end;
|
||||
|
||||
@ -589,8 +589,8 @@ public:
|
||||
void MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct* spu);
|
||||
void MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu);
|
||||
void SentPositionPacket(float dx, float dy, float dz, float dh, int anim, bool send_to_self = false);
|
||||
virtual void StopMoving();
|
||||
virtual void StopMoving(float new_heading);
|
||||
void StopMoving();
|
||||
void StopMoving(float new_heading);
|
||||
void SetSpawned() { spawned = true; };
|
||||
bool Spawned() { return spawned; };
|
||||
virtual bool ShouldISpawnFor(Client *c) { return true; }
|
||||
|
||||
@ -379,7 +379,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
|
||||
}
|
||||
|
||||
bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
|
||||
if((iSpellTypes & SpellTypes_Detrimental) != 0) {
|
||||
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
|
||||
//according to live, you can buff and heal through walls...
|
||||
//now with PCs, this only applies if you can TARGET the target, but
|
||||
// according to Rogean, Live NPCs will just cast through walls/floors, no problem..
|
||||
@ -2813,7 +2813,7 @@ DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID)
|
||||
entry.max_hp = atoi(row[8]);
|
||||
|
||||
// some spell types don't make much since to be priority 0, so fix that
|
||||
if (!(entry.type & SpellTypes_Innate) && entry.priority == 0)
|
||||
if (!(entry.type & SPELL_TYPES_INNATE) && entry.priority == 0)
|
||||
entry.priority = 1;
|
||||
|
||||
if (row[9])
|
||||
|
||||
@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "../common/eq_stream_ident.h"
|
||||
#include "../common/patches/patches.h"
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/profanity_manager.h"
|
||||
#include "../common/misc_functions.h"
|
||||
#include "../common/string_util.h"
|
||||
#include "../common/platform.h"
|
||||
@ -350,6 +351,10 @@ int main(int argc, char** argv) {
|
||||
Log(Logs::General, Logs::Zone_Server, "Loading corpse timers");
|
||||
database.GetDecayTimes(npcCorpseDecayTimes);
|
||||
|
||||
Log(Logs::General, Logs::Zone_Server, "Loading profanity list");
|
||||
if (!EQEmu::ProfanityManager::LoadProfanityList(&database))
|
||||
Log(Logs::General, Logs::Error, "Loading profanity list FAILED!");
|
||||
|
||||
Log(Logs::General, Logs::Zone_Server, "Loading commands");
|
||||
int retval = command_init();
|
||||
if (retval<0)
|
||||
|
||||
@ -975,133 +975,181 @@ void QuestManager::permagender(int gender_id) {
|
||||
|
||||
uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
uint16 book_slot, count;
|
||||
uint16 spell_id;
|
||||
int book_slot = initiator->GetNextAvailableSpellBookSlot();
|
||||
int spell_id = 0;
|
||||
int count = 0;
|
||||
|
||||
uint32 char_id = initiator->CharacterID();
|
||||
bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals);
|
||||
bool SpellBucketRule = RuleB(Spells, EnableSpellBuckets);
|
||||
bool SpellGlobalCheckResult = 0;
|
||||
bool SpellBucketCheckResult = 0;
|
||||
bool SpellGlobalCheckResult = false;
|
||||
bool SpellBucketCheckResult = false;
|
||||
|
||||
for (
|
||||
spell_id = 0,
|
||||
book_slot = initiator->GetNextAvailableSpellBookSlot(),
|
||||
count = 0; // ;
|
||||
spell_id < SPDAT_RECORDS &&
|
||||
book_slot < EQEmu::spells::SPELLBOOK_SIZE; // ;
|
||||
spell_id++,
|
||||
book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot)
|
||||
)
|
||||
{
|
||||
if
|
||||
(
|
||||
spells[spell_id].classes[WARRIOR] != 0 && //check if spell exists
|
||||
spells[spell_id].classes[initiator->GetPP().class_-1] <= max_level && //maximum level
|
||||
spells[spell_id].classes[initiator->GetPP().class_-1] >= min_level && //minimum level
|
||||
spells[spell_id].skill != 52 &&
|
||||
spells[spell_id].effectid[EFFECT_COUNT - 1] != 10
|
||||
)
|
||||
{
|
||||
if (book_slot == -1) //no more book slots
|
||||
for ( ; spell_id < SPDAT_RECORDS && book_slot < EQEmu::spells::SPELLBOOK_SIZE; ++spell_id) {
|
||||
if (book_slot == -1) {
|
||||
initiator->Message(
|
||||
13,
|
||||
"Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.",
|
||||
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
|
||||
spell_id
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
|
||||
initiator->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
|
||||
return count;
|
||||
}
|
||||
if (book_slot < 0 || book_slot >= EQEmu::spells::SPELLBOOK_SIZE) {
|
||||
initiator->Message(13, "FATAL ERROR: Book slot out-of-range (slot: %i, min: 0, max: %i)", book_slot, EQEmu::spells::SPELLBOOK_SIZE);
|
||||
return count;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
|
||||
break;
|
||||
if(!IsDiscipline(spell_id) && !initiator->HasSpellScribed(spell_id)) { //isn't a discipline & we don't already have it scribed
|
||||
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] > max_level) // maximum level
|
||||
break;
|
||||
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] < min_level) // minimum level
|
||||
break;
|
||||
if (spells[spell_id].skill == 52)
|
||||
break;
|
||||
if (spells[spell_id].effectid[EFFECT_COUNT - 1] == 10)
|
||||
break;
|
||||
|
||||
uint16 spell_id_ = (uint16)spell_id;
|
||||
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
|
||||
initiator->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
|
||||
return count;
|
||||
}
|
||||
|
||||
if (!IsDiscipline(spell_id_) && !initiator->HasSpellScribed(spell_id)) { // isn't a discipline & we don't already have it scribed
|
||||
if (SpellGlobalRule) {
|
||||
// Bool to see if the character has the required QGlobal to scribe it if one exists in the Spell_Globals table
|
||||
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id, char_id);
|
||||
// bool to see if the character has the required QGlobal to scribe it if one exists in the Spell_Globals table
|
||||
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id_, char_id);
|
||||
if (SpellGlobalCheckResult) {
|
||||
initiator->ScribeSpell(spell_id, book_slot);
|
||||
count++;
|
||||
initiator->ScribeSpell(spell_id_, book_slot);
|
||||
++count;
|
||||
}
|
||||
} else if (SpellBucketRule) {
|
||||
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id, char_id);
|
||||
}
|
||||
else if (SpellBucketRule) {
|
||||
// bool to see if the character has the required bucket to train it if one exists in the spell_buckets table
|
||||
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id_, char_id);
|
||||
if (SpellBucketCheckResult) {
|
||||
initiator->ScribeSpell(spell_id, book_slot);
|
||||
count++;
|
||||
initiator->ScribeSpell(spell_id_, book_slot);
|
||||
++count;
|
||||
}
|
||||
} else {
|
||||
initiator->ScribeSpell(spell_id, book_slot);
|
||||
count++;
|
||||
}
|
||||
else {
|
||||
initiator->ScribeSpell(spell_id_, book_slot);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot);
|
||||
}
|
||||
return count; //how many spells were scribed successfully
|
||||
|
||||
return count; // how many spells were scribed successfully
|
||||
}
|
||||
|
||||
uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
uint16 count;
|
||||
uint16 spell_id;
|
||||
int spell_id = 0;
|
||||
int count = 0;
|
||||
|
||||
uint32 char_id = initiator->CharacterID();
|
||||
bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals);
|
||||
bool SpellBucketRule = RuleB(Spells, EnableSpellBuckets);
|
||||
bool SpellGlobalCheckResult = 0;
|
||||
bool SpellBucketCheckResult = 0;
|
||||
bool SpellGlobalCheckResult = false;
|
||||
bool SpellBucketCheckResult = false;
|
||||
|
||||
for(spell_id = 0, count = 0; spell_id < SPDAT_RECORDS; spell_id++)
|
||||
{
|
||||
if
|
||||
(
|
||||
spells[spell_id].classes[WARRIOR] != 0 && //check if spell exists
|
||||
spells[spell_id].classes[initiator->GetPP().class_-1] <= max_level && //maximum level
|
||||
spells[spell_id].classes[initiator->GetPP().class_-1] >= min_level && //minimum level
|
||||
spells[spell_id].skill != 52 &&
|
||||
( !RuleB(Spells, UseCHAScribeHack) || spells[spell_id].effectid[EFFECT_COUNT - 1] != 10 )
|
||||
)
|
||||
{
|
||||
if(IsDiscipline(spell_id)){
|
||||
//we may want to come up with a function like Client::GetNextAvailableSpellBookSlot() to help speed this up a little
|
||||
for(uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) {
|
||||
if(initiator->GetPP().disciplines.values[r] == spell_id) {
|
||||
initiator->Message(13, "You already know this discipline.");
|
||||
break; //continue the 1st loop
|
||||
}
|
||||
else if(initiator->GetPP().disciplines.values[r] == 0) {
|
||||
if (SpellGlobalRule) {
|
||||
// Bool to see if the character has the required QGlobal to train it if one exists in the Spell_Globals table
|
||||
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id, char_id);
|
||||
if (SpellGlobalCheckResult) {
|
||||
initiator->GetPP().disciplines.values[r] = spell_id;
|
||||
database.SaveCharacterDisc(char_id, r, spell_id);
|
||||
initiator->SendDisciplineUpdate();
|
||||
initiator->Message(0, "You have learned a new discipline!");
|
||||
count++; //success counter
|
||||
}
|
||||
break; //continue the 1st loop
|
||||
} else if (SpellBucketRule) {
|
||||
// Bool to see if the character has the required bucket to train it if one exists in the spell_buckets table
|
||||
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id, char_id);
|
||||
if (SpellBucketCheckResult) {
|
||||
initiator->GetPP().disciplines.values[r] = spell_id;
|
||||
database.SaveCharacterDisc(char_id, r, spell_id);
|
||||
initiator->SendDisciplineUpdate();
|
||||
initiator->Message(0, "You have learned a new discipline!");
|
||||
count++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
initiator->GetPP().disciplines.values[r] = spell_id;
|
||||
database.SaveCharacterDisc(char_id, r, spell_id);
|
||||
initiator->SendDisciplineUpdate();
|
||||
bool change = false;
|
||||
|
||||
for( ; spell_id < SPDAT_RECORDS; ++spell_id) {
|
||||
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
|
||||
initiator->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
|
||||
return count;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
|
||||
break;
|
||||
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] > max_level) // maximum level
|
||||
break;
|
||||
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] < min_level) // minimum level
|
||||
break;
|
||||
if (spells[spell_id].skill == 52)
|
||||
break;
|
||||
if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effectid[EFFECT_COUNT - 1] == 10)
|
||||
break;
|
||||
|
||||
uint16 spell_id_ = (uint16)spell_id;
|
||||
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
|
||||
initiator->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
|
||||
return count;
|
||||
}
|
||||
|
||||
if (!IsDiscipline(spell_id_))
|
||||
break;
|
||||
|
||||
for (uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) {
|
||||
if (initiator->GetPP().disciplines.values[r] == spell_id_) {
|
||||
initiator->Message(13, "You already know this discipline.");
|
||||
break; // continue the 1st loop
|
||||
}
|
||||
else if (initiator->GetPP().disciplines.values[r] == 0) {
|
||||
if (SpellGlobalRule) {
|
||||
// bool to see if the character has the required QGlobal to train it if one exists in the Spell_Globals table
|
||||
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id_, char_id);
|
||||
if (SpellGlobalCheckResult) {
|
||||
initiator->GetPP().disciplines.values[r] = spell_id_;
|
||||
database.SaveCharacterDisc(char_id, r, spell_id_);
|
||||
change = true;
|
||||
initiator->Message(0, "You have learned a new discipline!");
|
||||
count++; //success counter
|
||||
break; //continue the 1st loop
|
||||
++count; // success counter
|
||||
}
|
||||
} //if we get to this point, there's already a discipline in this slot, so we skip it
|
||||
break; // continue the 1st loop
|
||||
}
|
||||
else if (SpellBucketRule) {
|
||||
// bool to see if the character has the required bucket to train it if one exists in the spell_buckets table
|
||||
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id_, char_id);
|
||||
if (SpellBucketCheckResult) {
|
||||
initiator->GetPP().disciplines.values[r] = spell_id_;
|
||||
database.SaveCharacterDisc(char_id, r, spell_id_);
|
||||
change = true;
|
||||
initiator->Message(0, "You have learned a new discipline!");
|
||||
++count;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
initiator->GetPP().disciplines.values[r] = spell_id_;
|
||||
database.SaveCharacterDisc(char_id, r, spell_id_);
|
||||
change = true;;
|
||||
initiator->Message(0, "You have learned a new discipline!");
|
||||
++count; // success counter
|
||||
break; // continue the 1st loop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return count; //how many disciplines were learned successfully
|
||||
|
||||
if (change)
|
||||
initiator->SendDisciplineUpdate();
|
||||
|
||||
return count; // how many disciplines were learned successfully
|
||||
}
|
||||
|
||||
void QuestManager::unscribespells() {
|
||||
QuestManagerCurrentQuestVars();
|
||||
initiator->UnscribeSpellAll();
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::untraindiscs() {
|
||||
QuestManagerCurrentQuestVars();
|
||||
|
||||
@ -1075,9 +1075,6 @@ void Bot::WalkTo(float x, float y, float z)
|
||||
if (IsSitting())
|
||||
Stand();
|
||||
|
||||
if (ping_timer.Enabled())
|
||||
ping_timer.Disable();
|
||||
|
||||
Mob::WalkTo(x, y, z);
|
||||
}
|
||||
|
||||
@ -1086,9 +1083,6 @@ void Bot::RunTo(float x, float y, float z)
|
||||
if (IsSitting())
|
||||
Stand();
|
||||
|
||||
if (ping_timer.Enabled())
|
||||
ping_timer.Disable();
|
||||
|
||||
Mob::RunTo(x, y, z);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "../common/misc_functions.h"
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/servertalk.h"
|
||||
#include "../common/profanity_manager.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "corpse.h"
|
||||
@ -793,6 +794,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_RefreshCensorship: {
|
||||
if (!EQEmu::ProfanityManager::LoadProfanityList(&database))
|
||||
Log(Logs::General, Logs::Error, "Received request to refresh the profanity list..but, the action failed");
|
||||
break;
|
||||
}
|
||||
case ServerOP_ChangeWID: {
|
||||
if (pack->size != sizeof(ServerChangeWID_Struct)) {
|
||||
std::cout << "Wrong size on ServerChangeWID_Struct. Got: " << pack->size << ", Expected: " << sizeof(ServerChangeWID_Struct) << std::endl;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user