mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-26 03:07:33 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4120d94fc8 | |||
| 96c32e6189 | |||
| 1a9ef6ea39 | |||
| 36d6613a47 | |||
| 8fd083b95c | |||
| f66b8607d3 | |||
| 584320202f | |||
| e089899e11 | |||
| 854fd3eff9 | |||
| ca704c7f88 | |||
| ef6dfe0469 | |||
| d7e010a3ec | |||
| 85c3255568 | |||
| c6ddf300a4 | |||
| 42d6004467 | |||
| cabe06ea92 | |||
| 4abd9c1b40 | |||
| f72bbab0e9 | |||
| 9647a5b82c | |||
| 84a90cef23 | |||
| a8db057440 | |||
| 6d6cc8ca95 | |||
| 6694281f22 |
@@ -32,8 +32,8 @@
|
||||
"loginserver1": {
|
||||
"account": "",
|
||||
"password": "",
|
||||
"legacy": 0,
|
||||
"host": "login.projecteq.net",
|
||||
"legacy": 1,
|
||||
"host": "login.eqemulator.net",
|
||||
"port": "5998"
|
||||
},
|
||||
"tcp": {
|
||||
@@ -70,4 +70,4 @@
|
||||
"plugins": "quests/plugins/"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- develop
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
linux:
|
||||
name: Linux
|
||||
@@ -25,6 +25,17 @@ jobs:
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y build-essential ninja-build ccache uuid-dev
|
||||
|
||||
- name: Restore vcpkg Cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
build/vcpkg_installed
|
||||
submodules/vcpkg/downloads
|
||||
key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}-
|
||||
${{ runner.os }}-vcpkg-
|
||||
|
||||
- name: Configure
|
||||
run: |
|
||||
cmake -S . -B build -G Ninja \
|
||||
@@ -47,6 +58,9 @@ jobs:
|
||||
windows:
|
||||
name: Windows
|
||||
runs-on: windows-latest
|
||||
env:
|
||||
VCPKG_DOWNLOADS: ${{ github.workspace }}\submodules\vcpkg\downloads
|
||||
VCPKG_BINARY_SOURCES: 'clear;files,${{ github.workspace }}\vcpkg_archives,readwrite'
|
||||
steps:
|
||||
- name: Checkout source
|
||||
uses: actions/checkout@v5
|
||||
@@ -55,16 +69,26 @@ jobs:
|
||||
|
||||
- name: Enable long paths
|
||||
run: git config --global core.longpaths true
|
||||
|
||||
|
||||
- name: Setup MSVC environment
|
||||
uses: ilammy/msvc-dev-cmd@v1
|
||||
uses: TheMrMilchmann/setup-msvc-dev@v4
|
||||
with:
|
||||
arch: x64
|
||||
|
||||
- name: Restore vcpkg Cache
|
||||
uses: actions/cache@v5
|
||||
with:
|
||||
path: |
|
||||
${{ env.VCPKG_DOWNLOADS }}
|
||||
${{ github.workspace }}/vcpkg_archives
|
||||
key: ${{ runner.os }}-vcpkg-${{ hashFiles('vcpkg.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-vcpkg-
|
||||
|
||||
- name: Configure
|
||||
shell: pwsh
|
||||
run: |
|
||||
cmake -S . -B build -G "Visual Studio 17 2022" -A x64 `
|
||||
cmake -S . -B build -G "Visual Studio 18 2026" -A x64 `
|
||||
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
|
||||
-DEQEMU_BUILD_TESTS=ON `
|
||||
-DEQEMU_BUILD_LOGIN=ON `
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ option(EQEMU_BUILD_PCH "Build with precompiled headers (Windows)" ON)
|
||||
|
||||
if(MSVC)
|
||||
add_compile_options(/bigobj)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS NOMINMAX WIN32_LEAN_AND_MEAN CRASH_LOGGING _HAS_AUTO_PTR_ETC)
|
||||
add_compile_definitions(_CRT_SECURE_NO_WARNINGS NOMINMAX WIN32_LEAN_AND_MEAN CRASH_LOGGING)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP")
|
||||
|
||||
option(EQEMU_DISABLE_MSVC_WARNINGS "Disable MSVC compile warnings." OFF)
|
||||
|
||||
@@ -6,6 +6,7 @@ set(common_sources
|
||||
bodytypes.cpp
|
||||
classes.cpp
|
||||
cli/eqemu_command_handler.cpp
|
||||
compiler_macros.h
|
||||
compression.cpp
|
||||
content/world_content_service.cpp
|
||||
crash.cpp
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define PUSH_DISABLE_DEPRECATED_WARNINGS() __pragma(warning(push)) \
|
||||
__pragma(warning(disable:4996))
|
||||
#define POP_DISABLE_DEPRECATED_WARNINGS() __pragma(warning(pop))
|
||||
#elif defined(__GNUC__) || defined(__clang__)
|
||||
#define PUSH_DISABLE_DEPRECATED_WARNINGS() _Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
|
||||
#define POP_DISABLE_DEPRECATED_WARNINGS() _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
#define PUSH_DISABLE_DEPRECATED_WARNINGS()
|
||||
#define POP_DISABLE_DEPRECATED_WARNINGS()
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && !defined(__clang__)
|
||||
#define UNREACHABLE() __assume(0)
|
||||
#else
|
||||
#define UNREACHABLE() __builtin_unreachable()
|
||||
#endif
|
||||
@@ -271,11 +271,7 @@ static size_t const stackLimit_g = JSONCPP_DEPRECATED_STACK_LIMIT; // see readVa
|
||||
|
||||
namespace Json {
|
||||
|
||||
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
|
||||
typedef std::unique_ptr<CharReader> CharReaderPtr;
|
||||
#else
|
||||
typedef std::auto_ptr<CharReader> CharReaderPtr;
|
||||
#endif
|
||||
|
||||
// Implementation of class Features
|
||||
// ////////////////////////////////
|
||||
@@ -4153,11 +4149,7 @@ Value& Path::make(Value& root) const {
|
||||
|
||||
namespace Json {
|
||||
|
||||
#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
|
||||
typedef std::unique_ptr<StreamWriter> StreamWriterPtr;
|
||||
#else
|
||||
typedef std::auto_ptr<StreamWriter> StreamWriterPtr;
|
||||
#endif
|
||||
|
||||
static bool containsControlCharacter(const char* str) {
|
||||
while (*str) {
|
||||
|
||||
@@ -908,6 +908,7 @@ RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spa
|
||||
RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms")
|
||||
RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)")
|
||||
RULE_BOOL(Bots, BotsRequireLoS, true, "Whether or not bots require line of sight to be told to attack their target")
|
||||
RULE_INT(Bots, BlindMoveChance, 5, "Chance for a bot to run to its target if it is blinded. Default 5.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Chat)
|
||||
|
||||
+50
-109
@@ -380,118 +380,59 @@ std::string Strings::NumberToWords(unsigned long long int n)
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string Strings::Money(uint64 platinum, uint64 gold, uint64 silver, uint64 copper)
|
||||
{
|
||||
std::string money_string = "Unknown";
|
||||
if (copper && silver && gold && platinum) { // CSGP
|
||||
money_string = fmt::format(
|
||||
"{} platinum, {} gold, {} silver, and {} copper",
|
||||
Strings::Commify(std::to_string(platinum)),
|
||||
Strings::Commify(std::to_string(gold)),
|
||||
Strings::Commify(std::to_string(silver)),
|
||||
Strings::Commify(std::to_string(copper))
|
||||
);
|
||||
std::string Strings::Money(uint64 platinum, uint64 gold, uint64 silver, uint64 copper, bool commify) {
|
||||
uint64 values[] = { platinum, gold, silver, copper };
|
||||
const char* names[] = { " platinum", " gold", " silver", " copper" };
|
||||
|
||||
std::vector<std::string> parts;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (values[i] > 0) {
|
||||
std::string s = std::to_string(values[i]);
|
||||
parts.push_back((commify ? Strings::Commify(s) : s) + names[i]);
|
||||
}
|
||||
}
|
||||
else if (copper && silver && !gold && platinum) { // CSP
|
||||
money_string = fmt::format(
|
||||
"{} platinum, {} silver, and {} copper",
|
||||
Strings::Commify(std::to_string(platinum)),
|
||||
Strings::Commify(std::to_string(silver)),
|
||||
Strings::Commify(std::to_string(copper))
|
||||
);
|
||||
|
||||
if (parts.empty()) return "0 copper";
|
||||
if (parts.size() == 1) return parts[0];
|
||||
|
||||
std::string result;
|
||||
for (size_t i = 0; i < parts.size(); ++i) {
|
||||
result += parts[i];
|
||||
if (i < parts.size() - 2) {
|
||||
result += ", ";
|
||||
}
|
||||
else if (i == parts.size() - 2) {
|
||||
// Oxford comma logic: ", and " for 3+ items, " and " for 2
|
||||
result += (parts.size() > 2) ? ", and " : " and ";
|
||||
}
|
||||
}
|
||||
else if (copper && silver && gold && !platinum) { // CSG
|
||||
money_string = fmt::format(
|
||||
"{} gold, {} silver, and {} copper",
|
||||
Strings::Commify(std::to_string(gold)),
|
||||
Strings::Commify(std::to_string(silver)),
|
||||
Strings::Commify(std::to_string(copper))
|
||||
);
|
||||
}
|
||||
else if (copper && !silver && !gold && platinum) { // CP
|
||||
money_string = fmt::format(
|
||||
"{} platinum and {} copper",
|
||||
Strings::Commify(std::to_string(platinum)),
|
||||
Strings::Commify(std::to_string(copper))
|
||||
);
|
||||
}
|
||||
else if (copper && silver && !gold && !platinum) { // CS
|
||||
money_string = fmt::format(
|
||||
"{} silver and {} copper",
|
||||
Strings::Commify(std::to_string(silver)),
|
||||
Strings::Commify(std::to_string(copper))
|
||||
);
|
||||
}
|
||||
else if (!copper && silver && gold && platinum) { // SGP
|
||||
money_string = fmt::format(
|
||||
"{} platinum, {} gold, and {} silver",
|
||||
Strings::Commify(std::to_string(platinum)),
|
||||
Strings::Commify(std::to_string(gold)),
|
||||
Strings::Commify(std::to_string(silver))
|
||||
);
|
||||
}
|
||||
else if (!copper && silver && !gold && platinum) { // SP
|
||||
money_string = fmt::format(
|
||||
"{} platinum and {} silver",
|
||||
Strings::Commify(std::to_string(platinum)),
|
||||
Strings::Commify(std::to_string(silver))
|
||||
);
|
||||
}
|
||||
else if (!copper && silver && gold && !platinum) { // SG
|
||||
money_string = fmt::format(
|
||||
"{} gold and {} silver",
|
||||
Strings::Commify(std::to_string(gold)),
|
||||
Strings::Commify(std::to_string(silver))
|
||||
);
|
||||
}
|
||||
else if (copper && !silver && gold && platinum) { // CGP
|
||||
money_string = fmt::format(
|
||||
"{} platinum, {} gold, and {} copper",
|
||||
Strings::Commify(std::to_string(platinum)),
|
||||
Strings::Commify(std::to_string(gold)),
|
||||
Strings::Commify(std::to_string(copper))
|
||||
);
|
||||
}
|
||||
else if (copper && !silver && gold && !platinum) { // CG
|
||||
money_string = fmt::format(
|
||||
"{} gold and {} copper",
|
||||
Strings::Commify(std::to_string(gold)),
|
||||
Strings::Commify(std::to_string(copper))
|
||||
);
|
||||
}
|
||||
else if (!copper && !silver && gold && platinum) { // GP
|
||||
money_string = fmt::format(
|
||||
"{} platinum and {} gold",
|
||||
Strings::Commify(std::to_string(platinum)),
|
||||
Strings::Commify(std::to_string(gold))
|
||||
);
|
||||
}
|
||||
else if (!copper && !silver && !gold && platinum) { // P
|
||||
money_string = fmt::format(
|
||||
"{} platinum",
|
||||
Strings::Commify(std::to_string(platinum))
|
||||
);
|
||||
}
|
||||
else if (!copper && !silver && gold && !platinum) { // G
|
||||
money_string = fmt::format(
|
||||
"{} gold",
|
||||
Strings::Commify(std::to_string(gold))
|
||||
);
|
||||
}
|
||||
else if (!copper && silver && !gold && !platinum) { // S
|
||||
money_string = fmt::format(
|
||||
"{} silver",
|
||||
Strings::Commify(std::to_string(silver))
|
||||
);
|
||||
}
|
||||
else if (copper && !silver && !gold && !platinum) { // C
|
||||
money_string = fmt::format(
|
||||
"{} copper",
|
||||
Strings::Commify(std::to_string(copper))
|
||||
);
|
||||
}
|
||||
return money_string;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string Strings::MoneyShort(uint64 copper, bool commify) {
|
||||
// Matches merchant format
|
||||
uint64 values[] = {
|
||||
copper / 1000,
|
||||
(copper / 100) % 10,
|
||||
(copper / 10) % 10,
|
||||
copper % 10
|
||||
};
|
||||
const char* names[] = { " platinum", " gold", " silver", " copper" };
|
||||
|
||||
std::string result;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
if (values[i] > 0) {
|
||||
if (!result.empty()) result += " ";
|
||||
|
||||
std::string s = std::to_string(values[i]);
|
||||
result += (commify ? Strings::Commify(s) : s) + names[i];
|
||||
}
|
||||
}
|
||||
|
||||
return result.empty() ? "0 copper" : result;
|
||||
}
|
||||
|
||||
std::string Strings::SecondsToTime(int duration, bool is_milliseconds)
|
||||
{
|
||||
if (duration <= 0) {
|
||||
|
||||
+2
-1
@@ -62,7 +62,8 @@ public:
|
||||
static std::string Join(const std::vector<std::string> &ar, const std::string &delim);
|
||||
static std::string Join(const std::vector<uint32_t> &ar, const std::string &delim);
|
||||
static std::string MillisecondsToTime(int duration);
|
||||
static std::string Money(uint64 platinum, uint64 gold = 0, uint64 silver = 0, uint64 copper = 0);
|
||||
static std::string Money(uint64 platinum, uint64 gold = 0, uint64 silver = 0, uint64 copper = 0, bool commify = true);
|
||||
static std::string MoneyShort(uint64 copper = 0, bool commify = true); // Matches merchant format when commify is false
|
||||
static std::string NumberToWords(unsigned long long int n);
|
||||
static std::string Repeat(std::string s, int n);
|
||||
static std::string Replace(std::string subject, const std::string &search, const std::string &replace);
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace luabind { namespace detail
|
||||
if (luabind::move_back_reference(L, ptr))
|
||||
return;
|
||||
|
||||
make_instance(L, std::auto_ptr<T>(ptr));
|
||||
make_instance(L, std::unique_ptr<T>(ptr));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -335,7 +335,7 @@ namespace luabind
|
||||
template <class T>
|
||||
struct default_pointer<null_type, T>
|
||||
{
|
||||
typedef std::auto_ptr<T> type;
|
||||
typedef std::unique_ptr<T> type;
|
||||
};
|
||||
|
||||
template <class Class, class Pointer, class Signature, class Policies>
|
||||
|
||||
@@ -46,7 +46,7 @@ struct construct_aux<0, T, Pointer, Signature>
|
||||
object_rep* self = touserdata<object_rep>(self_);
|
||||
class_rep* cls = self->crep();
|
||||
|
||||
std::auto_ptr<T> instance(new T);
|
||||
std::unique_ptr<T> instance(new T);
|
||||
inject_backref(self_.interpreter(), instance.get(), instance.get());
|
||||
|
||||
void* naked_ptr = instance.get();
|
||||
@@ -55,7 +55,7 @@ struct construct_aux<0, T, Pointer, Signature>
|
||||
void* storage = self->allocate(sizeof(holder_type));
|
||||
|
||||
self->set_instance(new (storage) holder_type(
|
||||
ptr, registered_class<T>::id, naked_ptr, cls));
|
||||
std::move(ptr), registered_class<T>::id, naked_ptr, cls));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -92,7 +92,7 @@ struct construct_aux<N, T, Pointer, Signature>
|
||||
object_rep* self = touserdata<object_rep>(self_);
|
||||
class_rep* cls = self->crep();
|
||||
|
||||
std::auto_ptr<T> instance(new T(BOOST_PP_ENUM_PARAMS(N,_)));
|
||||
std::unique_ptr<T> instance(new T(BOOST_PP_ENUM_PARAMS(N,_)));
|
||||
inject_backref(self_.interpreter(), instance.get(), instance.get());
|
||||
|
||||
void* naked_ptr = instance.get();
|
||||
@@ -101,7 +101,7 @@ struct construct_aux<N, T, Pointer, Signature>
|
||||
void* storage = self->allocate(sizeof(holder_type));
|
||||
|
||||
self->set_instance(new (storage) holder_type(
|
||||
ptr, registered_class<T>::id, naked_ptr, cls));
|
||||
std::move(ptr), registered_class<T>::id, naked_ptr, cls));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace has_get_pointer_
|
||||
T* get_pointer(T const volatile*);
|
||||
|
||||
template<class T>
|
||||
T* get_pointer(std::auto_ptr<T> const&);
|
||||
T* get_pointer(std::unique_ptr<T> const&);
|
||||
|
||||
# endif
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ inline mpl::true_ check_const_pointer(void const*)
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void release_ownership(std::auto_ptr<T>& p)
|
||||
void release_ownership(std::unique_ptr<T>& p)
|
||||
{
|
||||
p.release();
|
||||
}
|
||||
@@ -83,7 +83,7 @@ public:
|
||||
P p, class_id dynamic_id, void* dynamic_ptr, class_rep* cls
|
||||
)
|
||||
: instance_holder(cls, check_const_pointer(false ? get_pointer(p) : 0))
|
||||
, p(p)
|
||||
, p(std::move(p))
|
||||
, weak(0)
|
||||
, dynamic_id(dynamic_id)
|
||||
, dynamic_ptr(dynamic_ptr)
|
||||
|
||||
@@ -88,7 +88,7 @@ void make_instance(lua_State* L, P p)
|
||||
|
||||
try
|
||||
{
|
||||
new (storage) holder_type(p, dynamic.first, dynamic.second, cls);
|
||||
new (storage) holder_type(std::move(p), dynamic.first, dynamic.second, cls);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace luabind { namespace detail
|
||||
{
|
||||
if (get_pointer(x))
|
||||
{
|
||||
make_instance(L, x);
|
||||
make_instance(L, std::move(x));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -180,8 +180,8 @@ namespace luabind { namespace detail
|
||||
template <class T>
|
||||
void make_pointee_instance(lua_State* L, T& x, mpl::false_, mpl::true_)
|
||||
{
|
||||
std::auto_ptr<T> ptr(new T(x));
|
||||
make_instance(L, ptr);
|
||||
std::unique_ptr<T> ptr(new T(x));
|
||||
make_instance(L, std::move(ptr));
|
||||
}
|
||||
|
||||
template <class T>
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace detail
|
||||
template <class F, class Policies>
|
||||
scope def(char const* name, F f, Policies const& policies)
|
||||
{
|
||||
return scope(std::auto_ptr<detail::registration>(
|
||||
return scope(std::unique_ptr<detail::registration>(
|
||||
new detail::function_registration<F, Policies>(name, f, policies)));
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace luabind {
|
||||
struct LUABIND_API scope
|
||||
{
|
||||
scope();
|
||||
explicit scope(std::auto_ptr<detail::registration> reg);
|
||||
explicit scope(std::unique_ptr<detail::registration> reg);
|
||||
scope(scope const& other_);
|
||||
~scope();
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ namespace luabind { namespace detail {
|
||||
// -- interface ---------------------------------------------------------
|
||||
|
||||
class_base::class_base(char const* name)
|
||||
: scope(std::auto_ptr<registration>(
|
||||
: scope(std::unique_ptr<registration>(
|
||||
m_registration = new class_registration(name))
|
||||
)
|
||||
{
|
||||
@@ -258,14 +258,14 @@ namespace luabind { namespace detail {
|
||||
|
||||
void class_base::add_member(registration* member)
|
||||
{
|
||||
std::auto_ptr<registration> ptr(member);
|
||||
m_registration->m_members.operator,(scope(ptr));
|
||||
std::unique_ptr<registration> ptr(member);
|
||||
m_registration->m_members.operator,(scope(std::move(ptr)));
|
||||
}
|
||||
|
||||
void class_base::add_default_member(registration* member)
|
||||
{
|
||||
std::auto_ptr<registration> ptr(member);
|
||||
m_registration->m_default_members.operator,(scope(ptr));
|
||||
std::unique_ptr<registration> ptr(member);
|
||||
m_registration->m_default_members.operator,(scope(std::move(ptr)));
|
||||
}
|
||||
|
||||
const char* class_base::name() const
|
||||
|
||||
@@ -49,7 +49,8 @@ namespace luabind { namespace detail {
|
||||
{
|
||||
}
|
||||
|
||||
scope::scope(std::auto_ptr<detail::registration> reg)
|
||||
|
||||
scope::scope(std::unique_ptr<detail::registration> reg)
|
||||
: m_chain(reg.release())
|
||||
{
|
||||
}
|
||||
@@ -193,7 +194,7 @@ namespace luabind {
|
||||
};
|
||||
|
||||
namespace_::namespace_(char const* name)
|
||||
: scope(std::auto_ptr<detail::registration>(
|
||||
: scope(std::unique_ptr<detail::registration>(
|
||||
m_registration = new registration_(name)))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -37,3 +37,13 @@ target_include_directories(loginserver PRIVATE ..)
|
||||
|
||||
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
set_property(TARGET loginserver PROPERTY FOLDER executables/servers)
|
||||
|
||||
# vcpkg doesn't copy legacy.dll automatically because it is loaded at runtime, not via the import table.
|
||||
if(WIN32 AND DEFINED VCPKG_INSTALLED_DIR AND DEFINED VCPKG_TARGET_TRIPLET)
|
||||
add_custom_command(TARGET loginserver POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
"$<IF:$<CONFIG:Debug>,${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug/bin/legacy.dll,${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/bin/legacy.dll>"
|
||||
"$<TARGET_FILE_DIR:loginserver>/legacy.dll"
|
||||
VERBATIM
|
||||
)
|
||||
endif()
|
||||
|
||||
+114
-28
@@ -16,11 +16,12 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "encryption.h"
|
||||
#include "common/compiler_macros.h"
|
||||
|
||||
#ifdef EQEMU_USE_OPENSSL
|
||||
#include <openssl/des.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/md5.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/provider.h>
|
||||
#endif
|
||||
#ifdef EQEMU_USE_MBEDTLS
|
||||
#include <mbedtls/des.h>
|
||||
@@ -32,6 +33,8 @@
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#ifdef ENABLE_SECURITY
|
||||
|
||||
#include <sodium.h>
|
||||
@@ -127,21 +130,104 @@ const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buff
|
||||
#endif
|
||||
|
||||
#ifdef EQEMU_USE_OPENSSL
|
||||
DES_key_schedule k;
|
||||
DES_cblock v;
|
||||
|
||||
memset(&k, 0, sizeof(DES_key_schedule));
|
||||
memset(&v, 0, sizeof(DES_cblock));
|
||||
|
||||
// Decrypt requires block-aligned input; encrypt zero-pads a trailing
|
||||
// partial block to match the legacy DES_ncbc_encrypt semantics the
|
||||
// game protocol expects.
|
||||
if (!enc && buffer_in_sz && buffer_in_sz % 8 != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
DES_ncbc_encrypt((const unsigned char*)buffer_in, (unsigned char*)buffer_out, (long)buffer_in_sz, &k, &v, enc);
|
||||
unsigned char key[8] = {0};
|
||||
unsigned char iv[8] = {0};
|
||||
|
||||
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
|
||||
if (!ctx) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool result = EVP_CipherInit_ex2(ctx, EVP_des_cbc(), key, iv, enc, nullptr) == 1;
|
||||
if (result) {
|
||||
EVP_CIPHER_CTX_set_padding(ctx, 0);
|
||||
|
||||
const unsigned char* src = reinterpret_cast<const unsigned char*>(buffer_in);
|
||||
size_t src_len = buffer_in_sz;
|
||||
std::unique_ptr<unsigned char[]> padded;
|
||||
|
||||
if (enc && buffer_in_sz % 8 != 0) {
|
||||
src_len = ((buffer_in_sz / 8) + 1) * 8;
|
||||
padded.reset(new unsigned char[src_len]());
|
||||
memcpy(padded.get(), buffer_in, buffer_in_sz);
|
||||
src = padded.get();
|
||||
}
|
||||
|
||||
int outl = 0;
|
||||
int final_len = 0;
|
||||
result = EVP_CipherUpdate(ctx, reinterpret_cast<unsigned char*>(buffer_out), &outl, src, static_cast<int>(src_len)) == 1
|
||||
&& EVP_CipherFinal_ex(ctx, reinterpret_cast<unsigned char*>(buffer_out) + outl, &final_len) == 1;
|
||||
}
|
||||
|
||||
EVP_CIPHER_CTX_free(ctx);
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
return buffer_out;
|
||||
}
|
||||
|
||||
#ifdef EQEMU_USE_OPENSSL
|
||||
static OSSL_PROVIDER *s_legacy_provider = nullptr;
|
||||
static OSSL_PROVIDER *s_default_provider = nullptr;
|
||||
#endif
|
||||
|
||||
bool eqcrypt_init()
|
||||
{
|
||||
#ifdef EQEMU_USE_OPENSSL
|
||||
#ifdef _WIN32
|
||||
// Set OpenSSL default provider search path to the executable directory.
|
||||
char* exe_path = nullptr;
|
||||
if (_get_pgmptr(&exe_path) == 0 && exe_path != nullptr && *exe_path != '\0') {
|
||||
std::string exe_dir{exe_path};
|
||||
if (auto sep = exe_dir.find_last_of("\\/"); sep != std::string::npos) {
|
||||
exe_dir.resize(sep);
|
||||
OSSL_PROVIDER_set_default_search_path(nullptr, exe_dir.c_str());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!s_default_provider) {
|
||||
s_default_provider = OSSL_PROVIDER_load(nullptr, "default");
|
||||
}
|
||||
if (!s_legacy_provider) {
|
||||
s_legacy_provider = OSSL_PROVIDER_load(nullptr, "legacy");
|
||||
}
|
||||
|
||||
if (!s_default_provider || !s_legacy_provider) {
|
||||
char buf[256];
|
||||
while (auto err = ERR_get_error()) {
|
||||
ERR_error_string_n(err, buf, sizeof(buf));
|
||||
LogError("OpenSSL provider load failure: {}", buf);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void eqcrypt_shutdown()
|
||||
{
|
||||
#ifdef EQEMU_USE_OPENSSL
|
||||
if (s_legacy_provider) {
|
||||
OSSL_PROVIDER_unload(s_legacy_provider);
|
||||
s_legacy_provider = nullptr;
|
||||
}
|
||||
if (s_default_provider) {
|
||||
OSSL_PROVIDER_unload(s_default_provider);
|
||||
s_default_provider = nullptr;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string eqcrypt_md5(const std::string &msg)
|
||||
{
|
||||
std::string ret;
|
||||
@@ -164,12 +250,12 @@ std::string eqcrypt_md5(const std::string &msg)
|
||||
unsigned char md5_digest[16];
|
||||
char tmp[4];
|
||||
|
||||
MD5((const unsigned char*)msg.c_str(), msg.length(), md5_digest);
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
sprintf(&tmp[0], "%02x", md5_digest[i]);
|
||||
ret.push_back(tmp[0]);
|
||||
ret.push_back(tmp[1]);
|
||||
if (EVP_Digest(msg.data(), msg.length(), md5_digest, nullptr, EVP_md5(), nullptr) == 1) {
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
sprintf(&tmp[0], "%02x", md5_digest[i]);
|
||||
ret.push_back(tmp[0]);
|
||||
ret.push_back(tmp[1]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -198,12 +284,12 @@ std::string eqcrypt_sha1(const std::string &msg)
|
||||
unsigned char sha_digest[20];
|
||||
char tmp[4];
|
||||
|
||||
SHA1((const unsigned char*)msg.c_str(), msg.length(), sha_digest);
|
||||
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
sprintf(&tmp[0], "%02x", sha_digest[i]);
|
||||
ret.push_back(tmp[0]);
|
||||
ret.push_back(tmp[1]);
|
||||
if (EVP_Digest(msg.data(), msg.length(), sha_digest, nullptr, EVP_sha1(), nullptr) == 1) {
|
||||
for (int i = 0; i < 20; ++i) {
|
||||
sprintf(&tmp[0], "%02x", sha_digest[i]);
|
||||
ret.push_back(tmp[0]);
|
||||
ret.push_back(tmp[1]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -232,12 +318,12 @@ std::string eqcrypt_sha512(const std::string &msg)
|
||||
unsigned char sha_digest[64];
|
||||
char tmp[4];
|
||||
|
||||
SHA512((const unsigned char*)msg.c_str(), msg.length(), sha_digest);
|
||||
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
sprintf(&tmp[0], "%02x", sha_digest[i]);
|
||||
ret.push_back(tmp[0]);
|
||||
ret.push_back(tmp[1]);
|
||||
if (EVP_Digest(msg.data(), msg.length(), sha_digest, nullptr, EVP_sha512(), nullptr) == 1) {
|
||||
for (int i = 0; i < 64; ++i) {
|
||||
sprintf(&tmp[0], "%02x", sha_digest[i]);
|
||||
ret.push_back(tmp[0]);
|
||||
ret.push_back(tmp[1]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -48,10 +48,20 @@ namespace CryptoHash {
|
||||
}
|
||||
|
||||
std::string GetEncryptionByModeId(uint32 mode);
|
||||
|
||||
// DES-CBC with an all-zero key and IV (EQ login protocol obfuscation, not security).
|
||||
// On encrypt, a trailing partial block is zero-padded to the next 8-byte boundary, so
|
||||
// buffer_out must be at least ((buffer_in_sz + 7) / 8) * 8 bytes. On decrypt, buffer_in_sz
|
||||
// must already be a multiple of 8 or the call returns nullptr.
|
||||
const char *eqcrypt_block(const char *buffer_in, size_t buffer_in_sz, char *buffer_out, bool enc);
|
||||
std::string eqcrypt_hash(const std::string &username, const std::string &password, int mode);
|
||||
bool eqcrypt_verify_hash(const std::string &username, const std::string &password, const std::string &pwhash, int mode);
|
||||
|
||||
// OpenSSL 3.0 moved DES behind the "legacy" provider; these load/unload it
|
||||
// for the lifetime of the process. No-op when built against mbedtls.
|
||||
bool eqcrypt_init();
|
||||
void eqcrypt_shutdown();
|
||||
|
||||
struct EncryptionResult {
|
||||
std::string password;
|
||||
int mode = 0;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "common/platform.h"
|
||||
#include "common/timer.h"
|
||||
#include "common/types.h"
|
||||
#include "loginserver/encryption.h"
|
||||
#include "loginserver/login_server.h"
|
||||
#include "loginserver/loginserver_command_handler.h"
|
||||
#include "loginserver/loginserver_webserver.h"
|
||||
@@ -158,12 +159,12 @@ void start_web_server()
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
RegisterExecutablePlatform(ExePlatformLogin);
|
||||
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
|
||||
set_exception_handler();
|
||||
|
||||
LogInfo("Logging System Init");
|
||||
|
||||
if (argc == 1) {
|
||||
EQEmuLogSys::Instance()->LoadLogSettingsDefaults();
|
||||
if (!eqcrypt_init()) {
|
||||
LogError("Failed to initialize crypto providers");
|
||||
return 1;
|
||||
}
|
||||
|
||||
PathManager::Instance()->Init();
|
||||
@@ -280,5 +281,7 @@ int main(int argc, char **argv)
|
||||
LogInfo("Server Manager Shutdown");
|
||||
delete server.server_manager;
|
||||
|
||||
eqcrypt_shutdown();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
+5
-1
@@ -3186,7 +3186,11 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
|
||||
// if other is a bot, add the bots client to the hate list
|
||||
if (RuleB(Bots, Enabled)) {
|
||||
if (other->IsBot()) {
|
||||
if (other->GetOwner() && other->GetOwner()->IsBot()) {
|
||||
other = other->GetOwner();
|
||||
}
|
||||
|
||||
if (other->IsBot()) {
|
||||
auto other_ = other->CastToBot();
|
||||
|
||||
if (!other_ || !other_->GetBotOwner()) {
|
||||
|
||||
@@ -3432,6 +3432,10 @@ bool Bot::CheckIfIncapacitated() {
|
||||
}
|
||||
|
||||
if (currently_fleeing) {
|
||||
if (!FindType(SpellEffect::Fear)) { // Blinded
|
||||
return BotProcessBlind();
|
||||
}
|
||||
|
||||
if (RuleB(Combat, EnableFearPathing) && AI_movement_timer->Check()) {
|
||||
// Check if we have reached the last fear point
|
||||
if (DistanceNoZ(glm::vec3(GetX(), GetY(), GetZ()), m_FearWalkTarget) <= 5.0f) {
|
||||
@@ -3453,6 +3457,23 @@ bool Bot::CheckIfIncapacitated() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Bot::BotProcessBlind() {
|
||||
if (m_combat_jitter_timer.Check() && zone->random.Int(1, 100) <= RuleI(Bots, BlindMoveChance)) {
|
||||
Mob* tar = GetTarget() ? GetTarget() : GetBotOwner();
|
||||
|
||||
if (tar) {
|
||||
glm::vec3 Goal = tar->GetPosition();
|
||||
|
||||
RunTo(Goal.x, Goal.y, Goal.z);
|
||||
SetCombatJitter();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::SetBerserkState() {// Berserk updates should occur if primary AI criteria are met
|
||||
if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) {
|
||||
if (!berserk && GetHPRatio() < RuleI(Combat, BerserkerFrenzyStart)) {
|
||||
@@ -5335,6 +5356,12 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
|
||||
if (ma_time) {
|
||||
switch (GetClass()) {
|
||||
case Class::Monk: {
|
||||
|
||||
if (!GetSkill(EQ::skills::SkillTigerClaw)) {
|
||||
monkattack_timer.Disable();
|
||||
return;
|
||||
}
|
||||
|
||||
int reuse = (MonkSpecialAttack(target, EQ::skills::SkillTigerClaw) - 1);
|
||||
|
||||
// Live AA - Technique of Master Wu
|
||||
|
||||
+2
-1
@@ -542,7 +542,7 @@ public:
|
||||
bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id);
|
||||
|
||||
// Cast checks
|
||||
bool PrecastChecks(Mob* tar, uint16 spell_type);
|
||||
bool PrecastChecks(Mob* tar, uint16 spell_type);
|
||||
bool CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks = false, bool ae_check = false);
|
||||
bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster);
|
||||
bool CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar);
|
||||
@@ -1055,6 +1055,7 @@ public:
|
||||
bool BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type);
|
||||
|
||||
bool CheckIfIncapacitated();
|
||||
bool BotProcessBlind();
|
||||
bool IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid);
|
||||
|
||||
Client* SetLeashOwner(Client* bot_owner, Group* bot_group, Raid* raid, uint32 r_group) const;
|
||||
|
||||
@@ -775,7 +775,7 @@ void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type)
|
||||
bot_owner->Message(Chat::Green, "%s", description.c_str());
|
||||
}
|
||||
|
||||
void SendSpellTypeWindow(Client* c, const Seperator* sep) {
|
||||
void SendSpellTypeWindow(Client* c, const Seperator* sep, bool short_names) {
|
||||
std::string arg0 = sep->arg[0];
|
||||
std::string arg1 = sep->arg[1];
|
||||
|
||||
@@ -828,7 +828,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) {
|
||||
std::string popup_text = DialogueWindow::TableRow(
|
||||
DialogueWindow::TableCell(DialogueWindow::ColorMessage(goldenrod, spell_type_field))
|
||||
+
|
||||
DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(goldenrod, id_field) : DialogueWindow::ColorMessage(goldenrod, shortname_field)))
|
||||
DialogueWindow::TableCell((!short_names ? DialogueWindow::ColorMessage(goldenrod, id_field) : DialogueWindow::ColorMessage(goldenrod, shortname_field)))
|
||||
);
|
||||
|
||||
popup_text += DialogueWindow::TableRow(
|
||||
@@ -845,7 +845,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) {
|
||||
popup_text += DialogueWindow::TableRow(
|
||||
DialogueWindow::TableCell(DialogueWindow::ColorMessage(forest_green, Bot::GetSpellTypeNameByID(i)))
|
||||
+
|
||||
DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, Bot::GetSpellTypeShortNameByID(i))))
|
||||
DialogueWindow::TableCell((!short_names ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, Bot::GetSpellTypeShortNameByID(i))))
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1182,4 +1182,4 @@ bool helper_is_help_or_usage(const char* arg);
|
||||
bool helper_no_available_bots(Client *bot_owner, Bot *my_bot = nullptr);
|
||||
void helper_send_available_subcommands(Client *bot_owner, const char* command_simile, std::vector<const char*> subcommand_list);
|
||||
void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type);
|
||||
void SendSpellTypeWindow(Client* c, const Seperator* sep);
|
||||
void SendSpellTypeWindow(Client* c, const Seperator* sep, bool short_names = false);
|
||||
|
||||
@@ -206,8 +206,8 @@ void bot_command_item_use(Client* c, const Seperator* sep)
|
||||
}
|
||||
|
||||
if (
|
||||
(!RuleB(Bots, AllowBotEquipAnyRaceGear) && ((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace()))) ||
|
||||
(!RuleB(Bots, AllowBotEquipAnyClassGear) && ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass())))
|
||||
(!RuleB(Bots, AllowBotEquipAnyRaceGear) && !(item_data->Races & GetPlayerRaceBit(bot_iter->GetBaseRace()))) ||
|
||||
(!RuleB(Bots, AllowBotEquipAnyClassGear) && !(item_data->Classes & GetPlayerClassBit(bot_iter->GetClass())))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ void bot_command_spelltype_ids(Client* c, const Seperator* sep)
|
||||
|
||||
void bot_command_spelltype_names(Client* c, const Seperator* sep)
|
||||
{
|
||||
SendSpellTypeWindow(c, sep);
|
||||
SendSpellTypeWindow(c, sep, true);
|
||||
}
|
||||
|
||||
@@ -512,7 +512,7 @@ bool BotDatabase::SaveNewBot(Bot* b, uint32& bot_id)
|
||||
e.poison = b->GetBasePR();
|
||||
e.disease = b->GetBaseDR();
|
||||
e.corruption = b->GetBaseCorrup();
|
||||
e.expansion_bitmask = b->GetExpansionBitmask();
|
||||
e.expansion_bitmask = RuleI(Bots, BotExpansionSettings);
|
||||
|
||||
e = BotDataRepository::InsertOne(database, e);
|
||||
|
||||
|
||||
+39
-16
@@ -14378,9 +14378,8 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
sizeof(Merchant_Purchase_Struct), app->size);
|
||||
return;
|
||||
}
|
||||
RDTSC_Timer t1(true);
|
||||
|
||||
Merchant_Purchase_Struct* mp = (Merchant_Purchase_Struct*)app->pBuffer;
|
||||
|
||||
Mob* vendor = entity_list.GetMob(mp->npcid);
|
||||
|
||||
if (vendor == 0 || !vendor->IsNPC() || vendor->GetClass() != Class::Merchant)
|
||||
@@ -14390,35 +14389,51 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
if (DistanceSquared(m_Position, vendor->GetPosition()) > USE_NPC_RANGE2)
|
||||
return;
|
||||
|
||||
uint32 price = 0;
|
||||
uint32 itemid = GetItemIDAt(mp->itemslot);
|
||||
if (itemid == 0)
|
||||
return;
|
||||
|
||||
const EQ::ItemData* item = database.GetItem(itemid);
|
||||
EQ::ItemInstance* inst = GetInv().GetItem(mp->itemslot);
|
||||
if (!item || !inst) {
|
||||
Message(Chat::Red, "You seemed to have misplaced that item..");
|
||||
Message(Chat::Red, "You seem to have misplaced that item..");
|
||||
return;
|
||||
}
|
||||
if (mp->quantity > 1)
|
||||
{
|
||||
|
||||
if (!item->NoDrop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mp->quantity > 1) {
|
||||
if ((inst->GetCharges() < 0) || (mp->quantity > (uint32)inst->GetCharges()))
|
||||
return;
|
||||
}
|
||||
|
||||
if (!item->NoDrop) {
|
||||
//Message(Chat::Red,"%s tells you, 'LOL NOPE'", vendor->GetName());
|
||||
return;
|
||||
// Check for veto from script
|
||||
if (parse->PlayerHasQuestSub(EVENT_MERCHANT_PRESELL)) {
|
||||
std::string export_string = fmt::format("{} {} {}", mp->itemslot, itemid, inst->GetItemType());
|
||||
std::vector<std::any> extra_pointers = { vendor, inst };
|
||||
|
||||
int result = parse->EventPlayer(EVENT_MERCHANT_PRESELL, this, export_string, 0, &extra_pointers);
|
||||
// CANCEL: If a script returns -1 for this event, the sale wil be cancelled. Sends a dummy packet sent to satisfy the client
|
||||
if (result == -1) {
|
||||
auto outapp = new EQApplicationPacket(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct));
|
||||
Merchant_Purchase_Struct* mco = (Merchant_Purchase_Struct*)outapp->pBuffer;
|
||||
mco->npcid = vendor->GetID();
|
||||
mco->itemslot = -1; // Critical or the client will remove the item visually
|
||||
mco->quantity = 0;
|
||||
mco->price = 0;
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 cost_quantity = mp->quantity;
|
||||
if (inst->IsCharged())
|
||||
uint32 cost_quantity = 1;
|
||||
|
||||
uint32 i;
|
||||
uint32 cost_quantity = inst->IsCharged() ? 1 : mp->quantity;
|
||||
uint32 price = 0;
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
for (i = 1; i <= cost_quantity; i++) {
|
||||
for (uint32 i = 1; i <= cost_quantity; i++) {
|
||||
price = (uint32)(item->Price * i) * Client::CalcPriceMod(vendor, true);
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
@@ -14436,7 +14451,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 1; i <= cost_quantity; i++) {
|
||||
for (uint32 i = 1; i <= cost_quantity; i++) {
|
||||
price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod)) + 0.5); // need to round up, because client does it automatically when displaying price
|
||||
if (price > 4000000000) {
|
||||
cost_quantity = i;
|
||||
@@ -14448,6 +14463,7 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
|
||||
AddMoneyToPP(price);
|
||||
|
||||
// Update merchant stock and refresh client
|
||||
if (inst->IsStackable() || inst->IsCharged())
|
||||
{
|
||||
unsigned int i_quan = inst->GetCharges();
|
||||
@@ -14561,6 +14577,8 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
SendMoneyUpdate();
|
||||
|
||||
RDTSC_Timer t1(true);
|
||||
t1.start();
|
||||
Save(1);
|
||||
t1.stop();
|
||||
@@ -14679,6 +14697,11 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
|
||||
if ((tabs_to_display & Parcel) == Parcel) {
|
||||
SendBulkParcels();
|
||||
}
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_MERCHANT_OPEN)) {
|
||||
std::vector<std::any> extra_pointers = { tmp };
|
||||
parse->EventPlayer(EVENT_MERCHANT_OPEN, this, "", 0, &extra_pointers);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
+60
-25
@@ -17,11 +17,13 @@
|
||||
*/
|
||||
#ifdef EMBPERL
|
||||
|
||||
#include "zone/embparser.h"
|
||||
|
||||
#include "common/compiler_macros.h"
|
||||
#include "common/features.h"
|
||||
#include "common/misc_functions.h"
|
||||
#include "common/seperator.h"
|
||||
#include "common/strings.h"
|
||||
#include "zone/embparser.h"
|
||||
#include "zone/masterentity.h"
|
||||
#include "zone/qglobals.h"
|
||||
#include "zone/questmgr.h"
|
||||
@@ -161,8 +163,10 @@ const char* QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_LANGUAGE_SKILL_UP",
|
||||
"EVENT_ALT_CURRENCY_MERCHANT_BUY",
|
||||
"EVENT_ALT_CURRENCY_MERCHANT_SELL",
|
||||
"EVENT_MERCHANT_OPEN",
|
||||
"EVENT_MERCHANT_BUY",
|
||||
"EVENT_MERCHANT_SELL",
|
||||
"EVENT_MERCHANT_PRESELL",
|
||||
"EVENT_INSPECT",
|
||||
"EVENT_TASK_BEFORE_UPDATE",
|
||||
"EVENT_AA_BUY",
|
||||
@@ -398,6 +402,8 @@ int PerlembParser::EventCommon(
|
||||
zone
|
||||
);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PerlembParser::EventNPC(
|
||||
@@ -1211,31 +1217,33 @@ QuestType PerlembParser::GetQuestTypes(
|
||||
event_id == EVENT_SPELL_EFFECT_TRANSLOCATE_COMPLETE
|
||||
) {
|
||||
return is_global ? QuestType::SpellGlobal : QuestType::Spell;
|
||||
} else {
|
||||
if (npc_mob) {
|
||||
if (!inst) {
|
||||
if (npc_mob->IsBot()) {
|
||||
return is_global ? QuestType::BotGlobal : QuestType::Bot;
|
||||
} else if (npc_mob->IsMerc()) {
|
||||
return is_global ? QuestType::MercGlobal : QuestType::Merc;
|
||||
} else if (npc_mob->IsNPC()) {
|
||||
return is_global ? QuestType::NPCGlobal : QuestType::NPC;
|
||||
}
|
||||
} else {
|
||||
return is_global ? QuestType::ItemGlobal : QuestType::Item;
|
||||
}
|
||||
} else if (!npc_mob && mob) {
|
||||
if (!inst) {
|
||||
if (mob->IsClient()) {
|
||||
return is_global ? QuestType::PlayerGlobal : QuestType::Player;
|
||||
}
|
||||
} else {
|
||||
return is_global ? QuestType::ItemGlobal : QuestType::Item;
|
||||
}
|
||||
} else if (zone) {
|
||||
return is_global ? QuestType::ZoneGlobal : QuestType::Zone;
|
||||
}
|
||||
}
|
||||
|
||||
if (npc_mob) {
|
||||
if (!inst) {
|
||||
if (npc_mob->IsBot()) {
|
||||
return is_global ? QuestType::BotGlobal : QuestType::Bot;
|
||||
} else if (npc_mob->IsMerc()) {
|
||||
return is_global ? QuestType::MercGlobal : QuestType::Merc;
|
||||
} else if (npc_mob->IsNPC()) {
|
||||
return is_global ? QuestType::NPCGlobal : QuestType::NPC;
|
||||
}
|
||||
} else {
|
||||
return is_global ? QuestType::ItemGlobal : QuestType::Item;
|
||||
}
|
||||
} else if (mob) {
|
||||
if (!inst) {
|
||||
if (mob->IsClient()) {
|
||||
return is_global ? QuestType::PlayerGlobal : QuestType::Player;
|
||||
}
|
||||
} else {
|
||||
return is_global ? QuestType::ItemGlobal : QuestType::Item;
|
||||
}
|
||||
} else if (zone) {
|
||||
return is_global ? QuestType::ZoneGlobal : QuestType::Zone;
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
std::string PerlembParser::GetQuestPackageName(
|
||||
@@ -2283,6 +2291,33 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_MERCHANT_OPEN: {
|
||||
if (!extra_pointers || extra_pointers->size() < 1) break;
|
||||
|
||||
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
|
||||
if (!mob_ptr) break;
|
||||
|
||||
ExportVar(package_name.c_str(), "other", "Mob", mob_ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_MERCHANT_PRESELL: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "slot_id", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "item_id", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "item_type", sep.arg[2]);
|
||||
|
||||
if (!extra_pointers || extra_pointers->size() < 2) break;
|
||||
|
||||
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
|
||||
auto inst_ptr = std::any_cast<EQ::ItemInstance*>(extra_pointers->at(1));
|
||||
if (!mob_ptr || !inst_ptr) break;
|
||||
|
||||
ExportVar(package_name.c_str(), "other", "Mob", mob_ptr);
|
||||
ExportVar(package_name.c_str(), "item", "ItemInstance", inst_ptr);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_AA_BUY: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "aa_cost", sep.arg[0]);
|
||||
|
||||
@@ -116,8 +116,10 @@ enum QuestEventID {
|
||||
EVENT_LANGUAGE_SKILL_UP,
|
||||
EVENT_ALT_CURRENCY_MERCHANT_BUY,
|
||||
EVENT_ALT_CURRENCY_MERCHANT_SELL,
|
||||
EVENT_MERCHANT_OPEN,
|
||||
EVENT_MERCHANT_BUY,
|
||||
EVENT_MERCHANT_SELL,
|
||||
EVENT_MERCHANT_PRESELL,
|
||||
EVENT_INSPECT,
|
||||
EVENT_TASK_BEFORE_UPDATE,
|
||||
EVENT_AA_BUY,
|
||||
|
||||
@@ -6968,8 +6968,10 @@ luabind::scope lua_register_events() {
|
||||
luabind::value("language_skill_up", static_cast<int>(EVENT_LANGUAGE_SKILL_UP)),
|
||||
luabind::value("alt_currency_merchant_buy", static_cast<int>(EVENT_ALT_CURRENCY_MERCHANT_BUY)),
|
||||
luabind::value("alt_currency_merchant_sell", static_cast<int>(EVENT_ALT_CURRENCY_MERCHANT_SELL)),
|
||||
luabind::value("merchant_open", static_cast<int>(EVENT_MERCHANT_OPEN)),
|
||||
luabind::value("merchant_buy", static_cast<int>(EVENT_MERCHANT_BUY)),
|
||||
luabind::value("merchant_sell", static_cast<int>(EVENT_MERCHANT_SELL)),
|
||||
luabind::value("merchant_presell", static_cast<int>(EVENT_MERCHANT_PRESELL)),
|
||||
luabind::value("inspect", static_cast<int>(EVENT_INSPECT)),
|
||||
luabind::value("task_before_update", static_cast<int>(EVENT_TASK_BEFORE_UPDATE)),
|
||||
luabind::value("aa_buy", static_cast<int>(EVENT_AA_BUY)),
|
||||
|
||||
@@ -158,6 +158,11 @@ uint32 Lua_ItemInst::GetItemScriptID() {
|
||||
return self->GetItemScriptID();
|
||||
}
|
||||
|
||||
uint8 Lua_ItemInst::GetItemType() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetItemType();
|
||||
}
|
||||
|
||||
int Lua_ItemInst::GetCharges() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetCharges();
|
||||
@@ -497,6 +502,7 @@ luabind::scope lua_register_iteminst() {
|
||||
.def("GetItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItemID)
|
||||
.def("GetItemLink", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemLink)
|
||||
.def("GetItemScriptID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemScriptID)
|
||||
.def("GetItemType", (uint8(Lua_ItemInst::*)(void)) & Lua_ItemInst::GetItemType)
|
||||
.def("GetMaxEvolveLvl", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetMaxEvolveLvl)
|
||||
.def("GetName", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetName)
|
||||
.def("GetSerialNumber", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetSerialNumber)
|
||||
|
||||
@@ -71,6 +71,7 @@ public:
|
||||
bool IsAmmo();
|
||||
uint32 GetID();
|
||||
uint32 GetItemScriptID();
|
||||
uint8 GetItemType();
|
||||
int GetCharges();
|
||||
void SetCharges(int charges);
|
||||
uint32 GetPrice();
|
||||
|
||||
@@ -82,6 +82,16 @@ Lua_Packet::Lua_Packet(const Lua_Packet& o) {
|
||||
}
|
||||
}
|
||||
|
||||
Lua_Packet::~Lua_Packet()
|
||||
{
|
||||
if (owned_) {
|
||||
EQApplicationPacket* ptr = GetLuaPtrData();
|
||||
if (ptr) {
|
||||
delete ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int Lua_Packet::GetSize() {
|
||||
Lua_Safe_Call_Int();
|
||||
return static_cast<int>(self->size);
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ public:
|
||||
Lua_Packet(int opcode, int size, bool raw);
|
||||
Lua_Packet& operator=(const Lua_Packet& o);
|
||||
Lua_Packet(const Lua_Packet& o);
|
||||
virtual ~Lua_Packet() { if(owned_) { EQApplicationPacket *ptr = GetLuaPtrData(); if(ptr) { delete ptr; } } }
|
||||
virtual ~Lua_Packet();
|
||||
|
||||
int GetSize();
|
||||
int GetOpcode();
|
||||
|
||||
@@ -160,8 +160,10 @@ const char *LuaEvents[_LargestEventID] = {
|
||||
"event_language_skill_up",
|
||||
"event_alt_currency_merchant_buy",
|
||||
"event_alt_currency_merchant_sell",
|
||||
"event_merchant_open",
|
||||
"event_merchant_buy",
|
||||
"event_merchant_sell",
|
||||
"event_merchant_presell",
|
||||
"event_inspect",
|
||||
"event_task_before_update",
|
||||
"event_aa_buy",
|
||||
@@ -335,8 +337,10 @@ LuaParser::LuaParser() {
|
||||
PlayerArgumentDispatch[EVENT_LANGUAGE_SKILL_UP] = handle_player_language_skill_up;
|
||||
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_MERCHANT_BUY] = handle_player_alt_currency_merchant;
|
||||
PlayerArgumentDispatch[EVENT_ALT_CURRENCY_MERCHANT_SELL] = handle_player_alt_currency_merchant;
|
||||
PlayerArgumentDispatch[EVENT_MERCHANT_OPEN] = handle_player_merchant_open;
|
||||
PlayerArgumentDispatch[EVENT_MERCHANT_BUY] = handle_player_merchant;
|
||||
PlayerArgumentDispatch[EVENT_MERCHANT_SELL] = handle_player_merchant;
|
||||
PlayerArgumentDispatch[EVENT_MERCHANT_PRESELL] = handle_player_merchant_presell;
|
||||
PlayerArgumentDispatch[EVENT_INSPECT] = handle_player_inspect;
|
||||
PlayerArgumentDispatch[EVENT_AA_BUY] = handle_player_aa_buy;
|
||||
PlayerArgumentDispatch[EVENT_AA_GAIN] = handle_player_aa_gain;
|
||||
|
||||
@@ -2340,6 +2340,53 @@ void handle_player_merchant(
|
||||
lua_setfield(L, -2, "item_cost");
|
||||
}
|
||||
|
||||
void handle_player_merchant_open(
|
||||
QuestInterface* parse,
|
||||
lua_State* L,
|
||||
Client* client,
|
||||
std::string data,
|
||||
uint32 extra_data,
|
||||
std::vector<std::any>* extra_pointers
|
||||
) {
|
||||
if (!extra_pointers || extra_pointers->size() < 1) return;
|
||||
|
||||
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
|
||||
if (!mob_ptr) return;
|
||||
|
||||
Lua_Mob l_mob(mob_ptr);
|
||||
luabind::adl::object l_mob_o = luabind::adl::object(L, l_mob);
|
||||
l_mob_o.push(L);
|
||||
lua_setfield(L, -2, "other");
|
||||
}
|
||||
|
||||
void handle_player_merchant_presell(
|
||||
QuestInterface* parse,
|
||||
lua_State* L,
|
||||
Client* client,
|
||||
std::string data,
|
||||
uint32 extra_data,
|
||||
std::vector<std::any>* extra_pointers
|
||||
) {
|
||||
Seperator sep(data.c_str());
|
||||
lua_pushinteger(L, Strings::ToInt(sep.arg[0])); lua_setfield(L, -2, "slot_id");
|
||||
lua_pushinteger(L, Strings::ToInt(sep.arg[1])); lua_setfield(L, -2, "item_id");
|
||||
lua_pushinteger(L, Strings::ToInt(sep.arg[2])); lua_setfield(L, -2, "item_type");
|
||||
|
||||
if (!extra_pointers || extra_pointers->size() < 2) return;
|
||||
|
||||
auto mob_ptr = std::any_cast<Mob*>(extra_pointers->at(0));
|
||||
auto inst_ptr = std::any_cast<EQ::ItemInstance*>(extra_pointers->at(1));
|
||||
if (!mob_ptr || !inst_ptr) return;
|
||||
|
||||
Lua_Mob l_mob(mob_ptr);
|
||||
luabind::adl::object(L, l_mob).push(L);
|
||||
lua_setfield(L, -2, "other");
|
||||
|
||||
Lua_ItemInst l_iteminst(inst_ptr);
|
||||
luabind::adl::object(L, l_iteminst).push(L);
|
||||
lua_setfield(L, -2, "item");
|
||||
}
|
||||
|
||||
void handle_player_augment_insert(
|
||||
QuestInterface *parse,
|
||||
lua_State* L,
|
||||
|
||||
@@ -671,6 +671,24 @@ void handle_player_merchant(
|
||||
std::vector<std::any> *extra_pointers
|
||||
);
|
||||
|
||||
void handle_player_merchant_open(
|
||||
QuestInterface* parse,
|
||||
lua_State* L,
|
||||
Client* client,
|
||||
std::string data,
|
||||
uint32 extra_data,
|
||||
std::vector<std::any>* extra_pointers
|
||||
);
|
||||
|
||||
void handle_player_merchant_presell(
|
||||
QuestInterface* parse,
|
||||
lua_State* L,
|
||||
Client* client,
|
||||
std::string data,
|
||||
uint32 extra_data,
|
||||
std::vector<std::any>* extra_pointers
|
||||
);
|
||||
|
||||
void handle_player_inspect(
|
||||
QuestInterface *parse,
|
||||
lua_State* L,
|
||||
|
||||
+2
-1
@@ -48,7 +48,8 @@ public:
|
||||
Lua_Ptr(T *d) : d_(d) {
|
||||
}
|
||||
|
||||
~Lua_Ptr() {
|
||||
|
||||
virtual ~Lua_Ptr() {
|
||||
}
|
||||
|
||||
T *GetLuaPtrData() {
|
||||
|
||||
+20
-32
@@ -711,48 +711,36 @@ void UpdateWindowTitle(char *iNewTitle)
|
||||
|
||||
bool CheckForCompatibleQuestPlugins()
|
||||
{
|
||||
const std::vector<std::pair<std::string, bool *>> directories = {
|
||||
{"lua_modules", nullptr},
|
||||
{"plugins", nullptr}
|
||||
};
|
||||
|
||||
bool lua_found = false;
|
||||
bool perl_found = false;
|
||||
|
||||
try {
|
||||
for (const auto &[directory, flag]: directories) {
|
||||
std::string dir_path = PathManager::Instance()->GetServerPath() + "/" + directory;
|
||||
if (!File::Exists(dir_path)) { continue; }
|
||||
|
||||
for (const auto &file: fs::directory_iterator(dir_path)) {
|
||||
auto check_dir = [&](const std::string& dir_path, bool& found) {
|
||||
if (!File::Exists(dir_path)) { return; }
|
||||
try {
|
||||
for (const auto& file : fs::directory_iterator(dir_path)) {
|
||||
if (!file.is_regular_file()) { continue; }
|
||||
|
||||
std::string file_path = file.path().string();
|
||||
if (!File::Exists(file_path)) { continue; }
|
||||
|
||||
auto r = File::GetContents(file_path);
|
||||
if (!Strings::Contains(r.contents, "CheckHandin")) { continue; }
|
||||
|
||||
if (directory == "lua_modules") {
|
||||
lua_found = true;
|
||||
auto r = File::GetContents(file.path().string());
|
||||
if (Strings::Contains(r.contents, "CheckHandin")) {
|
||||
found = true;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
perl_found = true;
|
||||
}
|
||||
|
||||
if (lua_found && perl_found) { return true; }
|
||||
}
|
||||
}
|
||||
} catch (const fs::filesystem_error &ex) {
|
||||
LogError("Failed to check for compatible quest plugins: {}", ex.what());
|
||||
catch (const fs::filesystem_error& ex) {
|
||||
LogError("Failed to check for compatible quest plugins: {}", ex.what());
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto& path : PathManager::Instance()->GetLuaModulePaths()) {
|
||||
check_dir(path, lua_found);
|
||||
}
|
||||
|
||||
if (!lua_found) {
|
||||
LogError("Failed to find CheckHandin in lua_modules");
|
||||
}
|
||||
if (!perl_found) {
|
||||
LogError("Failed to find CheckHandin in plugins");
|
||||
for (const auto& path : PathManager::Instance()->GetPluginPaths()) {
|
||||
check_dir(path, perl_found);
|
||||
}
|
||||
|
||||
if (!lua_found) { LogError("Failed to find CheckHandin in the Lua module quest directories"); }
|
||||
if (!perl_found) { LogError("Failed to find CheckHandin in the Perl plugins quest directories");}
|
||||
|
||||
return lua_found && perl_found;
|
||||
}
|
||||
|
||||
+24
-23
@@ -512,29 +512,6 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk)
|
||||
|
||||
bool found_skill = false;
|
||||
|
||||
if (
|
||||
ca_atk->m_atk == 100 &&
|
||||
ca_atk->m_skill == EQ::skills::SkillKick &&
|
||||
can_use_kick
|
||||
) {
|
||||
if (GetTarget() != this) {
|
||||
CheckIncreaseSkill(EQ::skills::SkillKick, GetTarget(), 10);
|
||||
DoAnim(animKick, 0, false);
|
||||
|
||||
int hate_override = 0;
|
||||
if (GetWeaponDamage(GetTarget(), GetInv().GetItem(EQ::invslot::slotFeet)) <= 0) {
|
||||
damage = -5;
|
||||
} else {
|
||||
hate_override = damage = GetBaseSkillDamage(EQ::skills::SkillKick, GetTarget());
|
||||
}
|
||||
|
||||
reuse_time = KickReuseTime - 1 - skill_reduction;
|
||||
DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillKick, damage, 0, hate_override, reuse_time);
|
||||
|
||||
found_skill = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (class_id == Class::Monk) {
|
||||
reuse_time = MonkSpecialAttack(GetTarget(), ca_atk->m_skill) - 1 - skill_reduction;
|
||||
|
||||
@@ -596,6 +573,30 @@ void Client::OPCombatAbility(const CombatAbility_Struct *ca_atk)
|
||||
|
||||
found_skill = true;
|
||||
}
|
||||
else {
|
||||
if (
|
||||
ca_atk->m_atk == 100 &&
|
||||
ca_atk->m_skill == EQ::skills::SkillKick &&
|
||||
can_use_kick
|
||||
) {
|
||||
if (GetTarget() != this) {
|
||||
CheckIncreaseSkill(EQ::skills::SkillKick, GetTarget(), 10);
|
||||
DoAnim(animKick, 0, false);
|
||||
|
||||
int hate_override = 0;
|
||||
if (GetWeaponDamage(GetTarget(), GetInv().GetItem(EQ::invslot::slotFeet)) <= 0) {
|
||||
damage = -5;
|
||||
} else {
|
||||
hate_override = damage = GetBaseSkillDamage(EQ::skills::SkillKick, GetTarget());
|
||||
}
|
||||
|
||||
reuse_time = KickReuseTime - 1 - skill_reduction;
|
||||
DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillKick, damage, 0, hate_override, reuse_time);
|
||||
|
||||
found_skill = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
ca_atk->m_atk == 100 &&
|
||||
|
||||
@@ -1362,6 +1362,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (IsBot()) {
|
||||
currently_fleeing = true;
|
||||
CastToBot()->BotProcessBlind();
|
||||
}
|
||||
else if (!IsClient()) {
|
||||
CalculateNewFearpoint();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user