Merge pull request #1 from EQEmu/master

Update from Root Repository
This commit is contained in:
Brian Kinney 2019-04-12 13:51:51 -04:00 committed by GitHub
commit e8127f4b8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
52 changed files with 1658 additions and 656 deletions

View File

@ -1,6 +1,101 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 3/1/2019 ==
Noudess: Major faction conversion to use client data.
Pull request #802 New min/max personal faction per faction. Use of actual
client mods for race/class/deity.
This PR involves major changes to your database and your quests.
The clients recently exposed raw data included
- the min/max personal faction for each faction
- the actual faction id the client uses for each faction
- the actual mods that come into play when a PC cons an opponent that
determine your overall con to that faction.
The approach I took resulted in minimal change to the code base. I did
alter the code to enforce the new validated min/max from the client. This
min/max applies to personally earned faction. So if a faction has a min
of 0 and a max of 2000, that means your personally earned value can never
go below 0 or over 2000. The actual con, will, however often do so because
of class/race/deity modifications. I also changed the con ranges, per
Mackal's data that was proven to be accurate:
Ally = 1100+
Warmly = 750 to 1099
Kindly = 500 to 749
Amiable = 100 to 499
Indifferent = 0 to 99
Apprehensive = -1 to -100
Dubious = -101 to -500
Threateningly = -501 to -750
Ready to Attack = -751
The above means that dubious is a much smaller range now. For that reason
the scripts modify any custom faction base values to put them in the same
range, hopefully as the creators of the custom factions intended.
Also to be noted as characters that have a faction between -501 and -700
wont be dubious anymore, they will be threateningly. This is expected with
the new ranges, but might take players by suprise as the old ranges we used
were more liberal but were incorrect.
The database is changed extensively, but really only content. We're
translating faction_list to use the clients ids. As such every place a
faction_is is used, namely (see below) are being converted.
- faction_list
- faction_list_mod
- npc_faction (primary_faction field only)
- npc_faction_entries (faction_id field only)
- faction_values
Quests will also automatically be adjusted. This MUST be done after the
PR sql and before starting the server. This is automated by
eqemu_server.pl (or starting world)
Be assured, custom factions that you may have created, or obsolete or
duplicate factions in our original faction_list, that you may have used,
will be preserved. Anything that does not map directly is being moved to
the 5000 range in faction_list and any references are corrected to point
there.
A great example of this is Ebon Mask and Hall of the Ebon Mask. Many peqdb
style servers have both of these. Some have used one, some the other. We
map Ebon Mask to the clients Ebon mask and the Hall of the Ebon Mask gets
moved to the 5000 range, and all its references are preserved. However,
if you would like to make proper use of client mobs to Ebon mask, or other
factions that have duplicitous entries, I recommend you manually move to
using the correct one. In that way all of the new raw data mapped in from
the client into faction_list_mod will get used instead of what your db had
before these values were known.
In my experience converting 4 different server's data, there are only
about 20 factions moved into the 5000 range.
This PR has only 1 new, permanent table faction_base_data, which is taken
right from the client. The base field is left in case you want to mod your
server, but we are very sure that the client doesn't use a base. It uses
global mods to race or class for this as you'll see in the
new faction_list_mod.
The PR makes many backup tables, and two mapping tables that are used during
the conversion process to fix quests. This table was hand created by
analysis. This table serves no purpose after conversion except an audit
trail if we see any issues.
I will release a new PR that will clean up all these backups and temporary
tables in about a month.
== 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)

View File

