mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 21:01:29 +00:00
commit
e8127f4b8a
@ -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)
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -118,3 +118,37 @@ EQEmu::bug::CategoryID EQEmu::bug::CategoryNameToCategoryID(const char* category
|
||||
|
||||
return catOther;
|
||||
}
|
||||
|
||||
const char *EQEmu::constants::GetStanceName(StanceType stance_type) {
|
||||
switch (stance_type) {
|
||||
case stanceUnknown:
|
||||
return "Unknown";
|
||||
case stancePassive:
|
||||
return "Passive";
|
||||
case stanceBalanced:
|
||||
return "Balanced";
|
||||
case stanceEfficient:
|
||||
return "Efficient";
|
||||
case stanceReactive:
|
||||
return "Reactive";
|
||||
case stanceAggressive:
|
||||
return "Aggressive";
|
||||
case stanceAssist:
|
||||
return "Assist";
|
||||
case stanceBurn:
|
||||
return "Burn";
|
||||
case stanceEfficient2:
|
||||
return "Efficient2";
|
||||
case stanceBurnAE:
|
||||
return "BurnAE";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
}
|
||||
|
||||
int EQEmu::constants::ConvertStanceTypeToIndex(StanceType stance_type) {
|
||||
if (stance_type >= EQEmu::constants::stancePassive && stance_type <= EQEmu::constants::stanceBurnAE)
|
||||
return (stance_type - EQEmu::constants::stancePassive);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -203,6 +203,26 @@ namespace EQEmu
|
||||
const size_t SAY_LINK_CLOSER_SIZE = 1;
|
||||
const size_t SAY_LINK_MAXIMUM_SIZE = (SAY_LINK_OPENER_SIZE + SAY_LINK_BODY_SIZE + SAY_LINK_TEXT_SIZE + SAY_LINK_CLOSER_SIZE);
|
||||
|
||||
enum StanceType : int {
|
||||
stanceUnknown = 0,
|
||||
stancePassive,
|
||||
stanceBalanced,
|
||||
stanceEfficient,
|
||||
stanceReactive,
|
||||
stanceAggressive,
|
||||
stanceAssist,
|
||||
stanceBurn,
|
||||
stanceEfficient2,
|
||||
stanceBurnAE
|
||||
};
|
||||
|
||||
const char *GetStanceName(StanceType stance_type);
|
||||
int ConvertStanceTypeToIndex(StanceType stance_type);
|
||||
|
||||
const int STANCE_TYPE_FIRST = stancePassive;
|
||||
const int STANCE_TYPE_LAST = stanceBurnAE;
|
||||
const int STANCE_TYPE_COUNT = stanceBurnAE;
|
||||
|
||||
} /*constants*/
|
||||
|
||||
namespace profile {
|
||||
@ -287,6 +307,12 @@ namespace EQEmu
|
||||
|
||||
} // namespace bug
|
||||
|
||||
enum WaypointStatus : int {
|
||||
RoamBoxPauseInProgress = -3,
|
||||
QuestControlNoGrid = -2,
|
||||
QuestControlGrid = -1
|
||||
};
|
||||
|
||||
} /*EQEmu*/
|
||||
|
||||
#endif /*COMMON_EMU_CONSTANTS_H*/
|
||||
|
||||
@ -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_*/
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
100
common/event/task.h
Normal 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;
|
||||
};
|
||||
}
|
||||
@ -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
4
common/faction.h
Normal file → Executable 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];
|
||||
};
|
||||
|
||||
|
||||
@ -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++;
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -68,16 +68,16 @@ enum SpellTypes : uint32
|
||||
SpellType_InCombatBuffSong = (1 << 18), // bard in-combat group/ae buffs
|
||||
SpellType_OutOfCombatBuffSong = (1 << 19), // bard out-of-combat group/ae buffs
|
||||
SpellType_PreCombatBuff = (1 << 20),
|
||||
SpellType_PreCombatBuffSong = (1 << 21),
|
||||
|
||||
SpellTypes_Detrimental = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow),
|
||||
SpellTypes_Beneficial = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong),
|
||||
|
||||
SpellTypes_Innate = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root),
|
||||
|
||||
SpellType_Any = 0xFFFFFFFF
|
||||
SpellType_PreCombatBuffSong = (1 << 21)
|
||||
};
|
||||
|
||||
const uint32 SPELL_TYPE_MIN = (SpellType_Nuke << 1) - 1;
|
||||
const uint32 SPELL_TYPE_MAX = (SpellType_PreCombatBuffSong << 1) - 1;
|
||||
const uint32 SPELL_TYPE_ANY = 0xFFFFFFFF;
|
||||
|
||||
const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow);
|
||||
const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong);
|
||||
const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root);
|
||||
|
||||
// These should not be used to determine spell category..
|
||||
// They are a graphical affects (effects?) index only
|
||||
|
||||
@ -30,9 +30,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
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
9019|2018_04_12_bots_stop_melee_level.sql|SHOW COLUMNS FROM `bot_data` LIKE 'stop_melee_level'|empty|
|
||||
9020|2018_08_13_bots_inventory_update.sql|SELECT * FROM `inventory_versions` WHERE `version` = 2 and `bot_step` = 0|not_empty|
|
||||
9021|2018_10_09_bots_owner_options.sql|SHOW TABLES LIKE 'bot_owner_options'|empty|
|
||||
9022|2019_02_07_bots_stance_type_update.sql|SELECT * FROM `bot_spell_casting_chances` WHERE `spell_type_index` = '255' AND `class_id` = '255' AND `stance_index` = '0'|not_empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@ -0,0 +1,11 @@
|
||||
-- Update `bot_stances`.`stance_id` to new values
|
||||
UPDATE `bot_stances` SET `stance_id` = '9' WHERE `stance_id` = '6';
|
||||
UPDATE `bot_stances` SET `stance_id` = '7' WHERE `stance_id` = '5';
|
||||
UPDATE `bot_stances` SET `stance_id` = (`stance_id` + 1) WHERE `stance_id` in (0,1,2,3,4);
|
||||
|
||||
-- Update `bot_spell_casting_chances`.`stance_index` to new values
|
||||
UPDATE `bot_spell_casting_chances` SET `stance_index` = '8' WHERE `stance_index` = '6';
|
||||
UPDATE `bot_spell_casting_chances` SET `stance_index` = '6' WHERE `stance_index` = '5';
|
||||
|
||||
-- Update `bot_spell_casting_chances` implicit versioning
|
||||
UPDATE `bot_spell_casting_chances` SET `stance_index` = '1' WHERE `spell_type_index` = '255' AND `class_id` = '255';
|
||||
53
utils/sql/git/optional/2019_03_05_convert_item_factions.sql
Executable file
53
utils/sql/git/optional/2019_03_05_convert_item_factions.sql
Executable 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;
|
||||
82
utils/sql/git/required/2018_12_12_client_faction_tables.sql
Normal file
82
utils/sql/git/required/2018_12_12_client_faction_tables.sql
Normal file
File diff suppressed because one or more lines are too long
173
utils/sql/git/required/2018_12_12_convert_to_client_functions.sql
Executable file
173
utils/sql/git/required/2018_12_12_convert_to_client_functions.sql
Executable 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;
|
||||
|
||||
*/
|
||||
@ -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.
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
48
zone/bot.cpp
48
zone/bot.cpp
@ -163,7 +163,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
if (!stance_flag && bot_owner)
|
||||
bot_owner->Message(13, "Could not locate stance for '%s'", GetCleanName());
|
||||
|
||||
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == BotStanceAggressive));
|
||||
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == EQEmu::constants::stanceAggressive));
|
||||
SetPauseAI(false);
|
||||
|
||||
rest_timer.Disable();
|
||||
@ -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
|
||||
|
||||
148
zone/bot.h
148
zone/bot.h
@ -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
|
||||
|
||||
@ -4249,7 +4249,7 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
int clone_stance = BotStancePassive;
|
||||
int clone_stance = EQEmu::constants::stancePassive;
|
||||
if (!botdb.LoadStance(my_bot->GetBotID(), clone_stance))
|
||||
c->Message(m_fail, "%s for bot '%s'", BotDatabase::fail::LoadStance(), my_bot->GetCleanName());
|
||||
if (!botdb.SaveStance(clone_id, clone_stance))
|
||||
@ -5160,29 +5160,34 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
|
||||
if (helper_command_alias_fail(c, "bot_subcommand_bot_stance", sep->arg[0], "botstance"))
|
||||
return;
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(m_usage, "usage: %s [current | value: 0-6] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
|
||||
c->Message(m_usage, "usage: %s [current | value: 1-9] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
|
||||
c->Message(m_note, "value: %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s)",
|
||||
BotStancePassive, GetBotStanceName(BotStancePassive),
|
||||
BotStanceBalanced, GetBotStanceName(BotStanceBalanced),
|
||||
BotStanceEfficient, GetBotStanceName(BotStanceEfficient),
|
||||
BotStanceReactive, GetBotStanceName(BotStanceReactive),
|
||||
BotStanceAggressive, GetBotStanceName(BotStanceAggressive),
|
||||
BotStanceBurn, GetBotStanceName(BotStanceBurn),
|
||||
BotStanceBurnAE, GetBotStanceName(BotStanceBurnAE)
|
||||
EQEmu::constants::stancePassive, EQEmu::constants::GetStanceName(EQEmu::constants::stancePassive),
|
||||
EQEmu::constants::stanceBalanced, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBalanced),
|
||||
EQEmu::constants::stanceEfficient, EQEmu::constants::GetStanceName(EQEmu::constants::stanceEfficient),
|
||||
EQEmu::constants::stanceReactive, EQEmu::constants::GetStanceName(EQEmu::constants::stanceReactive),
|
||||
EQEmu::constants::stanceAggressive, EQEmu::constants::GetStanceName(EQEmu::constants::stanceAggressive),
|
||||
EQEmu::constants::stanceAssist, EQEmu::constants::GetStanceName(EQEmu::constants::stanceAssist),
|
||||
EQEmu::constants::stanceBurn, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBurn),
|
||||
EQEmu::constants::stanceEfficient2, EQEmu::constants::GetStanceName(EQEmu::constants::stanceEfficient2),
|
||||
EQEmu::constants::stanceBurnAE, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBurnAE)
|
||||
);
|
||||
return;
|
||||
}
|
||||
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
|
||||
|
||||
bool current_flag = false;
|
||||
auto bst = BotStanceUnknown;
|
||||
auto bst = EQEmu::constants::stanceUnknown;
|
||||
|
||||
if (!strcasecmp(sep->arg[1], "current"))
|
||||
current_flag = true;
|
||||
else if (sep->IsNumber(1))
|
||||
bst = VALIDBOTSTANCE(atoi(sep->arg[1]));
|
||||
else if (sep->IsNumber(1)) {
|
||||
bst = (EQEmu::constants::StanceType)atoi(sep->arg[1]);
|
||||
if (bst < EQEmu::constants::stanceUnknown || bst > EQEmu::constants::stanceBurnAE)
|
||||
bst = EQEmu::constants::stanceUnknown;
|
||||
}
|
||||
|
||||
if (!current_flag && bst == BotStanceUnknown) {
|
||||
if (!current_flag && bst == EQEmu::constants::stanceUnknown) {
|
||||
c->Message(m_fail, "A [current] argument or valid numeric [value] is required to use this command");
|
||||
return;
|
||||
}
|
||||
@ -5200,7 +5205,12 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
|
||||
bot_iter->Save();
|
||||
}
|
||||
|
||||
Bot::BotGroupSay(bot_iter, "My current stance is '%s' (%u)", GetBotStanceName(bot_iter->GetBotStance()), bot_iter->GetBotStance());
|
||||
Bot::BotGroupSay(
|
||||
bot_iter,
|
||||
"My current stance is '%s' (%i)",
|
||||
EQEmu::constants::GetStanceName(bot_iter->GetBotStance()),
|
||||
bot_iter->GetBotStance()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7220,7 +7230,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
|
||||
|
||||
inst = my_bot->CastToBot()->GetBotItem(i);
|
||||
if (!inst || !inst->GetItem()) {
|
||||
c->Message(m_message, "I need something for my %s (slot %i)", GetBotEquipSlotName(i), i);
|
||||
c->Message(m_message, "I need something for my %s (slot %i)", EQEmu::invslot::GetInvPossessionsSlotName(i), i);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -7230,7 +7240,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
linker.SetItemInst(inst);
|
||||
c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), GetBotEquipSlotName(i), i);
|
||||
c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), EQEmu::invslot::GetInvPossessionsSlotName(i), i);
|
||||
|
||||
++inventory_count;
|
||||
}
|
||||
@ -7333,14 +7343,14 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
|
||||
case EQEmu::invslot::slotWaist:
|
||||
case EQEmu::invslot::slotPowerSource:
|
||||
case EQEmu::invslot::slotAmmo:
|
||||
c->Message(m_message, "My %s is %s unequipped", GetBotEquipSlotName(slotId), ((itm) ? ("now") : ("already")));
|
||||
c->Message(m_message, "My %s is %s unequipped", EQEmu::invslot::GetInvPossessionsSlotName(slotId), ((itm) ? ("now") : ("already")));
|
||||
break;
|
||||
case EQEmu::invslot::slotShoulders:
|
||||
case EQEmu::invslot::slotArms:
|
||||
case EQEmu::invslot::slotHands:
|
||||
case EQEmu::invslot::slotLegs:
|
||||
case EQEmu::invslot::slotFeet:
|
||||
c->Message(m_message, "My %s are %s unequipped", GetBotEquipSlotName(slotId), ((itm) ? ("now") : ("already")));
|
||||
c->Message(m_message, "My %s are %s unequipped", EQEmu::invslot::GetInvPossessionsSlotName(slotId), ((itm) ? ("now") : ("already")));
|
||||
break;
|
||||
default:
|
||||
c->Message(m_fail, "I'm soo confused...");
|
||||
@ -7383,7 +7393,7 @@ void bot_subcommand_inventory_window(Client *c, const Seperator *sep)
|
||||
item = inst->GetItem();
|
||||
|
||||
window_text.append("<c \"#FFFFFF\">");
|
||||
window_text.append(GetBotEquipSlotName(i));
|
||||
window_text.append(EQEmu::invslot::GetInvPossessionsSlotName(i));
|
||||
window_text.append(": ");
|
||||
if (item) {
|
||||
//window_text.append("</c>");
|
||||
|
||||
@ -83,12 +83,8 @@ bool BotDatabase::LoadBotCommandSettings(std::map<std::string, std::pair<uint8,
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8 spell_casting_chances[MaxSpellTypes][PLAYER_CLASS_COUNT][MaxStances][cntHSND];
|
||||
|
||||
bool BotDatabase::LoadBotSpellCastingChances()
|
||||
{
|
||||
memset(spell_casting_chances, 0, sizeof(spell_casting_chances));
|
||||
|
||||
query =
|
||||
"SELECT"
|
||||
" `spell_type_index`,"
|
||||
@ -119,14 +115,14 @@ bool BotDatabase::LoadBotSpellCastingChances()
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
uint8 spell_type_index = atoi(row[0]);
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
|
||||
continue;
|
||||
uint8 class_index = atoi(row[1]);
|
||||
if (class_index < WARRIOR || class_index > BERSERKER)
|
||||
continue;
|
||||
--class_index;
|
||||
uint8 stance_index = atoi(row[2]);
|
||||
if (stance_index >= MaxStances)
|
||||
if (stance_index >= EQEmu::constants::STANCE_TYPE_COUNT)
|
||||
continue;
|
||||
|
||||
for (uint8 conditional_index = nHSND; conditional_index < cntHSND; ++conditional_index) {
|
||||
@ -136,7 +132,7 @@ bool BotDatabase::LoadBotSpellCastingChances()
|
||||
if (value > 100)
|
||||
value = 100;
|
||||
|
||||
spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
|
||||
Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
@ -877,7 +873,7 @@ bool BotDatabase::LoadStance(Bot* bot_inst, bool& stance_flag)
|
||||
return true;
|
||||
|
||||
auto row = results.begin();
|
||||
bot_inst->SetBotStance((BotStanceType)atoi(row[0]));
|
||||
bot_inst->SetBotStance((EQEmu::constants::StanceType)atoi(row[0]));
|
||||
stance_flag = true;
|
||||
|
||||
return true;
|
||||
@ -2853,16 +2849,16 @@ bool BotDatabase::DeleteAllHealRotations(const uint32 owner_id)
|
||||
/* Bot miscellaneous functions */
|
||||
uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index) // class_index is 0-based
|
||||
{
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
|
||||
return 0;
|
||||
if (class_index >= PLAYER_CLASS_COUNT)
|
||||
return 0;
|
||||
if (stance_index >= MaxStances)
|
||||
if (stance_index >= EQEmu::constants::STANCE_TYPE_COUNT)
|
||||
return 0;
|
||||
if (conditional_index >= cntHSND)
|
||||
return 0;
|
||||
|
||||
return spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
|
||||
return Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -192,25 +192,24 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
else {
|
||||
float hpRatioToCast = 0.0f;
|
||||
|
||||
switch(this->GetBotStance())
|
||||
{
|
||||
case BotStanceEfficient:
|
||||
case BotStanceAggressive:
|
||||
hpRatioToCast = isPrimaryHealer?90.0f:50.0f;
|
||||
break;
|
||||
case BotStanceBalanced:
|
||||
hpRatioToCast = isPrimaryHealer?95.0f:75.0f;
|
||||
break;
|
||||
case BotStanceReactive:
|
||||
hpRatioToCast = isPrimaryHealer?100.0f:90.0f;
|
||||
break;
|
||||
case BotStanceBurn:
|
||||
case BotStanceBurnAE:
|
||||
hpRatioToCast = isPrimaryHealer?75.0f:25.0f;
|
||||
break;
|
||||
default:
|
||||
hpRatioToCast = isPrimaryHealer?100.0f:0.0f;
|
||||
break;
|
||||
switch(this->GetBotStance()) {
|
||||
case EQEmu::constants::stanceEfficient:
|
||||
case EQEmu::constants::stanceAggressive:
|
||||
hpRatioToCast = isPrimaryHealer?90.0f:50.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
hpRatioToCast = isPrimaryHealer?95.0f:75.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceReactive:
|
||||
hpRatioToCast = isPrimaryHealer?100.0f:90.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBurn:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
hpRatioToCast = isPrimaryHealer?75.0f:25.0f;
|
||||
break;
|
||||
default:
|
||||
hpRatioToCast = isPrimaryHealer?100.0f:0.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
//If we're at specified mana % or below, don't heal as hybrid
|
||||
@ -381,23 +380,22 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
{
|
||||
float manaRatioToCast = 75.0f;
|
||||
|
||||
switch(this->GetBotStance())
|
||||
{
|
||||
case BotStanceEfficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case BotStanceBalanced:
|
||||
case BotStanceAggressive:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case BotStanceReactive:
|
||||
case BotStanceBurn:
|
||||
case BotStanceBurnAE:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
default:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
switch(this->GetBotStance()) {
|
||||
case EQEmu::constants::stanceEfficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
case EQEmu::constants::stanceAggressive:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceReactive:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
default:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
//If we're at specified mana % or below, don't rune as enchanter
|
||||
@ -461,25 +459,24 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
{
|
||||
float manaRatioToCast = 75.0f;
|
||||
|
||||
switch(this->GetBotStance())
|
||||
{
|
||||
case BotStanceEfficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case BotStanceBalanced:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case BotStanceReactive:
|
||||
case BotStanceAggressive:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
case BotStanceBurn:
|
||||
case BotStanceBurnAE:
|
||||
manaRatioToCast = 25.0f;
|
||||
break;
|
||||
default:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
switch(this->GetBotStance()) {
|
||||
case EQEmu::constants::stanceEfficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceReactive:
|
||||
case EQEmu::constants::stanceAggressive:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
case EQEmu::constants::stanceBurn:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
manaRatioToCast = 25.0f;
|
||||
break;
|
||||
default:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
}
|
||||
|
||||
//If we're at specified mana % or below, don't nuke as cleric or enchanter
|
||||
@ -1310,7 +1307,7 @@ bool Bot::AI_EngagedCastCheck() {
|
||||
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
||||
|
||||
uint8 botClass = GetClass();
|
||||
BotStanceType botStance = GetBotStance();
|
||||
EQEmu::constants::StanceType botStance = GetBotStance();
|
||||
bool mayGetAggro = HasOrMayGetAggro();
|
||||
|
||||
Log(Logs::Detail, Logs::AI, "Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells.");
|
||||
@ -2573,79 +2570,79 @@ bool Bot::CheckDisciplineRecastTimers(Bot *caster, int timer_index) {
|
||||
|
||||
uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
||||
{
|
||||
uint8 spell_type_index = MaxSpellTypes;
|
||||
uint8 spell_type_index = SPELL_TYPE_COUNT;
|
||||
switch (spellType) {
|
||||
case SpellType_Nuke:
|
||||
spell_type_index = SpellType_NukeIndex;
|
||||
spell_type_index = spellTypeIndexNuke;
|
||||
break;
|
||||
case SpellType_Heal:
|
||||
spell_type_index = SpellType_HealIndex;
|
||||
spell_type_index = spellTypeIndexHeal;
|
||||
break;
|
||||
case SpellType_Root:
|
||||
spell_type_index = SpellType_RootIndex;
|
||||
spell_type_index = spellTypeIndexRoot;
|
||||
break;
|
||||
case SpellType_Buff:
|
||||
spell_type_index = SpellType_BuffIndex;
|
||||
spell_type_index = spellTypeIndexBuff;
|
||||
break;
|
||||
case SpellType_Escape:
|
||||
spell_type_index = SpellType_EscapeIndex;
|
||||
spell_type_index = spellTypeIndexEscape;
|
||||
break;
|
||||
case SpellType_Pet:
|
||||
spell_type_index = SpellType_PetIndex;
|
||||
spell_type_index = spellTypeIndexPet;
|
||||
break;
|
||||
case SpellType_Lifetap:
|
||||
spell_type_index = SpellType_LifetapIndex;
|
||||
spell_type_index = spellTypeIndexLifetap;
|
||||
break;
|
||||
case SpellType_Snare:
|
||||
spell_type_index = SpellType_SnareIndex;
|
||||
spell_type_index = spellTypeIndexSnare;
|
||||
break;
|
||||
case SpellType_DOT:
|
||||
spell_type_index = SpellType_DOTIndex;
|
||||
spell_type_index = spellTypeIndexDot;
|
||||
break;
|
||||
case SpellType_Dispel:
|
||||
spell_type_index = SpellType_DispelIndex;
|
||||
spell_type_index = spellTypeIndexDispel;
|
||||
break;
|
||||
case SpellType_InCombatBuff:
|
||||
spell_type_index = SpellType_InCombatBuffIndex;
|
||||
spell_type_index = spellTypeIndexInCombatBuff;
|
||||
break;
|
||||
case SpellType_Mez:
|
||||
spell_type_index = SpellType_MezIndex;
|
||||
spell_type_index = spellTypeIndexMez;
|
||||
break;
|
||||
case SpellType_Charm:
|
||||
spell_type_index = SpellType_CharmIndex;
|
||||
spell_type_index = spellTypeIndexCharm;
|
||||
break;
|
||||
case SpellType_Slow:
|
||||
spell_type_index = SpellType_SlowIndex;
|
||||
spell_type_index = spellTypeIndexSlow;
|
||||
break;
|
||||
case SpellType_Debuff:
|
||||
spell_type_index = SpellType_DebuffIndex;
|
||||
spell_type_index = spellTypeIndexDebuff;
|
||||
break;
|
||||
case SpellType_Cure:
|
||||
spell_type_index = SpellType_CureIndex;
|
||||
spell_type_index = spellTypeIndexCure;
|
||||
break;
|
||||
case SpellType_Resurrect:
|
||||
spell_type_index = SpellType_ResurrectIndex;
|
||||
spell_type_index = spellTypeIndexResurrect;
|
||||
break;
|
||||
case SpellType_HateRedux:
|
||||
spell_type_index = SpellType_HateReduxIndex;
|
||||
spell_type_index = spellTypeIndexHateRedux;
|
||||
break;
|
||||
case SpellType_InCombatBuffSong:
|
||||
spell_type_index = SpellType_InCombatBuffSongIndex;
|
||||
spell_type_index = spellTypeIndexInCombatBuffSong;
|
||||
break;
|
||||
case SpellType_OutOfCombatBuffSong:
|
||||
spell_type_index = SpellType_OutOfCombatBuffSongIndex;
|
||||
spell_type_index = spellTypeIndexOutOfCombatBuffSong;
|
||||
break;
|
||||
case SpellType_PreCombatBuff:
|
||||
spell_type_index = SpellType_PreCombatBuffIndex;
|
||||
spell_type_index = spellTypeIndexPreCombatBuff;
|
||||
break;
|
||||
case SpellType_PreCombatBuffSong:
|
||||
spell_type_index = SpellType_PreCombatBuffSongIndex;
|
||||
spell_type_index = spellTypeIndexPreCombatBuffSong;
|
||||
break;
|
||||
default:
|
||||
spell_type_index = MaxSpellTypes;
|
||||
spell_type_index = SPELL_TYPE_COUNT;
|
||||
break;
|
||||
}
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
if (spell_type_index >= SPELL_TYPE_COUNT)
|
||||
return 0;
|
||||
|
||||
uint8 class_index = GetClass();
|
||||
@ -2653,11 +2650,13 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
||||
return 0;
|
||||
--class_index;
|
||||
|
||||
uint8 stance_index = (uint8)GetBotStance();
|
||||
if (stance_index >= MaxStances)
|
||||
EQEmu::constants::StanceType stance_type = GetBotStance();
|
||||
if (stance_type < EQEmu::constants::stancePassive || stance_type > EQEmu::constants::stanceBurnAE)
|
||||
return 0;
|
||||
|
||||
uint8 stance_index = EQEmu::constants::ConvertStanceTypeToIndex(stance_type);
|
||||
uint8 type_index = nHSND;
|
||||
|
||||
if (HasGroup()) {
|
||||
if (IsGroupHealer()/* || IsRaidHealer()*/)
|
||||
type_index |= pH;
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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());
|
||||
|
||||
387
zone/command.cpp
387
zone/command.cpp
@ -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"
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,7 +66,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading)
|
||||
memset(equipment, 0, sizeof(equipment));
|
||||
|
||||
SetMercID(0);
|
||||
SetStance(MercStanceBalanced);
|
||||
SetStance(EQEmu::constants::stanceBalanced);
|
||||
rest_timer.Disable();
|
||||
|
||||
if (GetClass() == ROGUE)
|
||||
@ -1908,7 +1908,7 @@ bool Merc::AI_IdleCastCheck() {
|
||||
|
||||
bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
|
||||
|
||||
if((iSpellTypes&SpellTypes_Detrimental) != 0) {
|
||||
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
|
||||
//according to live, you can buff and heal through walls...
|
||||
//now with PCs, this only applies if you can TARGET the target, but
|
||||
// according to Rogean, Live NPCs will just cast through walls/floors, no problem..
|
||||
@ -3669,13 +3669,13 @@ MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) {
|
||||
|
||||
switch(caster->GetStance())
|
||||
{
|
||||
case MercStanceBurnAE:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
initialCastChance = 50;
|
||||
break;
|
||||
case MercStanceBalanced:
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
initialCastChance = 25;
|
||||
break;
|
||||
case MercStanceBurn:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
initialCastChance = 0;
|
||||
break;
|
||||
}
|
||||
@ -3717,11 +3717,11 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) {
|
||||
|
||||
switch(caster->GetStance())
|
||||
{
|
||||
case MercStanceBurnAE:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
numTargetsCheck = 1;
|
||||
break;
|
||||
case MercStanceBalanced:
|
||||
case MercStanceBurn:
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
numTargetsCheck = 2;
|
||||
break;
|
||||
}
|
||||
@ -3769,11 +3769,11 @@ MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) {
|
||||
|
||||
switch(caster->GetStance())
|
||||
{
|
||||
case MercStanceBurnAE:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
numTargetsCheck = 2;
|
||||
break;
|
||||
case MercStanceBalanced:
|
||||
case MercStanceBurn:
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
numTargetsCheck = 3;
|
||||
break;
|
||||
}
|
||||
@ -3820,11 +3820,11 @@ MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) {
|
||||
|
||||
switch(caster->GetStance())
|
||||
{
|
||||
case MercStanceBurnAE:
|
||||
case EQEmu::constants::stanceBurnAE:
|
||||
numTargetsCheck = 1;
|
||||
break;
|
||||
case MercStanceBalanced:
|
||||
case MercStanceBurn:
|
||||
case EQEmu::constants::stanceBalanced:
|
||||
case EQEmu::constants::stanceBurn:
|
||||
numTargetsCheck = 2;
|
||||
break;
|
||||
}
|
||||
@ -5649,7 +5649,7 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats) {
|
||||
merc->SetSuspended(false);
|
||||
SetMerc(merc);
|
||||
merc->Unsuspend(setMaxStats);
|
||||
merc->SetStance(GetMercInfo().Stance);
|
||||
merc->SetStance((EQEmu::constants::StanceType)GetMercInfo().Stance);
|
||||
|
||||
Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName());
|
||||
|
||||
|
||||
18
zone/merc.h
18
zone/merc.h
@ -30,18 +30,6 @@ namespace EQEmu
|
||||
|
||||
const int MercAISpellRange = 100; // TODO: Write a method that calcs what the merc's spell range is based on spell, equipment, AA, whatever and replace this
|
||||
|
||||
enum MercStanceType {
|
||||
MercStancePassive = 1,
|
||||
MercStanceBalanced,
|
||||
MercStanceEfficient,
|
||||
MercStanceReactive,
|
||||
MercStanceAggressive,
|
||||
MercStanceAssist,
|
||||
MercStanceBurn,
|
||||
MercStanceEfficient2,
|
||||
MercStanceBurnAE
|
||||
};
|
||||
|
||||
struct MercSpell {
|
||||
uint16 spellid; // <= 0 = no spell
|
||||
uint32 type; // 0 = never, must be one (and only one) of the defined values
|
||||
@ -175,7 +163,7 @@ public:
|
||||
uint8 GetTierID() { return _TierID; }
|
||||
uint32 GetCostFormula() { return _CostFormula; }
|
||||
uint32 GetMercNameType() { return _NameType; }
|
||||
uint32 GetStance() { return _currentStance; }
|
||||
EQEmu::constants::StanceType GetStance() { return _currentStance; }
|
||||
int GetHatedCount() { return _hatedCount; }
|
||||
|
||||
inline const uint8 GetClientVersion() const { return _OwnerClientVersion; }
|
||||
@ -265,7 +253,7 @@ public:
|
||||
void SetMercNameType( uint8 nametype ) { _NameType = nametype; }
|
||||
void SetClientVersion(uint8 clientVersion) { _OwnerClientVersion = clientVersion; }
|
||||
void SetSuspended(bool suspended) { _suspended = suspended; }
|
||||
void SetStance( uint32 stance ) { _currentStance = stance; }
|
||||
void SetStance( EQEmu::constants::StanceType stance ) { _currentStance = stance; }
|
||||
void SetHatedCount( int count ) { _hatedCount = count; }
|
||||
|
||||
void Sit();
|
||||
@ -385,7 +373,7 @@ private:
|
||||
uint8 _CostFormula;
|
||||
uint8 _NameType;
|
||||
uint8 _OwnerClientVersion;
|
||||
uint32 _currentStance;
|
||||
EQEmu::constants::StanceType _currentStance;
|
||||
|
||||
EQEmu::InventoryProfile m_inv;
|
||||
int32 max_end;
|
||||
|
||||
12
zone/mob.cpp
12
zone/mob.cpp
@ -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) {
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
32
zone/zonedb.cpp
Normal file → Executable 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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user