@ -60,6 +60,29 @@ int main(int argc, char **argv) {
database.LoadLogSettings(LogSys.log_settings);
LogSys.StartFileLogs();
std::string arg_1;
if (argv[1]) {
arg_1 = argv[1];
}
if (arg_1 == "spells") {
ExportSpells(&database);
return 0;
}
if (arg_1 == "skills") {
ExportSkillCaps(&database);
return 0;
}
if (arg_1 == "basedata") {
ExportBaseData(&database);
return 0;
}
if (arg_1 == "dbstring") {
ExportDBStrings(&database);
return 0;
}
ExportSpells(&database);
ExportSkillCaps(&database);
ExportBaseData(&database);

View File

@ -73,7 +73,6 @@ SET(common_sources
unix.cpp
xml_parser.cpp
platform.cpp
event/event_loop.cpp
json/jsoncpp.cpp
net/console_server.cpp
net/console_server_connection.cpp
@ -209,8 +208,8 @@ SET(common_headers
version.h
xml_parser.h
zone_numbers.h
event/background_task.h
event/event_loop.h
event/task.h
event/timer.h
json/json.h
json/json-forwards.h
@ -267,10 +266,9 @@ SET(common_headers
)
SOURCE_GROUP(Event FILES
event/background_task.h
event/event_loop.cpp
event/event_loop.h
event/timer.h
event/task.h
)
SOURCE_GROUP(Json FILES

View File

@ -171,30 +171,27 @@ void Database::LoginIP(uint32 AccountID, const char* LoginIP) {
QueryDatabase(query);
}
int16 Database::CheckStatus(uint32 account_id) {
std::string query = StringFormat("SELECT `status`, UNIX_TIMESTAMP(`suspendeduntil`) as `suspendeduntil`, UNIX_TIMESTAMP() as `current`"
" FROM `account` WHERE `id` = %i", account_id);
int16 Database::CheckStatus(uint32 account_id)
{
std::string query = StringFormat(
"SELECT `status`, TIMESTAMPDIFF(SECOND, NOW(), `suspendeduntil`) FROM `account` WHERE `id` = %i",
account_id);
auto results = QueryDatabase(query);
if (!results.Success()) {
auto results = QueryDatabase(query);
if (!results.Success())
return 0;
}
if (results.RowCount() != 1)
return 0;
auto row = results.begin();
int16 status = atoi(row[0]);
int32 suspendeduntil = 0;
// MariaDB initalizes with NULL if unix_timestamp() is out of range
if (row[1] != nullptr) {
suspendeduntil = atoi(row[1]);
}
auto row = results.begin();
int16 status = atoi(row[0]);
int32 date_diff = 0;
int32 current = atoi(row[2]);
if (row[1] != nullptr)
date_diff = atoi(row[1]);
if(suspendeduntil > current)
if (date_diff > 0)
return -1;
return status;

View File

@ -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;
}

View File

@ -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 {
@ -287,6 +307,12 @@ namespace EQEmu
} // namespace bug
enum WaypointStatus : int {
RoamBoxPauseInProgress = -3,
QuestControlNoGrid = -2,
QuestControlGrid = -1
};
} /*EQEmu*/
#endif /*COMMON_EMU_CONSTANTS_H*/

View File

@ -6,6 +6,7 @@
#include <string>
#include "emu_versions.h"
#include "eq_packet.h"
#include "net/daybreak_connection.h"
typedef enum {
ESTABLISHED,
@ -56,6 +57,8 @@ public:
virtual const uint32 GetBytesSentPerSecond() const { return 0; }
virtual const uint32 GetBytesRecvPerSecond() const { return 0; }
virtual const EQEmu::versions::ClientVersion ClientVersion() const { return EQEmu::versions::ClientVersion::Unknown; }
virtual std::shared_ptr<EQ::Net::DaybreakConnection> GetRawConnection() = 0;
};
#endif /*EQSTREAMINTF_H_*/

View File

@ -110,6 +110,10 @@ void EQStreamProxy::RemoveData() {
m_stream->RemoveData();
}
std::shared_ptr<EQ::Net::DaybreakConnection> EQStreamProxy::GetRawConnection() {
return m_stream->GetRawConnection();
}
bool EQStreamProxy::CheckState(EQStreamState state) {
if(m_stream)
return(m_stream->CheckState(state));

View File

@ -37,6 +37,8 @@ public:
virtual const uint32 GetBytesSentPerSecond() const;
virtual const uint32 GetBytesRecvPerSecond() const;
virtual std::shared_ptr<EQ::Net::DaybreakConnection> GetRawConnection();
protected:
std::shared_ptr<EQStreamInterface> const m_stream; //we own this stream object.
const StructStrategy *const m_structs; //we do not own this object.

View File

@ -1,42 +0,0 @@
#pragma once
#include <functional>
#include "../any.h"
#include "event_loop.h"
namespace EQ {
class BackgroundTask
{
public:
typedef std::function<void(EQEmu::Any&)> BackgroundTaskFunction;
struct BackgroundTaskBaton
{
BackgroundTaskFunction fn;
BackgroundTaskFunction on_finish;
EQEmu::Any data;
};
BackgroundTask(BackgroundTaskFunction fn, BackgroundTaskFunction on_finish, EQEmu::Any data) {
uv_work_t *m_work = new uv_work_t;
memset(m_work, 0, sizeof(uv_work_t));
BackgroundTaskBaton *baton = new BackgroundTaskBaton();
baton->fn = fn;
baton->on_finish = on_finish;
baton->data = data;
m_work->data = baton;
uv_queue_work(EventLoop::Get().Handle(), m_work, [](uv_work_t* req) {
BackgroundTaskBaton *baton = (BackgroundTaskBaton*)req->data;
baton->fn(baton->data);
}, [](uv_work_t* req, int status) {
BackgroundTaskBaton *baton = (BackgroundTaskBaton*)req->data;
baton->on_finish(baton->data);
delete baton;
delete req;
});
}
~BackgroundTask() {
}
};
}

100
common/event/task.h Normal file
View File

@ -0,0 +1,100 @@
#pragma once
#include <functional>
#include <exception>
#include "event_loop.h"
#include "../any.h"
namespace EQ {
class Task
{
public:
typedef std::function<void(const EQEmu::Any&)> ResolveFn;
typedef std::function<void(const std::exception&)> RejectFn;
typedef std::function<void()> FinallyFn;
typedef std::function<void(ResolveFn, RejectFn)> TaskFn;
struct TaskBaton
{
TaskFn fn;
ResolveFn on_then;
RejectFn on_catch;
FinallyFn on_finally;
bool has_result;
EQEmu::Any result;
bool has_error;
std::exception error;
};
Task(TaskFn fn) {
m_fn = fn;
}
~Task() {
}
Task& Then(ResolveFn fn) {
m_then = fn;
return *this;
}
Task& Catch(RejectFn fn) {
m_catch = fn;
return *this;
}
Task& Finally(FinallyFn fn) {
m_finally = fn;
return *this;
}
void Run() {
uv_work_t *m_work = new uv_work_t;
memset(m_work, 0, sizeof(uv_work_t));
TaskBaton *baton = new TaskBaton();
baton->fn = m_fn;
baton->on_then = m_then;
baton->on_catch = m_catch;
baton->on_finally = m_finally;
baton->has_result = false;
baton->has_error = false;
m_work->data = baton;
uv_queue_work(EventLoop::Get().Handle(), m_work, [](uv_work_t* req) {
TaskBaton *baton = (TaskBaton*)req->data;
baton->fn([baton](const EQEmu::Any& result) {
baton->has_error = false;
baton->has_result = true;
baton->result = result;
}, [baton](const std::exception &err) {
baton->has_error = true;
baton->has_result = false;
baton->error = err;
});
}, [](uv_work_t* req, int status) {
TaskBaton *baton = (TaskBaton*)req->data;
if (baton->has_error && baton->on_catch) {
baton->on_catch(baton->error);
}
else if (baton->has_result && baton->on_then) {
baton->on_then(baton->result);
}
if (baton->on_finally) {
baton->on_finally();
}
delete baton;
delete req;
});
}
private:
TaskFn m_fn;
ResolveFn m_then;
RejectFn m_catch;
FinallyFn m_finally;
};
}

View File

@ -59,31 +59,31 @@ FACTION_VALUE CalculateFaction(FactionMods* fm, int32 tmpCharacter_value)
if (fm) {
character_value += fm->base + fm->class_mod + fm->race_mod + fm->deity_mod;
}
if (character_value >= 1101) {
if (character_value >= 1100) {
return FACTION_ALLY;
}
if (character_value >= 701 && character_value <= 1100) {
if (character_value >= 750 && character_value <= 1099) {
return FACTION_WARMLY;
}
if (character_value >= 401 && character_value <= 700) {
if (character_value >= 500 && character_value <= 749) {
return FACTION_KINDLY;
}
if (character_value >= 101 && character_value <= 400) {
if (character_value >= 100 && character_value <= 499) {
return FACTION_AMIABLE;
}
if (character_value >= 0 && character_value <= 100) {
if (character_value >= 0 && character_value <= 99) {
return FACTION_INDIFFERENT;
}
if (character_value >= -100 && character_value <= -1) {
return FACTION_APPREHENSIVE;
}
if (character_value >= -700 && character_value <= -101) {
if (character_value >= -500 && character_value <= -101) {
return FACTION_DUBIOUS;
}
if (character_value >= -999 && character_value <= -701) {
if (character_value >= -750 && character_value <= -501) {
return FACTION_THREATENLY;
}
if (character_value <= -1000) {
if (character_value <= -751) {
return FACTION_SCOWLS;
}
return FACTION_INDIFFERENT;

4
common/faction.h Normal file → Executable file
View File

@ -50,6 +50,8 @@ struct NPCFactionList {
struct FactionMods
{
int32 base;
int16 min; // The lowest your personal earned faction can go - before race/class/diety adjustments.
int16 max; // The highest your personal earned faction can go - before race/class/diety adjustments.
int32 class_mod;
int32 race_mod;
int32 deity_mod;
@ -59,6 +61,8 @@ struct Faction {
int32 id;
std::map<std::string, int16> mods;
int16 base;
int16 min; // The lowest your personal earned faction can go - before race/class/diety adjustments.
int16 max; // The highest your personal earned faction can go - before race/class/diety adjustments.
char name[50];
};

View File

@ -1,5 +1,6 @@
#include "daybreak_connection.h"
#include "../event/event_loop.h"
#include "../event/task.h"
#include "../eqemu_logsys.h"
#include "../data_verification.h"
#include "crc32.h"
@ -278,7 +279,6 @@ EQ::Net::DaybreakConnection::DaybreakConnection(DaybreakConnectionManager *owner
m_hold_time = Clock::now();
m_buffered_packets_length = 0;
m_rolling_ping = 500;
m_resend_delay = (m_rolling_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms;
m_combined.reset(new char[512]);
m_combined[0] = 0;
m_combined[1] = OP_Combined;
@ -301,7 +301,6 @@ EQ::Net::DaybreakConnection::DaybreakConnection(DaybreakConnectionManager *owner
m_hold_time = Clock::now();
m_buffered_packets_length = 0;
m_rolling_ping = 500;
m_resend_delay = (m_rolling_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms;
m_combined.reset(new char[512]);
m_combined[0] = 0;
m_combined[1] = OP_Combined;
@ -356,9 +355,6 @@ void EQ::Net::DaybreakConnection::ResetStats()
void EQ::Net::DaybreakConnection::Process()
{
try {
m_resend_delay = (size_t)(m_rolling_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms;
m_resend_delay = EQEmu::Clamp(m_resend_delay, m_owner->m_options.resend_delay_min, m_owner->m_options.resend_delay_max);
auto now = Clock::now();
auto time_since_hold = (size_t)std::chrono::duration_cast<std::chrono::milliseconds>(now - m_hold_time).count();
if (time_since_hold >= m_owner->m_options.hold_length_ms) {
@ -1020,17 +1016,19 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
if (m_status == DbProtocolStatus::StatusDisconnected) {
return;
}
auto resends = 0;
auto now = Clock::now();
auto s = &m_streams[stream];
for (auto &entry : s->sent_packets) {
auto time_since_last_send = std::chrono::duration_cast<std::chrono::milliseconds>(now - entry.second.last_sent);
if (entry.second.times_resent == 0) {
if ((size_t)time_since_last_send.count() > m_resend_delay) {
if ((size_t)time_since_last_send.count() > entry.second.resend_delay) {
InternalBufferedSend(entry.second.packet);
entry.second.last_sent = now;
entry.second.times_resent++;
m_rolling_ping += 100;
entry.second.resend_delay = EQEmu::Clamp(entry.second.resend_delay * 2, m_owner->m_options.resend_delay_min, m_owner->m_options.resend_delay_max);
resends++;
}
}
else {
@ -1039,12 +1037,13 @@ void EQ::Net::DaybreakConnection::ProcessResend(int stream)
Close();
return;
}
if ((size_t)time_since_last_send.count() > m_resend_delay) {
if ((size_t)time_since_last_send.count() > entry.second.resend_delay) {
InternalBufferedSend(entry.second.packet);
entry.second.last_sent = now;
entry.second.times_resent++;
m_rolling_ping += 100;
entry.second.resend_delay = EQEmu::Clamp(entry.second.resend_delay * 2, m_owner->m_options.resend_delay_min, m_owner->m_options.resend_delay_max);
resends++;
}
}
}
@ -1195,20 +1194,20 @@ void EQ::Net::DaybreakConnection::InternalSend(Packet &p)
for (int i = 0; i < 2; ++i) {
switch (m_encode_passes[i]) {
case EncodeCompression:
if(out.GetInt8(0) == 0)
Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
else
Compress(out, 1, out.Length() - 1);
break;
case EncodeXOR:
if (out.GetInt8(0) == 0)
Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
else
Encode(out, 1, out.Length() - 1);
break;
default:
break;
case EncodeCompression:
if (out.GetInt8(0) == 0)
Compress(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
else
Compress(out, 1, out.Length() - 1);
break;
case EncodeXOR:
if (out.GetInt8(0) == 0)
Encode(out, DaybreakHeader::size(), out.Length() - DaybreakHeader::size());
else
Encode(out, 1, out.Length() - 1);
break;
default:
break;
}
}
@ -1294,6 +1293,10 @@ void EQ::Net::DaybreakConnection::InternalQueuePacket(Packet &p, int stream_id,
sent.last_sent = Clock::now();
sent.first_sent = Clock::now();
sent.times_resent = 0;
sent.resend_delay = EQEmu::Clamp(
static_cast<size_t>((m_rolling_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms),
m_owner->m_options.resend_delay_min,
m_owner->m_options.resend_delay_max);
stream->sent_packets.insert(std::make_pair(stream->sequence_out, sent));
stream->sequence_out++;
@ -1322,6 +1325,10 @@ void EQ::Net::DaybreakConnection::InternalQueuePacket(Packet &p, int stream_id,
sent.last_sent = Clock::now();
sent.first_sent = Clock::now();
sent.times_resent = 0;
sent.resend_delay = EQEmu::Clamp(
static_cast<size_t>((m_rolling_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms),
m_owner->m_options.resend_delay_min,
m_owner->m_options.resend_delay_max);
stream->sent_packets.insert(std::make_pair(stream->sequence_out, sent));
stream->sequence_out++;
@ -1342,6 +1349,10 @@ void EQ::Net::DaybreakConnection::InternalQueuePacket(Packet &p, int stream_id,
sent.last_sent = Clock::now();
sent.first_sent = Clock::now();
sent.times_resent = 0;
sent.resend_delay = EQEmu::Clamp(
static_cast<size_t>((m_rolling_ping * m_owner->m_options.resend_delay_factor) + m_owner->m_options.resend_delay_ms),
m_owner->m_options.resend_delay_min,
m_owner->m_options.resend_delay_max);
stream->sent_packets.insert(std::make_pair(stream->sequence_out, sent));
stream->sequence_out++;

View File

@ -109,10 +109,16 @@ namespace EQ
void QueuePacket(Packet &p);
void QueuePacket(Packet &p, int stream);
void QueuePacket(Packet &p, int stream, bool reliable);
const DaybreakConnectionStats& GetStats() const { return m_stats; }
DaybreakConnectionStats &GetStats() { return m_stats; }
void ResetStats();
size_t GetRollingPing() const { return m_rolling_ping; }
DbProtocolStatus GetStatus() { return m_status; }
DbProtocolStatus GetStatus() const { return m_status; }
const DaybreakEncodeType* GetEncodePasses() const { return m_encode_passes; }
const DaybreakConnectionManager* GetManager() const { return m_owner; }
DaybreakConnectionManager* GetManager() { return m_owner; }
private:
DaybreakConnectionManager *m_owner;
std::string m_endpoint;
@ -132,7 +138,6 @@ namespace EQ
std::unique_ptr<char[]> m_combined;
DaybreakConnectionStats m_stats;
Timestamp m_last_session_stats;
size_t m_resend_delay;
size_t m_rolling_ping;
Timestamp m_close_time;
@ -142,6 +147,7 @@ namespace EQ
Timestamp last_sent;
Timestamp first_sent;
size_t times_resent;
size_t resend_delay;
};
struct DaybreakStream
@ -205,10 +211,10 @@ namespace EQ
DaybreakConnectionManagerOptions() {
max_connection_count = 0;
keepalive_delay_ms = 9000;
resend_delay_ms = 150;
resend_delay_factor = 1.5;
resend_delay_ms = 30;
resend_delay_factor = 1.25;
resend_delay_min = 150;
resend_delay_max = 1000;
resend_delay_max = 5000;
connect_delay_ms = 500;
stale_connection_ms = 90000;
connect_stale_ms = 5000;
@ -261,6 +267,8 @@ namespace EQ
void OnNewConnection(std::function<void(std::shared_ptr<DaybreakConnection>)> func) { m_on_new_connection = func; }
void OnConnectionStateChange(std::function<void(std::shared_ptr<DaybreakConnection>, DbProtocolStatus, DbProtocolStatus)> func) { m_on_connection_state_change = func; }
void OnPacketRecv(std::function<void(std::shared_ptr<DaybreakConnection>, const Packet &)> func) { m_on_packet_recv = func; }
DaybreakConnectionManagerOptions& GetOptions() { return m_options; }
private:
void Attach(uv_loop_t *loop);
void Detach();

View File

@ -84,6 +84,10 @@ namespace EQ
m_opcode_manager = opm;
}
virtual std::shared_ptr<EQ::Net::DaybreakConnection> GetRawConnection() {
return m_connection;
}
const std::string& RemoteEndpoint() const { return m_connection->RemoteEndpoint(); }
const DaybreakConnectionStats& GetStats() const { return m_connection->GetStats(); }
void ResetStats() { m_connection->ResetStats(); }
@ -96,4 +100,4 @@ namespace EQ
friend class EQStreamManager;
};
}
}
}

View File

@ -704,6 +704,14 @@ RULE_CATEGORY(Console)
RULE_INT(Console, SessionTimeOut, 600000) // Amount of time in ms for the console session to time out
RULE_CATEGORY_END()
RULE_CATEGORY(Network)
RULE_INT(Network, ResendDelayBaseMS, 100)
RULE_REAL(Network, ResendDelayFactor, 1.5)
RULE_INT(Network, ResendDelayMinMS, 100)
RULE_INT(Network, ResendDelayMaxMS, 5000)
RULE_INT(Network, ResendsPerCycle, 1000)
RULE_CATEGORY_END()
RULE_CATEGORY(QueryServ)
RULE_BOOL(QueryServ, PlayerLogChat, false) // Logs Player Chat
RULE_BOOL(QueryServ, PlayerLogTrades, false) // Logs Player Trades

View File

@ -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

View File

@ -30,9 +30,11 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9136
#define CURRENT_BINARY_DATABASE_VERSION 9138
#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

View File

@ -469,6 +469,11 @@ Clientlist::Clientlist(int ChatPort) {
EQ::Net::EQStreamManagerOptions chat_opts(ChatPort, false, false);
chat_opts.opcode_size = 1;
chat_opts.daybreak_options.stale_connection_ms = 300000;
chat_opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS);
chat_opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor);
chat_opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS);
chat_opts.daybreak_options.resend_delay_max = RuleI(Network, ResendDelayMaxMS);
chatsf = new EQ::Net::EQStreamManager(chat_opts);
ChatOpMgr = new RegularOpcodeManager;

View File

@ -637,7 +637,7 @@ sub do_self_update_check_routine {
if ($OS eq "Linux") {
system("chmod 755 eqemu_server.pl");
}
system("perl eqemu_server.pl start_from_world");
exec("perl eqemu_server.pl ran_from_world");
}
}
print "[Install] Done\n";
@ -787,6 +787,7 @@ sub show_menu_prompt {
elsif ($input eq "conversions") {
print "\n>>> Conversions Menu\n\n";
print " [quest_heading_convert] Converts old heading format in quest scripts to new (live format)\n";
print " [quest_faction_convert] Converts to new faction values imported from client\n";
print " \n> main - go back to main menu\n";
print "Enter a command #> ";
$last_menu = trim($input);
@ -905,6 +906,10 @@ sub show_menu_prompt {
quest_heading_convert();
$dc = 1;
}
elsif ($input eq "quest_faction_convert") {
quest_faction_convert();
$dc = 1;
}
elsif ($input eq "source_peq_db") {
fetch_peq_db_full();
$dc = 1;
@ -2209,6 +2214,10 @@ sub run_database_check {
if ($bots_db_management == 1 && $val == 9000) {
modify_db_for_bots();
}
if ($val == 9138) {
fix_quest_factions();
}
}
$db_run_stage = 2;
}
@ -2493,3 +2502,127 @@ sub quest_heading_convert {
print "Total matches: " . $total_matches . "\n";
}
sub quest_faction_convert {
if(trim(get_mysql_result("SELECT value FROM variables WHERE varname = 'new_faction_conversion'")) eq "true") {
print "Conversion script has already ran... doing this again would skew proper faction values in function calls...\n";
exit;
}
%matches = (
0 => [ "GetCharacterFactionLevel", 0],
1 => [ "GetModCharacterFactionLevel", 0],
2 => [ "SetFactionLevel2", 1],
3 => [ "GetFactionLevel", 5 ],
4 => [ "CheckNPCFactionAlly", 0 ],
5 => [ ":Faction", 0 ],
);
$total_matches = 0;
use Scalar::Util qw(looks_like_number);
my @files;
my $start_dir = "quests/.";
find(
sub {push @files, $File::Find::name unless -d;},
$start_dir
);
for my $file (@files) {
#::: Skip non script files
if ($file !~ /lua|pl/i) {
next;
}
if ($file =~ /lua|pl/i) {
$print_buffer = "";
$changes_made = 0;
#::: Open and read line by line
open(FILE, $file);
while (<FILE>) {
chomp;
$line = $_;
#::: Loop through matches
foreach my $key (sort (keys %matches)) {
$argument_position = $matches{$key}[1];
$match = $matches{$key}[0];
if ($line =~ /$match\(/i || $line =~ /$match \(/i) {
$line_temp = $line;
$line_temp =~ s/^.*$match\(//gi;
$line_temp =~ s/^.*$match \(//gi;
$line_temp =~ s/"//g;
$line_temp =~ s/\);.*//;
@line_data = split(",", $line_temp);
$faction_value = $line_data[$argument_position];
$faction_value_clean = trim($faction_value);
if (looks_like_number($faction_value_clean)) {
$new_faction = get_mysql_result("select clientid from client_server_faction_map where serverid = $faction_value_clean");
chomp $new_faction;
if ($new_faction == 0) {
$new_faction = get_mysql_result("select new_faction from custom_faction_mappings where old_faction = $faction_value_clean");
chomp $new_faction;
}
if ($new_faction > 0) {
print "BEFORE: " . $line . "\n";
$line =~ s/$faction_value_clean/$new_faction/g;
print "AFTER: " . $line . "\n";
$changes_made = 1;
}
else {
print "Unknown Faction: '$match' FACTION VALUE: '" . $faction_value_clean . "'\n";
}
}
$total_matches++;
}
}
$print_buffer .= $line . "\n";
}
close(FILE);
#::: Write changes
if ($changes_made == 1) {
open(NEW_FILE, '>', $file);
print NEW_FILE $print_buffer;
close NEW_FILE;
}
}
}
#::: Mark conversion as ran
print get_mysql_result("INSERT INTO `variables` (varname, value, information, ts) VALUES ('new_faction_conversion', 'true', 'Script ran against quests folder to convert new faction values', NOW())");
print "Total matches: " . $total_matches . "\n";
}
sub fix_quest_factions {
# Backup the quests
mkdir('backups');
my @files;
my $start_dir = "quests/";
find(
sub { push @files, $File::Find::name unless -d; },
$start_dir
);
for my $file (@files) {
$destination_file = $file;
my $date = strftime "%m-%d-%Y", localtime;
$destination_file =~ s/quests/quests-$date/;
print "Backing up :: " . $destination_file . "\n";
# unlink($destination_file);
copy_file($file, 'backups/' . $destination_file);
}
# Fix the factions
quest_faction_convert();
}

View File

@ -390,6 +390,8 @@
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|
9137|2018_12_12_client_faction_tables.sql|SHOW TABLES LIKE 'faction_base_data'|empty|
9138|2018_12_12_convert_to_client_functions.sql|SELECT `id` FROM `faction_list` WHERE `id` > 4999|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -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

View File

@ -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';

View File

@ -0,0 +1,53 @@
/* Fix any items with faction adjustments */
SET SQL_MODE='ALLOW_INVALID_DATES'; /* Some dbs have bad dates which prevents the index creation */
CREATE INDEX itemfac1 ON items (factionmod1);
CREATE INDEX itemfac2 ON items (factionmod2);
CREATE INDEX itemfac3 ON items (factionmod3);
CREATE INDEX itemfac4 ON items (factionmod4);
UPDATE items i
INNER JOIN custom_faction_mappings m ON i.factionmod1 = m.old_faction
SET i.factionmod1 = m.new_faction
WHERE i.factionmod1 > 0;
UPDATE items i
INNER JOIN custom_faction_mappings m ON i.factionmod2 = m.old_faction
SET i.factionmod2 = m.new_faction
WHERE i.factionmod2 > 0;
UPDATE items i
INNER JOIN custom_faction_mappings m ON i.factionmod3 = m.old_faction
SET i.factionmod3 = m.new_faction
WHERE i.factionmod3 > 0;
UPDATE items i
INNER JOIN custom_faction_mappings m ON i.factionmod4 = m.old_faction
SET i.factionmod4 = m.new_faction
WHERE i.factionmod4 > 0;
UPDATE items i
INNER JOIN client_server_faction_map m ON i.factionmod1 = m.serverid
SET i.factionmod1 = m.clientid
WHERE i.factionmod1 > 0;
UPDATE items i
INNER JOIN client_server_faction_map m ON i.factionmod2 = m.serverid
SET i.factionmod2 = m.clientid
WHERE i.factionmod2 > 0;
UPDATE items i
INNER JOIN client_server_faction_map m ON i.factionmod3 = m.serverid
SET i.factionmod3 = m.clientid
WHERE i.factionmod3 > 0;
UPDATE items i
INNER JOIN client_server_faction_map m ON i.factionmod4 = m.serverid
SET i.factionmod4 = m.clientid
WHERE i.factionmod4 > 0;
DROP INDEX itemfac1 ON items;
DROP INDEX itemfac2 ON items;
DROP INDEX itemfac3 ON items;
DROP INDEX itemfac4 ON items;

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,173 @@
/*
This SQL update utilizes the new raw faction data from the client
First we create a temporary table - which we will use to map any
custom factions in the eqemu db, that are either:
- eqemu utility factions
- obsoleted factions with no new mapping to the client
This is done so that we can keep these factions while server owners either
stay with them, or migrate. They are moved to the 5000+ range, to not conflict
with client faction_ids.
*/
/* Create the temp table and start mappings at 5000 */
CREATE TABLE custom_faction_mappings (old_faction int, new_faction int, primary key (old_faction)) engine=INNODB;
select "Moving custom factions to safe range, well above known client values" ``;
select @startcustom:=5000;
/* Insert the custom/obsolete factions into the temp mapping table */
insert into custom_faction_mappings (select id, @startcustom := @startcustom +1 from faction_list where id not in (select serverid from client_server_faction_map) and id < 5000);
CREATE TABLE IF NOT EXISTS faction_list_mod_prefix AS SELECT * from faction_list_mod;
/* Now we update all the tables for these custom factions */
update faction_list_mod set faction_id = (select new_faction from custom_faction_mappings where old_faction = faction_id) where faction_id < 5000 and faction_id in (select old_faction from custom_faction_mappings);
CREATE TABLE IF NOT EXISTS faction_list_prefix AS SELECT * from faction_list;
DROP TABLE faction_list;
CREATE TABLE `faction_list` (
`id` int(11) NOT NULL,
`name` varchar(50) NOT NULL DEFAULT '',
`base` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=486 DEFAULT CHARSET=utf8 as select id, name, base from faction_list_prefix;
update faction_list set id =
(select new_faction from custom_faction_mappings where old_faction = id) where id < 5000 and id in (select old_faction from custom_faction_mappings);
/* At this point all faction_mods for unmapped factions will be ids 5000+ */
/* So we can delete all the old ones still under 5000 - making room for the */
/* new faction ids */
delete from faction_list_mod where faction_id < 5000;
delete from faction_list where id < 5000;
/* Make an entry for each faction */
/* No base on client factions */
insert into faction_list (id, name, base) (select id, name, 0 from client_faction_names);
/* Now restore any base for factions for which the client has no mods (social factions) */
DROP TABLE IF EXISTS oldbases;
CREATE TABLE `oldbases` (
`id` int(11) DEFAULT 0,
`name` varchar(50) NOT NULL DEFAULT '',
`base` smallint(6) NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 as
(select m.clientid as id, p.name, p.base from faction_list_prefix p
join client_server_faction_map m on m.serverid = p.id where p.base <> 0
&& m.clientid in (select id from faction_list where id not in (SELECT faction_id from client_faction_associations group by faction_id)));
update faction_list f
INNER JOIN oldbases o on o.id = f.id
set f.base = o.base;
/* Adjust for the big change in the dubious range */
update faction_list set base = base + 200 where base between -900 and -501;
DROP TABLE IF EXISTS oldbases;
/* Create mods based on the client_faction_associations */
/* No code changes required */
insert into faction_list_mod
(select null, faction_id, `mod`, concat("r", other_faction_id-50)
from client_faction_associations a
join client_faction_names n on n.id = a.other_faction_id
where other_faction_id between 51 and 180);
insert into faction_list_mod
(select null, faction_id, `mod`, concat("c", other_faction_id)
from client_faction_associations a
join client_faction_names n on n.id = a.other_faction_id
where other_faction_id between 1 and 50);
insert into faction_list_mod
(select null, faction_id, `mod`, concat("d", other_faction_id)
from client_faction_associations a
join client_faction_names n on n.id = a.other_faction_id
where other_faction_id between 201 and 216);
/* And now we need to fix all the other faction tables to point to the new factions. */
CREATE TABLE IF NOT EXISTS npc_faction_prefix AS SELECT * from npc_faction;
update npc_faction set primaryfaction = (select new_faction from custom_faction_mappings where old_faction = primaryfaction)
where primaryfaction in (select old_faction from custom_faction_mappings);
update npc_faction set primaryfaction = (select clientid from client_server_faction_map where serverid = primaryfaction)
where primaryfaction in (select serverid from client_server_faction_map);
update npc_faction_entries set faction_id = (select new_faction from custom_faction_mappings where old_faction = faction_id)
where faction_id in (select old_faction from custom_faction_mappings);
CREATE TABLE IF NOT EXISTS npc_faction_entries_prefix AS SELECT * from npc_faction_entries;
/* Move existing factions out of wat - the following replace would create key */
/* duplicates along the way, but none when complete. */
update npc_faction_entries set faction_id = faction_id + 20000
where faction_id in (select serverid from client_server_faction_map);
update npc_faction_entries set faction_id = (select clientid from client_server_faction_map where faction_id > 20000 && serverid = (faction_id-20000))
where faction_id > 20000 && (faction_id-20000) in (select serverid from client_server_faction_map);
/* Removes any duplicates from the use of factions that are obsoleted */
/* These are entries that have no new mapping whatsoever */
delete from npc_faction_entries where faction_id > 20000;
/*
Update the faction_values now.
*/
CREATE TABLE IF NOT EXISTS faction_values_prefix AS SELECT * from faction_values;
delete from faction_values
where faction_id not in (select old_faction from custom_faction_mappings) and faction_id not in (select serverid from client_server_faction_map);
/* Custom faction mappings dont have to worry about range collision */
select "Updating faction_values for custom factions" ``;
update faction_values set faction_id = (select new_faction from custom_faction_mappings where old_faction = faction_id)
where faction_id in (select old_faction from custom_faction_mappings);
/*
There are so many of these, Im going to update in place to save time.
To do this we must remove the unique keys, as these will be violated until
the update is complete
*/
select "Updating core faction_values to use new faction ids...." ``;
alter table faction_values drop primary key;
update faction_values v
join client_server_faction_map m on v.faction_id = m.serverid
set faction_id = m.clientid;
ALTER TABLE `faction_values` ADD PRIMARY KEY `lookup` (`char_id`,`faction_id`);
/*
* The following to be deleted in a future update, once everyone is
* happy with the conversion
DROP TABLE IF EXISTS custom_faction_mappings;
DROP TABLE IF EXISTS faction_list_mod_prefix;
DROP TABLE IF EXISTS faction_list_prefix;
DROP TABLE IF EXISTS npc_faction_prefix;
DROP TABLE IF EXISTS npc_faction_entries_prefix;
DROP TABLE IF EXISTS faction_values_prefix;
*/

View File

@ -498,6 +498,11 @@ int main(int argc, char** argv) {
});
EQ::Net::EQStreamManagerOptions opts(9000, false, false);
opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS);
opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor);
opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS);
opts.daybreak_options.resend_delay_max = RuleI(Network, ResendDelayMaxMS);
EQ::Net::EQStreamManager eqsm(opts);
//register all the patches we have avaliable with the stream identifier.

View File

@ -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;

View File

@ -7,7 +7,7 @@
Aura::Aura(NPCType *type_data, Mob *owner, AuraRecord &record)
: NPC(type_data, 0, owner->GetPosition(), GravityBehavior::Flying), spell_id(record.spell_id), distance(record.distance),
remove_timer(record.duration), movement_timer(100), process_timer(100), aura_id(-1)
remove_timer(record.duration), movement_timer(100), process_timer(1000), aura_id(-1)
{
GiveNPCTypeData(type_data); // we will delete this later on
m_owner = owner->GetID();

View File

@ -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();
@ -2766,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..)
@ -2893,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;
}
@ -2901,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())
@ -2959,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);
}
@ -3004,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;
@ -8213,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;
}
@ -8268,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) {
@ -8819,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;
}
@ -9096,4 +9096,6 @@ std::string Bot::CreateSayLink(Client* c, const char* message, const char* name)
return saylink;
}
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQEmu::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
#endif

View File

@ -54,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
@ -228,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);
@ -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

View File

@ -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>");

View File

@ -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];
}

View File

@ -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;

View File

@ -3470,7 +3470,7 @@ float Client::CalcPriceMod(Mob* other, bool reverse)
float chaformula = 0;
if (other)
{
int factionlvl = GetFactionLevel(CharacterID(), other->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), other->CastToNPC()->GetPrimaryFaction(), other);
int factionlvl = GetFactionLevel(CharacterID(), other->CastToNPC()->GetNPCTypeID(), GetFactionRace(), GetClass(), GetDeity(), other->CastToNPC()->GetPrimaryFaction(), other);
if (factionlvl >= FACTION_APPREHENSIVE) // Apprehensive or worse.
{
if (GetCHA() > 103)
@ -7705,7 +7705,7 @@ FACTION_VALUE Client::GetReverseFactionCon(Mob* iOther) {
if (iOther->GetPrimaryFaction() == 0)
return FACTION_INDIFFERENT;
return GetFactionLevel(CharacterID(), 0, GetRace(), GetClass(), GetDeity(), iOther->GetPrimaryFaction(), iOther);
return GetFactionLevel(CharacterID(), 0, GetFactionRace(), GetClass(), GetDeity(), iOther->GetPrimaryFaction(), iOther);
}
//o--------------------------------------------------------------
@ -7792,7 +7792,7 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui
// Find out starting faction for this faction
// It needs to be used to adj max and min personal
// The range is still the same, 1200-3000(4200), but adjusted for base
database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(),
database.GetFactionData(&fm, GetClass(), GetFactionRace(), GetDeity(),
faction_id[i]);
if (quest)
@ -7842,7 +7842,7 @@ void Client::SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class
// Find out starting faction for this faction
// It needs to be used to adj max and min personal
// The range is still the same, 1200-3000(4200), but adjusted for base
database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(),
database.GetFactionData(&fm, GetClass(), GetFactionRace(), GetDeity(),
faction_id);
// Adjust the amount you can go up or down so the resulting range
@ -7943,9 +7943,15 @@ return;
int32 Client::GetModCharacterFactionLevel(int32 faction_id) {
int32 Modded = GetCharacterFactionLevel(faction_id);
FactionMods fm;
if (database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), faction_id))
if (database.GetFactionData(&fm, GetClass(), GetFactionRace(), GetDeity(), faction_id))
{
Modded += fm.base + fm.class_mod + fm.race_mod + fm.deity_mod;
//Tack on any bonuses from Alliance type spell effects
Modded += GetFactionBonus(faction_id);
Modded += GetItemFactionBonus(faction_id);
}
return Modded;
}
@ -7958,7 +7964,7 @@ void Client::MerchantRejectMessage(Mob *merchant, int primaryfaction)
// If a faction is involved, get the data.
if (primaryfaction > 0) {
if (database.GetFactionData(&fmod, GetClass(), GetRace(), GetDeity(), primaryfaction)) {
if (database.GetFactionData(&fmod, GetClass(), GetFactionRace(), GetDeity(), primaryfaction)) {
tmpFactionValue = GetCharacterFactionLevel(primaryfaction);
lowestvalue = std::min(std::min(tmpFactionValue, fmod.deity_mod),
std::min(fmod.class_mod, fmod.race_mod));

View File

@ -4623,7 +4623,7 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app)
con->playerid = GetID();
con->targetid = conin->targetid;
if (tmob->IsNPC())
con->faction = GetFactionLevel(character_id, tmob->GetNPCTypeID(), race, class_, deity, (tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction() : 0, tmob); // Dec. 20, 2001; TODO: Send the players proper deity
con->faction = GetFactionLevel(character_id, tmob->GetNPCTypeID(), GetFactionRace(), class_, deity, (tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction() : 0, tmob); // Dec. 20, 2001; TODO: Send the players proper deity
else
con->faction = 1;
con->level = GetLevelCon(tmob->GetLevel());
@ -9394,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());

View File

@ -52,7 +52,7 @@
#include "../common/rulesys.h"
#include "../common/serverinfo.h"
#include "../common/string_util.h"
#include "../say_link.h"
#include "../common/say_link.h"
#include "../common/eqemu_logsys.h"
#include "../common/profanity_manager.h"
@ -278,6 +278,7 @@ int command_init(void)
command_add("mystats", "- Show details about you or your pet", 50, command_mystats) ||
command_add("name", "[newname] - Rename your player target", 150, command_name) ||
command_add("netstats", "- Gets the network stats for a stream.", 200, command_netstats) ||
command_add("network", "- Admin commands for the udp network interface.", 250, command_network) ||
command_add("npccast", "[targetname/entityid] [spellid] - Causes NPC target to cast spellid on targetname/entityid", 80, command_npccast) ||
command_add("npcedit", "[column] [value] - Mega NPC editing command", 100, command_npcedit) ||
command_add("npcemote", "[message] - Make your NPC target emote a message.", 150, command_npcemote) ||
@ -6407,34 +6408,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;
}
@ -6443,42 +6439,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());
@ -8734,28 +8759,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;
}
@ -8769,35 +8790,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)
@ -12117,6 +12161,169 @@ void command_who(Client *c, const Seperator *sep)
c->Message(5, message.c_str());
}
void command_network(Client *c, const Seperator *sep)
{
if (!strcasecmp(sep->arg[1], "getopt"))
{
auto eqsi = c->Connection();
auto dbc = eqsi->GetRawConnection();
auto manager = dbc->GetManager();
auto &opts = manager->GetOptions();
if (!strcasecmp(sep->arg[2], "all"))
{
c->Message(0, "max_packet_size: %llu", opts.max_packet_size);
c->Message(0, "max_connection_count: %llu", opts.max_connection_count);
c->Message(0, "keepalive_delay_ms: %llu", opts.keepalive_delay_ms);
c->Message(0, "resend_delay_factor: %.2f", opts.resend_delay_factor);
c->Message(0, "resend_delay_ms: %llu", opts.resend_delay_ms);
c->Message(0, "resend_delay_min: %llu", opts.resend_delay_min);
c->Message(0, "resend_delay_max: %llu", opts.resend_delay_max);
c->Message(0, "connect_delay_ms: %llu", opts.connect_delay_ms);
c->Message(0, "connect_stale_ms: %llu", opts.connect_stale_ms);
c->Message(0, "stale_connection_ms: %llu", opts.stale_connection_ms);
c->Message(0, "crc_length: %llu", opts.crc_length);
c->Message(0, "hold_size: %llu", opts.hold_size);
c->Message(0, "hold_length_ms: %llu", opts.hold_length_ms);
c->Message(0, "simulated_in_packet_loss: %llu", opts.simulated_in_packet_loss);
c->Message(0, "simulated_out_packet_loss: %llu", opts.simulated_out_packet_loss);
c->Message(0, "tic_rate_hertz: %.2f", opts.tic_rate_hertz);
c->Message(0, "resend_timeout: %llu", opts.resend_timeout);
c->Message(0, "connection_close_time: %llu", opts.connection_close_time);
c->Message(0, "encode_passes[0]: %llu", opts.encode_passes[0]);
c->Message(0, "encode_passes[1]: %llu", opts.encode_passes[1]);
c->Message(0, "port: %llu", opts.port);
}
else {
c->Message(0, "Unknown get option: %s", sep->arg[2]);
c->Message(0, "Available options:");
//Todo the rest of these when im less lazy.
//c->Message(0, "max_packet_size");
//c->Message(0, "max_connection_count");
//c->Message(0, "keepalive_delay_ms");
//c->Message(0, "resend_delay_factor");
//c->Message(0, "resend_delay_ms");
//c->Message(0, "resend_delay_min");
//c->Message(0, "resend_delay_max");
//c->Message(0, "connect_delay_ms");
//c->Message(0, "connect_stale_ms");
//c->Message(0, "stale_connection_ms");
//c->Message(0, "crc_length");
//c->Message(0, "hold_size");
//c->Message(0, "hold_length_ms");
//c->Message(0, "simulated_in_packet_loss");
//c->Message(0, "simulated_out_packet_loss");
//c->Message(0, "tic_rate_hertz");
//c->Message(0, "resend_timeout");
//c->Message(0, "connection_close_time");
//c->Message(0, "encode_passes[0]");
//c->Message(0, "encode_passes[1]");
//c->Message(0, "port");
c->Message(0, "all");
}
}
else if (!strcasecmp(sep->arg[1], "setopt"))
{
auto eqsi = c->Connection();
auto dbc = eqsi->GetRawConnection();
auto manager = dbc->GetManager();
auto &opts = manager->GetOptions();
if (!strcasecmp(sep->arg[3], ""))
{
c->Message(0, "Missing value for set");
return;
}
std::string value = sep->arg[3];
if (!strcasecmp(sep->arg[2], "max_connection_count"))
{
opts.max_connection_count = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "keepalive_delay_ms"))
{
opts.keepalive_delay_ms = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "resend_delay_factor"))
{
opts.resend_delay_factor = std::stod(value);
}
else if (!strcasecmp(sep->arg[2], "resend_delay_ms"))
{
opts.resend_delay_ms = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "resend_delay_min"))
{
opts.resend_delay_min = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "resend_delay_max"))
{
opts.resend_delay_max = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "connect_delay_ms"))
{
opts.connect_delay_ms = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "connect_stale_ms"))
{
opts.connect_stale_ms = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "stale_connection_ms"))
{
opts.stale_connection_ms = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "hold_size"))
{
opts.hold_size = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "hold_length_ms"))
{
opts.hold_length_ms = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "simulated_in_packet_loss"))
{
opts.simulated_in_packet_loss = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "simulated_out_packet_loss"))
{
opts.simulated_out_packet_loss = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "resend_timeout"))
{
opts.resend_timeout = std::stoull(value);
}
else if (!strcasecmp(sep->arg[2], "connection_close_time"))
{
opts.connection_close_time = std::stoull(value);
}
else {
c->Message(0, "Unknown set option: %s", sep->arg[2]);
c->Message(0, "Available options:");
c->Message(0, "max_connection_count");
c->Message(0, "keepalive_delay_ms");
c->Message(0, "resend_delay_factor");
c->Message(0, "resend_delay_ms");
c->Message(0, "resend_delay_min");
c->Message(0, "resend_delay_max");
c->Message(0, "connect_delay_ms");
c->Message(0, "connect_stale_ms");
c->Message(0, "stale_connection_ms");
c->Message(0, "hold_size");
c->Message(0, "hold_length_ms");
c->Message(0, "simulated_in_packet_loss");
c->Message(0, "simulated_out_packet_loss");
c->Message(0, "resend_timeout");
c->Message(0, "connection_close_time");
}
}
else {
c->Message(0, "Unknown command: %s", sep->arg[1]);
c->Message(0, "Network commands avail:");
c->Message(0, "getopt optname - Retrieve the current option value set.");
c->Message(0, "setopt optname - Set the current option allowed.");
}
}
// All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block.
#ifdef BOTS
#include "bot_command.h"

View File

@ -179,6 +179,7 @@ void command_mysqltest(Client *c, const Seperator *sep);
void command_mystats(Client *c, const Seperator *sep);
void command_name(Client *c, const Seperator *sep);
void command_netstats(Client *c, const Seperator *sep);
void command_network(Client *c, const Seperator *sep);
void command_npccast(Client *c, const Seperator *sep);
void command_npcedit(Client *c, const Seperator *sep);
void command_npcemote(Client *c, const Seperator *sep);

View File

@ -1046,7 +1046,7 @@ void PerlembParser::ExportMobVariables(bool isPlayerQuest, bool isGlobalPlayerQu
if (mob && npcmob && mob->IsClient()) {
Client* client = mob->CastToClient();
fac = client->GetFactionLevel(client->CharacterID(), npcmob->GetID(), client->GetRace(),
fac = client->GetFactionLevel(client->CharacterID(), npcmob->GetID(), client->GetFactionRace(),
client->GetClass(), client->GetDeity(), npcmob->GetPrimaryFaction(), npcmob);
}
}

View File

@ -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());

View File

@ -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;

View File

@ -1553,7 +1553,7 @@ void Mob::ShowStats(Client* client)
}
}
else {
client->Message(0, " Level: %i AC: %i Class: %i Size: %1.1f Haste: %i", GetLevel(), GetAC(), GetClass(), GetSize(), GetHaste());
client->Message(0, " Level: %i AC: %i Class: %i Size: %1.1f Haste: %i", GetLevel(), ACSum(), GetClass(), GetSize(), GetHaste());
client->Message(0, " HP: %i Max HP: %i",GetHP(), GetMaxHP());
client->Message(0, " Mana: %i Max Mana: %i", GetMana(), GetMaxMana());
client->Message(0, " Total ATK: %i Worn/Spell ATK (Cap %i): %i", GetATK(), RuleI(Character, ItemATKCap), GetATKBonus());
@ -2121,6 +2121,16 @@ bool Mob::IsPlayerRace(uint16 in_race) {
return false;
}
uint16 Mob::GetFactionRace() {
uint16 current_race = GetRace();
if (IsPlayerRace(current_race) || current_race == TREE ||
current_race == MINOR_ILL_OBJ) {
return current_race;
}
else {
return (GetBaseRace());
}
}
uint8 Mob::GetDefaultGender(uint16 in_race, uint8 in_gender) {
if (Mob::IsPlayerRace(in_race) || in_race == 15 || in_race == 50 || in_race == 57 || in_race == 70 || in_race == 98 || in_race == 118 || in_race == 562) {

View File

@ -442,6 +442,7 @@ public:
virtual void SetMaxHP() { current_hp = max_hp; }
virtual inline uint16 GetBaseRace() const { return base_race; }
virtual inline uint8 GetBaseGender() const { return base_gender; }
virtual uint16 GetFactionRace();
virtual inline uint16 GetDeity() const { return deity; }
virtual EQEmu::deity::DeityTypeBit GetDeityBit() { return EQEmu::deity::ConvertDeityTypeToDeityTypeBit((EQEmu::deity::DeityType)deity); }
inline uint16 GetRace() const { return race; }
@ -1090,7 +1091,7 @@ public:
inline glm::vec4 GetCurrentWayPoint() const { return m_CurrentWayPoint; }
inline float GetCWPP() const { return(static_cast<float>(cur_wp_pause)); }
inline int GetCWP() const { return(cur_wp); }
void SetCurrentWP(uint16 waypoint) { cur_wp = waypoint; }
void SetCurrentWP(int waypoint) { cur_wp = waypoint; }
virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther) { return FACTION_INDIFFERENT; }
virtual const bool IsUnderwaterOnly() const { return false; }

View File

@ -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..
@ -1574,7 +1574,18 @@ void NPC::AI_DoMovement() {
*/
if (roambox_distance > 0) {
if (!IsMoving()) {
// Check if we're already moving to a WP
// If so, if we're not moving we have arrived and need to set delay
if (GetCWP() == EQEmu::WaypointStatus::RoamBoxPauseInProgress && !IsMoving()) {
// We have arrived
time_until_can_move = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay);
SetCurrentWP(0);
return;
}
// Set a new destination
if (!IsMoving() && time_until_can_move < Timer::GetCurrentTime()) {
auto move_x = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
auto move_y = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
@ -1642,12 +1653,10 @@ void NPC::AI_DoMovement() {
roambox_min_y,
roambox_max_y,
roambox_destination_y);
}
Log(Logs::Detail, Logs::NPCRoamBox, "Dest Z is (%f)", roambox_destination_z);
NavigateTo(roambox_destination_x, roambox_destination_y, roambox_destination_z);
if (m_Position.x == roambox_destination_x && m_Position.y == roambox_destination_y) {
time_until_can_move = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay);
SetCurrentWP(EQEmu::WaypointStatus::RoamBoxPauseInProgress);
NavigateTo(roambox_destination_x, roambox_destination_y, roambox_destination_z);
}
return;
@ -1660,7 +1669,7 @@ void NPC::AI_DoMovement() {
int32 gridno = CastToNPC()->GetGrid();
if (gridno > 0 || cur_wp == -2) {
if (gridno > 0 || cur_wp == EQEmu::WaypointStatus::QuestControlNoGrid) {
if (pause_timer_complete == true) { // time to pause at wp is over
AI_SetupNextWaypoint();
} // endif (pause_timer_complete==true)
@ -1692,7 +1701,7 @@ void NPC::AI_DoMovement() {
// as that is where roamer is unset and we don't want
// the next trip through to move again based on grid stuff.
doMove = false;
if (cur_wp == -2) {
if (cur_wp == EQEmu::WaypointStatus::QuestControlNoGrid) {
AI_SetupNextWaypoint();
}
@ -1790,9 +1799,9 @@ void NPC::AI_SetupNextWaypoint() {
else {
pause_timer_complete = false;
Log(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp);
//if we were under quest control (with no grid), we are done now..
if (cur_wp == -2) {
if (cur_wp == EQEmu::WaypointStatus::QuestControlNoGrid) {
Log(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode.");
roamer = false;
cur_wp = 0;
@ -2813,7 +2822,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])

View File

@ -468,6 +468,10 @@ int main(int argc, char** argv) {
Log(Logs::General, Logs::Zone_Server, "Starting EQ Network server on port %d", Config->ZonePort);
EQ::Net::EQStreamManagerOptions opts(Config->ZonePort, false, true);
opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS);
opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor);
opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS);
opts.daybreak_options.resend_delay_max = RuleI(Network, ResendDelayMaxMS);
eqsm.reset(new EQ::Net::EQStreamManager(opts));
eqsf_open = true;

View File

@ -1,5 +1,5 @@
#include "../common/global_define.h"
#include "../common/event/background_task.h"
#include "../common/event/task.h"
#include "client.h"
#include "zone.h"
@ -40,53 +40,56 @@ void CullPoints(std::vector<FindPerson_Point> &points) {
}
void Client::SendPathPacket(const std::vector<FindPerson_Point> &points) {
EQ::BackgroundTask task([](EQEmu::Any &data) {
auto &points = EQEmu::any_cast<std::vector<FindPerson_Point>&>(data);
EQEmu::Any data(points);
EQ::Task([=](EQ::Task::ResolveFn resolve, EQ::Task::RejectFn reject) {
auto points = EQEmu::any_cast<std::vector<FindPerson_Point>>(data);
CullPoints(points);
}, [this](EQEmu::Any &data) {
auto &points = EQEmu::any_cast<std::vector<FindPerson_Point>&>(data);
resolve(points);
})
.Then([this](const EQEmu::Any &result) {
auto points = EQEmu::any_cast<std::vector<FindPerson_Point>>(result);
if (points.size() < 2) {
if (Admin() > 10) {
Message(MT_System, "Too few points");
}
EQApplicationPacket outapp(OP_FindPersonReply, 0);
QueuePacket(&outapp);
return;
}
if (points.size() > 36) {
if (Admin() > 10) {
Message(MT_System, "Too many points %u", points.size());
}
EQApplicationPacket outapp(OP_FindPersonReply, 0);
QueuePacket(&outapp);
return;
}
if (Admin() > 10) {
Message(MT_System, "Total points %u", points.size());
}
int len = sizeof(FindPersonResult_Struct) + (points.size() + 1) * sizeof(FindPerson_Point);
auto outapp = new EQApplicationPacket(OP_FindPersonReply, len);
FindPersonResult_Struct* fpr = (FindPersonResult_Struct*)outapp->pBuffer;
std::vector<FindPerson_Point>::iterator cur, end;
cur = points.begin();
end = points.end();
unsigned int r;
for (r = 0; cur != end; ++cur, r++) {
fpr->path[r] = *cur;
}
//put the last element into the destination field
--cur;
fpr->path[r] = *cur;
fpr->dest = *cur;
FastQueuePacket(&outapp);
}, points);
})
.Run();
}

View File

@ -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();
@ -2851,7 +2899,7 @@ uint8 QuestManager::FactionValue()
FACTION_VALUE oldfac;
uint8 newfac = 0;
if(initiator && owner->IsNPC()) {
oldfac = initiator->GetFactionLevel(initiator->GetID(), owner->GetID(), initiator->GetRace(), initiator->GetClass(), initiator->GetDeity(), owner->GetPrimaryFaction(), owner);
oldfac = initiator->GetFactionLevel(initiator->GetID(), owner->GetID(), initiator->GetFactionRace(), initiator->GetClass(), initiator->GetDeity(), owner->GetPrimaryFaction(), owner);
// now, reorder the faction to have it make sense (higher values are better)
switch (oldfac) {

View File

@ -100,6 +100,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
#include "bot.h"
#endif
#include "mob_movement_manager.h"
extern Zone* zone;
@ -4205,7 +4206,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
{
if(GetSpecialAbility(UNMEZABLE)) {
Log(Logs::Detail, Logs::Spells, "We are immune to Mez spells.");
caster->Message_StringID(MT_Shout, CANNOT_MEZ);
caster->Message_StringID(MT_SpellFailure, CANNOT_MEZ);
int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) {
AddToHateList(caster, aggro);
@ -4223,7 +4224,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
(!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))))
{
Log(Logs::Detail, Logs::Spells, "Our level (%d) is higher than the limit of this Mez spell (%d)", GetLevel(), spells[spell_id].max[effect_index]);
caster->Message_StringID(MT_Shout, CANNOT_MEZ_WITH_SPELL);
caster->Message_StringID(MT_SpellFailure, CANNOT_MEZ_WITH_SPELL);
AddToHateList(caster, 1,0,true,false,false,spell_id);
return true;
}
@ -4233,7 +4234,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(GetSpecialAbility(UNSLOWABLE) && IsEffectInSpell(spell_id, SE_AttackSpeed))
{
Log(Logs::Detail, Logs::Spells, "We are immune to Slow spells.");
caster->Message_StringID(MT_Shout, IMMUNE_ATKSPEED);
caster->Message_StringID(CC_Red, IMMUNE_ATKSPEED);
int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) {
AddToHateList(caster, aggro);
@ -4249,7 +4250,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
effect_index = GetSpellEffectIndex(spell_id, SE_Fear);
if(GetSpecialAbility(UNFEARABLE)) {
Log(Logs::Detail, Logs::Spells, "We are immune to Fear spells.");
caster->Message_StringID(MT_Shout, IMMUNE_FEAR);
caster->Message_StringID(CC_Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up
int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) {
AddToHateList(caster, aggro);
@ -4260,7 +4261,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
} else if(IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false))
{
Log(Logs::Detail, Logs::Spells, "Clients cannot fear eachother!");
caster->Message_StringID(MT_Shout, IMMUNE_FEAR);
caster->Message_StringID(CC_Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up
return true;
}
else if(GetLevel() > spells[spell_id].max[effect_index] && spells[spell_id].max[effect_index] != 0)
@ -4279,7 +4280,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
{
Message(13, "Your are immune to fear.");
Log(Logs::Detail, Logs::Spells, "Clients has WarCry effect, immune to fear!");
caster->Message_StringID(MT_Shout, IMMUNE_FEAR);
caster->Message_StringID(CC_Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up
return true;
}
}
@ -4289,7 +4290,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(GetSpecialAbility(UNCHARMABLE))
{
Log(Logs::Detail, Logs::Spells, "We are immune to Charm spells.");
caster->Message_StringID(MT_Shout, CANNOT_CHARM);
caster->Message_StringID(CC_Red, CANNOT_CHARM); // need to verify message type, not in MQ2Cast for easy look up
int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) {
AddToHateList(caster, aggro);
@ -4302,7 +4303,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(this == caster)
{
Log(Logs::Detail, Logs::Spells, "You are immune to your own charms.");
caster->Message(MT_Shout, "You cannot charm yourself.");
caster->Message(CC_Red, "You cannot charm yourself."); // need to look up message?
return true;
}
@ -4315,8 +4316,8 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(GetLevel() > spells[spell_id].max[effect_index] && spells[spell_id].max[effect_index] != 0)
{
Log(Logs::Detail, Logs::Spells, "Our level (%d) is higher than the limit of this Charm spell (%d)", GetLevel(), spells[spell_id].max[effect_index]);
caster->Message_StringID(MT_Shout, CANNOT_CHARM_YET);
AddToHateList(caster, 1,0,true,false,false,spell_id);
caster->Message_StringID(CC_Red, CANNOT_CHARM_YET); // need to verify message type, not in MQ2Cast for easy look up<Paste>
AddToHateList(caster, 1,0,true,false,false,spell_id);
return true;
}
}
@ -4330,7 +4331,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
{
if(GetSpecialAbility(UNSNAREABLE)) {
Log(Logs::Detail, Logs::Spells, "We are immune to Snare spells.");
caster->Message_StringID(MT_Shout, IMMUNE_MOVEMENT);
caster->Message_StringID(CC_Red, IMMUNE_MOVEMENT);
int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) {
AddToHateList(caster, aggro);
@ -4346,7 +4347,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(this == caster)
{
Log(Logs::Detail, Logs::Spells, "You cannot lifetap yourself.");
caster->Message_StringID(MT_Shout, CANT_DRAIN_SELF);
caster->Message_StringID(MT_SpellFailure, CANT_DRAIN_SELF);
return true;
}
}
@ -4356,7 +4357,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(this == caster)
{
Log(Logs::Detail, Logs::Spells, "You cannot sacrifice yourself.");
caster->Message_StringID(MT_Shout, CANNOT_SAC_SELF);
caster->Message_StringID(MT_SpellFailure, CANNOT_SAC_SELF);
return true;
}
}
@ -4818,7 +4819,22 @@ void Mob::Spin() {
safe_delete(outapp);
}
else {
GMMove(GetX(), GetY(), GetZ(), GetHeading()+5);
float x,y,z,h;
x=GetX();
y=GetY();
z=GetZ();
h=GetHeading()+5;
if (IsCorpse() || (IsClient() && !IsAIControlled())) {
m_Position.x = x;
m_Position.y = y;
m_Position.z = z;
mMovementManager->SendCommandToClients(this, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny);
}
else {
Teleport(glm::vec4(x, y, z, h));
}
}
}

View File

@ -108,7 +108,7 @@ void NPC::ResumeWandering()
{ // we were paused by a quest
AI_walking_timer->Disable();
SetGrid(0 - GetGrid());
if (cur_wp == -1)
if (cur_wp == EQEmu::WaypointStatus::QuestControlGrid)
{ // got here by a MoveTo()
cur_wp = save_wp;
UpdateWaypoint(cur_wp); // have him head to last destination from here
@ -164,28 +164,32 @@ void NPC::PauseWandering(int pausetime)
return;
}
void NPC::MoveTo(const glm::vec4& position, bool saveguardspot)
{ // makes mob walk to specified location
if (IsNPC() && GetGrid() != 0)
{ // he is on a grid
if (GetGrid() < 0)
{ // currently stopped by a quest command
SetGrid(0 - GetGrid()); // get him moving again
Log(Logs::Detail, Logs::AI, "MoveTo during quest wandering. Canceling quest wandering and going back to grid %d when MoveTo is done.", GetGrid());
void NPC::MoveTo(const glm::vec4 &position, bool saveguardspot)
{ // makes mob walk to specified location
if (IsNPC() && GetGrid() != 0) { // he is on a grid
if (GetGrid() < 0) { // currently stopped by a quest command
SetGrid(0 - GetGrid()); // get him moving again
Log(Logs::Detail,
Logs::AI,
"MoveTo during quest wandering. Canceling quest wandering and going back to grid %d when MoveTo is done.",
GetGrid());
}
AI_walking_timer->Disable(); // disable timer in case he is paused at a wp
if (cur_wp >= 0)
{ // we've not already done a MoveTo()
save_wp = cur_wp; // save the current waypoint
cur_wp = -1; // flag this move as quest controlled
AI_walking_timer->Disable(); // disable timer in case he is paused at a wp
if (cur_wp >= 0) { // we've not already done a MoveTo()
save_wp = cur_wp; // save the current waypoint
cur_wp = EQEmu::WaypointStatus::QuestControlGrid;
}
Log(Logs::Detail, Logs::AI, "MoveTo %s, pausing regular grid wandering. Grid %d, save_wp %d", to_string(static_cast<glm::vec3>(position)).c_str(), -GetGrid(), save_wp);
Log(Logs::Detail,
Logs::AI,
"MoveTo %s, pausing regular grid wandering. Grid %d, save_wp %d",
to_string(static_cast<glm::vec3>(position)).c_str(),
-GetGrid(),
save_wp);
}
else
{ // not on a grid
roamer = true;
else { // not on a grid
roamer = true;
save_wp = 0;
cur_wp = -2; // flag as quest controlled w/no grid
cur_wp = EQEmu::WaypointStatus::QuestControlNoGrid;
Log(Logs::Detail, Logs::AI, "MoveTo %s without a grid.", to_string(static_cast<glm::vec3>(position)).c_str());
}
@ -194,23 +198,27 @@ void NPC::MoveTo(const glm::vec4& position, bool saveguardspot)
m_CurrentWayPoint = position;
m_CurrentWayPoint.z = GetFixedZ(dest);
if (saveguardspot)
{
if (saveguardspot) {
m_GuardPoint = m_CurrentWayPoint;
if (m_GuardPoint.w == 0)
m_GuardPoint.w = 0.0001; //hack to make IsGuarding simpler
if (m_GuardPoint.w == 0) {
m_GuardPoint.w = 0.0001;
} //hack to make IsGuarding simpler
if (m_GuardPoint.w == -1)
m_GuardPoint.w = this->CalculateHeadingToTarget(position.x, position.y);
Log(Logs::Detail, Logs::AI, "Setting guard position to %s", to_string(static_cast<glm::vec3>(m_GuardPoint)).c_str());
Log(Logs::Detail,
Logs::AI,
"Setting guard position to %s",
to_string(static_cast<glm::vec3>(m_GuardPoint)).c_str());
}
cur_wp_pause = 0;
cur_wp_pause = 0;
time_until_can_move = 0;
if (AI_walking_timer->Enabled())
if (AI_walking_timer->Enabled()) {
AI_walking_timer->Start(100);
}
}
void NPC::UpdateWaypoint(int wp_index)

32
zone/zonedb.cpp Normal file → Executable file
View File

@ -3914,6 +3914,8 @@ bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race
}
fm->base = faction_array[faction_id]->base;
fm->min = faction_array[faction_id]->min; // The lowest your personal earned faction can go - before race/class/diety adjustments.
fm->max = faction_array[faction_id]->max; // The highest your personal earned faction can go - before race/class/diety adjustments.
if(class_mod > 0) {
char str[32];
@ -4060,14 +4062,32 @@ bool ZoneDatabase::LoadFactionData()
faction_array[index] = new Faction;
strn0cpy(faction_array[index]->name, row[1], 50);
faction_array[index]->base = atoi(row[2]);
faction_array[index]->min = MIN_PERSONAL_FACTION;
faction_array[index]->max = MAX_PERSONAL_FACTION;
query = StringFormat("SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id = %u", index);
auto modResults = QueryDatabase(query);
if (!modResults.Success())
continue;
// Load in the mimimum and maximum faction that can be earned for this faction
query = StringFormat("SELECT `min` , `max` FROM `faction_base_data` WHERE client_faction_id = %u", index);
auto baseResults = QueryDatabase(query);
if (!baseResults.Success() || baseResults.RowCount() == 0) {
Log(Logs::General, Logs::General, "Faction %d has no base data", (int)index);
}
else {
for (auto modRow = baseResults.begin(); modRow != baseResults.end(); ++modRow) {
faction_array[index]->min = atoi(modRow[0]);
faction_array[index]->max = atoi(modRow[1]);
Log(Logs::General, Logs::None, "Min(%d), Max(%d) for faction (%u)",faction_array[index]->min, faction_array[index]->max, index);
}
}
for (auto modRow = modResults.begin(); modRow != modResults.end(); ++modRow)
faction_array[index]->mods[modRow[1]] = atoi(modRow[0]);
// Load in modifiers to the faction based on characters race, class and diety.
query = StringFormat("SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id = %u", index);
auto modResults = QueryDatabase(query);
if (!modResults.Success())
continue;
for (auto modRow = modResults.begin(); modRow != modResults.end(); ++modRow) {
faction_array[index]->mods[modRow[1]] = atoi(modRow[0]);
}
}
return true;