mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-22 16:28:28 +00:00
Compare commits
57 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c3481daf9 | |||
| b93dec357f | |||
| 2f4af4f0c2 | |||
| a0f2a8a743 | |||
| 4c7016bd7b | |||
| 6c18cd0bee | |||
| 87e63e1e36 | |||
| a771882cff | |||
| b3f6a8c55f | |||
| 605502cd9d | |||
| 4712ca471b | |||
| 836c3d6596 | |||
| 53169ae217 | |||
| fd31915fae | |||
| 52763b6dd2 | |||
| 51cd43b4ea | |||
| 2db84f5a4f | |||
| 79cc2d5351 | |||
| d31cb09214 | |||
| 8bedcd8751 | |||
| 473c5096f6 | |||
| 0b181d5048 | |||
| a7e9af2d27 | |||
| 7a72d5d67e | |||
| 25872203ff | |||
| 6396a6fbef | |||
| 6db0a5c3f0 | |||
| 4fa9e1d66f | |||
| 556af8c5e9 | |||
| f3ef8a0993 | |||
| 267c280db8 | |||
| e06c7d7735 | |||
| 028ebc3a0c | |||
| b3bd44cd76 | |||
| 75a627a3a2 | |||
| 0194aedc92 | |||
| 5cc87cbda7 | |||
| 703862d977 | |||
| 6e325c1ee3 | |||
| 933b83add6 | |||
| b3cd4e63f1 | |||
| 3c894cb533 | |||
| b19ad64800 | |||
| 2cd3d27c67 | |||
| d3b46becd0 | |||
| 286479198f | |||
| 21ec832ca6 | |||
| bdf5f8b4a3 | |||
| 4ca6485398 | |||
| 0c9c2e25c1 | |||
| 7e651877c7 | |||
| 9739c1c8ef | |||
| 8aae59eebe | |||
| c1b07afae9 | |||
| 9c238cd08d | |||
| 33adb9bcc1 | |||
| 2e8bf82861 |
+1
-1
@@ -15,7 +15,7 @@ volumes:
|
||||
|
||||
steps:
|
||||
- name: Build Linux X64
|
||||
image: akkadius/eqemu-server:v13
|
||||
image: akkadius/eqemu-server:v14
|
||||
environment:
|
||||
GITHUB_TOKEN:
|
||||
from_secret: GH_RELEASE_GITHUB_API_TOKEN
|
||||
|
||||
+136
@@ -1,3 +1,139 @@
|
||||
## [22.39.1] - 12/31/2023
|
||||
|
||||
### Code
|
||||
|
||||
* Appearance not appearence ([#3819](https://github.com/EQEmu/Server/pull/3819)) @Kinglykrab 2023-12-30
|
||||
* Delete errmsg.h in common and zone ([#3821](https://github.com/EQEmu/Server/pull/3821)) @Kinglykrab 2023-12-30
|
||||
* Gender constants cleanup ([#3817](https://github.com/EQEmu/Server/pull/3817)) @Kinglykrab 2023-12-30
|
||||
* Remove MakeSpawnUpdateNoDelta from mob.cpp/mob.h ([#3816](https://github.com/EQEmu/Server/pull/3816)) @Kinglykrab 2023-12-31
|
||||
* Remove SendStunAppearance from mob.cpp/mob.h ([#3818](https://github.com/EQEmu/Server/pull/3818)) @Kinglykrab 2023-12-31
|
||||
* Remove unused PlotPosition methods from mob.cpp/mob.h ([#3820](https://github.com/EQEmu/Server/pull/3820)) @Kinglykrab 2023-12-31
|
||||
|
||||
### Database
|
||||
|
||||
* Database update improvements, content db and terminal checks ([#3814](https://github.com/EQEmu/Server/pull/3814)) @Akkadius 2023-12-31
|
||||
|
||||
### Fixes
|
||||
|
||||
* Client:SetBucket Overload Incorrectly Named ([#3825](https://github.com/EQEmu/Server/pull/3825)) @fryguy503 2023-12-30
|
||||
* Fix crash in Client::Handle_OP_GMGoto ([#3832](https://github.com/EQEmu/Server/pull/3832)) @Kinglykrab 2023-12-31
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add HasItemOnCorpse() to Perl/Lua ([#3824](https://github.com/EQEmu/Server/pull/3824)) @Kinglykrab 2023-12-31
|
||||
* Fix issue with death events. ([#3823](https://github.com/EQEmu/Server/pull/3823)) @Kinglykrab 2023-12-31
|
||||
|
||||
### Repositories
|
||||
|
||||
* Protected extended repositories from being overwritten if exists ([#3815](https://github.com/EQEmu/Server/pull/3815)) @Akkadius 2023-12-31
|
||||
|
||||
## [22.39.0] - 12/27/2023
|
||||
|
||||
### Character
|
||||
|
||||
* Fix character copier due to schema change ([#3805](https://github.com/EQEmu/Server/pull/3805)) @Akkadius 2023-12-28
|
||||
|
||||
### Combat
|
||||
|
||||
* Disarm was not dropping item to ground due to bug ([#3811](https://github.com/EQEmu/Server/pull/3811)) @noudess 2023-12-27
|
||||
|
||||
### Logs
|
||||
|
||||
* Bulk insert new log settings ([#3810](https://github.com/EQEmu/Server/pull/3810)) @Akkadius 2023-12-28
|
||||
* Reclassify unhelpful Info message ([#3809](https://github.com/EQEmu/Server/pull/3809)) @Akkadius 2023-12-28
|
||||
|
||||
### MySQL
|
||||
|
||||
* Fix MySQL Query error formatting ([#3808](https://github.com/EQEmu/Server/pull/3808)) @Akkadius 2023-12-28
|
||||
|
||||
### Objects
|
||||
|
||||
* Remove "No objects to load for zone" error message ([#3807](https://github.com/EQEmu/Server/pull/3807)) @Akkadius 2023-12-28
|
||||
|
||||
### Player Events
|
||||
|
||||
* Bulk replace settings on boot ([#3806](https://github.com/EQEmu/Server/pull/3806)) @Akkadius 2023-12-28
|
||||
|
||||
### Tasks
|
||||
|
||||
* Add enabled column ([#3804](https://github.com/EQEmu/Server/pull/3804)) @Akkadius 2023-12-28
|
||||
|
||||
## [22.38.0] - 12/26/2023
|
||||
|
||||
### Bots
|
||||
|
||||
* Remove unnecessary error on SetItemReuse ([#3795](https://github.com/EQEmu/Server/pull/3795)) @nytmyr 2023-12-20
|
||||
|
||||
### Code
|
||||
|
||||
* Consolidate GetHateRandom(), GetHateRandomBot(), GetHateRandomClient(), and GetHateRandomNPC() ([#3794](https://github.com/EQEmu/Server/pull/3794)) @Kinglykrab 2023-12-25
|
||||
* Race constants refactor ([#3782](https://github.com/EQEmu/Server/pull/3782)) @Akkadius 2023-12-23
|
||||
|
||||
### Database
|
||||
|
||||
* Fix issue with saylinks query in MySQL 8.0+ ([#3800](https://github.com/EQEmu/Server/pull/3800)) @Akkadius 2023-12-24
|
||||
* Update faction mods with Live data ([#3799](https://github.com/EQEmu/Server/pull/3799)) @joligario 2023-12-23
|
||||
|
||||
### Fixes
|
||||
|
||||
* Disable Hide/Improved Hide on Trap damage ([#3791](https://github.com/EQEmu/Server/pull/3791)) @Kinglykrab 2023-12-19
|
||||
* Fix Bard Invisibility Songs breaking every 4 ticks ([#3783](https://github.com/EQEmu/Server/pull/3783)) @Kinglykrab 2023-12-19
|
||||
* Fix can_riposte parameter in DoMeleeSkillAttackDmg ([#3792](https://github.com/EQEmu/Server/pull/3792)) @Kinglykrab 2023-12-19
|
||||
|
||||
### Forage
|
||||
|
||||
* Add a rule to disabled using common_food_ids from the list in forage.cpp. currently set to enabled. ([#3796](https://github.com/EQEmu/Server/pull/3796)) @regneq 2023-12-22
|
||||
|
||||
### NPC
|
||||
|
||||
* Support for multiple emotes per type, emote variables ([#3801](https://github.com/EQEmu/Server/pull/3801)) @regneq 2023-12-25
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add GetHateTopBot(), GetHateTopClient(), and GetHateTopNPC() to Perl/Lua ([#3793](https://github.com/EQEmu/Server/pull/3793)) @Kinglykrab 2023-12-22
|
||||
* Add SummonItemIntoInventory() to Perl/Lua ([#3797](https://github.com/EQEmu/Server/pull/3797)) @Kinglykrab 2023-12-22
|
||||
|
||||
### Repositories
|
||||
|
||||
* Add ReplaceOne and ReplaceMany ([#3802](https://github.com/EQEmu/Server/pull/3802)) @Akkadius 2023-12-26
|
||||
|
||||
## [22.37.0] - 12/18/2023
|
||||
|
||||
### Bots
|
||||
|
||||
* Add ScanCloseMobs support to fix AEs ([#3786](https://github.com/EQEmu/Server/pull/3786)) @nytmyr 2023-12-18
|
||||
* Expand ^itemuse options ([#3756](https://github.com/EQEmu/Server/pull/3756)) @nytmyr 2023-12-17
|
||||
* Fix ^defensive from checking aggressive disciplines. ([#3787](https://github.com/EQEmu/Server/pull/3787)) @nytmyr 2023-12-18
|
||||
* Fix ^oo autodefend from sending bots/pets to invalid haters ([#3772](https://github.com/EQEmu/Server/pull/3772)) @nytmyr 2023-12-16
|
||||
* Fix unnecessary failed to save timer error ([#3788](https://github.com/EQEmu/Server/pull/3788)) @nytmyr 2023-12-18
|
||||
* [Quest API] Add ^clickitem, ^timer, fix GetBestBotSpellForCure ([#3755](https://github.com/EQEmu/Server/pull/3755)) @nytmyr 2023-12-17
|
||||
|
||||
### CI
|
||||
|
||||
* Switch to use clang for Linux builds (speed) ([#3777](https://github.com/EQEmu/Server/pull/3777)) @Akkadius 2023-12-17
|
||||
|
||||
### Compilation
|
||||
|
||||
* Use pre-compiled headers for Windows (speed) ([#3778](https://github.com/EQEmu/Server/pull/3778)) @Akkadius 2023-12-18
|
||||
|
||||
### Fixes
|
||||
|
||||
* Drop Invisibility when hit by traps ([#3785](https://github.com/EQEmu/Server/pull/3785)) @Kinglykrab 2023-12-18
|
||||
* Fix NPCs routing to 0.0, 0.0 on #summon ([#3780](https://github.com/EQEmu/Server/pull/3780)) @Kinglykrab 2023-12-18
|
||||
* Fix bad merge @Akkadius 2023-12-17
|
||||
* Fix issue with HOTBonusHealingSplitOverDuration Rule ([#3776](https://github.com/EQEmu/Server/pull/3776)) @Kinglykrab 2023-12-17
|
||||
* Fixed the discrepacy with time using command #time and in quests. ([#3767](https://github.com/EQEmu/Server/pull/3767)) @regneq 2023-12-17
|
||||
* Send Entity ID in Death Events to resolve #3721 ([#3779](https://github.com/EQEmu/Server/pull/3779)) @Kinglykrab 2023-12-18
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add EVENT_ALT_CURRENCY_GAIN and EVENT_ALT_CURRENCY_LOSS to Perl/Lua ([#3734](https://github.com/EQEmu/Server/pull/3734)) @Kinglykrab 2023-12-17
|
||||
* Add EVENT_CRYSTAL_GAIN and EVENT_CRYSTAL_LOSS to Perl/Lua ([#3735](https://github.com/EQEmu/Server/pull/3735)) @Kinglykrab 2023-12-17
|
||||
* Add EVENT_LDON_POINTS_GAIN and EVENT_LDON_POINTS_LOSS to Perl/Lua ([#3742](https://github.com/EQEmu/Server/pull/3742)) @Kinglykrab 2023-12-17
|
||||
* Add EVENT_LEVEL_UP and EVENT_LEVEL_DOWN to Bots ([#3750](https://github.com/EQEmu/Server/pull/3750)) @Kinglykrab 2023-12-17
|
||||
* Add EVENT_LOOT_ADDED to Perl/Lua ([#3739](https://github.com/EQEmu/Server/pull/3739)) @Kinglykrab 2023-12-17
|
||||
* Add GetNPCAggro() and SetNPCAggro() to Perl/Lua ([#3781](https://github.com/EQEmu/Server/pull/3781)) @Kinglykrab 2023-12-18
|
||||
|
||||
## [22.36.0] - 12/16/2023
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -17,6 +17,7 @@ SET(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
SET(CMAKE_CXX_EXTENSIONS OFF)
|
||||
|
||||
OPTION(EQEMU_BUILD_STATIC "Build with static linking" OFF)
|
||||
OPTION(EQEMU_BUILD_PCH "Build with precompiled headers (Windows)" ON)
|
||||
|
||||
IF (EQEMU_BUILD_STATIC)
|
||||
SET(BUILD_SHARED_LIBS OFF)
|
||||
|
||||
@@ -13,7 +13,6 @@ SET(common_sources
|
||||
crc32.cpp
|
||||
database/database_dump_service.cpp
|
||||
database.cpp
|
||||
database_conversions.cpp
|
||||
database_instances.cpp
|
||||
database/database_update_manifest.cpp
|
||||
database/database_update_manifest_bots.cpp
|
||||
@@ -541,7 +540,6 @@ SET(common_headers
|
||||
events/player_event_logs.h
|
||||
events/player_event_discord_formatter.h
|
||||
events/player_events.h
|
||||
errmsg.h
|
||||
event_sub.h
|
||||
expedition_lockout_timer.h
|
||||
extprofile.h
|
||||
@@ -792,5 +790,8 @@ IF (UNIX)
|
||||
SET_SOURCE_FILES_PROPERTIES("patches/sod.cpp" "patches/sof.cpp" "patches/rof.cpp" "patches/rof2.cpp" "patches/uf.cpp" PROPERTIES COMPILE_FLAGS -O0)
|
||||
ENDIF (UNIX)
|
||||
|
||||
IF (WIN32 AND EQEMU_BUILD_PCH)
|
||||
TARGET_PRECOMPILE_HEADERS(common PRIVATE pch/pch.h)
|
||||
ENDIF()
|
||||
|
||||
SET(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin)
|
||||
|
||||
+9
-1
@@ -2246,6 +2246,11 @@ bool Database::CopyCharacter(
|
||||
row = results.begin();
|
||||
std::string new_character_id = row[0];
|
||||
|
||||
std::vector<std::string> tables_to_zero_id = {
|
||||
"keyring",
|
||||
"data_buckets",
|
||||
};
|
||||
|
||||
TransactionBegin();
|
||||
for (const auto &iter : DatabaseSchema::GetCharacterTables()) {
|
||||
std::string table_name = iter.first;
|
||||
@@ -2279,6 +2284,10 @@ bool Database::CopyCharacter(
|
||||
std::string column = columns[column_index];
|
||||
std::string value = row[column_index] ? row[column_index] : "null";
|
||||
|
||||
if (column == "id" && Strings::Contains(tables_to_zero_id, table_name)) {
|
||||
value = "0";
|
||||
}
|
||||
|
||||
if (column == character_id_column_name) {
|
||||
value = new_character_id;
|
||||
}
|
||||
@@ -2326,7 +2335,6 @@ bool Database::CopyCharacter(
|
||||
if (!insert.ErrorMessage().empty()) {
|
||||
TransactionRollback();
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,11 +226,6 @@ public:
|
||||
|
||||
void PurgeAllDeletedDataBuckets();
|
||||
|
||||
/* Database Conversions 'database_conversions.cpp' */
|
||||
|
||||
bool CheckDatabaseConversions();
|
||||
bool CheckDatabaseConvertCorpseDeblob();
|
||||
bool CheckDatabaseConvertPPDeblob();
|
||||
|
||||
/* Database Variables */
|
||||
|
||||
|
||||
@@ -76,9 +76,9 @@ void DatabaseUpdate::CheckDbUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
std::string DatabaseUpdate::GetQueryResult(std::string query)
|
||||
std::string DatabaseUpdate::GetQueryResult(const ManifestEntry& e)
|
||||
{
|
||||
auto results = m_database->QueryDatabase(query);
|
||||
auto results = (e.content_schema_update ? m_content_database : m_database)->QueryDatabase(e.check);
|
||||
|
||||
std::vector<std::string> result_lines = {};
|
||||
|
||||
@@ -121,6 +121,16 @@ bool DatabaseUpdate::ShouldRunMigration(ManifestEntry &e, std::string query_resu
|
||||
return false;
|
||||
}
|
||||
|
||||
// check if we are running in a terminal
|
||||
bool is_atty()
|
||||
{
|
||||
#ifdef _WINDOWS
|
||||
return ::_isatty(_fileno(stdin));
|
||||
#else
|
||||
return isatty(fileno(stdin));
|
||||
#endif
|
||||
}
|
||||
|
||||
// return true if we ran updates
|
||||
bool DatabaseUpdate::UpdateManifest(
|
||||
std::vector<ManifestEntry> entries,
|
||||
@@ -136,7 +146,7 @@ bool DatabaseUpdate::UpdateManifest(
|
||||
for (auto &e: entries) {
|
||||
if (e.version == version) {
|
||||
bool has_migration = true;
|
||||
std::string r = GetQueryResult(e.check);
|
||||
std::string r = GetQueryResult(e);
|
||||
if (ShouldRunMigration(e, r)) {
|
||||
has_migration = false;
|
||||
missing_migrations.emplace_back(e.version);
|
||||
@@ -179,7 +189,7 @@ bool DatabaseUpdate::UpdateManifest(
|
||||
if (e.version == m) {
|
||||
bool errored_migration = false;
|
||||
|
||||
auto r = m_database->QueryDatabaseMulti(e.sql);
|
||||
auto r = (e.content_schema_update ? m_content_database : m_database)->QueryDatabaseMulti(e.sql);
|
||||
|
||||
// ignore empty query result "errors"
|
||||
if (r.ErrorNumber() != 1065 && !r.ErrorMessage().empty()) {
|
||||
@@ -187,31 +197,38 @@ bool DatabaseUpdate::UpdateManifest(
|
||||
errored_migration = true;
|
||||
|
||||
LogInfo("Required database update failed. This could be a problem");
|
||||
LogInfo("Would you like to skip this update? [y/n] (Timeout 60s)");
|
||||
|
||||
// user input
|
||||
std::string input;
|
||||
bool gave_input = false;
|
||||
time_t start_time = time(nullptr);
|
||||
time_t wait_time_seconds = 60;
|
||||
// if terminal attached then prompt for skip
|
||||
if (is_atty()) {
|
||||
LogInfo("Would you like to skip this update? [y/n] (Timeout 60s)");
|
||||
|
||||
// spawn a concurrent thread that waits for input from std::cin
|
||||
std::thread t1(
|
||||
[&]() {
|
||||
std::cin >> input;
|
||||
gave_input = true;
|
||||
// user input
|
||||
std::string input;
|
||||
bool gave_input = false;
|
||||
time_t start_time = time(nullptr);
|
||||
time_t wait_time_seconds = 60;
|
||||
|
||||
// spawn a concurrent thread that waits for input from std::cin
|
||||
std::thread t1(
|
||||
[&]() {
|
||||
std::cin >> input;
|
||||
gave_input = true;
|
||||
}
|
||||
);
|
||||
t1.detach();
|
||||
|
||||
// check the inputReceived flag once every 50ms for 10 seconds
|
||||
while (time(nullptr) < start_time + wait_time_seconds && !gave_input) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
);
|
||||
t1.detach();
|
||||
|
||||
// check the inputReceived flag once every 50ms for 10 seconds
|
||||
while (time(nullptr) < start_time + wait_time_seconds && !gave_input) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
|
||||
// prompt for user skip
|
||||
if (Strings::Trim(input) == "y") {
|
||||
errored_migration = false;
|
||||
// prompt for user skip
|
||||
if (Strings::Trim(input) == "y") {
|
||||
errored_migration = false;
|
||||
LogInfo("Skipping update [{}] [{}]", e.version, e.description);
|
||||
}
|
||||
} else {
|
||||
errored_migration = true;
|
||||
LogInfo("Skipping update [{}] [{}]", e.version, e.description);
|
||||
}
|
||||
}
|
||||
@@ -247,6 +264,13 @@ DatabaseUpdate *DatabaseUpdate::SetDatabase(Database *db)
|
||||
return this;
|
||||
}
|
||||
|
||||
DatabaseUpdate *DatabaseUpdate::SetContentDatabase(Database *db)
|
||||
{
|
||||
m_content_database = db;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
bool DatabaseUpdate::CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b)
|
||||
{
|
||||
LogInfo("{}", Strings::Repeat("-", BREAK_LENGTH));
|
||||
|
||||
@@ -10,6 +10,7 @@ struct ManifestEntry {
|
||||
std::string condition{}; // condition or "match_type" - Possible values [contains|match|missing|empty|not_empty]
|
||||
std::string match{}; // match field that is not always used, but works in conjunction with "condition" values [missing|match|contains]
|
||||
std::string sql{}; // the SQL DDL that gets ran when the condition is true
|
||||
bool content_schema_update{}; // if true, this migration is a content schema update and should be ran against the content database
|
||||
};
|
||||
|
||||
struct DatabaseVersion {
|
||||
@@ -22,14 +23,16 @@ public:
|
||||
DatabaseVersion GetDatabaseVersions();
|
||||
DatabaseVersion GetBinaryDatabaseVersions();
|
||||
void CheckDbUpdates();
|
||||
std::string GetQueryResult(std::string query);
|
||||
std::string GetQueryResult(const ManifestEntry& e);
|
||||
static bool ShouldRunMigration(ManifestEntry &e, std::string query_result);
|
||||
bool UpdateManifest(std::vector<ManifestEntry> entries, int version_low, int version_high);
|
||||
|
||||
DatabaseUpdate *SetDatabase(Database *db);
|
||||
DatabaseUpdate *SetContentDatabase(Database *db);
|
||||
bool HasPendingUpdates();
|
||||
private:
|
||||
Database *m_database;
|
||||
Database *m_content_database;
|
||||
static bool CheckVersionsUpToDate(DatabaseVersion v, DatabaseVersion b);
|
||||
void InjectBotsVersionColumn();
|
||||
};
|
||||
|
||||
@@ -5016,7 +5016,7 @@ CREATE TABLE `spawn2_disabled` (
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
|
||||
INSERT INTO spawn2_disabled (spawn2_id, disabled) SELECT id, 1 FROM spawn2 WHERE enabled = 0;
|
||||
ALTER TABLE `spawn2` DROP COLUMN `enabled`;
|
||||
)"
|
||||
)",
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9242,
|
||||
@@ -5028,7 +5028,8 @@ ALTER TABLE `spawn2` DROP COLUMN `enabled`;
|
||||
ALTER TABLE `spawnentry`
|
||||
ADD COLUMN `min_time` smallint(4) NOT NULL DEFAULT 0 AFTER `condition_value_filter`,
|
||||
ADD COLUMN `max_time` smallint(4) NOT NULL DEFAULT 0 AFTER `min_time`;
|
||||
)"
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9243,
|
||||
@@ -5082,7 +5083,8 @@ INSERT INTO
|
||||
|
||||
DROP TABLE `starting_items`;
|
||||
RENAME TABLE `starting_items_new` TO `starting_items`;
|
||||
)"
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9244,
|
||||
@@ -5092,7 +5094,8 @@ RENAME TABLE `starting_items_new` TO `starting_items`;
|
||||
.match = "0000-00-00 00:00:00",
|
||||
.sql = R"(
|
||||
ALTER TABLE `items` MODIFY COLUMN `updated` datetime NULL DEFAULT NULL;
|
||||
)"
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9245,
|
||||
@@ -5104,7 +5107,8 @@ ALTER TABLE `items` MODIFY COLUMN `updated` datetime NULL DEFAULT NULL;
|
||||
ALTER TABLE `object` CHANGE COLUMN `unknown08` `size_percentage` float NOT NULL DEFAULT 0 AFTER `icon`;
|
||||
ALTER TABLE `object` CHANGE COLUMN `unknown10` `solid_type` mediumint(5) NOT NULL DEFAULT 0 AFTER `size`;
|
||||
ALTER TABLE `object` CHANGE COLUMN `unknown20` `incline` int(11) NOT NULL DEFAULT 0 AFTER `solid_type`;
|
||||
)"
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9246,
|
||||
@@ -5133,7 +5137,31 @@ CHANGE COLUMN `slot` `inventory_slot` mediumint(9) NOT NULL DEFAULT -1 AFTER `st
|
||||
|
||||
ALTER TABLE `starting_items`
|
||||
CHANGE COLUMN `temporary` `class_list` text CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL AFTER `id`;
|
||||
)"
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9248,
|
||||
.description = "2023_12_22_drop_npc_emotes_index.sql",
|
||||
.check = "show index from npc_emotes where key_name = 'emoteid'",
|
||||
.condition = "not_empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `npc_emotes` DROP INDEX `emoteid`;
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9249,
|
||||
.description = "2023_12_26_add_tasks_enabled_column.sql",
|
||||
.check = "SHOW COLUMNS FROM `tasks` LIKE 'enabled'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `tasks`
|
||||
ADD COLUMN `enabled` smallint NULL DEFAULT 1 AFTER `faction_amount`
|
||||
)",
|
||||
.content_schema_update = true
|
||||
}
|
||||
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
|
||||
@@ -82,6 +82,28 @@ CREATE TABLE `bot_starting_items` (
|
||||
`content_flags_disabled` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NULL DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci;
|
||||
)",
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9041,
|
||||
.description = "2023_12_04_bot_timers.sql",
|
||||
.check = "SHOW COLUMNS FROM `bot_timers` LIKE 'recast_time'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `bot_timers`
|
||||
ADD COLUMN `recast_time` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `timer_value`,
|
||||
ADD COLUMN `is_spell` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `recast_time`,
|
||||
ADD COLUMN `is_disc` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `is_spell`,
|
||||
ADD COLUMN `spell_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `is_disc`,
|
||||
ADD COLUMN `is_item` TINYINT(2) UNSIGNED NOT NULL DEFAULT 0 AFTER `spell_id`,
|
||||
ADD COLUMN `item_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `is_item`;
|
||||
ALTER TABLE `bot_timers`
|
||||
DROP FOREIGN KEY `FK_bot_timers_1`;
|
||||
ALTER TABLE `bot_timers`
|
||||
DROP PRIMARY KEY;
|
||||
ALTER TABLE `bot_timers`
|
||||
ADD PRIMARY KEY (`bot_id`, `timer_id`, `spell_id`, `item_id`);
|
||||
)"
|
||||
}
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
#include "../common/global_define.h"
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/strings.h"
|
||||
|
||||
#include "database.h"
|
||||
#include "database/database_update.h"
|
||||
|
||||
|
||||
// Disgrace: for windows compile
|
||||
#ifdef _WINDOWS
|
||||
#include <windows.h>
|
||||
#define snprintf _snprintf
|
||||
#define strncasecmp _strnicmp
|
||||
#define strcasecmp _stricmp
|
||||
#else
|
||||
|
||||
#include "unix.h"
|
||||
#include <netinet/in.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#endif
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
DatabaseUpdate database_update;
|
||||
|
||||
bool Database::CheckDatabaseConversions()
|
||||
{
|
||||
auto *r = RuleManager::Instance();
|
||||
r->LoadRules(this, "default", false);
|
||||
if (!RuleB(Bots, Enabled) && DoesTableExist("bot_data")) {
|
||||
LogInfo("Bot tables found but rule not enabled, enabling");
|
||||
r->SetRule("Bots:Enabled", "true", this, true, true);
|
||||
}
|
||||
|
||||
database_update.SetDatabase(this)->CheckDbUpdates();
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -71,7 +71,7 @@ namespace DatabaseSchema {
|
||||
{"character_tasks", "charid"},
|
||||
{"character_tribute", "character_id"},
|
||||
{"completed_tasks", "charid"},
|
||||
{"data_buckets", "id"},
|
||||
{"data_buckets", "character_id"},
|
||||
{"faction_values", "char_id"},
|
||||
{"friends", "charid"},
|
||||
{"guild_members", "char_id"},
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@
|
||||
|
||||
#include "dbcore.h"
|
||||
|
||||
#include <errmsg.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <mysqld_error.h>
|
||||
@@ -138,7 +137,7 @@ MySQLRequestResult DBcore::QueryDatabase(const char *query, uint32 querylen, boo
|
||||
* Error logging
|
||||
*/
|
||||
if (mysql_errno(mysql) > 0 && query[0] != '\0') {
|
||||
LogMySQLError("[{}] [{}]\n[{}]", mysql_errno(mysql), mysql_error(mysql), query);
|
||||
LogMySQLError("MySQL Error ({}) [{}] Query [{}]", mysql_errno(mysql), mysql_error(mysql), query);
|
||||
}
|
||||
|
||||
return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(mysql), errorBuffer);
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
#include <string.h>
|
||||
#include <mutex>
|
||||
|
||||
#define CR_SERVER_GONE_ERROR 2006
|
||||
#define CR_SERVER_LOST 2013
|
||||
|
||||
class DBcore {
|
||||
public:
|
||||
enum eStatus {
|
||||
|
||||
@@ -124,6 +124,12 @@ struct LDoNTrapTemplate
|
||||
uint8 locked;
|
||||
};
|
||||
|
||||
enum CrystalReclaimTypes
|
||||
{
|
||||
Ebon = 5,
|
||||
Radiant = 4,
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
+11
-7
@@ -651,6 +651,9 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
||||
}
|
||||
|
||||
// Auto inject categories that don't exist in the database...
|
||||
|
||||
std::vector<LogsysCategoriesRepository::LogsysCategories> db_categories_to_add{};
|
||||
|
||||
for (int i = Logs::AA; i != Logs::MaxCategoryID; i++) {
|
||||
|
||||
bool is_missing_in_database = std::find(db_categories.begin(), db_categories.end(), i) == db_categories.end();
|
||||
@@ -665,11 +668,7 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
||||
}
|
||||
|
||||
if (is_missing_in_database && !is_deprecated_category) {
|
||||
LogInfo(
|
||||
"Automatically adding new log category [{}] ({})",
|
||||
Logs::LogCategoryName[i],
|
||||
i
|
||||
);
|
||||
LogInfo("Automatically adding new log category [{}] ({})", Logs::LogCategoryName[i], i);
|
||||
|
||||
auto new_category = LogsysCategoriesRepository::NewEntity();
|
||||
new_category.log_category_id = i;
|
||||
@@ -678,11 +677,16 @@ EQEmuLogSys *EQEmuLogSys::LoadLogDatabaseSettings()
|
||||
new_category.log_to_gmsay = log_settings[i].log_to_gmsay;
|
||||
new_category.log_to_file = log_settings[i].log_to_file;
|
||||
new_category.log_to_discord = log_settings[i].log_to_discord;
|
||||
|
||||
LogsysCategoriesRepository::InsertOne(*m_database, new_category);
|
||||
db_categories_to_add.emplace_back(new_category);
|
||||
}
|
||||
}
|
||||
|
||||
if (!db_categories_to_add.empty()) {
|
||||
LogsysCategoriesRepository::ReplaceMany(*m_database, db_categories_to_add);
|
||||
LoadLogDatabaseSettings();
|
||||
return this;
|
||||
}
|
||||
|
||||
LogInfo("Loaded [{}] log categories", categories.size());
|
||||
|
||||
auto webhooks = DiscordWebhooksRepository::GetWhere(*m_database, fmt::format("id < {}", MAX_DISCORD_WEBHOOK_ID));
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA */
|
||||
|
||||
/* Error messages for mysql clients */
|
||||
/* error messages for the demon is in share/language/errmsg.sys */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void init_client_errs(void);
|
||||
extern const char *client_errors[]; /* Error messages */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CR_MIN_ERROR 2000 /* For easier client code */
|
||||
#define CR_MAX_ERROR 2999
|
||||
#if defined(OS2) && defined( MYSQL_SERVER)
|
||||
#define CER(X) client_errors[(X)-CR_MIN_ERROR]
|
||||
#else
|
||||
#define ER(X) client_errors[(X)-CR_MIN_ERROR]
|
||||
#endif
|
||||
#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */
|
||||
|
||||
#define CR_UNKNOWN_ERROR 2000
|
||||
#define CR_SOCKET_CREATE_ERROR 2001
|
||||
#define CR_CONNECTION_ERROR 2002
|
||||
#define CR_CONN_HOST_ERROR 2003
|
||||
#define CR_IPSOCK_ERROR 2004
|
||||
#define CR_UNKNOWN_HOST 2005
|
||||
#define CR_SERVER_GONE_ERROR 2006
|
||||
#define CR_VERSION_ERROR 2007
|
||||
#define CR_OUT_OF_MEMORY 2008
|
||||
#define CR_WRONG_HOST_INFO 2009
|
||||
#define CR_LOCALHOST_CONNECTION 2010
|
||||
#define CR_TCP_CONNECTION 2011
|
||||
#define CR_SERVER_HANDSHAKE_ERR 2012
|
||||
#define CR_SERVER_LOST 2013
|
||||
#define CR_COMMANDS_OUT_OF_SYNC 2014
|
||||
#define CR_NAMEDPIPE_CONNECTION 2015
|
||||
#define CR_NAMEDPIPEWAIT_ERROR 2016
|
||||
#define CR_NAMEDPIPEOPEN_ERROR 2017
|
||||
#define CR_NAMEDPIPESETSTATE_ERROR 2018
|
||||
#define CR_CANT_READ_CHARSET 2019
|
||||
#define CR_NET_PACKET_TOO_LARGE 2020
|
||||
@@ -37,6 +37,8 @@ void PlayerEventLogs::Init()
|
||||
db.emplace_back(e.id);
|
||||
}
|
||||
|
||||
std::vector<PlayerEventLogSettingsRepository::PlayerEventLogSettings> settings_to_insert{};
|
||||
|
||||
// insert entries that don't exist in database
|
||||
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
||||
bool is_in_database = std::find(db.begin(), db.end(), i) != db.end();
|
||||
@@ -56,21 +58,21 @@ void PlayerEventLogs::Init()
|
||||
|
||||
bool is_missing_in_database = std::find(db.begin(), db.end(), i) == db.end();
|
||||
if (is_missing_in_database && is_implemented && !is_deprecated) {
|
||||
LogInfo(
|
||||
"[New] PlayerEvent [{}] ({})",
|
||||
PlayerEvent::EventName[i],
|
||||
i
|
||||
);
|
||||
LogInfo("[New] PlayerEvent [{}] ({})", PlayerEvent::EventName[i], i);
|
||||
|
||||
auto c = PlayerEventLogSettingsRepository::NewEntity();
|
||||
c.id = i;
|
||||
c.event_name = PlayerEvent::EventName[i];
|
||||
c.event_enabled = m_settings[i].event_enabled;
|
||||
c.retention_days = m_settings[i].retention_days;
|
||||
PlayerEventLogSettingsRepository::InsertOne(*m_database, c);
|
||||
settings_to_insert.emplace_back(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (!settings_to_insert.empty()) {
|
||||
PlayerEventLogSettingsRepository::ReplaceMany(*m_database, settings_to_insert);
|
||||
}
|
||||
|
||||
bool processing_in_world = !RuleB(Logging, PlayerEventsQSProcess) && IsWorld();
|
||||
bool processing_in_qs = RuleB(Logging, PlayerEventsQSProcess) && IsQueryServ();
|
||||
|
||||
@@ -611,7 +613,7 @@ void PlayerEventLogs::Process()
|
||||
|
||||
void PlayerEventLogs::ProcessRetentionTruncation()
|
||||
{
|
||||
LogInfo("Running truncation");
|
||||
LogPlayerEvents("Running truncation");
|
||||
|
||||
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
||||
if (m_settings[i].retention_days > 0) {
|
||||
|
||||
+2
-2
@@ -96,12 +96,12 @@ bool IsOfEqualRace(int r1, int r2)
|
||||
// TODO: add more values
|
||||
switch (r1) {
|
||||
case DARK_ELF:
|
||||
if (r2 == RACE_NERIAK_CITIZEN_77) {
|
||||
if (r2 == Race::NeriakCitizen) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case BARBARIAN:
|
||||
if (r2 == RACE_HALAS_CITIZEN_90) {
|
||||
if (r2 == Race::HalasCitizen) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,34 +801,35 @@ int16 EQ::InventoryProfile::HasItemByLoreGroup(uint32 loregroup, uint8 where)
|
||||
// Returns slot_id when there's one available, else SLOT_INVALID
|
||||
int16 EQ::InventoryProfile::FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size, bool is_arrow)
|
||||
{
|
||||
// Check basic inventory
|
||||
for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) {
|
||||
if ((((uint64)1 << i) & m_lookup->PossessionsBitmask) == 0)
|
||||
continue;
|
||||
const int16 last_bag_slot = RuleI(World, ExpansionSettings) & EQ::expansions::bitHoT ? EQ::invslot::slotGeneral10 : EQ::invslot::slotGeneral8;
|
||||
|
||||
if (!GetItem(i))
|
||||
// Found available slot in personal inventory
|
||||
return i;
|
||||
for (int16 i = invslot::GENERAL_BEGIN; i <= last_bag_slot; i++) { // Check basic inventory
|
||||
if ((((uint64) 1 << i) & m_lookup->PossessionsBitmask) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!GetItem(i)) {
|
||||
return i; // Found available slot in personal inventory
|
||||
}
|
||||
}
|
||||
|
||||
if (!for_bag) {
|
||||
for (int16 i = invslot::GENERAL_BEGIN; i <= invslot::GENERAL_END; i++) {
|
||||
if ((((uint64)1 << i) & m_lookup->PossessionsBitmask) == 0)
|
||||
for (int16 i = invslot::GENERAL_BEGIN; i <= last_bag_slot; i++) {
|
||||
if ((((uint64) 1 << i) & m_lookup->PossessionsBitmask) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ItemInstance* inst = GetItem(i);
|
||||
if (inst && inst->IsClassBag() && inst->GetItem()->BagSize >= min_size)
|
||||
{
|
||||
if (inst->GetItem()->BagType == item::BagTypeQuiver && inst->GetItem()->ItemType != item::ItemTypeArrow)
|
||||
{
|
||||
const auto *inst = GetItem(i);
|
||||
if (inst && inst->IsClassBag() && inst->GetItem()->BagSize >= min_size) {
|
||||
if (inst->GetItem()->BagType == item::BagTypeQuiver &&
|
||||
inst->GetItem()->ItemType != item::ItemTypeArrow) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int16 base_slot_id = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN);
|
||||
const int16 base_slot_id = InventoryProfile::CalcSlotId(i, invbag::SLOT_BEGIN);
|
||||
|
||||
uint8 slots = inst->GetItem()->BagSlots;
|
||||
uint8 j;
|
||||
for (j = invbag::SLOT_BEGIN; j<slots; j++) {
|
||||
const uint8 slots = inst->GetItem()->BagSlots;
|
||||
for (uint8 j = invbag::SLOT_BEGIN; j < slots; j++) {
|
||||
if (!GetItem(base_slot_id + j)) {
|
||||
// Found available slot within bag
|
||||
return (base_slot_id + j);
|
||||
|
||||
@@ -132,7 +132,7 @@ namespace EQ
|
||||
|
||||
// Swap items in inventory
|
||||
enum SwapItemFailState : int8 { swapInvalid = -1, swapPass = 0, swapNotAllowed, swapNullData, swapRaceClass, swapDeity, swapLevel };
|
||||
bool SwapItem(int16 source_slot, int16 destination_slot, SwapItemFailState& fail_state, uint16 race_id = RACE_DOUG_0, uint8 class_id = Class::None, uint16 deity_id = deity::DeityType::DeityUnknown, uint8 level = 0);
|
||||
bool SwapItem(int16 source_slot, int16 destination_slot, SwapItemFailState& fail_state, uint16 race_id = Race::Doug, uint8 class_id = Class::None, uint16 deity_id = deity::DeityType::DeityUnknown, uint8 level = 0);
|
||||
|
||||
// Remove item from inventory
|
||||
bool DeleteItem(int16 slot_id, int16 quantity = 0);
|
||||
|
||||
@@ -1273,7 +1273,7 @@ int EQ::ItemInstance::GetItemBaneDamageBody(bool augments) const
|
||||
|
||||
int EQ::ItemInstance::GetItemBaneDamageRace(bool augments) const
|
||||
{
|
||||
int race = RACE_DOUG_0;
|
||||
int race = Race::Doug;
|
||||
const auto item = GetItem();
|
||||
if (item) {
|
||||
race = item->BaneDmgRace;
|
||||
|
||||
@@ -3868,8 +3868,8 @@ namespace RoF
|
||||
}
|
||||
|
||||
float SpawnSize = emu->size;
|
||||
if (!((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522))
|
||||
if (!((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin))
|
||||
)
|
||||
{
|
||||
PacketSize += 60;
|
||||
@@ -4002,8 +4002,8 @@ namespace RoF
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown18
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // unknown19
|
||||
|
||||
if ((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522)
|
||||
if ((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin)
|
||||
)
|
||||
{
|
||||
for (k = EQ::textures::textureBegin; k < EQ::textures::materialCount; ++k)
|
||||
|
||||
@@ -4007,8 +4007,8 @@ namespace RoF2
|
||||
}
|
||||
|
||||
float SpawnSize = emu->size;
|
||||
if (!((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522))
|
||||
if (!((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin))
|
||||
)
|
||||
{
|
||||
PacketSize += 60;
|
||||
@@ -4212,8 +4212,8 @@ namespace RoF2
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // These do something with OP_WeaponEquip1
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0xffffffff); // ^
|
||||
|
||||
if ((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522)
|
||||
if ((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin)
|
||||
)
|
||||
{
|
||||
for (k = EQ::textures::textureBegin; k < EQ::textures::materialCount; ++k)
|
||||
|
||||
@@ -2507,8 +2507,8 @@ namespace SoD
|
||||
}
|
||||
|
||||
float SpawnSize = emu->size;
|
||||
if (!((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522))
|
||||
if (!((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin))
|
||||
)
|
||||
{
|
||||
PacketSize -= (sizeof(structs::Texture_Struct) * EQ::textures::materialCount);
|
||||
@@ -2706,8 +2706,8 @@ namespace SoD
|
||||
|
||||
Buffer += sizeof(structs::Spawn_Struct_Position);
|
||||
|
||||
if ((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522)
|
||||
if ((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin)
|
||||
)
|
||||
{
|
||||
for (k = EQ::textures::textureBegin; k < EQ::textures::materialCount; ++k)
|
||||
@@ -2733,8 +2733,8 @@ namespace SoD
|
||||
}
|
||||
|
||||
|
||||
if ((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522)
|
||||
if ((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin)
|
||||
)
|
||||
{
|
||||
structs::Texture_Struct *Equipment = (structs::Texture_Struct *)Buffer;
|
||||
|
||||
@@ -2779,8 +2779,8 @@ namespace UF
|
||||
}
|
||||
|
||||
float SpawnSize = emu->size;
|
||||
if (!((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522))
|
||||
if (!((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin))
|
||||
)
|
||||
{
|
||||
PacketSize -= (sizeof(structs::Texture_Struct) * EQ::textures::materialCount);
|
||||
@@ -2982,8 +2982,8 @@ namespace UF
|
||||
|
||||
Buffer += sizeof(structs::Spawn_Struct_Position);
|
||||
|
||||
if ((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522)
|
||||
if ((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin)
|
||||
)
|
||||
{
|
||||
for (k = EQ::textures::textureBegin; k < EQ::textures::materialCount; ++k)
|
||||
@@ -3018,8 +3018,8 @@ namespace UF
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0);
|
||||
}
|
||||
|
||||
if ((emu->NPC == 0) || (emu->race <= RACE_GNOME_12) || (emu->race == RACE_IKSAR_128) ||
|
||||
(emu->race == RACE_VAH_SHIR_130) || (emu->race == RACE_FROGLOK_330) || (emu->race == RACE_DRAKKIN_522)
|
||||
if ((emu->NPC == 0) || (emu->race <= Race::Gnome) || (emu->race == Race::Iksar) ||
|
||||
(emu->race == Race::VahShir) || (emu->race == Race::Froglok2) || (emu->race == Race::Drakkin)
|
||||
)
|
||||
{
|
||||
structs::Texture_Struct *Equipment = (structs::Texture_Struct *)Buffer;
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
// types
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <cctype>
|
||||
#include <sstream>
|
||||
|
||||
// containers
|
||||
#include <iterator>
|
||||
#include <set>
|
||||
#include <unordered_set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
// utilities
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <tuple>
|
||||
#include <fstream>
|
||||
#include <cstdio>
|
||||
|
||||
// fmt
|
||||
#include <fmt/format.h>
|
||||
|
||||
// lua
|
||||
#include "lua.hpp"
|
||||
#include <luabind/luabind.hpp>
|
||||
#include <luabind/object.hpp>
|
||||
+791
-791
File diff suppressed because it is too large
Load Diff
+744
-1474
File diff suppressed because it is too large
Load Diff
@@ -16,12 +16,19 @@
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
|
||||
class BaseBotTimersRepository {
|
||||
public:
|
||||
struct BotTimers {
|
||||
uint32_t bot_id;
|
||||
uint32_t timer_id;
|
||||
uint32_t timer_value;
|
||||
uint32_t recast_time;
|
||||
uint8_t is_spell;
|
||||
uint8_t is_disc;
|
||||
uint32_t spell_id;
|
||||
uint8_t is_item;
|
||||
uint32_t item_id;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -35,6 +42,12 @@ public:
|
||||
"bot_id",
|
||||
"timer_id",
|
||||
"timer_value",
|
||||
"recast_time",
|
||||
"is_spell",
|
||||
"is_disc",
|
||||
"spell_id",
|
||||
"is_item",
|
||||
"item_id",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,6 +57,12 @@ public:
|
||||
"bot_id",
|
||||
"timer_id",
|
||||
"timer_value",
|
||||
"recast_time",
|
||||
"is_spell",
|
||||
"is_disc",
|
||||
"spell_id",
|
||||
"is_item",
|
||||
"item_id",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -87,6 +106,12 @@ public:
|
||||
e.bot_id = 0;
|
||||
e.timer_id = 0;
|
||||
e.timer_value = 0;
|
||||
e.recast_time = 0;
|
||||
e.is_spell = 0;
|
||||
e.is_disc = 0;
|
||||
e.spell_id = 0;
|
||||
e.is_item = 0;
|
||||
e.item_id = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -112,8 +137,9 @@ public:
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
bot_timers_id
|
||||
)
|
||||
);
|
||||
@@ -125,6 +151,12 @@ public:
|
||||
e.bot_id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.timer_id = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
|
||||
e.timer_value = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.recast_time = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
|
||||
e.is_spell = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
|
||||
e.is_disc = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
|
||||
e.spell_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
|
||||
e.is_item = static_cast<uint8_t>(strtoul(row[7], nullptr, 10));
|
||||
e.item_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -161,6 +193,12 @@ public:
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.bot_id));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.timer_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.timer_value));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.recast_time));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.is_spell));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.is_disc));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.spell_id));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.is_item));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.item_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -185,6 +223,12 @@ public:
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.timer_id));
|
||||
v.push_back(std::to_string(e.timer_value));
|
||||
v.push_back(std::to_string(e.recast_time));
|
||||
v.push_back(std::to_string(e.is_spell));
|
||||
v.push_back(std::to_string(e.is_disc));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.is_item));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -217,6 +261,12 @@ public:
|
||||
v.push_back(std::to_string(e.bot_id));
|
||||
v.push_back(std::to_string(e.timer_id));
|
||||
v.push_back(std::to_string(e.timer_value));
|
||||
v.push_back(std::to_string(e.recast_time));
|
||||
v.push_back(std::to_string(e.is_spell));
|
||||
v.push_back(std::to_string(e.is_disc));
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back(std::to_string(e.is_item));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -253,6 +303,12 @@ public:
|
||||
e.bot_id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.timer_id = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
|
||||
e.timer_value = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.recast_time = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
|
||||
e.is_spell = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
|
||||
e.is_disc = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
|
||||
e.spell_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
|
||||
e.is_item = static_cast<uint8_t>(strtoul(row[7], nullptr, 10));
|
||||
e.item_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -280,6 +336,12 @@ public:
|
||||
e.bot_id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.timer_id = static_cast<uint32_t>(strtoul(row[1], nullptr, 10));
|
||||
e.timer_value = static_cast<uint32_t>(strtoul(row[2], nullptr, 10));
|
||||
e.recast_time = static_cast<uint32_t>(strtoul(row[3], nullptr, 10));
|
||||
e.is_spell = static_cast<uint8_t>(strtoul(row[4], nullptr, 10));
|
||||
e.is_disc = static_cast<uint8_t>(strtoul(row[5], nullptr, 10));
|
||||
e.spell_id = static_cast<uint32_t>(strtoul(row[6], nullptr, 10));
|
||||
e.is_item = static_cast<uint8_t>(strtoul(row[7], nullptr, 10));
|
||||
e.item_id = static_cast<uint32_t>(strtoul(row[8], nullptr, 10));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
* @docs https://docs.eqemu.io/developer/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_LOGSYS_CATEGORIES_REPOSITORY_H
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
|
||||
class BaseLogsysCategoriesRepository {
|
||||
public:
|
||||
struct LogsysCategories {
|
||||
@@ -128,8 +129,9 @@ public:
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
logsys_categories_id
|
||||
)
|
||||
);
|
||||
@@ -378,6 +380,74 @@ public:
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static std::string BaseReplace()
|
||||
{
|
||||
return fmt::format(
|
||||
"REPLACE INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static int ReplaceOne(
|
||||
Database& db,
|
||||
const LogsysCategories &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.log_category_id));
|
||||
v.push_back("'" + Strings::Escape(e.log_category_description) + "'");
|
||||
v.push_back(std::to_string(e.log_to_console));
|
||||
v.push_back(std::to_string(e.log_to_file));
|
||||
v.push_back(std::to_string(e.log_to_gmsay));
|
||||
v.push_back(std::to_string(e.log_to_discord));
|
||||
v.push_back(std::to_string(e.discord_webhook_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int ReplaceMany(
|
||||
Database& db,
|
||||
const std::vector<LogsysCategories> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.log_category_id));
|
||||
v.push_back("'" + Strings::Escape(e.log_category_description) + "'");
|
||||
v.push_back(std::to_string(e.log_to_console));
|
||||
v.push_back(std::to_string(e.log_to_file));
|
||||
v.push_back(std::to_string(e.log_to_gmsay));
|
||||
v.push_back(std::to_string(e.log_to_discord));
|
||||
v.push_back(std::to_string(e.discord_webhook_id));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_LOGSYS_CATEGORIES_REPOSITORY_H
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
|
||||
class BaseNpcEmotesRepository {
|
||||
public:
|
||||
struct NpcEmotes {
|
||||
@@ -120,8 +121,9 @@ public:
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
npc_emotes_id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
* @docs https://docs.eqemu.io/developer/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
|
||||
class BasePlayerEventLogSettingsRepository {
|
||||
public:
|
||||
struct PlayerEventLogSettings {
|
||||
@@ -359,6 +360,70 @@ public:
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static std::string BaseReplace()
|
||||
{
|
||||
return fmt::format(
|
||||
"REPLACE INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static int ReplaceOne(
|
||||
Database& db,
|
||||
const PlayerEventLogSettings &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back("'" + Strings::Escape(e.event_name) + "'");
|
||||
v.push_back(std::to_string(e.event_enabled));
|
||||
v.push_back(std::to_string(e.retention_days));
|
||||
v.push_back(std::to_string(e.discord_webhook_id));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int ReplaceMany(
|
||||
Database& db,
|
||||
const std::vector<PlayerEventLogSettings> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back("'" + Strings::Escape(e.event_name) + "'");
|
||||
v.push_back(std::to_string(e.event_enabled));
|
||||
v.push_back(std::to_string(e.retention_days));
|
||||
v.push_back(std::to_string(e.discord_webhook_id));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_PLAYER_EVENT_LOG_SETTINGS_REPOSITORY_H
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
* @docs https://docs.eqemu.io/developer/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_PLAYER_EVENT_LOGS_REPOSITORY_H
|
||||
@@ -240,8 +240,8 @@ public:
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.z));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.heading));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.event_type_id));
|
||||
v.push_back(columns[10] + " = '" + db.Escape(e.event_type_name) + "'");
|
||||
v.push_back(columns[11] + " = '" + db.Escape(e.event_data) + "'");
|
||||
v.push_back(columns[10] + " = '" + Strings::Escape(e.event_type_name) + "'");
|
||||
v.push_back(columns[11] + " = '" + Strings::Escape(e.event_data) + "'");
|
||||
v.push_back(columns[12] + " = FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
@@ -274,8 +274,8 @@ public:
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.event_type_id));
|
||||
v.push_back("'" + db.Escape(e.event_type_name) + "'");
|
||||
v.push_back("'" + db.Escape(e.event_data) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_data) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
@@ -316,8 +316,8 @@ public:
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.event_type_id));
|
||||
v.push_back("'" + db.Escape(e.event_type_name) + "'");
|
||||
v.push_back("'" + db.Escape(e.event_data) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_data) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
@@ -460,6 +460,86 @@ public:
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static std::string BaseReplace()
|
||||
{
|
||||
return fmt::format(
|
||||
"REPLACE INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static int ReplaceOne(
|
||||
Database& db,
|
||||
const PlayerEventLogs &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.account_id));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.zone_id));
|
||||
v.push_back(std::to_string(e.instance_id));
|
||||
v.push_back(std::to_string(e.x));
|
||||
v.push_back(std::to_string(e.y));
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.event_type_id));
|
||||
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_data) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int ReplaceMany(
|
||||
Database& db,
|
||||
const std::vector<PlayerEventLogs> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.account_id));
|
||||
v.push_back(std::to_string(e.character_id));
|
||||
v.push_back(std::to_string(e.zone_id));
|
||||
v.push_back(std::to_string(e.instance_id));
|
||||
v.push_back(std::to_string(e.x));
|
||||
v.push_back(std::to_string(e.y));
|
||||
v.push_back(std::to_string(e.z));
|
||||
v.push_back(std::to_string(e.heading));
|
||||
v.push_back(std::to_string(e.event_type_id));
|
||||
v.push_back("'" + Strings::Escape(e.event_type_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.event_data) + "'");
|
||||
v.push_back("FROM_UNIXTIME(" + (e.created_at > 0 ? std::to_string(e.created_at) : "null") + ")");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_PLAYER_EVENT_LOGS_REPOSITORY_H
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
* @docs https://docs.eqemu.io/developer/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_TASKS_REPOSITORY_H
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
|
||||
class BaseTasksRepository {
|
||||
public:
|
||||
struct Tasks {
|
||||
@@ -47,6 +48,7 @@ public:
|
||||
uint32_t dz_template_id;
|
||||
int32_t lock_activity_id;
|
||||
int32_t faction_amount;
|
||||
int16_t enabled;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -85,6 +87,7 @@ public:
|
||||
"dz_template_id",
|
||||
"lock_activity_id",
|
||||
"faction_amount",
|
||||
"enabled",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -119,6 +122,7 @@ public:
|
||||
"dz_template_id",
|
||||
"lock_activity_id",
|
||||
"faction_amount",
|
||||
"enabled",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -187,6 +191,7 @@ public:
|
||||
e.dz_template_id = 0;
|
||||
e.lock_activity_id = -1;
|
||||
e.faction_amount = 0;
|
||||
e.enabled = 1;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -212,8 +217,9 @@ public:
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
tasks_id
|
||||
)
|
||||
);
|
||||
@@ -250,6 +256,7 @@ public:
|
||||
e.dz_template_id = static_cast<uint32_t>(strtoul(row[25], nullptr, 10));
|
||||
e.lock_activity_id = static_cast<int32_t>(atoi(row[26]));
|
||||
e.faction_amount = static_cast<int32_t>(atoi(row[27]));
|
||||
e.enabled = static_cast<int16_t>(atoi(row[28]));
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -311,6 +318,7 @@ public:
|
||||
v.push_back(columns[25] + " = " + std::to_string(e.dz_template_id));
|
||||
v.push_back(columns[26] + " = " + std::to_string(e.lock_activity_id));
|
||||
v.push_back(columns[27] + " = " + std::to_string(e.faction_amount));
|
||||
v.push_back(columns[28] + " = " + std::to_string(e.enabled));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -360,6 +368,7 @@ public:
|
||||
v.push_back(std::to_string(e.dz_template_id));
|
||||
v.push_back(std::to_string(e.lock_activity_id));
|
||||
v.push_back(std::to_string(e.faction_amount));
|
||||
v.push_back(std::to_string(e.enabled));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -417,6 +426,7 @@ public:
|
||||
v.push_back(std::to_string(e.dz_template_id));
|
||||
v.push_back(std::to_string(e.lock_activity_id));
|
||||
v.push_back(std::to_string(e.faction_amount));
|
||||
v.push_back(std::to_string(e.enabled));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -478,6 +488,7 @@ public:
|
||||
e.dz_template_id = static_cast<uint32_t>(strtoul(row[25], nullptr, 10));
|
||||
e.lock_activity_id = static_cast<int32_t>(atoi(row[26]));
|
||||
e.faction_amount = static_cast<int32_t>(atoi(row[27]));
|
||||
e.enabled = static_cast<int16_t>(atoi(row[28]));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -530,6 +541,7 @@ public:
|
||||
e.dz_template_id = static_cast<uint32_t>(strtoul(row[25], nullptr, 10));
|
||||
e.lock_activity_id = static_cast<int32_t>(atoi(row[26]));
|
||||
e.faction_amount = static_cast<int32_t>(atoi(row[27]));
|
||||
e.enabled = static_cast<int16_t>(atoi(row[28]));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -588,6 +600,118 @@ public:
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static std::string BaseReplace()
|
||||
{
|
||||
return fmt::format(
|
||||
"REPLACE INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static int ReplaceOne(
|
||||
Database& db,
|
||||
const Tasks &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.duration));
|
||||
v.push_back(std::to_string(e.duration_code));
|
||||
v.push_back("'" + Strings::Escape(e.title) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.description) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.reward_text) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.reward_id_list) + "'");
|
||||
v.push_back(std::to_string(e.cash_reward));
|
||||
v.push_back(std::to_string(e.exp_reward));
|
||||
v.push_back(std::to_string(e.reward_method));
|
||||
v.push_back(std::to_string(e.reward_points));
|
||||
v.push_back(std::to_string(e.reward_point_type));
|
||||
v.push_back(std::to_string(e.min_level));
|
||||
v.push_back(std::to_string(e.max_level));
|
||||
v.push_back(std::to_string(e.level_spread));
|
||||
v.push_back(std::to_string(e.min_players));
|
||||
v.push_back(std::to_string(e.max_players));
|
||||
v.push_back(std::to_string(e.repeatable));
|
||||
v.push_back(std::to_string(e.faction_reward));
|
||||
v.push_back("'" + Strings::Escape(e.completion_emote) + "'");
|
||||
v.push_back(std::to_string(e.replay_timer_group));
|
||||
v.push_back(std::to_string(e.replay_timer_seconds));
|
||||
v.push_back(std::to_string(e.request_timer_group));
|
||||
v.push_back(std::to_string(e.request_timer_seconds));
|
||||
v.push_back(std::to_string(e.dz_template_id));
|
||||
v.push_back(std::to_string(e.lock_activity_id));
|
||||
v.push_back(std::to_string(e.faction_amount));
|
||||
v.push_back(std::to_string(e.enabled));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int ReplaceMany(
|
||||
Database& db,
|
||||
const std::vector<Tasks> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.type));
|
||||
v.push_back(std::to_string(e.duration));
|
||||
v.push_back(std::to_string(e.duration_code));
|
||||
v.push_back("'" + Strings::Escape(e.title) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.description) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.reward_text) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.reward_id_list) + "'");
|
||||
v.push_back(std::to_string(e.cash_reward));
|
||||
v.push_back(std::to_string(e.exp_reward));
|
||||
v.push_back(std::to_string(e.reward_method));
|
||||
v.push_back(std::to_string(e.reward_points));
|
||||
v.push_back(std::to_string(e.reward_point_type));
|
||||
v.push_back(std::to_string(e.min_level));
|
||||
v.push_back(std::to_string(e.max_level));
|
||||
v.push_back(std::to_string(e.level_spread));
|
||||
v.push_back(std::to_string(e.min_players));
|
||||
v.push_back(std::to_string(e.max_players));
|
||||
v.push_back(std::to_string(e.repeatable));
|
||||
v.push_back(std::to_string(e.faction_reward));
|
||||
v.push_back("'" + Strings::Escape(e.completion_emote) + "'");
|
||||
v.push_back(std::to_string(e.replay_timer_group));
|
||||
v.push_back(std::to_string(e.replay_timer_seconds));
|
||||
v.push_back(std::to_string(e.request_timer_group));
|
||||
v.push_back(std::to_string(e.request_timer_seconds));
|
||||
v.push_back(std::to_string(e.dz_template_id));
|
||||
v.push_back(std::to_string(e.lock_activity_id));
|
||||
v.push_back(std::to_string(e.faction_amount));
|
||||
v.push_back(std::to_string(e.enabled));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_TASKS_REPOSITORY_H
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* Any modifications to base repositories are to be made by the generator only
|
||||
*
|
||||
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||
* @docs https://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
* @docs https://docs.eqemu.io/developer/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_{{TABLE_NAME_UPPER}}_REPOSITORY_H
|
||||
@@ -319,6 +319,62 @@ public:
|
||||
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||
}
|
||||
|
||||
static std::string BaseReplace()
|
||||
{
|
||||
return fmt::format(
|
||||
"REPLACE INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static int ReplaceOne(
|
||||
Database& db,
|
||||
const {{TABLE_NAME_STRUCT}} &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
{{INSERT_ONE_ENTRIES}}
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int ReplaceMany(
|
||||
Database& db,
|
||||
const std::vector<{{TABLE_NAME_STRUCT}}> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
{{INSERT_MANY_ENTRIES}}
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES {}",
|
||||
BaseReplace(),
|
||||
Strings::Implode(",", insert_chunks)
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_{{TABLE_NAME_UPPER}}_REPOSITORY_H
|
||||
|
||||
@@ -215,6 +215,7 @@ RULE_BOOL(Character, EnableRaidEXPModifier, true, "Enable or disable the raid ex
|
||||
RULE_BOOL(Character, EnableRaidMemberEXPModifier, true, "Enable or disable the raid experience modifier based on members in raid, default is true")
|
||||
RULE_BOOL(Character, LeaveCursorMoneyOnCorpse, false, "Enable or disable leaving cursor money on player corpses")
|
||||
RULE_BOOL(Character, ItemExtraSkillDamageCalcAsPercent, false, "If enabled, apply Item Extra Skill Damage as Percentage-based modifiers")
|
||||
RULE_BOOL(Character, UseForageCommonFood, true, "If enabled, use the common foods specified in the code.")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mercs)
|
||||
@@ -656,6 +657,9 @@ RULE_BOOL(Bots, AllowPickpocketCommand, true, "Allows the use of the bot command
|
||||
RULE_BOOL(Bots, BotHealOnLevel, false, "Setting whether a bot should heal completely when leveling. Default FALSE.")
|
||||
RULE_INT(Bots, AutosaveIntervalSeconds, 300, "Number of seconds after which a timer is triggered which stores the bot data. The value 0 means no periodic automatic saving.")
|
||||
RULE_BOOL(Bots, CazicTouchBotsOwner, true, "Default True. Cazic Touch/DT will hit bot owner rather than bot.")
|
||||
RULE_INT(Bots, BotsClickItemsMinLvl, 1, "Minimum level for bots to be able to use ^clickitem. Default 1.")
|
||||
RULE_BOOL(Bots, BotsCanClickItems, true, "Enabled the ability for bots to click items they have equipped. Default TRUE")
|
||||
RULE_BOOL(Bots, CanClickMageEpicV1, true, "Whether or not bots are allowed to click Mage Epic 1.0. Default TRUE")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Chat)
|
||||
|
||||
+1
-1
@@ -376,7 +376,7 @@ std::string EQ::SayLinkEngine::InjectSaylinksIfNotExist(const char *message)
|
||||
|
||||
void EQ::SayLinkEngine::LoadCachedSaylinks()
|
||||
{
|
||||
auto saylinks = SaylinkRepository::GetWhere(database, "phrase not REGEXP BINARY '[A-Z]' and phrase not REGEXP '[0-9]'");
|
||||
auto saylinks = SaylinkRepository::GetWhere(database, "phrase not REGEXP '[A-Z]' and phrase not REGEXP '[0-9]'");
|
||||
LogSaylink("Loaded [{}] saylinks into cache", saylinks.size());
|
||||
g_cached_saylinks = saylinks;
|
||||
}
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// these get injected during the build pipeline
|
||||
#define CURRENT_VERSION "22.36.0-dev" // always append -dev to the current version for custom-builds
|
||||
#define CURRENT_VERSION "22.39.1-dev" // always append -dev to the current version for custom-builds
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
@@ -42,9 +42,9 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9247
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9249
|
||||
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9040
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9041
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "22.36.0",
|
||||
"version": "22.39.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errmsg.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
|
||||
+1
-2
@@ -24,7 +24,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errmsg.h>
|
||||
#include <mysqld_error.h>
|
||||
#include <limits.h>
|
||||
#include <ctype.h>
|
||||
@@ -160,7 +159,7 @@ bool UCSDatabase::VerifyMailKey(const std::string& characterName, int IPAddress,
|
||||
LogInfo("No mailkeys found for [{}].", characterName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
auto row = results.begin();
|
||||
|
||||
// The key is the client's IP address (expressed as 8 hex digits) and an 8 hex digit random string generated
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
module constantconvert
|
||||
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/gammazero/deque v0.2.0 // indirect
|
||||
github.com/gammazero/workerpool v1.1.3 // indirect
|
||||
)
|
||||
@@ -0,0 +1,4 @@
|
||||
github.com/gammazero/deque v0.2.0 h1:SkieyNB4bg2/uZZLxvya0Pq6diUlwx7m2TeT7GAIWaA=
|
||||
github.com/gammazero/deque v0.2.0/go.mod h1:LFroj8x4cMYCukHJDbxFCkT+r9AndaJnFMuZDV34tuU=
|
||||
github.com/gammazero/workerpool v1.1.3 h1:WixN4xzukFoN0XSeXF6puqEqFTl2mECI9S6W44HWy9Q=
|
||||
github.com/gammazero/workerpool v1.1.3/go.mod h1:wPjyBLDbyKnUn2XwwyD3EEwo9dHutia9/fwNmSHWACc=
|
||||
@@ -0,0 +1,313 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gammazero/workerpool"
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
loadDefinitions()
|
||||
|
||||
// get processor count
|
||||
wp := workerpool.New(runtime.NumCPU())
|
||||
|
||||
// loop through all files in current dir that are cpp files or h files
|
||||
err := filepath.WalkDir("../../", func(path string, d fs.DirEntry, err error) error {
|
||||
if d.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !strings.Contains(path, ".cpp") && !strings.Contains(path, ".h") {
|
||||
return nil
|
||||
}
|
||||
|
||||
// if file ends with ".o" skip it
|
||||
if strings.HasSuffix(path, ".o") {
|
||||
return nil
|
||||
}
|
||||
|
||||
var ignoreFiles = []string{
|
||||
"submodules", "/libs", "utils/", "races.h", "backward", "database_update_manifest.cpp", "zonedb.h",
|
||||
}
|
||||
|
||||
ignore := false
|
||||
for _, ignoreString := range ignoreFiles {
|
||||
if strings.Contains(path, ignoreString) {
|
||||
ignore = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if ignore {
|
||||
return nil
|
||||
}
|
||||
|
||||
wp.Submit(func() {
|
||||
|
||||
// open file for reading
|
||||
// get file contents
|
||||
contents, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
log.Fatalf("impossible to read file: %s", err)
|
||||
}
|
||||
|
||||
content := string(contents)
|
||||
|
||||
wroteChanges := false
|
||||
|
||||
var newLines []string
|
||||
for _, line := range strings.Split(content, "\n") {
|
||||
newLine := line
|
||||
|
||||
// loop through oldDefs and see if any of them are in contents
|
||||
for key, value := range oldDefs {
|
||||
// combine all of the above contains into a slice
|
||||
// loop through slice and if any of them are in line, continue
|
||||
var ignoreMatches = []string{
|
||||
"#define ", "MALE", "FEMALE", "_BIT", "LANG_",
|
||||
}
|
||||
|
||||
ignore := false
|
||||
for _, ignoreString := range ignoreMatches {
|
||||
if strings.Contains(newLine, ignoreString) && !strings.Contains(newLine, "NPC_") {
|
||||
ignore = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if ignore {
|
||||
continue
|
||||
}
|
||||
|
||||
// below we hackishly use a series of specific string contains to avoid
|
||||
// making blind and wrong replacements
|
||||
// but hey - at least its 100% accurate :)
|
||||
if strings.Contains(line, "case "+key+":") {
|
||||
for key2, value2 := range newDefs {
|
||||
if value == value2 {
|
||||
newLine = strings.ReplaceAll(newLine, " "+key+":", " "+key2+":")
|
||||
wroteChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(line, "\t"+key) {
|
||||
for key2, value2 := range newDefs {
|
||||
if value == value2 {
|
||||
newLine = strings.ReplaceAll(newLine, "\t"+key, "\t"+key2)
|
||||
wroteChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(line, key+",") {
|
||||
for key2, value2 := range newDefs {
|
||||
if value == value2 {
|
||||
newLine = strings.ReplaceAll(newLine, key+",", key2+",")
|
||||
wroteChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(line, ", "+key) {
|
||||
for key2, value2 := range newDefs {
|
||||
if value == value2 {
|
||||
newLine = strings.ReplaceAll(newLine, ", "+key, ", "+key2)
|
||||
wroteChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(line, "= "+key+" ") {
|
||||
for key2, value2 := range newDefs {
|
||||
if value == value2 {
|
||||
newLine = strings.ReplaceAll(newLine, "= "+key+" ", "= "+key2+" ")
|
||||
wroteChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(line, "= "+key+")") {
|
||||
for key2, value2 := range newDefs {
|
||||
if value == value2 {
|
||||
newLine = strings.ReplaceAll(newLine, "= "+key+")", "= "+key2+")")
|
||||
wroteChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(line, "= "+key+";") {
|
||||
for key2, value2 := range newDefs {
|
||||
if value == value2 {
|
||||
newLine = strings.ReplaceAll(newLine, "= "+key+";", "= "+key2+";")
|
||||
wroteChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(line, "= "+key+" ||") {
|
||||
for key2, value2 := range newDefs {
|
||||
if value == value2 {
|
||||
newLine = strings.ReplaceAll(newLine, "= "+key+" ||", "= "+key2+" ||")
|
||||
wroteChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// match cases where our match is on the last line and last column
|
||||
// we need to be exact in the last column and not do a partial because we can
|
||||
// accidentally rename say OGRE to OGRE2 mistakenly
|
||||
if strings.Contains(line, key) {
|
||||
columns := strings.Split(line, " ")
|
||||
// get the last column
|
||||
lastColumn := strings.TrimSpace(columns[len(columns)-1])
|
||||
if lastColumn == key {
|
||||
for key2, value2 := range newDefs {
|
||||
if value == value2 {
|
||||
newLine = strings.ReplaceAll(newLine, lastColumn, key2)
|
||||
wroteChanges = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if strings.Contains(line, "race == "+key) {
|
||||
// for key2, value2 := range newDefs {
|
||||
// if value == value2 {
|
||||
// newLine = strings.ReplaceAll(newLine, "race == "+key, "race == "+key2)
|
||||
// wroteChanges = true
|
||||
// break
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
newLines = append(newLines, newLine)
|
||||
}
|
||||
|
||||
// write contents back to file
|
||||
if wroteChanges {
|
||||
fmt.Printf("wrote changes to file [%v]\n", path)
|
||||
err = os.WriteFile(path, []byte(strings.Join(newLines, "\n")), 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("impossible to write file: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatalf("impossible to walk directories: %s", err)
|
||||
}
|
||||
|
||||
wp.StopWait()
|
||||
}
|
||||
|
||||
var newDefs = make(map[string]int)
|
||||
var oldDefs = make(map[string]int)
|
||||
|
||||
func loadDefinitions() {
|
||||
// git show master:common/races.h
|
||||
cmd := exec.Command("git", "show", "master:common/races.h")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// load into a string -> int map
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
if strings.Contains(line, "#define ") {
|
||||
if len(strings.Split(line, " ")) <= 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// ignore
|
||||
// #define MALE 0
|
||||
// #define FEMALE 1
|
||||
// #define NEUTER 2
|
||||
if strings.Contains(line, "#define MALE") {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, "#define FEMALE") {
|
||||
continue
|
||||
}
|
||||
if strings.Contains(line, "#define NEUTER") {
|
||||
continue
|
||||
}
|
||||
|
||||
// load "#define RACE_FLYING_CARPET_720 720" into map
|
||||
|
||||
key := strings.Split(line, " ")[1]
|
||||
value := strings.Split(line, " ")[2]
|
||||
value = strings.ReplaceAll(value, "//", "")
|
||||
value = strings.TrimSpace(value)
|
||||
//fmt.Printf("key [%v] value [%v]\n", key, value)
|
||||
|
||||
if !strings.HasPrefix(key, "RACE_") && !strings.HasPrefix(key, "RT_") {
|
||||
continue
|
||||
}
|
||||
|
||||
// convert value to int
|
||||
intValue, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
oldDefs[key] = intValue
|
||||
|
||||
fmt.Printf("oldDefs key [%v] value [%v]\n", key, intValue)
|
||||
}
|
||||
}
|
||||
|
||||
// cleanup/races_cpp_h
|
||||
cmd = exec.Command("git", "show", "cleanup/races_cpp_h:common/races.h")
|
||||
out, err = cmd.Output()
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// load into a string -> int map
|
||||
for _, line := range strings.Split(string(out), "\n") {
|
||||
if strings.Contains(line, "constexpr uint16") {
|
||||
if len(strings.Split(line, " ")) <= 2 {
|
||||
continue
|
||||
}
|
||||
|
||||
// remove all extra spaces in between characters in line
|
||||
line = strings.Join(strings.Fields(line), " ")
|
||||
|
||||
// load " constexpr uint16 Doug = 0;" into map
|
||||
key := strings.Split(line, " ")[2]
|
||||
value := strings.Split(line, " ")[4]
|
||||
value = strings.ReplaceAll(value, "//", "")
|
||||
value = strings.ReplaceAll(value, ";", "")
|
||||
value = strings.TrimSpace(value)
|
||||
|
||||
// convert value to int
|
||||
intValue, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
println(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
mapKey := "Race::" + key
|
||||
|
||||
newDefs[mapKey] = intValue
|
||||
|
||||
fmt.Printf("newDefs key [%v] value [%v]\n", mapKey, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,8 +15,8 @@ mkdir -p build && cd build && \
|
||||
-DEQEMU_BUILD_LOGIN=ON \
|
||||
-DEQEMU_BUILD_LUA=ON \
|
||||
-DEQEMU_BUILD_PERL=ON \
|
||||
-DCMAKE_CXX_FLAGS:STRING="-O1 -g" \
|
||||
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING="-O1 -g" \
|
||||
-DCMAKE_CXX_FLAGS:STRING="-O1 -g -Wno-everything" \
|
||||
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING="-O1 -g -Wno-everything" \
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
|
||||
-G 'Unix Makefiles' \
|
||||
.. && make -j$((`nproc`-4))
|
||||
|
||||
@@ -10,7 +10,7 @@ require (
|
||||
require (
|
||||
github.com/golang/protobuf v1.3.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
golang.org/x/crypto v0.14.0 // indirect
|
||||
golang.org/x/crypto v0.17.0 // indirect
|
||||
golang.org/x/net v0.17.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
)
|
||||
|
||||
@@ -10,8 +10,8 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
|
||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
|
||||
|
||||
@@ -513,6 +513,13 @@ foreach my $table_to_generate (@tables) {
|
||||
#############################################
|
||||
if ($repository_generation_option eq "all" || $repository_generation_option eq "extended") {
|
||||
my $generated_repository = './common/repositories/' . $table_to_generate . '_repository.h';
|
||||
|
||||
# check if file exists firsts
|
||||
if (-e $generated_repository) {
|
||||
print "File [$generated_repository] already exists! Can't overwrite extended once created!\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $cmake_generated_reference = $generated_repository;
|
||||
$cmake_generated_reference =~ s/.\/common\///g;
|
||||
$generated_repository_files .= $cmake_generated_reference . "\n";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -78,6 +78,10 @@ ADD_EXECUTABLE(world ${world_sources} ${world_headers})
|
||||
|
||||
INSTALL(TARGETS world RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
IF (WIN32 AND EQEMU_BUILD_PCH)
|
||||
TARGET_PRECOMPILE_HEADERS(world PRIVATE ../common/pch/pch.h)
|
||||
ENDIF ()
|
||||
|
||||
ADD_DEFINITIONS(-DWORLD)
|
||||
|
||||
TARGET_LINK_LIBRARIES(world ${SERVER_LIBS})
|
||||
|
||||
@@ -9,5 +9,7 @@ void WorldserverCLI::DatabaseUpdates(int argc, char **argv, argh::parser &cmd, s
|
||||
}
|
||||
|
||||
DatabaseUpdate update;
|
||||
update.SetDatabase(&database)->CheckDbUpdates();
|
||||
update.SetDatabase(&database)
|
||||
->SetContentDatabase(&content_db)
|
||||
->CheckDbUpdates();
|
||||
}
|
||||
|
||||
+1
-1
@@ -434,7 +434,7 @@ int main(int argc, char **argv)
|
||||
tod.year,
|
||||
tod.month,
|
||||
tod.day,
|
||||
tod.hour,
|
||||
tod.hour - 1,
|
||||
tod.minute
|
||||
);
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ void SharedTaskManager::LoadSharedTaskState()
|
||||
|
||||
SharedTaskManager *SharedTaskManager::LoadTaskData()
|
||||
{
|
||||
m_task_data = TasksRepository::All(*m_content_database);
|
||||
m_task_data = TasksRepository::GetWhere(*m_content_database, "enabled = 1");
|
||||
m_task_activity_data = TaskActivitiesRepository::All(*m_content_database);
|
||||
|
||||
LogTasks("Loaded tasks [{}] activities [{}]", m_task_data.size(), m_task_activity_data.size());
|
||||
|
||||
+13
-1
@@ -26,6 +26,7 @@
|
||||
#include "../common/ip_util.h"
|
||||
#include "../common/zone_store.h"
|
||||
#include "../common/path_manager.h"
|
||||
#include "../common/database/database_update.h"
|
||||
|
||||
extern ZSList zoneserver_list;
|
||||
extern WorldConfig Config;
|
||||
@@ -293,7 +294,18 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
||||
const auto c = EQEmuConfig::get();
|
||||
if (c->auto_database_updates) {
|
||||
LogInfo("Checking Database Conversions");
|
||||
database.CheckDatabaseConversions();
|
||||
|
||||
auto *r = RuleManager::Instance();
|
||||
r->LoadRules(&database, "default", false);
|
||||
if (!RuleB(Bots, Enabled) && database.DoesTableExist("bot_data")) {
|
||||
LogInfo("Bot tables found but rule not enabled, enabling");
|
||||
r->SetRule("Bots:Enabled", "true", &database, true, true);
|
||||
}
|
||||
|
||||
DatabaseUpdate update{};
|
||||
update.SetDatabase(&database)
|
||||
->SetContentDatabase(&content_db)
|
||||
->CheckDbUpdates();
|
||||
}
|
||||
|
||||
if (RuleB(Logging, WorldGMSayLogging)) {
|
||||
|
||||
+6
-1
@@ -191,7 +191,6 @@ SET(zone_headers
|
||||
embperl.h
|
||||
encounter.h
|
||||
entity.h
|
||||
errmsg.h
|
||||
event_codes.h
|
||||
expedition.h
|
||||
expedition_database.h
|
||||
@@ -284,6 +283,12 @@ ADD_EXECUTABLE(zone ${zone_sources} ${zone_headers})
|
||||
|
||||
INSTALL(TARGETS zone RUNTIME DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||
|
||||
IF (WIN32 AND EQEMU_BUILD_PCH)
|
||||
TARGET_PRECOMPILE_HEADERS(zone PRIVATE ../common/pch/pch.h)
|
||||
TARGET_PRECOMPILE_HEADERS(zone PRIVATE ../common/types.h ../common/eqemu_logsys.h ../common/eqemu_logsys_log_aliases.h ../common/features.h ../common/global_define.h)
|
||||
TARGET_PRECOMPILE_HEADERS(zone PRIVATE mob.h npc.h corpse.h doors.h bot.h entity.h client.h zone.h)
|
||||
ENDIF()
|
||||
|
||||
ADD_DEFINITIONS(-DZONE)
|
||||
|
||||
TARGET_LINK_LIBRARIES(zone ${ZONE_LIBS})
|
||||
|
||||
+5
-5
@@ -46,7 +46,7 @@ void EntityList::DescribeAggro(Client *to_who, NPC *from_who, float d, bool verb
|
||||
);
|
||||
|
||||
bool is_engaged = from_who->IsEngaged();
|
||||
bool will_aggro_npcs = from_who->WillAggroNPCs();
|
||||
bool will_aggro_npcs = from_who->GetNPCAggro();
|
||||
if (is_engaged) {
|
||||
Mob *top = from_who->GetHateTop();
|
||||
to_who->Message(
|
||||
@@ -983,14 +983,14 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage, ExtraAtt
|
||||
float size_mod = GetSize();
|
||||
float other_size_mod = other->GetSize();
|
||||
|
||||
if (GetRace() == RACE_LAVA_DRAGON_49 || GetRace() == RACE_WURM_158 || GetRace() == RACE_GHOST_DRAGON_196) { //For races with a fixed size
|
||||
if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) { //For races with a fixed size
|
||||
size_mod = 60.0f;
|
||||
}
|
||||
else if (size_mod < 6.0) {
|
||||
size_mod = 8.0f;
|
||||
}
|
||||
|
||||
if (other->GetRace() == RACE_LAVA_DRAGON_49 || other->GetRace() == RACE_WURM_158 || other->GetRace() == RACE_GHOST_DRAGON_196) { //For races with a fixed size
|
||||
if (other->GetRace() == Race::LavaDragon || other->GetRace() == Race::Wurm || other->GetRace() == Race::GhostDragon) { //For races with a fixed size
|
||||
other_size_mod = 60.0f;
|
||||
}
|
||||
else if (other_size_mod < 6.0) {
|
||||
@@ -1011,11 +1011,11 @@ bool Mob::CombatRange(Mob* other, float fixed_size_mod, bool aeRampage, ExtraAtt
|
||||
size_mod *= size_mod * 4;
|
||||
}
|
||||
|
||||
if (other->GetRace() == RACE_VELIOUS_DRAGON_184) // Lord Vyemm and other velious dragons
|
||||
if (other->GetRace() == Race::VeliousDragon) // Lord Vyemm and other velious dragons
|
||||
{
|
||||
size_mod *= 1.75;
|
||||
}
|
||||
if (other->GetRace() == RACE_DRAGON_SKELETON_122) // Dracoliche in Fear. Skeletal Dragon
|
||||
if (other->GetRace() == Race::DragonSkeleton) // Dracoliche in Fear. Skeletal Dragon
|
||||
{
|
||||
size_mod *= 2.25;
|
||||
}
|
||||
|
||||
@@ -245,7 +245,7 @@ Json::Value ApiGetNpcListDetail(EQ::Net::WebsocketServerConnection *connection,
|
||||
row["swarm_owner"] = npc->GetSwarmOwner();
|
||||
row["swarm_target"] = npc->GetSwarmTarget();
|
||||
row["waypoint_max"] = npc->GetWaypointMax();
|
||||
row["will_aggro_npcs"] = npc->WillAggroNPCs();
|
||||
row["npc_aggro"] = npc->GetNPCAggro();
|
||||
|
||||
response.append(row);
|
||||
}
|
||||
|
||||
+18
-13
@@ -1820,7 +1820,7 @@ bool Client::Death(Mob* killerMob, int64 damage, uint16 spell, EQ::skills::Skill
|
||||
|
||||
auto emote_id = killerMob->GetEmoteID();
|
||||
if (emote_id) {
|
||||
killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid);
|
||||
killerMob->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledPC, emoteid, this);
|
||||
}
|
||||
|
||||
killerMob->TrySpellOnKill(killed_level, spell);
|
||||
@@ -2705,6 +2705,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
Corpse* corpse = nullptr;
|
||||
|
||||
const uint16 entity_id = GetID();
|
||||
|
||||
if (!HasOwner() && !IsMerc() && !GetSwarmInfo() && (!is_merchant || allow_merchant_corpse) &&
|
||||
((killer && (killer->IsClient() || (killer->HasOwner() && killer->GetUltimateOwner()->IsClient()) ||
|
||||
(killer->IsNPC() && killer->CastToNPC()->GetSwarmInfo() && killer->CastToNPC()->GetSwarmInfo()->GetOwner() && killer->CastToNPC()->GetSwarmInfo()->GetOwner()->IsClient())))
|
||||
@@ -2736,7 +2738,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
ApplyIllusionToCorpse(illusion_spell_id, corpse);
|
||||
|
||||
if (killer != 0 && emoteid != 0)
|
||||
corpse->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emoteid);
|
||||
corpse->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::AfterDeath, emoteid, killer);
|
||||
if (killer != 0 && killer->IsClient()) {
|
||||
corpse->AllowPlayerLoot(killer, 0);
|
||||
if (killer->IsGrouped()) {
|
||||
@@ -2814,12 +2816,13 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
entity_list.RemoveFromXTargets(this);
|
||||
}
|
||||
|
||||
|
||||
// Parse quests even if we're killed by an NPC
|
||||
if (oos) {
|
||||
if (IsNPC()) {
|
||||
auto emote_id = GetEmoteID();
|
||||
if (emote_id) {
|
||||
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid);
|
||||
DoNPCEmote(EQ::constants::EmoteEventTypes::OnDeath, emoteid, killer_mob);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2830,7 +2833,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
|
||||
auto emote_id = oos->GetEmoteID();
|
||||
if (emote_id) {
|
||||
oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id);
|
||||
oos->CastToNPC()->DoNPCEmote(EQ::constants::EmoteEventTypes::KilledNPC, emote_id, this);
|
||||
}
|
||||
if (killer_mob) {
|
||||
killer_mob->TrySpellOnKill(killed_level, spell);
|
||||
@@ -2855,13 +2858,15 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
entity_list.UpdateFindableNPCState(this, true);
|
||||
|
||||
m_combat_record.Stop();
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_DEATH_COMPLETE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
"{} {} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
static_cast<int>(attack_skill),
|
||||
entity_id
|
||||
);
|
||||
|
||||
std::vector<std::any> args = { corpse };
|
||||
@@ -2869,15 +2874,15 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
parse->EventNPC(EVENT_DEATH_COMPLETE, this, oos, export_string, 0, &args);
|
||||
}
|
||||
|
||||
/* Zone controller process EVENT_DEATH_ZONE (Death events) */
|
||||
|
||||
// Zone controller process EVENT_DEATH_ZONE (Death events)
|
||||
if (parse->HasQuestSub(ZONE_CONTROLLER_NPC_ID, EVENT_DEATH_ZONE)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {} {} {}",
|
||||
"{} {} {} {} {}",
|
||||
killer_mob ? killer_mob->GetID() : 0,
|
||||
damage,
|
||||
spell,
|
||||
static_cast<int>(attack_skill)
|
||||
static_cast<int>(attack_skill),
|
||||
entity_id
|
||||
);
|
||||
|
||||
std::vector<std::any> args = { corpse, this };
|
||||
@@ -4087,7 +4092,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
IsPlayerRace(GetBaseRace()) &&
|
||||
RuleI(Combat, FrontalStunImmunityRaces) & GetPlayerRaceBit(GetBaseRace())
|
||||
) ||
|
||||
GetBaseRace() == RACE_OGGOK_CITIZEN_93
|
||||
GetBaseRace() == Race::OggokCitizen
|
||||
) {
|
||||
is_immune_to_frontal_stun = true;
|
||||
}
|
||||
@@ -4107,7 +4112,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
IsPlayerRace(GetBaseRace()) &&
|
||||
RuleI(Combat, FrontalStunImmunityRaces) & GetPlayerRaceBit(GetBaseRace())
|
||||
) ||
|
||||
GetBaseRace() == RACE_OGGOK_CITIZEN_93
|
||||
GetBaseRace() == Race::OggokCitizen
|
||||
)
|
||||
) {
|
||||
is_immune_to_frontal_stun = true;
|
||||
@@ -5015,7 +5020,7 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
||||
hit.damage_done = (hit.damage_done * SlayDmgBonus) / 100;
|
||||
|
||||
/* Female */
|
||||
if (GetGender() == 1) {
|
||||
if (GetGender() == Gender::Female) {
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
|
||||
+3
-3
@@ -49,9 +49,9 @@ Beacon::Beacon(const glm::vec4 &in_pos, int lifetime) : Mob(
|
||||
nullptr, // in_lastname
|
||||
0, // in_cur_hp
|
||||
0, // in_max_hp
|
||||
MALE, // in_gender
|
||||
INVISIBLE_MAN, // in_race
|
||||
0, // in_class
|
||||
Gender::Male, // in_gender
|
||||
Race::InvisibleMan, // in_race
|
||||
Class::None, // in_class
|
||||
BT_NoTarget, // in_bodytype
|
||||
0, // in_deity
|
||||
0, // in_level
|
||||
|
||||
+675
-48
@@ -94,6 +94,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
||||
SetPullFlag(false);
|
||||
SetPullingFlag(false);
|
||||
SetReturningFlag(false);
|
||||
SetIsUsingItemClick(false);
|
||||
m_previous_pet_order = SPO_Guard;
|
||||
|
||||
rest_timer.Disable();
|
||||
@@ -107,6 +108,8 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
||||
// Do this once and only in this constructor
|
||||
GenerateAppearance();
|
||||
GenerateBaseStats();
|
||||
bot_timers.clear();
|
||||
|
||||
// Calculate HitPoints Last As It Uses Base Stats
|
||||
current_hp = GenerateBaseHitPoints();
|
||||
current_mana = GenerateBaseManaPoints();
|
||||
@@ -114,8 +117,6 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
||||
hp_regen = CalcHPRegen();
|
||||
mana_regen = CalcManaRegen();
|
||||
end_regen = CalcEnduranceRegen();
|
||||
for (int i = 0; i < MaxTimer; i++)
|
||||
timers[i] = 0;
|
||||
|
||||
strcpy(name, GetCleanName());
|
||||
memset(&_botInspectMessage, 0, sizeof(InspectMessage_Struct));
|
||||
@@ -213,6 +214,7 @@ Bot::Bot(
|
||||
SetPullFlag(false);
|
||||
SetPullingFlag(false);
|
||||
SetReturningFlag(false);
|
||||
SetIsUsingItemClick(false);
|
||||
m_previous_pet_order = SPO_Guard;
|
||||
|
||||
rest_timer.Disable();
|
||||
@@ -238,9 +240,6 @@ Bot::Bot(
|
||||
error_message.clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < MaxTimer; i++)
|
||||
timers[i] = 0;
|
||||
|
||||
if (GetClass() == Class::Rogue) {
|
||||
m_evade_timer.Start();
|
||||
}
|
||||
@@ -252,8 +251,10 @@ Bot::Bot(
|
||||
|
||||
GenerateBaseStats();
|
||||
|
||||
if (!database.botdb.LoadTimers(this) && bot_owner)
|
||||
bot_timers.clear();
|
||||
if (!database.botdb.LoadTimers(this) && bot_owner) {
|
||||
bot_owner->Message(Chat::White, "%s for '%s'", BotDatabase::fail::LoadTimers(), GetCleanName());
|
||||
}
|
||||
|
||||
LoadAAs();
|
||||
|
||||
@@ -280,10 +281,10 @@ Bot::Bot(
|
||||
case SE_IllusionCopy:
|
||||
case SE_Illusion: {
|
||||
if (spell.base_value[x1] == -1) {
|
||||
if (gender == FEMALE) {
|
||||
gender = MALE;
|
||||
} else if (gender == MALE) {
|
||||
gender = FEMALE;
|
||||
if (gender == Gender::Female) {
|
||||
gender = Gender::Male;
|
||||
} else if (gender == Gender::Male) {
|
||||
gender = Gender::Female;
|
||||
}
|
||||
|
||||
SendIllusionPacket(
|
||||
@@ -1668,7 +1669,7 @@ bool Bot::Process()
|
||||
mob_close_scan_timer.GetDuration()
|
||||
);
|
||||
|
||||
entity_list.ScanCloseClientMobs(close_mobs, this);
|
||||
entity_list.ScanCloseMobs(close_mobs, this, IsMoving());
|
||||
}
|
||||
|
||||
SpellProcess();
|
||||
@@ -2680,7 +2681,7 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it
|
||||
float other_size_mod = tar->GetSize();
|
||||
|
||||
// For races with a fixed size
|
||||
if (GetRace() == RT_DRAGON || GetRace() == RT_WURM || GetRace() == RT_DRAGON_7) {
|
||||
if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) {
|
||||
// size_mod = 60.0f;
|
||||
}
|
||||
|
||||
@@ -2689,7 +2690,7 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it
|
||||
}
|
||||
|
||||
// For races with a fixed size
|
||||
if (tar->GetRace() == RT_DRAGON || tar->GetRace() == RT_WURM || tar->GetRace() == RT_DRAGON_7) {
|
||||
if (tar->GetRace() == Race::LavaDragon || tar->GetRace() == Race::Wurm || tar->GetRace() == Race::GhostDragon) {
|
||||
other_size_mod = 60.0f;
|
||||
}
|
||||
|
||||
@@ -2901,12 +2902,12 @@ void Bot::AcquireBotTarget(Group* bot_group, Raid* raid, Client* leash_owner, fl
|
||||
}
|
||||
} else {
|
||||
// This will keep bots on target for now..but, future updates will allow for rooting/stunning
|
||||
if (auto escaping = hate_list.GetEscapingEntOnHateList(leash_owner, leash_distance)) {
|
||||
if (auto escaping = hate_list.GetEscapingMobOnHateList(leash_owner, leash_distance)) {
|
||||
SetTarget(escaping);
|
||||
}
|
||||
|
||||
if (!GetTarget()) {
|
||||
auto most_hate = hate_list.GetEntWithMostHateOnList(this, nullptr, true);
|
||||
auto most_hate = hate_list.GetMobWithMostHateOnList(this, nullptr, true);
|
||||
if (most_hate) {
|
||||
SetTarget(most_hate);
|
||||
}
|
||||
@@ -3638,41 +3639,45 @@ uint32 Bot::SpawnedBotCount(const uint32 owner_id, uint8 class_id) {
|
||||
return spawned_bot_count;
|
||||
}
|
||||
|
||||
void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) {
|
||||
void Bot::LevelBotWithClient(Client* c, uint8 new_level, bool send_appearance) {
|
||||
// This essentially performs a '#bot update,' with appearance packets, based on the current methods.
|
||||
// This should not be called outside of Client::SetEXP() due to it's lack of rule checks.
|
||||
if (client) {
|
||||
std::list<Bot*> blist = entity_list.GetBotsByBotOwnerCharacterID(client->CharacterID());
|
||||
// This should not be called outside of Client::SetEXP() due to its lack of rule checks.
|
||||
|
||||
for (auto biter = blist.begin(); biter != blist.end(); ++biter) {
|
||||
Bot* bot = *biter;
|
||||
if (c) {
|
||||
const auto &l = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID());
|
||||
|
||||
if (bot && (bot->GetLevel() != client->GetLevel())) {
|
||||
bot->SetPetChooser(false); // not sure what this does, but was in bot 'update' code
|
||||
bot->CalcBotStats(client->GetBotOption(Client::booStatsUpdate));
|
||||
for (const auto &e : l) {
|
||||
if (e && (e->GetLevel() != c->GetLevel())) {
|
||||
int levels_change = (new_level - e->GetLevel());
|
||||
|
||||
if (sendlvlapp) {
|
||||
bot->SendLevelAppearance();
|
||||
if (levels_change < 0) {
|
||||
parse->EventBot(EVENT_LEVEL_DOWN, e, nullptr, std::to_string(std::abs(levels_change)), 0);
|
||||
} else {
|
||||
parse->EventBot(EVENT_LEVEL_UP, e, nullptr, std::to_string(levels_change), 0);
|
||||
}
|
||||
// modified from Client::SetLevel()
|
||||
|
||||
e->SetPetChooser(false); // not sure what this does, but was in bot 'update' code
|
||||
e->CalcBotStats(c->GetBotOption(Client::booStatsUpdate));
|
||||
|
||||
if (send_appearance) {
|
||||
e->SendLevelAppearance();
|
||||
}
|
||||
|
||||
if (!RuleB(Bots, BotHealOnLevel)) {
|
||||
int mhp = bot->CalcMaxHP();
|
||||
if (bot->GetHP() > mhp) {
|
||||
bot->SetHP(mhp);
|
||||
const int64 max_hp = e->CalcMaxHP();
|
||||
if (e->GetHP() > max_hp) {
|
||||
e->SetHP(max_hp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
bot->SetHP(bot->CalcMaxHP());
|
||||
bot->SetMana(bot->CalcMaxMana());
|
||||
} else {
|
||||
e->SetHP(e->CalcMaxHP());
|
||||
e->SetMana(e->CalcMaxMana());
|
||||
}
|
||||
|
||||
bot->SendHPUpdate();
|
||||
bot->SendAppearancePacket(AT_WhoLevel, level, true, true); // who level change
|
||||
bot->AI_AddBotSpells(bot->GetBotSpellID());
|
||||
e->SendHPUpdate();
|
||||
e->SendAppearancePacket(AT_WhoLevel, new_level, true, true); // who level change
|
||||
e->AI_AddBotSpells(e->GetBotSpellID());
|
||||
}
|
||||
}
|
||||
|
||||
blist.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7912,18 +7917,17 @@ bool Bot::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
return false;
|
||||
|
||||
if (spell.recast_time > 0) {
|
||||
if (CheckDisciplineRecastTimers(this, spells[spell_id].timer_id)) {
|
||||
if (spells[spell_id].timer_id > 0 && spells[spell_id].timer_id < MAX_DISCIPLINE_TIMERS)
|
||||
SetDisciplineRecastTimer(spells[spell_id].timer_id, spell.recast_time);
|
||||
if (CheckDisciplineReuseTimer(spell_id)) {
|
||||
if (spells[spell_id].timer_id > 0) {
|
||||
SetDisciplineReuseTimer(spell_id);
|
||||
}
|
||||
} else {
|
||||
uint32 remaining_time = (GetDisciplineRemainingTime(this, spells[spell_id].timer_id) / 1000);
|
||||
GetOwner()->Message(
|
||||
Chat::White,
|
||||
uint32 remaining_time = (GetDisciplineReuseRemainingTime(spell_id) / 1000);
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} can use this discipline in {}.",
|
||||
GetCleanName(),
|
||||
"I can use this discipline in {}.",
|
||||
Strings::SecondsToTime(remaining_time)
|
||||
).c_str()
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -8806,4 +8810,627 @@ void Bot::AddBotStartingItems(uint16 race_id, uint8 class_id)
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::SetSpellRecastTimer(uint16 spell_id, int32 recast_delay) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
OwnerMessage("Failed to set spell recast timer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!recast_delay) {
|
||||
recast_delay = CalcSpellRecastTimer(spell_id);
|
||||
}
|
||||
|
||||
if (CheckSpellRecastTimer(spell_id)) {
|
||||
BotTimer_Struct t;
|
||||
|
||||
t.timer_id = spells[ spell_id ].timer_id;
|
||||
t.timer_value = (Timer::GetCurrentTime() + recast_delay);
|
||||
t.recast_time = recast_delay;
|
||||
t.is_spell = true;
|
||||
t.is_disc = false;
|
||||
t.spell_id = spells[ spell_id ].id;
|
||||
t.is_item = false;
|
||||
t.item_id = 0;
|
||||
|
||||
bot_timers.push_back(t);
|
||||
} else {
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_spell &&
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[ i ].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = (Timer::GetCurrentTime() + recast_delay);
|
||||
bot_timers[i].recast_time = recast_delay;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Bot::GetSpellRecastTimer(uint16 spell_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (spell_id && !IsValidSpell(spell_id)) {
|
||||
OwnerMessage("Failed to get spell recast timer.");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_spell &&
|
||||
(
|
||||
!spell_id ||
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
)
|
||||
) {
|
||||
result = bot_timers[i].timer_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 Bot::GetSpellRecastRemainingTime(uint16 spell_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (GetSpellRecastTimer(spell_id) > Timer::GetCurrentTime()) {
|
||||
result = (GetSpellRecastTimer(spell_id) - Timer::GetCurrentTime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bot::CheckSpellRecastTimer(uint16 spell_id)
|
||||
{
|
||||
ClearExpiredTimers();
|
||||
|
||||
if (spell_id && !IsValidSpell(spell_id)) {
|
||||
OwnerMessage("Failed to check spell recast timer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetSpellRecastTimer(spell_id) < Timer::GetCurrentTime()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::SetDisciplineReuseTimer(uint16 spell_id, int32 reuse_timer)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
OwnerMessage("Failed to set discipline reuse timer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reuse_timer) {
|
||||
reuse_timer = CalcSpellRecastTimer(spell_id);
|
||||
}
|
||||
|
||||
if (CheckDisciplineReuseTimer(spell_id)) {
|
||||
BotTimer_Struct t;
|
||||
|
||||
t.timer_id = spells[ spell_id ].timer_id;
|
||||
t.timer_value = (Timer::GetCurrentTime() + reuse_timer);
|
||||
t.recast_time = reuse_timer;
|
||||
t.is_spell = false;
|
||||
t.is_disc = true;
|
||||
t.spell_id = spells[ spell_id ].id;
|
||||
t.is_item = false;
|
||||
t.item_id = 0;
|
||||
|
||||
bot_timers.push_back(t);
|
||||
} else {
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_disc &&
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = (Timer::GetCurrentTime() + reuse_timer);
|
||||
bot_timers[i].recast_time = reuse_timer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Bot::GetDisciplineReuseTimer(uint16 spell_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_disc &&
|
||||
(
|
||||
!spell_id ||
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
)
|
||||
) {
|
||||
result = bot_timers[i].timer_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 Bot::GetDisciplineReuseRemainingTime(uint16 spell_id) {
|
||||
uint32 result = 0;
|
||||
|
||||
if (GetDisciplineReuseTimer(spell_id) > Timer::GetCurrentTime()) {
|
||||
result = (GetDisciplineReuseTimer(spell_id) - Timer::GetCurrentTime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bot::CheckDisciplineReuseTimer(uint16 spell_id)
|
||||
{
|
||||
ClearExpiredTimers();
|
||||
|
||||
if (GetDisciplineReuseTimer(spell_id) < Timer::GetCurrentTime()) { //checks for spells on the same timer
|
||||
return true; //can cast spell
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::SetItemReuseTimer(uint32 item_id, uint32 reuse_timer)
|
||||
{
|
||||
const auto *item = database.GetItem(item_id);
|
||||
|
||||
if (!item) {
|
||||
OwnerMessage("Failed to set item reuse timer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->RecastDelay <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CheckItemReuseTimer(item_id)) {
|
||||
BotTimer_Struct t;
|
||||
|
||||
t.timer_id = (item->RecastType == NegativeItemReuse ? item->ID : item->RecastType);
|
||||
t.timer_value = (
|
||||
reuse_timer != 0 ?
|
||||
(Timer::GetCurrentTime() + reuse_timer) :
|
||||
(Timer::GetCurrentTime() + (item->RecastDelay * 1000))
|
||||
);
|
||||
t.recast_time = (reuse_timer != 0 ? reuse_timer : (item->RecastDelay * 1000));
|
||||
t.is_spell = false;
|
||||
t.is_disc = false;
|
||||
t.spell_id = 0;
|
||||
t.is_item = true;
|
||||
t.item_id = item->ID;
|
||||
|
||||
bot_timers.push_back(t);
|
||||
}
|
||||
else {
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_item &&
|
||||
(
|
||||
(
|
||||
item->RecastType != 0 &&
|
||||
item->RecastType == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].item_id == item_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = (
|
||||
reuse_timer != 0 ?
|
||||
(Timer::GetCurrentTime() + reuse_timer) :
|
||||
(Timer::GetCurrentTime() + (item->RecastDelay * 1000))
|
||||
);
|
||||
bot_timers[i].recast_time = (
|
||||
reuse_timer != 0 ?
|
||||
reuse_timer :
|
||||
(item->RecastDelay * 1000)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Bot::GetItemReuseTimer(uint32 item_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
const EQ::ItemData* item;
|
||||
|
||||
if (item_id) {
|
||||
item = database.GetItem(item_id);
|
||||
|
||||
if (!item) {
|
||||
OwnerMessage("Failed to get item reuse timer.");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_item &&
|
||||
(
|
||||
!item_id ||
|
||||
(
|
||||
(
|
||||
item->RecastType != 0 &&
|
||||
item->RecastType == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].item_id == item_id
|
||||
)
|
||||
)
|
||||
) {
|
||||
result = bot_timers[i].timer_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearExpiredTimers();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bot::CheckItemReuseTimer(uint32 item_id)
|
||||
{
|
||||
ClearExpiredTimers();
|
||||
|
||||
if (GetItemReuseTimer(item_id) < Timer::GetCurrentTime()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 Bot::GetItemReuseRemainingTime(uint32 item_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (GetItemReuseTimer(item_id) > Timer::GetCurrentTime()) {
|
||||
result = (GetItemReuseTimer(item_id) - Timer::GetCurrentTime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 Bot::CalcSpellRecastTimer(uint16 spell_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (spells[spell_id].recast_time == 0 && spells[spell_id].recovery_time == 0) {
|
||||
return result;
|
||||
} else {
|
||||
if (spells[spell_id].recovery_time > spells[spell_id].recast_time) {
|
||||
result = spells[spell_id].recovery_time;
|
||||
} else {
|
||||
result = spells[spell_id].recast_time;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Bot::ClearDisciplineReuseTimer(uint16 spell_id)
|
||||
{
|
||||
if (spell_id && !IsValidSpell(spell_id)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is not a valid spell ID.'",
|
||||
spell_id
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_disc &&
|
||||
bot_timers[i].timer_value >= Timer::GetCurrentTime()
|
||||
) {
|
||||
if (
|
||||
!spell_id ||
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearExpiredTimers();
|
||||
}
|
||||
|
||||
void Bot::ClearItemReuseTimer(uint32 item_id)
|
||||
{
|
||||
const EQ::ItemData* item;
|
||||
|
||||
if (item_id) {
|
||||
item = database.GetItem(item_id);
|
||||
|
||||
if (!item) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is not a valid item ID.",
|
||||
item_id
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (bot_timers[i].is_item && bot_timers[i].timer_value >= Timer::GetCurrentTime()) {
|
||||
if (
|
||||
!item_id ||
|
||||
(
|
||||
(
|
||||
item->RecastType != 0 &&
|
||||
item->RecastType == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].item_id == item_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearExpiredTimers();
|
||||
}
|
||||
|
||||
void Bot::ClearSpellRecastTimer(uint16 spell_id)
|
||||
{
|
||||
if (spell_id && !IsValidSpell(spell_id)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is not a valid spell ID.",
|
||||
spell_id
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (bot_timers[i].is_spell && bot_timers[i].timer_value >= Timer::GetCurrentTime()) {
|
||||
if (
|
||||
!spell_id ||
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearExpiredTimers();
|
||||
}
|
||||
|
||||
|
||||
void Bot::ClearExpiredTimers()
|
||||
{
|
||||
if (!bot_timers.empty()) {
|
||||
int current = 0;
|
||||
int end = bot_timers.size();
|
||||
|
||||
while (current < end) {
|
||||
if (bot_timers[current].timer_value < Timer::GetCurrentTime()) {
|
||||
bot_timers.erase(bot_timers.begin() + current);
|
||||
} else {
|
||||
current++;
|
||||
}
|
||||
|
||||
end = bot_timers.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::TryItemClick(uint16 slot_id)
|
||||
{
|
||||
if (!GetOwner()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto *inst = GetClickItem(slot_id);
|
||||
|
||||
if (!inst) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto *item = inst->GetItem();
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckItemReuseTimer(item->ID)) {
|
||||
uint32 remaining_time = (GetItemReuseRemainingTime(item->ID) / 1000);
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"I can use this item in {}.",
|
||||
Strings::SecondsToTime(remaining_time)
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
DoItemClick(item, slot_id);
|
||||
}
|
||||
|
||||
EQ::ItemInstance *Bot::GetClickItem(uint16 slot_id)
|
||||
{
|
||||
EQ::ItemInstance* inst = nullptr;
|
||||
const EQ::ItemData* item = nullptr;
|
||||
|
||||
inst = GetBotItem(slot_id);
|
||||
|
||||
if (!inst || !inst->GetItem()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
item = inst->GetItem();
|
||||
|
||||
if (item->ID == MAG_EPIC_1_0 && !RuleB(Bots, CanClickMageEpicV1)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is currently disabled for bots to click.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (item->Click.Effect <= 0) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} does not have a clickable effect.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!IsValidSpell(item->Click.Effect)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} does not have a valid clickable effect.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (item->ReqLevel > GetLevel()) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"I am below the level requirement of {} for {}.",
|
||||
item->ReqLevel,
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (item->Click.Level2 > GetLevel()) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"I must be level {} to use {}.",
|
||||
item->Click.Level2,
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (inst->GetCharges() == 0) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is out of charges.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id)
|
||||
{
|
||||
bool is_casting_bard_song = false;
|
||||
Mob* tar = (GetOwner()->GetTarget() ? GetOwner()->GetTarget() : this);
|
||||
|
||||
if (IsCasting()) {
|
||||
InterruptSpell();
|
||||
}
|
||||
|
||||
SetIsUsingItemClick(true);
|
||||
|
||||
BotGroupSay(
|
||||
this,
|
||||
fmt::format(
|
||||
"Attempting to cast [{}] on {}.",
|
||||
spells[item->Click.Effect].name,
|
||||
tar->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (!IsCastWhileInvisibleSpell(item->Click.Effect)) {
|
||||
CommonBreakInvisible();
|
||||
}
|
||||
|
||||
if (GetClass() == Class::Bard && IsCasting() && casting_spell_slot < EQ::spells::CastingSlot::MaxGems) {
|
||||
is_casting_bard_song = true;
|
||||
}
|
||||
|
||||
if (GetClass() == Class::Bard) {
|
||||
DoBardCastingFromItemClick(is_casting_bard_song, item->CastTime, item->Click.Effect, tar->GetID(), EQ::spells::CastingSlot::Item, slot_id, item->RecastType, item->RecastDelay);
|
||||
} else {
|
||||
if (!CastSpell(item->Click.Effect, tar->GetID(), EQ::spells::CastingSlot::Item, item->CastTime, 0, 0, slot_id)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"Casting failed for {}. This could be due to zone restrictions, target restrictions or other limiting factors.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
||||
|
||||
+30
-13
@@ -42,13 +42,12 @@ constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT_MAX = 2500; // as DSq value (50 uni
|
||||
|
||||
constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds
|
||||
|
||||
constexpr uint32 MAG_EPIC_1_0 = 28034;
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
constexpr int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this
|
||||
constexpr int MaxSpellTimer = 15;
|
||||
constexpr int MaxDisciplineTimer = 10;
|
||||
constexpr int DisciplineReuseStart = MaxSpellTimer + 1;
|
||||
constexpr int MaxTimer = MaxSpellTimer + MaxDisciplineTimer;
|
||||
constexpr int NegativeItemReuse = -1; // Unlinked timer for items
|
||||
|
||||
// nHSND negative Healer/Slower/Nuker/Doter
|
||||
// pH positive Healer
|
||||
@@ -222,6 +221,8 @@ public:
|
||||
void SetPullFlag(bool flag = true) { m_pull_flag = flag; }
|
||||
bool GetPullingFlag() const { return m_pulling_flag; }
|
||||
bool GetReturningFlag() const { return m_returning_flag; }
|
||||
bool GetIsUsingItemClick() { return is_using_item_click; }
|
||||
void SetIsUsingItemClick(bool flag = true) { is_using_item_click = flag; }
|
||||
bool UseDiscipline(uint32 spell_id, uint32 target);
|
||||
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid);
|
||||
uint8 GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid);
|
||||
@@ -286,6 +287,10 @@ public:
|
||||
void SetEndurance(int32 newEnd) override;
|
||||
void DoEnduranceUpkeep();
|
||||
|
||||
void TryItemClick(uint16 slot_id);
|
||||
EQ::ItemInstance* GetClickItem(uint16 slot_id);
|
||||
void DoItemClick(const EQ::ItemData* inst, uint16 slot_id);
|
||||
|
||||
bool AI_AddBotSpells(uint32 bot_spell_id);
|
||||
void AddSpellToBotList(
|
||||
int16 iPriority,
|
||||
@@ -408,11 +413,6 @@ public:
|
||||
static Bot* GetFirstBotInGroup(Group* group);
|
||||
static void ProcessClientZoneChange(Client* botOwner);
|
||||
static void ProcessBotOwnerRefDelete(Mob* botOwner); // Removes a Client* reference when the Client object is destroyed
|
||||
static int32 GetSpellRecastTimer(Bot *caster, int timer_index);
|
||||
static bool CheckSpellRecastTimers(Bot *caster, int SpellIndex);
|
||||
static int32 GetDisciplineRecastTimer(Bot *caster, int timer_index);
|
||||
static bool CheckDisciplineRecastTimers(Bot *caster, int timer_index);
|
||||
static uint32 GetDisciplineRemainingTime(Bot *caster, int timer_index);
|
||||
|
||||
//Raid methods
|
||||
static void ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite = false);
|
||||
@@ -612,8 +612,23 @@ public:
|
||||
_botStance = EQ::constants::stancePassive;
|
||||
}
|
||||
void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; }
|
||||
void SetSpellRecastTimer(int timer_index, int32 recast_delay);
|
||||
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
|
||||
uint32 GetSpellRecastTimer(uint16 spell_id = 0);
|
||||
bool CheckSpellRecastTimer(uint16 spell_id = 0);
|
||||
uint32 GetSpellRecastRemainingTime(uint16 spell_id = 0);
|
||||
void SetSpellRecastTimer(uint16 spell_id, int32 recast_delay = 0);
|
||||
uint32 CalcSpellRecastTimer(uint16 spell_id);
|
||||
uint32 GetDisciplineReuseTimer(uint16 spell_id = 0);
|
||||
bool CheckDisciplineReuseTimer(uint16 spell_id = 0);
|
||||
uint32 GetDisciplineReuseRemainingTime(uint16 spell_id = 0);
|
||||
void SetDisciplineReuseTimer(uint16 spell_id, int32 reuse_timer = 0);
|
||||
uint32 GetItemReuseTimer(uint32 item_id = 0);
|
||||
bool CheckItemReuseTimer(uint32 item_id = 0);
|
||||
void SetItemReuseTimer(uint32 item_id, uint32 reuse_timer = 0);
|
||||
void ClearDisciplineReuseTimer(uint16 spell_id = 0);
|
||||
void ClearItemReuseTimer(uint32 item_id = 0);
|
||||
void ClearSpellRecastTimer(uint16 spell_id = 0);
|
||||
uint32 GetItemReuseRemainingTime(uint32 item_id = 0);
|
||||
void ClearExpiredTimers();
|
||||
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
|
||||
void SetShowHelm(bool showhelm) { _showhelm = showhelm; }
|
||||
void SetBeardColor(uint8 value) { beardcolor = value; }
|
||||
@@ -713,7 +728,8 @@ public:
|
||||
|
||||
// New accessors for BotDatabase access
|
||||
bool DeleteBot();
|
||||
uint32* GetTimers() { return timers; }
|
||||
std::vector<BotTimer_Struct> GetBotTimers() { return bot_timers; }
|
||||
void SetBotTimers(std::vector<BotTimer_Struct> timers) { bot_timers = timers; }
|
||||
uint32 GetLastZoneID() const { return _lastZoneId; }
|
||||
int32 GetBaseAC() const { return _baseAC; }
|
||||
int32 GetBaseATK() const { return _baseATK; }
|
||||
@@ -828,6 +844,7 @@ protected:
|
||||
|
||||
std::vector<BotSpells_Struct> AIBot_spells;
|
||||
std::vector<BotSpells_Struct> AIBot_spells_enforced;
|
||||
std::vector<BotTimer_Struct> bot_timers;
|
||||
|
||||
private:
|
||||
// Class Members
|
||||
@@ -861,7 +878,6 @@ private:
|
||||
int32 cur_end;
|
||||
int32 max_end;
|
||||
int32 end_regen;
|
||||
uint32 timers[MaxTimer];
|
||||
|
||||
Timer m_evade_timer; // can be moved to pTimers at some point
|
||||
Timer m_alt_combat_hate_timer;
|
||||
@@ -875,6 +891,7 @@ private:
|
||||
bool m_pull_flag;
|
||||
bool m_pulling_flag;
|
||||
bool m_returning_flag;
|
||||
bool is_using_item_click;
|
||||
eStandingPetOrder m_previous_pet_order;
|
||||
uint32 m_bot_caster_range;
|
||||
BotCastingRoles m_CastingRoles;
|
||||
|
||||
+367
-30
@@ -1375,6 +1375,7 @@ int bot_command_init(void)
|
||||
bot_command_add("casterrange", "Controls the range casters will try to stay away from a mob (if too far, they will skip spells that are out-of-range)", AccountStatus::Player, bot_command_caster_range) ||
|
||||
bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) ||
|
||||
bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_subcommand_circle) ||
|
||||
bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) ||
|
||||
bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) ||
|
||||
bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) ||
|
||||
bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) ||
|
||||
@@ -1444,6 +1445,7 @@ int bot_command_init(void)
|
||||
bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", AccountStatus::Player, bot_command_summon_corpse) ||
|
||||
bot_command_add("suspend", "Suspends a bot's AI processing until released", AccountStatus::Player, bot_command_suspend) ||
|
||||
bot_command_add("taunt", "Toggles taunt use by a bot", AccountStatus::Player, bot_command_taunt) ||
|
||||
bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) ||
|
||||
bot_command_add("track", "Orders a capable bot to track enemies", AccountStatus::Player, bot_command_track) ||
|
||||
bot_command_add("viewcombos", "Views bot race class combinations", AccountStatus::Player, bot_command_view_combos) ||
|
||||
bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", AccountStatus::Player, bot_command_water_breathing)
|
||||
@@ -2832,7 +2834,7 @@ void bot_command_aggressive(Client *c, const Seperator *sep)
|
||||
}
|
||||
}
|
||||
|
||||
c->Message(Chat::White, "%i of %i bots have used aggressive disciplines", success_count, candidate_count);
|
||||
c->Message(Chat::White, "%i of %i bots have attempted to use aggressive disciplines", success_count, candidate_count);
|
||||
}
|
||||
|
||||
void bot_command_apply_poison(Client *c, const Seperator *sep)
|
||||
@@ -2868,7 +2870,7 @@ void bot_command_apply_poison(Client *c, const Seperator *sep)
|
||||
}
|
||||
if (my_rogue_bot->GetLevel() < 18) {
|
||||
|
||||
c->Message(Chat::White, "Your rogue bot must be level 18 before %s can apply poison!", (my_rogue_bot->GetGender() == 1 ? "she" : "he"));
|
||||
c->Message(Chat::White, "Your rogue bot must be level 18 before %s can apply poison!", (my_rogue_bot->GetGender() == Gender::Female ? "she" : "he"));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3300,7 +3302,7 @@ void bot_command_defensive(Client *c, const Seperator *sep)
|
||||
auto local_entry = list_iter->SafeCastToStance();
|
||||
if (helper_spell_check_fail(local_entry))
|
||||
continue;
|
||||
if (local_entry->stance_type != BCEnum::StT_Aggressive)
|
||||
if (local_entry->stance_type != BCEnum::StT_Defensive)
|
||||
continue;
|
||||
|
||||
for (auto bot_iter = sbl.begin(); bot_iter != sbl.end(); ) {
|
||||
@@ -3332,7 +3334,7 @@ void bot_command_defensive(Client *c, const Seperator *sep)
|
||||
}
|
||||
}
|
||||
|
||||
c->Message(Chat::White, "%i of %i bots have used defensive disciplines", success_count, candidate_count);
|
||||
c->Message(Chat::White, "%i of %i bots have attempted to use defensive disciplines", success_count, candidate_count);
|
||||
}
|
||||
|
||||
void bot_command_depart(Client *c, const Seperator *sep)
|
||||
@@ -3906,16 +3908,77 @@ void bot_command_invisibility(Client *c, const Seperator *sep)
|
||||
void bot_command_item_use(Client* c, const Seperator* sep)
|
||||
{
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "usage: %s ([empty])", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s empty] will display only bots that can use the item in an empty slot.", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s byclass classID] - Example: [%s byclass 7] will display only bots that match the class that can use the item. Example is a Monk, use [^create help] for a list of class IDs.", sep->arg[0], sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s casteronly] will display only caster bots that can use the item.", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s hybridonly] will display only hybrid bots that can use the item.", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s meleeonly] will display only melee bots that can use the item.", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s wiscasteronly] will display only Wisdom-based Caster bots that can use the item.", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s intcasteronly] will display only Intelligence-based Caster bots that can use the item.", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s plateonly] will display only Plate-wearing bots that can use the item.", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s chainonly] will display only Chain-wearing bots that can use the item.", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s leatheronly] will display only Leather-wearing bots that can use the item.", sep->arg[0]);
|
||||
c->Message(Chat::White, "usage: [%s clothonly] will display only Cloth-wearing bots that can use the item.", sep->arg[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
bool empty_only = false;
|
||||
int8 class_mask = 0;
|
||||
bool caster_only = false;
|
||||
bool hybrid_only = false;
|
||||
bool melee_only = false;
|
||||
bool wis_caster_only = false;
|
||||
bool int_caster_only = false;
|
||||
bool plate_only = false;
|
||||
bool chain_only = false;
|
||||
bool leather_only = false;
|
||||
bool cloth_only = false;
|
||||
|
||||
std::string arg1 = sep->arg[1];
|
||||
std::string arg2 = sep->arg[2];
|
||||
if (arg1.compare("empty") == 0) {
|
||||
empty_only = true;
|
||||
}
|
||||
|
||||
else if (arg1.compare("byclass") == 0) {
|
||||
if (Strings::IsNumber(sep->arg[2])) {
|
||||
class_mask = Strings::ToUnsignedInt(sep->arg[2]);
|
||||
if (!(class_mask >= Class::Warrior && class_mask <= Class::Berserker)) {
|
||||
c->Message(Chat::White, "Invalid class range, you must choose between 1 (Warrior) and 15 (Beastlord)");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (arg1.compare("casteronly") == 0) {
|
||||
caster_only = true;
|
||||
}
|
||||
else if (arg1.compare("hybridonly") == 0) {
|
||||
hybrid_only = true;
|
||||
}
|
||||
else if (arg1.compare("meleeonly") == 0) {
|
||||
melee_only = true;
|
||||
}
|
||||
else if (arg1.compare("wiscasteronly") == 0) {
|
||||
wis_caster_only = true;
|
||||
}
|
||||
else if (arg1.compare("intcasteronly") == 0) {
|
||||
int_caster_only = true;
|
||||
}
|
||||
else if (arg1.compare("plateonly") == 0) {
|
||||
plate_only = true;
|
||||
}
|
||||
else if (arg1.compare("chainonly") == 0) {
|
||||
chain_only = true;
|
||||
}
|
||||
else if (arg1.compare("leatheronly") == 0) {
|
||||
leather_only = true;
|
||||
}
|
||||
else if (arg1.compare("clothonly") == 0) {
|
||||
cloth_only = true;
|
||||
}
|
||||
else if (!arg1.empty()) {
|
||||
c->Message(Chat::White, "Please choose the correct subtype. For help use %s help.", sep->arg[0]);
|
||||
return;
|
||||
}
|
||||
const auto item_instance = c->GetInv().GetItem(EQ::invslot::slotCursor);
|
||||
if (!item_instance) {
|
||||
c->Message(Chat::White, "No item found on cursor!");
|
||||
@@ -3948,11 +4011,41 @@ void bot_command_item_use(Client* c, const Seperator* sep)
|
||||
std::list<Bot*> sbl;
|
||||
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
|
||||
|
||||
if (class_mask) {
|
||||
ActionableBots::Filter_ByClasses(c, sbl, GetPlayerClassBit(class_mask));
|
||||
}
|
||||
|
||||
for (const auto& bot_iter : sbl) {
|
||||
if (!bot_iter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (caster_only && !IsCasterClass(bot_iter->GetClass())) {
|
||||
continue;
|
||||
}
|
||||
if (hybrid_only && !IsSpellFighterClass(bot_iter->GetClass())) {
|
||||
continue;
|
||||
}
|
||||
if (melee_only && !IsNonSpellFighterClass(bot_iter->GetClass())) {
|
||||
continue;
|
||||
}
|
||||
if (wis_caster_only && !IsWISCasterClass(bot_iter->GetClass())) {
|
||||
continue;
|
||||
}
|
||||
if (int_caster_only && !IsINTCasterClass(bot_iter->GetClass())) {
|
||||
continue;
|
||||
}
|
||||
if (plate_only && !IsPlateClass(bot_iter->GetClass())) {
|
||||
continue;
|
||||
}
|
||||
if (chain_only && !IsChainClass(bot_iter->GetClass())) {
|
||||
continue;
|
||||
}
|
||||
if (leather_only && !IsLeatherClass(bot_iter->GetClass())) {
|
||||
continue;
|
||||
}
|
||||
if (cloth_only && !IsClothClass(bot_iter->GetClass())) {
|
||||
continue;
|
||||
}
|
||||
if (((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace())) || ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass()))) {
|
||||
continue;
|
||||
}
|
||||
@@ -3997,7 +4090,8 @@ void bot_command_item_use(Client* c, const Seperator* sep)
|
||||
);
|
||||
|
||||
bot_iter->DoAnim(29);
|
||||
} else if (!equipped_item) {
|
||||
}
|
||||
else if (!equipped_item) {
|
||||
c->Message(
|
||||
Chat::Say,
|
||||
fmt::format(
|
||||
@@ -5194,6 +5288,194 @@ void bot_command_taunt(Client *c, const Seperator *sep)
|
||||
}
|
||||
}
|
||||
|
||||
void bot_command_timer(Client* c, const Seperator* sep)
|
||||
{
|
||||
if (helper_command_alias_fail(c, "bot_command_timer", sep->arg[0], "timer"))
|
||||
return;
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "usage: %s [clear | has | set] [disc | item | spell] [timer ID | item ID | spell ID | all] [optional ms for set] [actionable].", sep->arg[0]);
|
||||
c->Message(Chat::White, "When setting, you can leave the value blank to use the default for the item or specify a value in ms to set the timer to.");
|
||||
c->Message(Chat::White, "Returns or sets the provided timer(s) for the selected bot(s) or clears the selected timer(s) for the selected bot(s).");
|
||||
return;
|
||||
}
|
||||
|
||||
const int ab_mask = ActionableBots::ABM_Type1;
|
||||
|
||||
std::string arg1 = sep->arg[1];
|
||||
std::string arg2 = sep->arg[2];
|
||||
std::string arg3 = sep->arg[3];
|
||||
int ab_arg = 4;
|
||||
bool clear = false;
|
||||
bool has = false;
|
||||
bool set = false;
|
||||
bool disc = false;
|
||||
bool item = false;
|
||||
bool spell = false;
|
||||
uint32 timer_id = 0;
|
||||
uint32 timer_value = 0;
|
||||
bool all = false;
|
||||
|
||||
if (!arg1.compare("clear")) {
|
||||
clear = true;
|
||||
}
|
||||
else if (!arg1.compare("has")) {
|
||||
has = true;
|
||||
}
|
||||
else if (!arg1.compare("set")) {
|
||||
set = true;
|
||||
}
|
||||
else {
|
||||
c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!arg2.compare("disc")) {
|
||||
disc = true;
|
||||
}
|
||||
else if (!arg2.compare("item")) {
|
||||
item = true;
|
||||
}
|
||||
else if (!arg2.compare("spell")) {
|
||||
spell = true;
|
||||
}
|
||||
else {
|
||||
c->Message(Chat::White, "Incorrect timer type, use %s help for a list of options.", sep->arg[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (sep->IsNumber(3)) {
|
||||
timer_id = atoi(sep->arg[3]);
|
||||
if (timer_id < 0) {
|
||||
c->Message(Chat::White, "You cannot use negative numbers.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!arg3.compare("all")) {
|
||||
if (has || set) {
|
||||
c->Message(Chat::White, "You can only use 'all' for clearing timers.");
|
||||
return;
|
||||
}
|
||||
|
||||
all = true;
|
||||
}
|
||||
else {
|
||||
c->Message(Chat::White, "Incorrect ID option, use %s help for a list of options.", sep->arg[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (set) {
|
||||
if (sep->IsNumber(4)) {
|
||||
ab_arg = 5;
|
||||
timer_value = atoi(sep->arg[4]);
|
||||
if (timer_value <= 0) {
|
||||
c->Message(Chat::White, "You cannot use 0 or negative numbers.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string class_race_arg = sep->arg[ab_arg];
|
||||
bool class_race_check = false;
|
||||
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
|
||||
class_race_check = true;
|
||||
}
|
||||
|
||||
std::list<Bot*> sbl;
|
||||
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
|
||||
return;
|
||||
}
|
||||
sbl.remove(nullptr);
|
||||
|
||||
for (auto my_bot : sbl) {
|
||||
bool found = false;
|
||||
|
||||
if (clear) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} says, 'Clearing {} timer{}'",
|
||||
my_bot->GetCleanName(),
|
||||
disc ? "Discipline" : item ? "Item" : "Spell",
|
||||
(all ? "s." : ".")
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (disc) {
|
||||
my_bot->ClearDisciplineReuseTimer(timer_id);
|
||||
}
|
||||
else if (item) {
|
||||
my_bot->ClearItemReuseTimer(timer_id);
|
||||
}
|
||||
else if (spell) {
|
||||
my_bot->ClearSpellRecastTimer(timer_id);
|
||||
}
|
||||
}
|
||||
else if (has) {
|
||||
uint32 remaining_time;
|
||||
std::string time_string = "";
|
||||
|
||||
if (disc) {
|
||||
if (!my_bot->CheckDisciplineReuseTimer(timer_id)) {
|
||||
remaining_time = my_bot->GetDisciplineReuseRemainingTime(timer_id) / 1000;
|
||||
time_string = Strings::SecondsToTime(remaining_time);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else if (item) {
|
||||
if (!my_bot->CheckItemReuseTimer(timer_id)) {
|
||||
remaining_time = my_bot->GetItemReuseRemainingTime(timer_id) / 1000;
|
||||
time_string = Strings::SecondsToTime(remaining_time);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
else if (spell) {
|
||||
if (!my_bot->CheckSpellRecastTimer(timer_id)) {
|
||||
remaining_time = my_bot->GetSpellRecastRemainingTime(timer_id) / 1000;
|
||||
time_string = Strings::SecondsToTime(remaining_time);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} says, 'I {}{}{}'",
|
||||
my_bot->GetCleanName(),
|
||||
(!found ? " do not have that timer currently" : " have "),
|
||||
(!found ? "" : time_string),
|
||||
(!found ? "." : " remaining.")
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
else if (set) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} says, 'Setting {} timer{} for {} to {}.'",
|
||||
my_bot->GetCleanName(),
|
||||
disc ? "Discipline" : item ? "Item" : "Spell",
|
||||
(all ? "s" : ""),
|
||||
timer_id,
|
||||
timer_value ? std::to_string(timer_value) : "the default value"
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (disc) {
|
||||
my_bot->ClearDisciplineReuseTimer(timer_id);
|
||||
my_bot->SetDisciplineReuseTimer(timer_id, timer_value);
|
||||
}
|
||||
else if (item) {
|
||||
my_bot->ClearItemReuseTimer(timer_id);
|
||||
my_bot->SetItemReuseTimer(timer_id, timer_value);
|
||||
}
|
||||
else if (spell) {
|
||||
my_bot->ClearSpellRecastTimer(timer_id);
|
||||
my_bot->SetSpellRecastTimer(timer_id, timer_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bot_command_track(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (helper_command_alias_fail(c, "bot_command_track", sep->arg[0], "track"))
|
||||
@@ -5351,7 +5633,7 @@ void bot_subcommand_bot_beard_color(Client *c, const Seperator *sep)
|
||||
uint8 uvalue = Strings::ToInt(sep->arg[1]);
|
||||
|
||||
auto fail_type = BCEnum::AFT_None;
|
||||
if (my_bot->GetGender() != MALE && my_bot->GetRace() != DWARF)
|
||||
if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF)
|
||||
fail_type = BCEnum::AFT_GenderRace;
|
||||
else if (!PlayerAppearance::IsValidBeardColor(my_bot->GetRace(), my_bot->GetGender(), uvalue))
|
||||
fail_type = BCEnum::AFT_Value;
|
||||
@@ -5388,7 +5670,7 @@ void bot_subcommand_bot_beard_style(Client *c, const Seperator *sep)
|
||||
uint8 uvalue = Strings::ToInt(sep->arg[1]);
|
||||
|
||||
auto fail_type = BCEnum::AFT_None;
|
||||
if (my_bot->GetGender() != MALE && my_bot->GetRace() != DWARF)
|
||||
if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF)
|
||||
fail_type = BCEnum::AFT_GenderRace;
|
||||
else if (!PlayerAppearance::IsValidBeard(my_bot->GetRace(), my_bot->GetGender(), uvalue))
|
||||
fail_type = BCEnum::AFT_Value;
|
||||
@@ -5631,11 +5913,11 @@ void bot_command_view_combos(Client *c, const Seperator *sep)
|
||||
};
|
||||
|
||||
const uint16 race_values[17] = {
|
||||
RACE_DOUG_0,
|
||||
RACE_HUMAN_1, RACE_BARBARIAN_2, RACE_ERUDITE_3, RACE_WOOD_ELF_4,
|
||||
RACE_HIGH_ELF_5, RACE_DARK_ELF_6, RACE_HALF_ELF_7, RACE_DWARF_8,
|
||||
RACE_TROLL_9, RACE_OGRE_10, RACE_HALFLING_11, RACE_GNOME_12,
|
||||
RACE_IKSAR_128, RACE_VAH_SHIR_130, RACE_FROGLOK_330, RACE_DRAKKIN_522
|
||||
Race::Doug,
|
||||
Race::Human, Race::Barbarian, Race::Erudite, Race::WoodElf,
|
||||
Race::HighElf, Race::DarkElf, Race::HalfElf, Race::Dwarf,
|
||||
Race::Troll, Race::Ogre, Race::Halfling, Race::Gnome,
|
||||
Race::Iksar, Race::VahShir, Race::Froglok2, Race::Drakkin
|
||||
};
|
||||
|
||||
if (helper_command_alias_fail(c, "bot_command_view_combos", sep->arg[0], "viewcombos")) {
|
||||
@@ -5746,11 +6028,11 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
|
||||
};
|
||||
|
||||
const uint16 race_values[17] = {
|
||||
RACE_DOUG_0,
|
||||
RACE_HUMAN_1, RACE_BARBARIAN_2, RACE_ERUDITE_3, RACE_WOOD_ELF_4,
|
||||
RACE_HIGH_ELF_5, RACE_DARK_ELF_6, RACE_HALF_ELF_7, RACE_DWARF_8,
|
||||
RACE_TROLL_9, RACE_OGRE_10, RACE_HALFLING_11, RACE_GNOME_12,
|
||||
RACE_IKSAR_128, RACE_VAH_SHIR_130, RACE_FROGLOK_330, RACE_DRAKKIN_522
|
||||
Race::Doug,
|
||||
Race::Human, Race::Barbarian, Race::Erudite, Race::WoodElf,
|
||||
Race::HighElf, Race::DarkElf, Race::HalfElf, Race::Dwarf,
|
||||
Race::Troll, Race::Ogre, Race::Halfling, Race::Gnome,
|
||||
Race::Iksar, Race::VahShir, Race::Froglok2, Race::Drakkin
|
||||
};
|
||||
|
||||
const std::string gender_substrs[2] = {
|
||||
@@ -5889,18 +6171,18 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
auto bot_gender = MALE;
|
||||
auto bot_gender = Gender::Male;
|
||||
|
||||
if (sep->IsNumber(4)) {
|
||||
bot_gender = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[4]));
|
||||
if (bot_gender == NEUTER) {
|
||||
bot_gender = MALE;
|
||||
if (bot_gender == Gender::Neuter) {
|
||||
bot_gender = Gender::Male;
|
||||
}
|
||||
} else {
|
||||
if (!strcasecmp(sep->arg[4], "m") || !strcasecmp(sep->arg[4], "male")) {
|
||||
bot_gender = MALE;
|
||||
bot_gender = Gender::Male;
|
||||
} else if (!strcasecmp(sep->arg[4], "f") || !strcasecmp(sep->arg[4], "female")) {
|
||||
bot_gender = FEMALE;
|
||||
bot_gender = Gender::Female;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9279,15 +9561,15 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
|
||||
return bot_id;
|
||||
}
|
||||
|
||||
if (!EQ::ValueWithin(bot_gender, MALE, FEMALE)) {
|
||||
if (!EQ::ValueWithin(bot_gender, Gender::Male, Gender::Female)) {
|
||||
bot_owner->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Gender: {} ({}) or {} ({})",
|
||||
GetGenderName(MALE),
|
||||
MALE,
|
||||
GetGenderName(FEMALE),
|
||||
FEMALE
|
||||
GetGenderName(Gender::Male),
|
||||
Gender::Male,
|
||||
GetGenderName(Gender::Female),
|
||||
Gender::Female
|
||||
).c_str()
|
||||
);
|
||||
return bot_id;
|
||||
@@ -10438,6 +10720,61 @@ void bot_command_caster_range(Client* c, const Seperator* sep)
|
||||
}
|
||||
}
|
||||
|
||||
void bot_command_click_item(Client* c, const Seperator* sep)
|
||||
{
|
||||
if (!RuleB(Bots, BotsCanClickItems)) {
|
||||
c->Message(Chat::White, "The ability for bots to click equipped items is currently disabled.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "usage: <slot id> %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
|
||||
c->Message(Chat::White, "This will cause the selected bots to click the item in the given slot ID.");
|
||||
c->Message(Chat::White, "Use ^invlist to see their items along with slot IDs.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!sep->IsNumber(1)) {
|
||||
c->Message(Chat::Yellow, "You must specify a slot ID. Use %s help for more information.", sep->arg[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
const int ab_mask = ActionableBots::ABM_Type1;
|
||||
|
||||
int ab_arg = 1;
|
||||
uint32 slot_id = 0;
|
||||
|
||||
if (sep->IsNumber(1)) {
|
||||
ab_arg = 2;
|
||||
slot_id = atoi(sep->arg[1]);
|
||||
if (slot_id < EQ::invslot::EQUIPMENT_BEGIN || slot_id > EQ::invslot::EQUIPMENT_END) {
|
||||
c->Message(Chat::Yellow, "You must specify a valid inventory slot from 0 to 22. Use %s help for more information", sep->arg[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string class_race_arg = sep->arg[ab_arg];
|
||||
bool class_race_check = false;
|
||||
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
|
||||
class_race_check = true;
|
||||
}
|
||||
|
||||
std::list<Bot*> sbl;
|
||||
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
|
||||
return;
|
||||
}
|
||||
sbl.remove(nullptr);
|
||||
|
||||
for (auto my_bot : sbl) {
|
||||
if (RuleI(Bots, BotsClickItemsMinLvl) > my_bot->GetLevel()) {
|
||||
c->Message(Chat::White, "%s must be level %i to use clickable items.", my_bot->GetCleanName(), RuleI(Bots, BotsClickItemsMinLvl));
|
||||
continue;
|
||||
}
|
||||
|
||||
my_bot->TryItemClick(slot_id);
|
||||
}
|
||||
}
|
||||
|
||||
void bot_command_pickpocket(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (helper_command_disabled(c, RuleB(Bots, AllowPickpocketCommand), "pickpocket")) {
|
||||
|
||||
@@ -554,6 +554,7 @@ void bot_command_bind_affinity(Client *c, const Seperator *sep);
|
||||
void bot_command_bot(Client *c, const Seperator *sep);
|
||||
void bot_command_caster_range(Client* c, const Seperator* sep);
|
||||
void bot_command_charm(Client *c, const Seperator *sep);
|
||||
void bot_command_click_item(Client* c, const Seperator* sep);
|
||||
void bot_command_cure(Client *c, const Seperator *sep);
|
||||
void bot_command_defensive(Client *c, const Seperator *sep);
|
||||
void bot_command_depart(Client *c, const Seperator *sep);
|
||||
@@ -595,6 +596,7 @@ void bot_command_enforce_spell_list(Client* c, const Seperator* sep);
|
||||
void bot_command_summon_corpse(Client *c, const Seperator *sep);
|
||||
void bot_command_suspend(Client *c, const Seperator *sep);
|
||||
void bot_command_taunt(Client *c, const Seperator *sep);
|
||||
void bot_command_timer(Client* c, const Seperator* sep);
|
||||
void bot_command_track(Client *c, const Seperator *sep);
|
||||
void bot_command_view_combos(Client *c, const Seperator *sep);
|
||||
void bot_command_water_breathing(Client *c, const Seperator *sep);
|
||||
|
||||
+76
-53
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "../common/repositories/bot_data_repository.h"
|
||||
#include "../common/repositories/bot_inventories_repository.h"
|
||||
#include "../common/repositories/bot_timers_repository.h"
|
||||
|
||||
#include "zonedb.h"
|
||||
#include "bot.h"
|
||||
@@ -917,45 +918,39 @@ bool BotDatabase::LoadTimers(Bot* bot_inst)
|
||||
if (!bot_inst)
|
||||
return false;
|
||||
|
||||
query = StringFormat(
|
||||
"SELECT"
|
||||
" IfNull(bt.`timer_id`, '0') As timer_id,"
|
||||
" IfNull(bt.`timer_value`, '0') As timer_value,"
|
||||
" IfNull(MAX(sn.`recast_time`), '0') AS MaxTimer"
|
||||
" FROM `bot_timers` bt, `spells_new` sn"
|
||||
" WHERE bt.`bot_id` = '%u' AND sn.`EndurTimerIndex` = ("
|
||||
"SELECT case"
|
||||
" WHEN timer_id > '%i' THEN timer_id - '%i'"
|
||||
" ELSE timer_id END AS timer_id"
|
||||
" FROM `bot_timers` WHERE `timer_id` = bt.`timer_id` AND `bot_id` = bt.`bot_id`" // double-check validity
|
||||
")"
|
||||
" AND sn.`classes%i` <= '%i'",
|
||||
bot_inst->GetBotID(),
|
||||
(DisciplineReuseStart - 1),
|
||||
(DisciplineReuseStart - 1),
|
||||
bot_inst->GetClass(),
|
||||
bot_inst->GetLevel()
|
||||
auto timers = BotTimersRepository::GetWhere(
|
||||
database,
|
||||
fmt::format("bot_id = {}", bot_inst->GetBotID())
|
||||
);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
if (!results.RowCount())
|
||||
return true;
|
||||
|
||||
uint32* bot_timers = bot_inst->GetTimers();
|
||||
if (!bot_timers)
|
||||
return false;
|
||||
std::vector<BotTimer_Struct> bot_timers;
|
||||
|
||||
int timer_id = 0;
|
||||
uint32 timer_value = 0;
|
||||
uint32 max_value = 0;
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
timer_id = Strings::ToInt(row[0]) - 1;
|
||||
timer_value = Strings::ToInt(row[1]);
|
||||
max_value = Strings::ToInt(row[2]);
|
||||
BotTimer_Struct t{};
|
||||
t.timer_id = 0;
|
||||
t.timer_value = 0;
|
||||
t.recast_time = 0;
|
||||
t.is_spell = false;
|
||||
t.is_disc = false;
|
||||
t.spell_id = 0;
|
||||
t.is_item = false;
|
||||
t.item_id = 0;
|
||||
|
||||
if (timer_id >= 0 && timer_id < MaxTimer && timer_value < (Timer::GetCurrentTime() + max_value))
|
||||
bot_timers[timer_id] = timer_value;
|
||||
for (auto& timer : timers) {
|
||||
if (t.timer_value < (Timer::GetCurrentTime() + t.recast_time)) {
|
||||
t.timer_id = timer.timer_id;
|
||||
t.timer_value = timer.timer_value;
|
||||
t.recast_time = timer.recast_time;
|
||||
t.is_spell = timer.is_spell ? true : false;
|
||||
t.is_disc = timer.is_disc ? true : false;
|
||||
t.spell_id = timer.spell_id;
|
||||
t.is_item = timer.is_item ? true : false;
|
||||
t.item_id = timer.item_id;
|
||||
bot_timers.push_back(t);
|
||||
}
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
bot_inst->SetBotTimers(bot_timers);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -963,26 +958,56 @@ bool BotDatabase::LoadTimers(Bot* bot_inst)
|
||||
|
||||
bool BotDatabase::SaveTimers(Bot* bot_inst)
|
||||
{
|
||||
if (!bot_inst)
|
||||
if (!bot_inst) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!DeleteTimers(bot_inst->GetBotID()))
|
||||
if (!DeleteTimers(bot_inst->GetBotID())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32* bot_timers = bot_inst->GetTimers();
|
||||
if (!bot_timers)
|
||||
return false;
|
||||
std::vector<BotTimer_Struct> bot_timers = bot_inst->GetBotTimers();
|
||||
|
||||
for (int timer_index = 0; timer_index < MaxTimer; ++timer_index) {
|
||||
if (bot_timers[timer_index] <= Timer::GetCurrentTime())
|
||||
continue;
|
||||
if (bot_timers.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
query = fmt::format(
|
||||
"REPLACE INTO `bot_timers` (`bot_id`, `timer_id`, `timer_value`) VALUES ('{}', '{}', '{}')",
|
||||
bot_inst->GetBotID(), (timer_index + 1), bot_timers[timer_index]
|
||||
std::vector<BotTimersRepository::BotTimers> timers;
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (auto & bot_timer : bot_timers) {
|
||||
if (bot_timer.timer_value <= Timer::GetCurrentTime()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto t = BotTimersRepository::BotTimers{
|
||||
.bot_id = bot_inst->GetBotID(),
|
||||
.timer_id = bot_timer.timer_id,
|
||||
.timer_value = bot_timer.timer_value,
|
||||
.recast_time = bot_timer.recast_time,
|
||||
.is_spell = bot_timer.is_spell ? true : false,
|
||||
.is_disc = bot_timer.is_disc ? true : false,
|
||||
.spell_id = bot_timer.spell_id,
|
||||
.is_item = bot_timer.is_item ? true : false,
|
||||
.item_id = bot_timer.item_id
|
||||
};
|
||||
|
||||
timers.push_back(t);
|
||||
}
|
||||
|
||||
if (timers.empty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// delete existing
|
||||
BotTimersRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format("bot_id = {}", bot_inst->GetBotID())
|
||||
);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
|
||||
// bulk insert current
|
||||
auto success = BotTimersRepository::InsertMany(database, timers);
|
||||
if (!success) {
|
||||
DeleteTimers(bot_inst->GetBotID());
|
||||
return false;
|
||||
}
|
||||
@@ -993,13 +1018,11 @@ bool BotDatabase::SaveTimers(Bot* bot_inst)
|
||||
|
||||
bool BotDatabase::DeleteTimers(const uint32 bot_id)
|
||||
{
|
||||
if (!bot_id)
|
||||
if (!bot_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
query = StringFormat("DELETE FROM `bot_timers` WHERE `bot_id` = '%u'", bot_id);
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
BotTimersRepository::DeleteWhere(database, fmt::format("bot_id = {}", bot_id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -81,4 +81,15 @@ struct BotSpells_Struct {
|
||||
uint8 bucket_comparison;
|
||||
};
|
||||
|
||||
struct BotTimer_Struct {
|
||||
uint32 timer_id;
|
||||
uint32 timer_value;
|
||||
uint32 recast_time;
|
||||
bool is_spell;
|
||||
bool is_disc;
|
||||
uint16 spell_id;
|
||||
bool is_item;
|
||||
uint32 item_id;
|
||||
};
|
||||
|
||||
#endif // BOT_STRUCTS
|
||||
|
||||
+46
-111
@@ -108,7 +108,7 @@ bool Bot::BotCastSong(Mob* tar, uint8 botLevel) {
|
||||
auto iter : botSongList) {
|
||||
if (!iter.SpellId)
|
||||
continue;
|
||||
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
|
||||
if (!CheckSpellRecastTimer(iter.SpellId))
|
||||
continue;
|
||||
if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType()))
|
||||
continue;
|
||||
@@ -142,7 +142,7 @@ bool Bot::BotCastCombatSong(Mob* tar, uint8 botLevel) {
|
||||
auto iter : botSongList) {
|
||||
if (!iter.SpellId)
|
||||
continue;
|
||||
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
|
||||
if (!CheckSpellRecastTimer(iter.SpellId))
|
||||
continue;
|
||||
if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType()))
|
||||
continue;
|
||||
@@ -174,7 +174,7 @@ bool Bot::BotCastHateReduction(Mob* tar, uint8 botLevel, const BotSpell& botSpel
|
||||
for (auto iter : botSongList) {
|
||||
if (!iter.SpellId)
|
||||
continue;
|
||||
if (!CheckSpellRecastTimers(this, iter.SpellIndex))
|
||||
if (!CheckSpellRecastTimer(iter.SpellId))
|
||||
continue;
|
||||
if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType()))
|
||||
continue;
|
||||
@@ -312,7 +312,7 @@ bool Bot::BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CheckSpellRecastTimers(this, iter.SpellIndex)) {
|
||||
if (!CheckSpellRecastTimer(iter.SpellId)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -403,7 +403,7 @@ bool Bot::BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const b
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CheckSpellRecastTimers(this, s.SpellIndex))
|
||||
if (CheckSpellRecastTimer(s.SpellId))
|
||||
{
|
||||
|
||||
if (!(!tar->IsImmuneToSpell(s.SpellId, this) && tar->CanBuffStack(s.SpellId, botLevel, true) >= 0)) {
|
||||
@@ -438,7 +438,7 @@ bool Bot::BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const b
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CheckSpellRecastTimers(this, s.SpellIndex)) {
|
||||
if (CheckSpellRecastTimer(s.SpellId)) {
|
||||
|
||||
if (!(!tar->IsImmuneToSpell(s.SpellId, this) &&
|
||||
tar->CanBuffStack(s.SpellId, botLevel, true) >= 0)) {
|
||||
@@ -652,7 +652,7 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckSpellRecastTimers(this, s.SpellIndex)) {
|
||||
if (CheckSpellRecastTimer(s.SpellId)) {
|
||||
uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore();
|
||||
casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontBuffMeBefore);
|
||||
if (TempDontBuffMeBefore != tar->DontBuffMeBefore()) {
|
||||
@@ -947,7 +947,7 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
||||
}
|
||||
}
|
||||
|
||||
if (CheckSpellRecastTimers(this, s.SpellIndex))
|
||||
if (CheckSpellRecastTimer(s.SpellId))
|
||||
{
|
||||
uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore();
|
||||
|
||||
@@ -1306,10 +1306,8 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
|
||||
SetMana(hasMana);
|
||||
}
|
||||
else {
|
||||
AIBot_spells[i].time_cancast = Timer::GetCurrentTime() + spells[AIBot_spells[i].spellid].recast_time;
|
||||
|
||||
if (spells[AIBot_spells[i].spellid].timer_id > 0) {
|
||||
SetSpellRecastTimer(spells[AIBot_spells[i].spellid].timer_id, spells[AIBot_spells[i].spellid].recast_time);
|
||||
if (CalcSpellRecastTimer(AIBot_spells[i].spellid) > 0) {
|
||||
SetSpellRecastTimer(AIBot_spells[i].spellid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2044,7 +2042,7 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((botSpellList[i].type & spellType) && CheckSpellRecastTimers(botCaster, i)) {
|
||||
if ((botSpellList[i].type & spellType) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) {
|
||||
result.SpellId = botSpellList[i].spellid;
|
||||
result.SpellIndex = i;
|
||||
result.ManaCost = botSpellList[i].manacost;
|
||||
@@ -2069,7 +2067,7 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) {
|
||||
|
||||
for (auto botSpellListItr : botSpellList) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsFastHealSpell(botSpellListItr.SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr.SpellIndex)) {
|
||||
if (IsFastHealSpell(botSpellListItr.SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr.SpellId)) {
|
||||
result.SpellId = botSpellListItr.SpellId;
|
||||
result.SpellIndex = botSpellListItr.SpellIndex;
|
||||
result.ManaCost = botSpellListItr.ManaCost;
|
||||
@@ -2106,7 +2104,7 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) {
|
||||
if (
|
||||
botSpellList[i].spellid == botSpellListItr.SpellId &&
|
||||
(botSpellList[i].type & SpellType_Heal) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr.SpellIndex)
|
||||
botCaster->CheckSpellRecastTimer(botSpellListItr.SpellId)
|
||||
) {
|
||||
result.SpellId = botSpellListItr.SpellId;
|
||||
result.SpellIndex = botSpellListItr.SpellIndex;
|
||||
@@ -2139,7 +2137,7 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (IsCompleteHealSpell(botSpellList[i].spellid) && CheckSpellRecastTimers(botCaster, i)) {
|
||||
if (IsCompleteHealSpell(botSpellList[i].spellid) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) {
|
||||
result.SpellId = botSpellList[i].spellid;
|
||||
result.SpellIndex = i;
|
||||
result.ManaCost = botSpellList[i].manacost;
|
||||
@@ -2163,7 +2161,7 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) {
|
||||
|
||||
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
@@ -2192,7 +2190,7 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) {
|
||||
IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) ||
|
||||
IsFastHealSpell(botSpellListItr->SpellId)
|
||||
) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
@@ -2219,7 +2217,7 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (
|
||||
IsRegularGroupHealSpell(botSpellListItr->SpellId) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
@@ -2257,7 +2255,7 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) {
|
||||
if (
|
||||
botSpellList[i].spellid == botSpellListItr->SpellId &&
|
||||
(botSpellList[i].type & SpellType_Heal) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
@@ -2287,7 +2285,7 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (
|
||||
IsGroupCompleteHealSpell(botSpellListItr->SpellId) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
@@ -2314,7 +2312,7 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (
|
||||
IsMesmerizeSpell(botSpellListItr->SpellId) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
@@ -2343,7 +2341,7 @@ BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) {
|
||||
if (
|
||||
IsSlowSpell(botSpellListItr->SpellId) &&
|
||||
spells[botSpellListItr->SpellId].resist_type == RESIST_MAGIC &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
@@ -2371,7 +2369,7 @@ BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) {
|
||||
if (
|
||||
IsSlowSpell(botSpellListItr->SpellId) &&
|
||||
spells[botSpellListItr->SpellId].resist_type == RESIST_DISEASE &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
@@ -2437,7 +2435,7 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) {
|
||||
|
||||
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsSummonPetSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if (IsSummonPetSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) {
|
||||
if (!strncmp(spells[botSpellListItr->SpellId].teleport_zone, petType.c_str(), petType.length())) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
@@ -2546,7 +2544,7 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType
|
||||
|
||||
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if ((IsPureNukeSpell(botSpellListItr->SpellId) || IsDamageSpell(botSpellListItr->SpellId)) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if ((IsPureNukeSpell(botSpellListItr->SpellId) || IsDamageSpell(botSpellListItr->SpellId)) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
@@ -2574,7 +2572,7 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType
|
||||
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr)
|
||||
{
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
if (IsStunSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex))
|
||||
if (IsStunSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId))
|
||||
{
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
@@ -2614,7 +2612,7 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ
|
||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||
bool spellSelected = false;
|
||||
|
||||
if (CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if (botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) {
|
||||
if (selectLureNuke && (spells[botSpellListItr->SpellId].resist_difficulty < lureResisValue)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
@@ -2682,7 +2680,7 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) {
|
||||
if (((botSpellList[i].type & SpellType_Debuff) || IsDebuffSpell(botSpellList[i].spellid))
|
||||
&& (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster)
|
||||
&& tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0)
|
||||
&& CheckSpellRecastTimers(botCaster, i)) {
|
||||
&& botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) {
|
||||
result.SpellId = botSpellList[i].spellid;
|
||||
result.SpellIndex = i;
|
||||
result.ManaCost = botSpellList[i].manacost;
|
||||
@@ -2735,7 +2733,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) {
|
||||
|| (needsDiseaseResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistDisease)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)))
|
||||
&& (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster)
|
||||
&& tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0)
|
||||
&& CheckSpellRecastTimers(botCaster, i)) {
|
||||
&& botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) {
|
||||
result.SpellId = botSpellList[i].spellid;
|
||||
result.SpellIndex = i;
|
||||
result.ManaCost = botSpellList[i].manacost;
|
||||
@@ -2786,31 +2784,31 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) {
|
||||
for (std::list<BotSpell_wPriority>::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) {
|
||||
BotSpell selectedBotSpell = *itr;
|
||||
|
||||
if (IsGroupSpell(itr->SpellId) && CheckSpellRecastTimers(botCaster, itr->SpellIndex)) {
|
||||
if (IsGroupSpell(itr->SpellId) && botCaster->CheckSpellRecastTimer(selectedBotSpell.SpellId)) {
|
||||
if (selectedBotSpell.SpellId == 0)
|
||||
continue;
|
||||
|
||||
if (isPoisoned && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) {
|
||||
if (isPoisoned && IsEffectInSpell(selectedBotSpell.SpellId, SE_PoisonCounter)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
else if (isDiseased && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) {
|
||||
else if (isDiseased && IsEffectInSpell(selectedBotSpell.SpellId, SE_DiseaseCounter)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
else if (isCursed && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) {
|
||||
else if (isCursed && IsEffectInSpell(selectedBotSpell.SpellId, SE_CurseCounter)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
else if (isCorrupted && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) {
|
||||
else if (isCorrupted && IsEffectInSpell(selectedBotSpell.SpellId, SE_CorruptionCounter)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
else if (IsEffectInSpell(itr->SpellId, SE_DispelDetrimental)) {
|
||||
else if (IsEffectInSpell(selectedBotSpell.SpellId, SE_DispelDetrimental)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
|
||||
if (spellSelected)
|
||||
{
|
||||
result.SpellId = itr->SpellId;
|
||||
result.SpellIndex = itr->SpellIndex;
|
||||
result.ManaCost = itr->ManaCost;
|
||||
result.SpellId = selectedBotSpell.SpellId;
|
||||
result.SpellIndex = selectedBotSpell.SpellIndex;
|
||||
result.ManaCost = selectedBotSpell.ManaCost;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -2823,31 +2821,31 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) {
|
||||
for(std::list<BotSpell_wPriority>::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) {
|
||||
BotSpell selectedBotSpell = *itr;
|
||||
|
||||
if (CheckSpellRecastTimers(botCaster, itr->SpellIndex)) {
|
||||
if (botCaster->CheckSpellRecastTimer(selectedBotSpell.SpellId)) {
|
||||
if (selectedBotSpell.SpellId == 0)
|
||||
continue;
|
||||
|
||||
if (isPoisoned && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) {
|
||||
if (isPoisoned && IsEffectInSpell(selectedBotSpell.SpellId, SE_PoisonCounter)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
else if (isDiseased && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) {
|
||||
else if (isDiseased && IsEffectInSpell(selectedBotSpell.SpellId, SE_DiseaseCounter)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
else if (isCursed && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) {
|
||||
else if (isCursed && IsEffectInSpell(selectedBotSpell.SpellId, SE_CurseCounter)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
else if (isCorrupted && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) {
|
||||
else if (isCorrupted && IsEffectInSpell(selectedBotSpell.SpellId, SE_CorruptionCounter)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
else if (IsEffectInSpell(itr->SpellId, SE_DispelDetrimental)) {
|
||||
else if (IsEffectInSpell(selectedBotSpell.SpellId, SE_DispelDetrimental)) {
|
||||
spellSelected = true;
|
||||
}
|
||||
|
||||
if (spellSelected)
|
||||
{
|
||||
result.SpellId = itr->SpellId;
|
||||
result.SpellIndex = itr->SpellIndex;
|
||||
result.ManaCost = itr->ManaCost;
|
||||
result.SpellId = selectedBotSpell.SpellId;
|
||||
result.SpellIndex = selectedBotSpell.SpellIndex;
|
||||
result.ManaCost = selectedBotSpell.ManaCost;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -2859,69 +2857,6 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void Bot::SetSpellRecastTimer(int timer_index, int32 recast_delay) {
|
||||
if (timer_index > 0 && timer_index <= MaxSpellTimer) {
|
||||
timers[timer_index - 1] = Timer::GetCurrentTime() + recast_delay;
|
||||
}
|
||||
}
|
||||
|
||||
int32 Bot::GetSpellRecastTimer(Bot *caster, int timer_index) {
|
||||
int32 result = 0;
|
||||
if (caster) {
|
||||
if (timer_index > 0 && timer_index <= MaxSpellTimer) {
|
||||
result = caster->timers[timer_index - 1];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bot::CheckSpellRecastTimers(Bot *caster, int SpellIndex) {
|
||||
if (caster) {
|
||||
if (caster->AIBot_spells[SpellIndex].time_cancast < Timer::GetCurrentTime()) { //checks spell recast
|
||||
if (GetSpellRecastTimer(caster, spells[caster->AIBot_spells[SpellIndex].spellid].timer_id) < Timer::GetCurrentTime()) { //checks for spells on the same timer
|
||||
return true; //can cast spell
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::SetDisciplineRecastTimer(int timer_index, int32 recast_delay) {
|
||||
if (timer_index > 0 && timer_index <= MaxDisciplineTimer) {
|
||||
timers[DisciplineReuseStart + timer_index - 1] = Timer::GetCurrentTime() + recast_delay;
|
||||
}
|
||||
}
|
||||
|
||||
int32 Bot::GetDisciplineRecastTimer(Bot *caster, int timer_index) {
|
||||
int32 result = 0;
|
||||
if (caster) {
|
||||
if (timer_index > 0 && timer_index <= MaxDisciplineTimer) {
|
||||
result = caster->timers[DisciplineReuseStart + timer_index - 1];
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 Bot::GetDisciplineRemainingTime(Bot *caster, int timer_index) {
|
||||
int32 result = 0;
|
||||
if (caster) {
|
||||
if (timer_index > 0 && timer_index <= MaxDisciplineTimer) {
|
||||
if (GetDisciplineRecastTimer(caster, timer_index) > Timer::GetCurrentTime())
|
||||
result = GetDisciplineRecastTimer(caster, timer_index) - Timer::GetCurrentTime();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bot::CheckDisciplineRecastTimers(Bot *caster, int timer_index) {
|
||||
if (caster) {
|
||||
if (GetDisciplineRecastTimer(caster, timer_index) < Timer::GetCurrentTime()) { //checks for spells on the same timer
|
||||
return true; //can cast spell
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
||||
{
|
||||
uint8 spell_type_index = SPELL_TYPE_COUNT;
|
||||
|
||||
+217
-103
@@ -86,9 +86,9 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
"", // in_lastname
|
||||
0, // in_cur_hp
|
||||
0, // in_max_hp
|
||||
0, // in_gender
|
||||
0, // in_race
|
||||
0, // in_class
|
||||
Gender::Male, // in_gender
|
||||
Race::Doug, // in_race
|
||||
Class::None, // in_class
|
||||
BT_Humanoid, // in_bodytype
|
||||
0, // in_deity
|
||||
0, // in_level
|
||||
@@ -1411,126 +1411,163 @@ void Client::SetMaxHP() {
|
||||
Save();
|
||||
}
|
||||
|
||||
bool Client::UpdateLDoNPoints(uint32 theme_id, int points) {
|
||||
|
||||
/* make sure total stays in sync with individual buckets
|
||||
m_pp.ldon_points_available = m_pp.ldon_points_guk
|
||||
+m_pp.ldon_points_mir
|
||||
+m_pp.ldon_points_mmc
|
||||
+m_pp.ldon_points_ruj
|
||||
+m_pp.ldon_points_tak; */
|
||||
|
||||
if(points < 0) {
|
||||
if(m_pp.ldon_points_available < (0 - points))
|
||||
bool Client::UpdateLDoNPoints(uint32 theme_id, int points)
|
||||
{
|
||||
if (points < 0) {
|
||||
if (m_pp.ldon_points_available < (0 - points)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool is_loss = false;
|
||||
|
||||
switch (theme_id) {
|
||||
case LDoNThemes::Unused: { // No theme, so distribute evenly across all
|
||||
int split_points = (points / 5);
|
||||
|
||||
int guk_points = (split_points + (points % 5));
|
||||
int mir_points = split_points;
|
||||
int mmc_points = split_points;
|
||||
int ruj_points = split_points;
|
||||
int tak_points = split_points;
|
||||
|
||||
split_points = 0;
|
||||
if(points < 0) {
|
||||
if(m_pp.ldon_points_available < (0 - points)) {
|
||||
|
||||
if (points < 0) {
|
||||
if (m_pp.ldon_points_available < (0 - points)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(m_pp.ldon_points_guk < (0 - guk_points)) {
|
||||
is_loss = true;
|
||||
|
||||
if (m_pp.ldon_points_guk < (0 - guk_points)) {
|
||||
mir_points += (guk_points + m_pp.ldon_points_guk);
|
||||
guk_points = (0 - m_pp.ldon_points_guk);
|
||||
}
|
||||
|
||||
if(m_pp.ldon_points_mir < (0 - mir_points)) {
|
||||
if (m_pp.ldon_points_mir < (0 - mir_points)) {
|
||||
mmc_points += (mir_points + m_pp.ldon_points_mir);
|
||||
mir_points = (0 - m_pp.ldon_points_mir);
|
||||
}
|
||||
|
||||
if(m_pp.ldon_points_mmc < (0 - mmc_points)) {
|
||||
if (m_pp.ldon_points_mmc < (0 - mmc_points)) {
|
||||
ruj_points += (mmc_points + m_pp.ldon_points_mmc);
|
||||
mmc_points = (0 - m_pp.ldon_points_mmc);
|
||||
}
|
||||
|
||||
if(m_pp.ldon_points_ruj < (0 - ruj_points)) {
|
||||
if (m_pp.ldon_points_ruj < (0 - ruj_points)) {
|
||||
tak_points += (ruj_points + m_pp.ldon_points_ruj);
|
||||
ruj_points = (0 - m_pp.ldon_points_ruj);
|
||||
}
|
||||
|
||||
if(m_pp.ldon_points_tak < (0 - tak_points)) {
|
||||
if (m_pp.ldon_points_tak < (0 - tak_points)) {
|
||||
split_points = (tak_points + m_pp.ldon_points_tak);
|
||||
tak_points = (0 - m_pp.ldon_points_tak);
|
||||
tak_points = (0 - m_pp.ldon_points_tak);
|
||||
}
|
||||
}
|
||||
|
||||
m_pp.ldon_points_guk += guk_points;
|
||||
m_pp.ldon_points_mir += mir_points;
|
||||
m_pp.ldon_points_mmc += mmc_points;
|
||||
m_pp.ldon_points_ruj += ruj_points;
|
||||
m_pp.ldon_points_tak += tak_points;
|
||||
|
||||
points -= split_points;
|
||||
|
||||
if (split_points != 0) { // if anything left, recursively loop thru again
|
||||
UpdateLDoNPoints(0, split_points);
|
||||
UpdateLDoNPoints(LDoNThemes::Unused, split_points);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case LDoNThemes::GUK: {
|
||||
if(points < 0) {
|
||||
if(m_pp.ldon_points_guk < (0 - points)) {
|
||||
case LDoNThemes::GUK: {
|
||||
if (points < 0) {
|
||||
if (m_pp.ldon_points_guk < (0 - points)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_loss = true;
|
||||
}
|
||||
|
||||
m_pp.ldon_points_guk += points;
|
||||
break;
|
||||
}
|
||||
case LDoNThemes::MIR: {
|
||||
if(points < 0) {
|
||||
if(m_pp.ldon_points_mir < (0 - points)) {
|
||||
if (points < 0) {
|
||||
if (m_pp.ldon_points_mir < (0 - points)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_loss = true;
|
||||
}
|
||||
|
||||
m_pp.ldon_points_mir += points;
|
||||
break;
|
||||
}
|
||||
case LDoNThemes::MMC: {
|
||||
if(points < 0) {
|
||||
if(m_pp.ldon_points_mmc < (0 - points)) {
|
||||
if (points < 0) {
|
||||
if (m_pp.ldon_points_mmc < (0 - points)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_loss = true;
|
||||
}
|
||||
|
||||
m_pp.ldon_points_mmc += points;
|
||||
break;
|
||||
}
|
||||
case LDoNThemes::RUJ: {
|
||||
if(points < 0) {
|
||||
if(m_pp.ldon_points_ruj < (0 - points)) {
|
||||
if (points < 0) {
|
||||
if (m_pp.ldon_points_ruj < (0 - points)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_loss = true;
|
||||
}
|
||||
|
||||
m_pp.ldon_points_ruj += points;
|
||||
break;
|
||||
}
|
||||
case LDoNThemes::TAK: {
|
||||
if(points < 0) {
|
||||
if(m_pp.ldon_points_tak < (0 - points)) {
|
||||
if (points < 0) {
|
||||
if (m_pp.ldon_points_tak < (0 - points)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_loss = true;
|
||||
}
|
||||
|
||||
m_pp.ldon_points_tak += points;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_pp.ldon_points_available += points;
|
||||
|
||||
QuestEventID event_id = is_loss ? EVENT_LDON_POINTS_LOSS : EVENT_LDON_POINTS_GAIN;
|
||||
|
||||
if (parse->PlayerHasQuestSub(event_id)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
"{} {}",
|
||||
theme_id,
|
||||
std::abs(points)
|
||||
);
|
||||
|
||||
parse->EventPlayer(event_id, this, export_string, 0);
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_AdventurePointsUpdate, sizeof(AdventurePoints_Update_Struct));
|
||||
AdventurePoints_Update_Struct* apus = (AdventurePoints_Update_Struct*)outapp->pBuffer;
|
||||
auto *apus = (AdventurePoints_Update_Struct *) outapp->pBuffer;
|
||||
|
||||
apus->ldon_available_points = m_pp.ldon_points_available;
|
||||
apus->ldon_guk_points = m_pp.ldon_points_guk;
|
||||
apus->ldon_mirugal_points = m_pp.ldon_points_mir;
|
||||
apus->ldon_guk_points = m_pp.ldon_points_guk;
|
||||
apus->ldon_mirugal_points = m_pp.ldon_points_mir;
|
||||
apus->ldon_mistmoore_points = m_pp.ldon_points_mmc;
|
||||
apus->ldon_rujarkian_points = m_pp.ldon_points_ruj;
|
||||
apus->ldon_takish_points = m_pp.ldon_points_tak;
|
||||
apus->ldon_takish_points = m_pp.ldon_points_tak;
|
||||
|
||||
outapp->priority = 6;
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
return true;
|
||||
@@ -5690,26 +5727,89 @@ void Client::AddPVPPoints(uint32 Points)
|
||||
SendPVPStats();
|
||||
}
|
||||
|
||||
void Client::AddCrystals(uint32 radiant, uint32 ebon)
|
||||
{
|
||||
m_pp.currentRadCrystals += radiant;
|
||||
m_pp.careerRadCrystals += radiant;
|
||||
m_pp.currentEbonCrystals += ebon;
|
||||
m_pp.careerEbonCrystals += ebon;
|
||||
void Client::AddEbonCrystals(uint32 amount, bool is_reclaim) {
|
||||
m_pp.currentEbonCrystals += amount;
|
||||
m_pp.careerEbonCrystals += amount;
|
||||
|
||||
SaveCurrency();
|
||||
|
||||
SendCrystalCounts();
|
||||
|
||||
// newer clients handle message client side (older clients likely used eqstr 5967 and 5968, this matches live)
|
||||
if (radiant > 0)
|
||||
{
|
||||
MessageString(Chat::Yellow, YOU_RECEIVE, fmt::format("{} Radiant Crystals", radiant).c_str());
|
||||
}
|
||||
MessageString(
|
||||
Chat::Yellow,
|
||||
YOU_RECEIVE,
|
||||
fmt::format(
|
||||
"{} {}",
|
||||
amount,
|
||||
database.CreateItemLink(RuleI(Zone, EbonCrystalItemID))
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (ebon > 0)
|
||||
{
|
||||
MessageString(Chat::Yellow, YOU_RECEIVE, fmt::format("{} Ebon Crystals", ebon).c_str());
|
||||
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_GAIN)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
"{} 0 {}",
|
||||
amount,
|
||||
is_reclaim ? 1 : 0
|
||||
);
|
||||
parse->EventPlayer(EVENT_CRYSTAL_GAIN, this, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::AddRadiantCrystals(uint32 amount, bool is_reclaim) {
|
||||
m_pp.currentRadCrystals += amount;
|
||||
m_pp.careerRadCrystals += amount;
|
||||
|
||||
SaveCurrency();
|
||||
SendCrystalCounts();
|
||||
|
||||
MessageString(
|
||||
Chat::Yellow,
|
||||
YOU_RECEIVE,
|
||||
fmt::format(
|
||||
"{} {}",
|
||||
amount,
|
||||
database.CreateItemLink(RuleI(Zone, RadiantCrystalItemID))
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_GAIN)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
"0 {} {}",
|
||||
amount,
|
||||
is_reclaim ? 1 : 0
|
||||
);
|
||||
parse->EventPlayer(EVENT_CRYSTAL_GAIN, this, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::RemoveEbonCrystals(uint32 amount, bool is_reclaim) {
|
||||
m_pp.currentEbonCrystals -= amount;
|
||||
|
||||
SaveCurrency();
|
||||
SendCrystalCounts();
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_LOSS)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
"{} 0 {}",
|
||||
amount,
|
||||
is_reclaim ? 1 : 0
|
||||
);
|
||||
parse->EventPlayer(EVENT_CRYSTAL_LOSS, this, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::RemoveRadiantCrystals(uint32 amount, bool is_reclaim) {
|
||||
m_pp.currentRadCrystals -= amount;
|
||||
|
||||
SaveCurrency();
|
||||
SendCrystalCounts();
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_CRYSTAL_LOSS)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
"0 {} {}",
|
||||
amount,
|
||||
is_reclaim ? 1 : 0
|
||||
);
|
||||
parse->EventPlayer(EVENT_CRYSTAL_LOSS, this, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6072,7 +6172,7 @@ void Client::CheckEmoteHail(NPC* n, const char* message)
|
||||
|
||||
const auto emote_id = n->GetEmoteID();
|
||||
if (emote_id) {
|
||||
n->DoNPCEmote(EQ::constants::EmoteEventTypes::Hailed, emote_id);
|
||||
n->DoNPCEmote(EQ::constants::EmoteEventTypes::Hailed, emote_id, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6454,11 +6554,10 @@ void Client::SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount)
|
||||
SendAlternateCurrencyValue(currency_id);
|
||||
}
|
||||
|
||||
int Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method)
|
||||
int Client::AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_scripted)
|
||||
{
|
||||
|
||||
/* Added via Quest, rest of the logging methods may be done inline due to information available in that area of the code */
|
||||
if (method == 1){
|
||||
if (is_scripted) {
|
||||
/* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */
|
||||
if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)){
|
||||
std::string event_desc = StringFormat("Added via Quest :: Cursor to Item :: alt_currency_id:%i amount:%i in zoneid:%i instid:%i", currency_id, GetZoneID(), GetInstanceID());
|
||||
@@ -6466,32 +6565,47 @@ int Client::AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 met
|
||||
}
|
||||
}
|
||||
|
||||
if(amount == 0) {
|
||||
if (!amount) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(!alternate_currency_loaded) {
|
||||
if (!alternate_currency_loaded) {
|
||||
alternate_currency_queued_operations.push(std::make_pair(currency_id, amount));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int new_value = 0;
|
||||
auto iter = alternate_currency.find(currency_id);
|
||||
if(iter == alternate_currency.end()) {
|
||||
if (iter == alternate_currency.end()) {
|
||||
new_value = amount;
|
||||
} else {
|
||||
new_value = (*iter).second + amount;
|
||||
}
|
||||
|
||||
if(new_value < 0) {
|
||||
if (new_value < 0) {
|
||||
new_value = 0;
|
||||
alternate_currency[currency_id] = 0;
|
||||
database.UpdateAltCurrencyValue(CharacterID(), currency_id, 0);
|
||||
} else {
|
||||
alternate_currency[currency_id] = new_value;
|
||||
database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_value);
|
||||
}
|
||||
|
||||
SendAlternateCurrencyValue(currency_id);
|
||||
|
||||
QuestEventID event_id = amount > 0 ? EVENT_ALT_CURRENCY_GAIN : EVENT_ALT_CURRENCY_LOSS;
|
||||
|
||||
if (parse->PlayerHasQuestSub(event_id)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
"{} {} {}",
|
||||
currency_id,
|
||||
std::abs(amount),
|
||||
new_value
|
||||
);
|
||||
|
||||
parse->EventPlayer(event_id, this, export_string, 0);
|
||||
}
|
||||
|
||||
return new_value;
|
||||
}
|
||||
|
||||
@@ -8518,8 +8632,8 @@ void Client::InitInnates()
|
||||
m_pp.InnateSkills[InnateInspect] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateOpen] = InnateEnabled;
|
||||
|
||||
if (race >= RT_FROGLOK_3) {
|
||||
if (race == RT_SKELETON_2 || race == RT_FROGLOK_3) {
|
||||
if (race >= Race::Froglok2) {
|
||||
if (race == Race::Skeleton2 || race == Race::Froglok2) {
|
||||
m_pp.InnateSkills[InnateUltraVision] = InnateEnabled;
|
||||
} else {
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
@@ -8527,75 +8641,75 @@ void Client::InitInnates()
|
||||
}
|
||||
|
||||
switch (race) {
|
||||
case RT_BARBARIAN:
|
||||
case RT_BARBARIAN_2:
|
||||
case Race::Barbarian:
|
||||
case Race::HalasCitizen:
|
||||
m_pp.InnateSkills[InnateSlam] = InnateEnabled;
|
||||
break;
|
||||
case RT_ERUDITE:
|
||||
case RT_ERUDITE_2:
|
||||
case Race::Erudite:
|
||||
case Race::EruditeCitizen:
|
||||
m_pp.InnateSkills[InnateLore] = InnateEnabled;
|
||||
break;
|
||||
case RT_WOOD_ELF:
|
||||
case RT_GUARD_3:
|
||||
case Race::WoodElf:
|
||||
case Race::Fayguard:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_GNOME:
|
||||
case RT_HIGH_ELF:
|
||||
case RT_GUARD_2:
|
||||
case Race::Gnome:
|
||||
case Race::HighElf:
|
||||
case Race::Felguard:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateLore] = InnateEnabled;
|
||||
break;
|
||||
case RT_TROLL:
|
||||
case RT_TROLL_2:
|
||||
case Race::Troll:
|
||||
case Race::GrobbCitizen:
|
||||
m_pp.InnateSkills[InnateRegen] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateSlam] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_DWARF:
|
||||
case RT_DWARF_2:
|
||||
case Race::Dwarf:
|
||||
case Race::KaladimCitizen:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_OGRE:
|
||||
case RT_OGRE_2:
|
||||
case Race::Ogre:
|
||||
case Race::OggokCitizen:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateSlam] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateNoBash] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateBashDoor] = InnateEnabled;
|
||||
break;
|
||||
case RT_HALFLING:
|
||||
case RT_HALFLING_2:
|
||||
case Race::Halfling:
|
||||
case Race::RivervaleCitizen:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_IKSAR:
|
||||
case Race::Iksar:
|
||||
m_pp.InnateSkills[InnateRegen] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_VAH_SHIR:
|
||||
case Race::VahShir:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_DARK_ELF:
|
||||
case RT_DARK_ELF_2:
|
||||
case RT_VAMPIRE_2:
|
||||
case RT_FROGLOK_2:
|
||||
case RT_GHOST:
|
||||
case RT_GHOUL:
|
||||
case RT_SKELETON:
|
||||
case RT_VAMPIRE:
|
||||
case RT_WILL_O_WISP:
|
||||
case RT_ZOMBIE:
|
||||
case RT_SPECTRE:
|
||||
case RT_GHOST_2:
|
||||
case RT_GHOST_3:
|
||||
case RT_DRAGON_2:
|
||||
case RT_INNORUUK:
|
||||
case Race::DarkElf:
|
||||
case Race::NeriakCitizen:
|
||||
case Race::ElfVampire:
|
||||
case Race::FroglokGhoul:
|
||||
case Race::Ghost:
|
||||
case Race::Ghoul:
|
||||
case Race::Skeleton:
|
||||
case Race::Vampire:
|
||||
case Race::Wisp:
|
||||
case Race::Zombie:
|
||||
case Race::Spectre:
|
||||
case Race::DwarfGhost:
|
||||
case Race::EruditeGhost:
|
||||
case Race::DragonSkeleton:
|
||||
case Race::Innoruuk:
|
||||
m_pp.InnateSkills[InnateUltraVision] = InnateEnabled;
|
||||
break;
|
||||
case RT_HUMAN:
|
||||
case RT_GUARD:
|
||||
case RT_BEGGAR:
|
||||
case RT_HUMAN_2:
|
||||
case RT_HUMAN_3:
|
||||
case RT_FROGLOK_3: // client does froglok weird, but this should work out fine
|
||||
case Race::Human:
|
||||
case Race::FreeportGuard:
|
||||
case Race::HumanBeggar:
|
||||
case Race::HighpassCitizen:
|
||||
case Race::QeynosCitizen:
|
||||
case Race::Froglok2: // client does froglok weird, but this should work out fine
|
||||
break;
|
||||
default:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
|
||||
+7
-2
@@ -611,11 +611,14 @@ public:
|
||||
void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; }
|
||||
uint32 GetPVPPoints() { return m_pp.PVPCurrentPoints; }
|
||||
void AddPVPPoints(uint32 Points);
|
||||
void AddEbonCrystals(uint32 amount, bool is_reclaim = false);
|
||||
void AddRadiantCrystals(uint32 amount, bool is_reclaim = false);
|
||||
void RemoveEbonCrystals(uint32 amount, bool is_reclaim = false);
|
||||
void RemoveRadiantCrystals(uint32 amount, bool is_reclaim = false);
|
||||
uint32 GetRadiantCrystals() { return m_pp.currentRadCrystals; }
|
||||
void SetRadiantCrystals(uint32 value);
|
||||
uint32 GetEbonCrystals() { return m_pp.currentEbonCrystals; }
|
||||
void SetEbonCrystals(uint32 value);
|
||||
void AddCrystals(uint32 Radiant, uint32 Ebon);
|
||||
void SendCrystalCounts();
|
||||
|
||||
uint64 GetExperienceForKill(Mob *against);
|
||||
@@ -979,11 +982,13 @@ public:
|
||||
void PutLootInInventory(int16 slot_id, const EQ::ItemInstance &inst, ServerLootItem_Struct** bag_item_data = 0);
|
||||
bool AutoPutLootInInventory(EQ::ItemInstance& inst, bool try_worn = false, bool try_cursor = true, ServerLootItem_Struct** bag_item_data = 0);
|
||||
bool SummonItem(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0, bool attuned = false, uint16 to_slot = EQ::invslot::slotCursor, uint32 ornament_icon = 0, uint32 ornament_idfile = 0, uint32 ornament_hero_model = 0);
|
||||
void SummonItemIntoInventory(uint32 item_id, int16 charges = -1, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0, bool is_attuned = false);
|
||||
void SummonBaggedItems(uint32 bag_item_id, const std::vector<ServerLootItem_Struct>& bag_items);
|
||||
void SetStats(uint8 type,int16 set_val);
|
||||
void IncStats(uint8 type,int16 increase_val);
|
||||
void DropItem(int16 slot_id, bool recurse = true);
|
||||
void DropItemQS(EQ::ItemInstance* inst, bool pickup);
|
||||
bool HasItemOnCorpse(uint32 item_id);
|
||||
|
||||
bool IsAugmentRestricted(uint8 item_type, uint32 augment_restriction);
|
||||
|
||||
@@ -1493,7 +1498,7 @@ public:
|
||||
void ConsentCorpses(std::string consent_name, bool deny = false);
|
||||
void SendAltCurrencies();
|
||||
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
|
||||
int AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);
|
||||
int AddAlternateCurrencyValue(uint32 currency_id, int amount, bool is_scripted = false);
|
||||
void SendAlternateCurrencyValues();
|
||||
void SendAlternateCurrencyValue(uint32 currency_id, bool send_if_null = true);
|
||||
uint32 GetAlternateCurrencyValue(uint32 currency_id) const;
|
||||
|
||||
+42
-37
@@ -2626,7 +2626,7 @@ void Client::Handle_OP_AltCurrencyPurchase(const EQApplicationPacket *app)
|
||||
parse->EventPlayer(EVENT_ALT_CURRENCY_MERCHANT_BUY, this, export_string, 0);
|
||||
}
|
||||
|
||||
uint64 current_balance = AddAlternateCurrencyValue(alt_cur_id, -((int32) cost));
|
||||
uint64 current_balance = AddAlternateCurrencyValue(alt_cur_id, -((int) cost));
|
||||
int16 charges = 1;
|
||||
if (item->MaxCharges != 0) {
|
||||
charges = item->MaxCharges;
|
||||
@@ -2701,7 +2701,7 @@ void Client::Handle_OP_AltCurrencyReclaim(const EQApplicationPacket *app)
|
||||
}
|
||||
else {
|
||||
SummonItem(item_id, reclaim->count, 0, 0, 0, 0, 0, 0, false, EQ::invslot::slotCursor);
|
||||
AddAlternateCurrencyValue(reclaim->currency_id, -((int32)reclaim->count));
|
||||
AddAlternateCurrencyValue(reclaim->currency_id, -((int)reclaim->count));
|
||||
}
|
||||
/* QS: PlayerLogAlternateCurrencyTransactions :: Cursor to Item Storage */
|
||||
if (RuleB(QueryServ, PlayerLogAlternateCurrencyTransactions)) {
|
||||
@@ -5475,59 +5475,50 @@ void Client::Handle_OP_CreateObject(const EQApplicationPacket *app)
|
||||
void Client::Handle_OP_CrystalCreate(const EQApplicationPacket *app)
|
||||
{
|
||||
VERIFY_PACKET_LENGTH(OP_CrystalCreate, app, CrystalReclaim_Struct);
|
||||
CrystalReclaim_Struct *cr = (CrystalReclaim_Struct*)app->pBuffer;
|
||||
auto *cr = (CrystalReclaim_Struct *) app->pBuffer;
|
||||
|
||||
const uint32 requestQty = cr->amount;
|
||||
const bool isRadiant = cr->type == 4;
|
||||
const bool isEbon = cr->type == 5;
|
||||
const uint32 quantity = cr->amount;
|
||||
const bool is_radiant = cr->type == CrystalReclaimTypes::Radiant;
|
||||
const bool is_ebon = cr->type == CrystalReclaimTypes::Ebon;
|
||||
|
||||
// Check: Valid type requested.
|
||||
if (!isRadiant && !isEbon) {
|
||||
if (!is_radiant && !is_ebon) {
|
||||
return;
|
||||
}
|
||||
// Check: Valid quantity requested.
|
||||
if (requestQty < 1) {
|
||||
|
||||
if (quantity < 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check: Valid client state to make request.
|
||||
// In this situation the client is either desynced or attempting an exploit.
|
||||
const uint32 currentQty = isRadiant ? GetRadiantCrystals() : GetEbonCrystals();
|
||||
if (currentQty == 0) {
|
||||
const uint32 current_quantity = is_radiant ? GetRadiantCrystals() : GetEbonCrystals();
|
||||
if (!current_quantity) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Prevent the client from creating more than they have.
|
||||
const uint32 amount = EQ::ClampUpper(requestQty, currentQty);
|
||||
const uint32 itemID = isRadiant ? RuleI(Zone, RadiantCrystalItemID) : RuleI(Zone, EbonCrystalItemID);
|
||||
|
||||
// Summon crystals for player.
|
||||
const bool success = SummonItem(itemID, amount);
|
||||
const uint32 amount = EQ::ClampUpper(quantity, current_quantity);
|
||||
const uint32 item_id = is_radiant ? RuleI(Zone, RadiantCrystalItemID) : RuleI(Zone, EbonCrystalItemID);
|
||||
|
||||
const bool success = SummonItem(item_id, amount);
|
||||
if (!success) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Deduct crystals from client and update them.
|
||||
if (isRadiant) {
|
||||
m_pp.currentRadCrystals -= amount;
|
||||
m_pp.careerRadCrystals -= amount;
|
||||
if (is_ebon) {
|
||||
RemoveEbonCrystals(amount, true);
|
||||
} else if (is_radiant) {
|
||||
RemoveRadiantCrystals(amount, true);
|
||||
}
|
||||
else if (isEbon) {
|
||||
m_pp.currentEbonCrystals -= amount;
|
||||
m_pp.careerEbonCrystals -= amount;
|
||||
}
|
||||
|
||||
SaveCurrency();
|
||||
SendCrystalCounts();
|
||||
}
|
||||
|
||||
void Client::Handle_OP_CrystalReclaim(const EQApplicationPacket *app)
|
||||
{
|
||||
uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor);
|
||||
uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor);
|
||||
const uint32 ebon = NukeItem(RuleI(Zone, EbonCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor);
|
||||
const uint32 radiant = NukeItem(RuleI(Zone, RadiantCrystalItemID), invWhereWorn | invWherePersonal | invWhereCursor);
|
||||
if ((ebon + radiant) > 0) {
|
||||
AddCrystals(radiant, ebon);
|
||||
AddEbonCrystals(ebon, true);
|
||||
AddRadiantCrystals(radiant, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6293,6 +6284,11 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
|
||||
);
|
||||
parse->EventPlayer(EVENT_ENVIRONMENTAL_DAMAGE, this, export_string, 0);
|
||||
}
|
||||
|
||||
if (ed->dmgtype == EQ::constants::EnvironmentalDamage::Trap) {
|
||||
BreakInvisibleSpells();
|
||||
CancelSneakHide();
|
||||
}
|
||||
}
|
||||
|
||||
if (GetHP() <= 0) {
|
||||
@@ -6602,17 +6598,26 @@ void Client::Handle_OP_GMGoto(const EQApplicationPacket *app)
|
||||
|
||||
auto *gmg = (GMSummon_Struct *) app->pBuffer;
|
||||
Mob *gt = entity_list.GetMob(gmg->charname);
|
||||
if (!gt) {
|
||||
MovePC(zone->GetZoneID(), zone->GetInstanceID(), gt->GetX(), gt->GetY(), gt->GetZ(), gt->GetHeading());
|
||||
if (gt) {
|
||||
MovePC(
|
||||
zone->GetZoneID(),
|
||||
zone->GetInstanceID(),
|
||||
gt->GetX(),
|
||||
gt->GetY(),
|
||||
gt->GetZ(),
|
||||
gt->GetHeading()
|
||||
);
|
||||
} else if (!worldserver.Connected()) {
|
||||
Message(Chat::Red, "Error: World server disconnected.");
|
||||
} else {
|
||||
auto pack = new ServerPacket(ServerOP_GMGoto, sizeof(ServerGMGoto_Struct));
|
||||
memset(pack->pBuffer, 0, pack->size);
|
||||
ServerGMGoto_Struct *wsgmg = (ServerGMGoto_Struct *) pack->pBuffer;
|
||||
strcpy(wsgmg->myname, GetName());
|
||||
strcpy(wsgmg->gotoname, gmg->charname);
|
||||
wsgmg->admin = admin;
|
||||
|
||||
auto* g = (ServerGMGoto_Struct *) pack->pBuffer;
|
||||
strcpy(g->myname, GetName());
|
||||
strcpy(g->gotoname, gmg->charname);
|
||||
g->admin = admin;
|
||||
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
+4
-3
@@ -523,14 +523,15 @@ int64 Mob::GetActSpellHealing(uint16 spell_id, int64 value, Mob* target, bool fr
|
||||
}
|
||||
}
|
||||
|
||||
if (RuleB(Spells, HOTBonusHealingSplitOverDuration)) {
|
||||
if (extra_heal) {
|
||||
if (extra_heal) {
|
||||
if (RuleB(Spells, HOTBonusHealingSplitOverDuration)) {
|
||||
const int duration = CalcBuffDuration(this, target, spell_id);
|
||||
if (duration > 0) {
|
||||
extra_heal /= duration;
|
||||
value += extra_heal;
|
||||
}
|
||||
}
|
||||
|
||||
value += extra_heal;
|
||||
}
|
||||
|
||||
value *= critical_modifier;
|
||||
|
||||
+61
-11
@@ -186,6 +186,14 @@ const char *QuestEventSubroutines[_LargestEventID] = {
|
||||
"EVENT_UNMEMORIZE_SPELL",
|
||||
"EVENT_SCRIBE_SPELL",
|
||||
"EVENT_UNSCRIBE_SPELL",
|
||||
"EVENT_LOOT_ADDED",
|
||||
"EVENT_LDON_POINTS_GAIN",
|
||||
"EVENT_LDON_POINTS_LOSS",
|
||||
"EVENT_ALT_CURRENCY_GAIN",
|
||||
"EVENT_ALT_CURRENCY_LOSS",
|
||||
"EVENT_CRYSTAL_GAIN",
|
||||
"EVENT_CRYSTAL_LOSS",
|
||||
|
||||
// Add new events before these or Lua crashes
|
||||
"EVENT_SPELL_EFFECT_BOT",
|
||||
"EVENT_SPELL_EFFECT_BUFF_TIC_BOT"
|
||||
@@ -1883,20 +1891,18 @@ void PerlembParser::ExportEventVariables(
|
||||
ExportVar(package_name.c_str(), "killer_damage", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "killer_spell", sep.arg[2]);
|
||||
ExportVar(package_name.c_str(), "killer_skill", sep.arg[3]);
|
||||
if (extra_pointers && extra_pointers->size() >= 1)
|
||||
{
|
||||
Corpse* corpse = std::any_cast<Corpse*>(extra_pointers->at(0));
|
||||
if (corpse)
|
||||
{
|
||||
ExportVar(package_name.c_str(), "killed_entity_id", sep.arg[4]);
|
||||
|
||||
if (extra_pointers && extra_pointers->size() >= 1) {
|
||||
Corpse *corpse = std::any_cast<Corpse *>(extra_pointers->at(0));
|
||||
if (corpse) {
|
||||
ExportVar(package_name.c_str(), "killed_corpse_id", corpse->GetID());
|
||||
}
|
||||
}
|
||||
if (extra_pointers && extra_pointers->size() >= 2)
|
||||
{
|
||||
NPC* killed = std::any_cast<NPC*>(extra_pointers->at(1));
|
||||
if (killed)
|
||||
{
|
||||
ExportVar(package_name.c_str(), "killed_entity_id", killed->GetID());
|
||||
|
||||
if (extra_pointers && extra_pointers->size() >= 2) {
|
||||
NPC *killed = std::any_cast<NPC *>(extra_pointers->at(1));
|
||||
if (killed) {
|
||||
ExportVar(package_name.c_str(), "killed_bot_id", killed->IsBot() ? killed->CastToBot()->GetBotID() : 0);
|
||||
ExportVar(package_name.c_str(), "killed_npc_id", killed->IsNPC() ? killed->GetNPCTypeID() : 0);
|
||||
ExportVar(package_name.c_str(), "killed_x", killed->GetX());
|
||||
@@ -2241,6 +2247,50 @@ void PerlembParser::ExportEventVariables(
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_LOOT_ADDED: {
|
||||
if (extra_pointers && extra_pointers->size() == 1) {
|
||||
auto *inst = std::any_cast<EQ::ItemInstance *>(extra_pointers->at(0));
|
||||
if (inst) {
|
||||
ExportVar(package_name.c_str(), "item", "QuestItem", inst);
|
||||
ExportVar(package_name.c_str(), "item_id", inst->GetID());
|
||||
ExportVar(package_name.c_str(), "item_name", inst->GetItem()->Name);
|
||||
ExportVar(package_name.c_str(), "item_charges", inst->GetCharges());
|
||||
ExportVar(package_name.c_str(), "augment_one", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN));
|
||||
ExportVar(package_name.c_str(), "augment_two", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 1));
|
||||
ExportVar(package_name.c_str(), "augment_three", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 2));
|
||||
ExportVar(package_name.c_str(), "augment_four", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 3));
|
||||
ExportVar(package_name.c_str(), "augment_five", inst->GetAugmentItemID(EQ::invaug::SOCKET_BEGIN + 4));
|
||||
ExportVar(package_name.c_str(), "augment_six", inst->GetAugmentItemID(EQ::invaug::SOCKET_END));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case EVENT_LDON_POINTS_GAIN:
|
||||
case EVENT_LDON_POINTS_LOSS: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "theme_id", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "points", sep.arg[1]);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_ALT_CURRENCY_GAIN:
|
||||
case EVENT_ALT_CURRENCY_LOSS: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "currency_id", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "amount", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "total", sep.arg[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
case EVENT_CRYSTAL_GAIN:
|
||||
case EVENT_CRYSTAL_LOSS: {
|
||||
Seperator sep(data);
|
||||
ExportVar(package_name.c_str(), "ebon_amount", sep.arg[0]);
|
||||
ExportVar(package_name.c_str(), "radiant_amount", sep.arg[1]);
|
||||
ExportVar(package_name.c_str(), "is_reclaim", sep.arg[2]);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
|
||||
+3
-3
@@ -37,9 +37,9 @@ Encounter::Encounter(const char *enc_name) : Mob(
|
||||
nullptr, // in_lastname
|
||||
0, // in_cur_hp
|
||||
0, // in_max_hp
|
||||
MALE, // in_gender
|
||||
INVISIBLE_MAN, // in_race
|
||||
0, // in_class
|
||||
Gender::Male, // in_gender
|
||||
Race::InvisibleMan, // in_race
|
||||
Class::None, // in_class
|
||||
BT_NoTarget, // in_bodytype
|
||||
0, // in_deity
|
||||
0, // in_level
|
||||
|
||||
+3
-3
@@ -4781,7 +4781,7 @@ void EntityList::SendAppearanceEffects(Client *c)
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
cur->SendSavedAppearenceEffects(c);
|
||||
cur->SendSavedAppearanceEffects(c);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
@@ -4920,7 +4920,7 @@ void EntityList::ZoneWho(Client *c, Who_All_Struct *Who)
|
||||
FormatMSGID = 5023; // 5023 %T1[ANONYMOUS] %2 %3 %4
|
||||
uint32 PlayerClass = Class::None;
|
||||
uint32 PlayerLevel = 0;
|
||||
uint32 PlayerRace = RACE_DOUG_0;
|
||||
uint32 PlayerRace = Race::Doug;
|
||||
uint32 ZoneMSGID = 0xFFFFFFFF;
|
||||
|
||||
if (ClientEntry->GetAnon()==0) {
|
||||
@@ -5750,7 +5750,7 @@ void EntityList::DespawnGridNodes(int32 grid_id) {
|
||||
Mob *mob = m.second;
|
||||
if (
|
||||
mob->IsNPC() &&
|
||||
mob->GetRace() == RACE_NODE_2254 &&
|
||||
mob->GetRace() == Race::Node &&
|
||||
mob->EntityVariableExists("grid_id") &&
|
||||
Strings::ToInt(mob->GetEntityVariable("grid_id")) == grid_id)
|
||||
{
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
/* Copyright (C) 2000 MySQL AB & MySQL Finland AB & TCX DataKonsult AB
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
|
||||
MA 02111-1307, USA */
|
||||
|
||||
/* Error messages for mysql clients */
|
||||
/* error messages for the demon is in share/language/errmsg.sys */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void init_client_errs(void);
|
||||
extern const char *client_errors[]; /* Error messages */
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#define CR_MIN_ERROR 2000 /* For easier client code */
|
||||
#define CR_MAX_ERROR 2999
|
||||
#if defined(OS2) && defined( MYSQL_SERVER)
|
||||
#define CER(X) client_errors[(X)-CR_MIN_ERROR]
|
||||
#else
|
||||
#define ER(X) client_errors[(X)-CR_MIN_ERROR]
|
||||
#endif
|
||||
#define CLIENT_ERRMAP 2 /* Errormap used by my_error() */
|
||||
|
||||
#define CR_UNKNOWN_ERROR 2000
|
||||
#define CR_SOCKET_CREATE_ERROR 2001
|
||||
#define CR_CONNECTION_ERROR 2002
|
||||
#define CR_CONN_HOST_ERROR 2003
|
||||
#define CR_IPSOCK_ERROR 2004
|
||||
#define CR_UNKNOWN_HOST 2005
|
||||
#define CR_SERVER_GONE_ERROR 2006
|
||||
#define CR_VERSION_ERROR 2007
|
||||
#define CR_OUT_OF_MEMORY 2008
|
||||
#define CR_WRONG_HOST_INFO 2009
|
||||
#define CR_LOCALHOST_CONNECTION 2010
|
||||
#define CR_TCP_CONNECTION 2011
|
||||
#define CR_SERVER_HANDSHAKE_ERR 2012
|
||||
#define CR_SERVER_LOST 2013
|
||||
#define CR_COMMANDS_OUT_OF_SYNC 2014
|
||||
#define CR_NAMEDPIPE_CONNECTION 2015
|
||||
#define CR_NAMEDPIPEWAIT_ERROR 2016
|
||||
#define CR_NAMEDPIPEOPEN_ERROR 2017
|
||||
#define CR_NAMEDPIPESETSTATE_ERROR 2018
|
||||
#define CR_CANT_READ_CHARSET 2019
|
||||
#define CR_NET_PACKET_TOO_LARGE 2020
|
||||
|
||||
@@ -128,6 +128,14 @@ typedef enum {
|
||||
EVENT_UNMEMORIZE_SPELL,
|
||||
EVENT_SCRIBE_SPELL,
|
||||
EVENT_UNSCRIBE_SPELL,
|
||||
EVENT_LOOT_ADDED,
|
||||
EVENT_LDON_POINTS_GAIN,
|
||||
EVENT_LDON_POINTS_LOSS,
|
||||
EVENT_ALT_CURRENCY_GAIN,
|
||||
EVENT_ALT_CURRENCY_LOSS,
|
||||
EVENT_CRYSTAL_GAIN,
|
||||
EVENT_CRYSTAL_LOSS,
|
||||
|
||||
// Add new events before these or Lua crashes
|
||||
EVENT_SPELL_EFFECT_BOT,
|
||||
EVENT_SPELL_EFFECT_BUFF_TIC_BOT,
|
||||
|
||||
+1
-1
@@ -456,7 +456,7 @@ void Client::ForageItem(bool guarantee) {
|
||||
}
|
||||
|
||||
//not an else in case theres no DB food
|
||||
if (foragedfood == 0) {
|
||||
if (foragedfood == 0 && RuleB(Character, UseForageCommonFood)) {
|
||||
uint8 index = 0;
|
||||
index = zone->random.Int(0, MAX_COMMON_FOOD_IDS-1);
|
||||
foragedfood = common_food_ids[index];
|
||||
|
||||
@@ -86,7 +86,7 @@ void command_appearanceeffects(Client *c, const Seperator *sep)
|
||||
.texture = t->GetTexture(),
|
||||
}
|
||||
);
|
||||
t->ClearAppearenceEffects();
|
||||
t->ClearAppearanceEffects();
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
|
||||
@@ -7,10 +7,7 @@ void FindEmote(Client *c, const Seperator *sep)
|
||||
if (sep->IsNumber(2)) {
|
||||
auto emote_id = Strings::ToUnsignedInt(sep->arg[2]);
|
||||
|
||||
LinkedListIterator<NPC_Emote_Struct *> iterator(zone->NPCEmoteList);
|
||||
iterator.Reset();
|
||||
while (iterator.MoreElements()) {
|
||||
auto &e = iterator.GetData();
|
||||
for (auto& e : zone->npc_emote_list) {
|
||||
if (emote_id == e->emoteid) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
@@ -40,7 +37,6 @@ void FindEmote(Client *c, const Seperator *sep)
|
||||
break;
|
||||
}
|
||||
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
if (found_count == 50) {
|
||||
@@ -70,10 +66,7 @@ void FindEmote(Client *c, const Seperator *sep)
|
||||
|
||||
const std::string& search_criteria = sep->argplus[2];
|
||||
|
||||
LinkedListIterator<NPC_Emote_Struct *> iterator(zone->NPCEmoteList);
|
||||
iterator.Reset();
|
||||
while (iterator.MoreElements()) {
|
||||
auto &e = iterator.GetData();
|
||||
for (auto& e : zone->npc_emote_list) {
|
||||
|
||||
const std::string& current_text = Strings::ToLower(e->text);
|
||||
|
||||
@@ -106,7 +99,6 @@ void FindEmote(Client *c, const Seperator *sep)
|
||||
break;
|
||||
}
|
||||
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
if (found_count == 50) {
|
||||
|
||||
@@ -5,7 +5,7 @@ void FindRace(Client *c, const Seperator *sep)
|
||||
if (sep->IsNumber(2)) {
|
||||
const auto race_id = static_cast<uint16>(Strings::ToUnsignedInt(sep->arg[2]));
|
||||
const std::string& race_name = GetRaceIDName(race_id);
|
||||
if (EQ::ValueWithin(race_id, RACE_HUMAN_1, RACE_PEGASUS_732)) {
|
||||
if (EQ::ValueWithin(race_id, Race::Human, Race::Pegasus3)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
@@ -41,7 +41,7 @@ void FindRace(Client *c, const Seperator *sep)
|
||||
|
||||
auto found_count = 0;
|
||||
|
||||
for (uint16 race_id = RACE_HUMAN_1; race_id <= RACE_PEGASUS_732; race_id++) {
|
||||
for (uint16 race_id = Race::Human; race_id <= Race::Pegasus3; race_id++) {
|
||||
std::string race_name = GetRaceIDName(race_id);
|
||||
auto race_name_lower = Strings::ToLower(race_name);
|
||||
if (!Strings::Contains(race_name_lower, search_criteria)) {
|
||||
|
||||
@@ -60,11 +60,11 @@ void command_fixmob(Client *c, const Seperator *sep)
|
||||
ChangeSetting = Race;
|
||||
}
|
||||
else if (strcasecmp(command, "gender") == 0) {
|
||||
if (Gender == MALE && codeMove == 'p') {
|
||||
Gender = NEUTER;
|
||||
if (Gender == Gender::Male && codeMove == 'p') {
|
||||
Gender = Gender::Neuter;
|
||||
}
|
||||
else if (Gender >= NEUTER && codeMove != 'p') {
|
||||
Gender = MALE;
|
||||
else if (Gender >= Gender::Neuter && codeMove != 'p') {
|
||||
Gender = Gender::Male;
|
||||
}
|
||||
else {
|
||||
Gender += Adjustment;
|
||||
|
||||
@@ -15,7 +15,7 @@ void SetGender(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
const uint8 gender_id = Strings::ToUnsignedInt(sep->arg[2]);
|
||||
if (!EQ::ValueWithin(gender_id, MALE, NEUTER)) {
|
||||
if (!EQ::ValueWithin(gender_id, Gender::Male, Gender::Neuter)) {
|
||||
c->Message(Chat::White, "Usage: #set gender [Gender ID]");
|
||||
c->Message(Chat::White, "Genders: 0 = Male, 1 = Female, 2 = Neuter");
|
||||
return;
|
||||
|
||||
@@ -15,7 +15,7 @@ void SetGenderPermanent(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
const uint8 gender_id = Strings::ToInt(sep->arg[2]);
|
||||
if (!EQ::ValueWithin(gender_id, MALE, NEUTER)) {
|
||||
if (!EQ::ValueWithin(gender_id, Gender::Male, Gender::Neuter)) {
|
||||
c->Message(Chat::White, "Usage: #set gender_permanent [Gender ID]");
|
||||
c->Message(Chat::White, "Genders: 0 = Male, 1 = Female, 2 = Neuter");
|
||||
return;
|
||||
|
||||
@@ -23,7 +23,7 @@ void SetRace(Client *c, const Seperator *sep)
|
||||
const uint16 race_id = Strings::ToUnsignedInt(sep->arg[2]);
|
||||
|
||||
if (
|
||||
!EQ::ValueWithin(race_id, RACE_DOUG_0, RuleI(NPC, MaxRaceID)) &&
|
||||
!EQ::ValueWithin(race_id, Race::Doug, RuleI(NPC, MaxRaceID)) &&
|
||||
!EQ::ValueWithin(race_id, 2253, 2259)
|
||||
) {
|
||||
c->Message(
|
||||
|
||||
@@ -9,17 +9,11 @@ void SetTime(Client *c, const Seperator *sep)
|
||||
TimeOfDay_Struct world_time{};
|
||||
zone->zone_time.GetCurrentEQTimeOfDay(time(0), &world_time);
|
||||
|
||||
auto time_string = fmt::format(
|
||||
"{} (Timezone: {})",
|
||||
Strings::ZoneTime(world_time.hour, world_time.minute),
|
||||
Strings::ZoneTime(zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneHr())
|
||||
);
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"It is currently {}.",
|
||||
time_string
|
||||
Strings::ZoneTime(world_time.hour - 1, world_time.minute)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
@@ -27,15 +21,15 @@ void SetTime(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
uint8 minutes = 0;
|
||||
uint8 hours = Strings::ToUnsignedInt(sep->arg[2]) + 1;
|
||||
uint8 hours = Strings::ToUnsignedInt(sep->arg[2]);
|
||||
|
||||
if (hours > 24) {
|
||||
hours = 24;
|
||||
}
|
||||
|
||||
uint8 real_hours = (
|
||||
(hours - 1) > 0 ?
|
||||
(hours - 1) :
|
||||
hours > 0 ?
|
||||
hours :
|
||||
0
|
||||
);
|
||||
|
||||
@@ -47,21 +41,20 @@ void SetTime(Client *c, const Seperator *sep)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Setting world time to {} (Timezone: {}).",
|
||||
Strings::ZoneTime(hours, minutes),
|
||||
Strings::ZoneTime(zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneHr())
|
||||
"Setting world time to {}.",
|
||||
Strings::ZoneTime(hours, minutes)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
zone->SetTime(real_hours, minutes);
|
||||
|
||||
LogInfo(
|
||||
"{} :: Setting world time to {} (Timezone: {})",
|
||||
"{} :: Setting world time to {}.",
|
||||
c->GetCleanName(),
|
||||
Strings::ZoneTime(hours, minutes),
|
||||
Strings::ZoneTime(zone->zone_time.getEQTimeZoneHr(), zone->zone_time.getEQTimeZoneHr())
|
||||
Strings::ZoneTime(hours, minutes)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,10 +12,7 @@ void ShowEmotes(Client *c, const Seperator *sep)
|
||||
uint32 emote_count = 0;
|
||||
const uint32 emote_id = t->GetEmoteID();
|
||||
|
||||
LinkedListIterator<NPC_Emote_Struct *> iterator(zone->NPCEmoteList);
|
||||
iterator.Reset();
|
||||
while (iterator.MoreElements()) {
|
||||
const auto& e = iterator.GetData();
|
||||
for (auto& e : zone->npc_emote_list) {
|
||||
if (emote_id == e->emoteid) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
@@ -41,7 +38,6 @@ void ShowEmotes(Client *c, const Seperator *sep)
|
||||
emote_count++;
|
||||
}
|
||||
|
||||
iterator.Advance();
|
||||
}
|
||||
|
||||
c->Message(
|
||||
|
||||
@@ -4,7 +4,7 @@ void ShowZonePoints(Client *c, const Seperator *sep)
|
||||
{
|
||||
for (const auto& m : entity_list.GetMobList()) {
|
||||
Mob* mob = m.second;
|
||||
if (mob->IsNPC() && mob->GetRace() == RACE_NODE_2254) {
|
||||
if (mob->IsNPC() && mob->GetRace() == Race::Node) {
|
||||
mob->Depop();
|
||||
}
|
||||
}
|
||||
|
||||
+14
-19
@@ -3,19 +3,16 @@
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
#include "../corpse.h"
|
||||
|
||||
void command_summon(Client *c, const Seperator *sep)
|
||||
{
|
||||
int arguments = sep->argnum;
|
||||
const int arguments = sep->argnum;
|
||||
if (!arguments && !c->GetTarget()) {
|
||||
c->Message(Chat::White, "Usage: #summon - Summon your target, if you have one, to your position");
|
||||
c->Message(Chat::White, "Usage: #summon [Character Name] - Summon a character by name to your position");
|
||||
c->Message(Chat::White, "Note: You may also summon your target if you have one.");
|
||||
return;
|
||||
}
|
||||
|
||||
Mob* t = c;
|
||||
Mob *t = c;
|
||||
|
||||
if (arguments == 1) {
|
||||
std::string character_name = sep->arg[1];
|
||||
@@ -31,9 +28,9 @@ void command_summon(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
auto search_client = entity_list.GetClientByName(character_name.c_str());
|
||||
if (search_client) {
|
||||
t = search_client->CastToMob();
|
||||
Client *s = entity_list.GetClientByName(character_name.c_str());
|
||||
if (s) {
|
||||
t = s->CastToMob();
|
||||
} else {
|
||||
if (!worldserver.Connected()) {
|
||||
c->Message(Chat::White, "World server is currently disconnected.");
|
||||
@@ -42,15 +39,18 @@ void command_summon(Client *c, const Seperator *sep)
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_ZonePlayer, sizeof(ServerZonePlayer_Struct));
|
||||
auto szp = (ServerZonePlayer_Struct *) pack->pBuffer;
|
||||
|
||||
strn0cpy(szp->adminname, c->GetName(), sizeof(szp->adminname));
|
||||
szp->adminrank = c->Admin();
|
||||
szp->ignorerestrictions = 2;
|
||||
strn0cpy(szp->name, character_name.c_str(), sizeof(szp->name));
|
||||
strn0cpy(szp->zone, zone->GetShortName(), sizeof(szp->zone));
|
||||
szp->x_pos = c->GetX();
|
||||
szp->y_pos = c->GetY();
|
||||
szp->z_pos = c->GetZ();
|
||||
szp->instance_id = zone->GetInstanceID();
|
||||
|
||||
szp->adminrank = c->Admin();
|
||||
szp->ignorerestrictions = 2;
|
||||
szp->instance_id = zone->GetInstanceID();
|
||||
szp->x_pos = c->GetX();
|
||||
szp->y_pos = c->GetY();
|
||||
szp->z_pos = c->GetZ();
|
||||
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
return;
|
||||
@@ -97,9 +97,4 @@ void command_summon(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
t->GMMove(c->GetPosition());
|
||||
|
||||
if (t->IsNPC()) {
|
||||
t->CastToNPC()->SaveGuardSpot(glm::vec4(0.0f));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+126
-257
@@ -149,7 +149,7 @@ Mob* HateList::GetDamageTopOnHateList(Mob* hater)
|
||||
return current;
|
||||
}
|
||||
|
||||
Mob* HateList::GetClosestEntOnHateList(Mob *hater, bool skip_mezzed, EntityFilterType entity_type) {
|
||||
Mob* HateList::GetClosestEntOnHateList(Mob *hater, bool skip_mezzed, EntityFilterType filter_type) {
|
||||
Mob* close_entity = nullptr;
|
||||
float close_distance = 99999.9f;
|
||||
float this_distance;
|
||||
@@ -163,7 +163,7 @@ Mob* HateList::GetClosestEntOnHateList(Mob *hater, bool skip_mezzed, EntityFilte
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (entity_type) {
|
||||
switch (filter_type) {
|
||||
case EntityFilterType::Bots:
|
||||
if (!e->entity_on_hatelist->IsBot()) {
|
||||
continue;
|
||||
@@ -344,191 +344,215 @@ int HateList::GetHateRatio(Mob *top, Mob *other)
|
||||
|
||||
// skip is used to ignore a certain mob on the list
|
||||
// Currently used for getting 2nd on list for aggro meter
|
||||
Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip, bool skip_mezzed)
|
||||
Mob *HateList::GetMobWithMostHateOnList(
|
||||
Mob *center,
|
||||
Mob *skip,
|
||||
bool skip_mezzed,
|
||||
EntityFilterType filter_type
|
||||
)
|
||||
{
|
||||
// hack fix for zone shutdown crashes on some servers
|
||||
if (!zone->IsLoaded())
|
||||
if (!zone->IsLoaded()) { // hack fix for zone shutdown crashes on some servers
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mob* top_hate = nullptr;
|
||||
int64 hate = -1;
|
||||
Mob *top_hate = nullptr;
|
||||
int64 hate = -1;
|
||||
|
||||
if (center == nullptr)
|
||||
if (!center) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (RuleB(Aggro, SmartAggroList)){
|
||||
Mob* top_client_type_in_range = nullptr;
|
||||
if (RuleB(Aggro, SmartAggroList)) {
|
||||
Mob *top_client_type_in_range = nullptr;
|
||||
int64 hate_client_type_in_range = -1;
|
||||
int skipped_count = 0;
|
||||
int skipped_count = 0;
|
||||
|
||||
auto iterator = list.begin();
|
||||
while (iterator != list.end())
|
||||
{
|
||||
struct_HateList *cur = (*iterator);
|
||||
int16 aggro_mod = 0;
|
||||
while (iterator != list.end()) {
|
||||
struct_HateList *cur = (*iterator);
|
||||
int16 aggro_mod = 0;
|
||||
|
||||
if (!cur){
|
||||
if (!cur) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!cur->entity_on_hatelist){
|
||||
Mob *m = cur->entity_on_hatelist;
|
||||
|
||||
if (!m) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur->entity_on_hatelist == skip) {
|
||||
if (m == skip) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skip_mezzed && cur->entity_on_hatelist->IsMezzed()) {
|
||||
if (skip_mezzed && m->IsMezzed()) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur->entity_on_hatelist->Sanctuary()) {
|
||||
if (hate == -1)
|
||||
{
|
||||
top_hate = cur->entity_on_hatelist;
|
||||
hate = 1;
|
||||
if (
|
||||
(filter_type == EntityFilterType::Bots && !m->IsBot()) ||
|
||||
(filter_type == EntityFilterType::Clients && !m->IsClient()) ||
|
||||
(filter_type == EntityFilterType::NPCs && !m->IsNPC())
|
||||
) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m->Sanctuary()) {
|
||||
if (hate == -1) {
|
||||
top_hate = m;
|
||||
hate = 1;
|
||||
}
|
||||
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur->entity_on_hatelist->DivineAura() || cur->entity_on_hatelist->IsMezzed() || cur->entity_on_hatelist->IsFeared()){
|
||||
if (hate == -1)
|
||||
{
|
||||
top_hate = cur->entity_on_hatelist;
|
||||
hate = 0;
|
||||
if (m->DivineAura() || m->IsMezzed() || m->IsFeared()) {
|
||||
if (hate == -1) {
|
||||
top_hate = m;
|
||||
hate = 0;
|
||||
}
|
||||
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
int64 current_hate = cur->stored_hate_amount;
|
||||
|
||||
if (cur->entity_on_hatelist->IsClient() || cur->entity_on_hatelist->IsBot()){
|
||||
if (cur->entity_on_hatelist->IsClient() && cur->entity_on_hatelist->CastToClient()->IsSitting()){
|
||||
if (m->IsOfClientBot()) {
|
||||
if (m->IsClient() && m->CastToClient()->IsSitting()) {
|
||||
aggro_mod += RuleI(Aggro, SittingAggroMod);
|
||||
}
|
||||
|
||||
if (center){
|
||||
if (center->GetTarget() == cur->entity_on_hatelist)
|
||||
if (center) {
|
||||
if (center->GetTarget() == m) {
|
||||
aggro_mod += RuleI(Aggro, CurrentTargetAggroMod);
|
||||
if (RuleI(Aggro, MeleeRangeAggroMod) != 0)
|
||||
{
|
||||
if (center->CombatRange(cur->entity_on_hatelist)){
|
||||
}
|
||||
|
||||
if (RuleI(Aggro, MeleeRangeAggroMod) != 0) {
|
||||
if (center->CombatRange(m)) {
|
||||
aggro_mod += RuleI(Aggro, MeleeRangeAggroMod);
|
||||
|
||||
if (current_hate > hate_client_type_in_range || cur->is_entity_frenzy){
|
||||
if (current_hate > hate_client_type_in_range || cur->is_entity_frenzy) {
|
||||
hate_client_type_in_range = current_hate;
|
||||
top_client_type_in_range = cur->entity_on_hatelist;
|
||||
top_client_type_in_range = m;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else{
|
||||
if (center){
|
||||
if (center->GetTarget() == cur->entity_on_hatelist)
|
||||
} else {
|
||||
if (center) {
|
||||
if (center->GetTarget() == m) {
|
||||
aggro_mod += RuleI(Aggro, CurrentTargetAggroMod);
|
||||
if (RuleI(Aggro, MeleeRangeAggroMod) != 0)
|
||||
{
|
||||
if (center->CombatRange(cur->entity_on_hatelist)){
|
||||
}
|
||||
|
||||
if (RuleI(Aggro, MeleeRangeAggroMod) != 0) {
|
||||
if (center->CombatRange(m)) {
|
||||
aggro_mod += RuleI(Aggro, MeleeRangeAggroMod);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cur->entity_on_hatelist->GetMaxHP() != 0 && ((cur->entity_on_hatelist->GetHP() * 100 / cur->entity_on_hatelist->GetMaxHP()) < 20)){
|
||||
if (m->GetMaxHP() != 0 && ((m->GetHP() * 100 / m->GetMaxHP()) < 20)) {
|
||||
aggro_mod += RuleI(Aggro, CriticallyWoundedAggroMod);
|
||||
}
|
||||
|
||||
if (aggro_mod){
|
||||
if (aggro_mod) {
|
||||
current_hate += (current_hate * aggro_mod / 100);
|
||||
}
|
||||
|
||||
if (current_hate > hate || cur->is_entity_frenzy){
|
||||
hate = current_hate;
|
||||
top_hate = cur->entity_on_hatelist;
|
||||
if (current_hate > hate || cur->is_entity_frenzy) {
|
||||
hate = current_hate;
|
||||
top_hate = m;
|
||||
}
|
||||
|
||||
++iterator;
|
||||
}
|
||||
|
||||
if (top_client_type_in_range != nullptr && top_hate != nullptr) {
|
||||
bool isTopClientType = top_hate->IsClient();
|
||||
if (!isTopClientType) {
|
||||
if (top_client_type_in_range && top_hate) {
|
||||
bool is_top_client_type = top_hate->IsClient();
|
||||
if (!is_top_client_type) {
|
||||
if (top_hate->IsBot()) {
|
||||
isTopClientType = true;
|
||||
is_top_client_type = true;
|
||||
top_client_type_in_range = top_hate;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTopClientType) {
|
||||
if (!is_top_client_type) {
|
||||
if (top_hate->IsMerc()) {
|
||||
isTopClientType = true;
|
||||
is_top_client_type = true;
|
||||
top_client_type_in_range = top_hate;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTopClientType) {
|
||||
if (top_hate->GetSpecialAbility(ALLOW_TO_TANK)){
|
||||
isTopClientType = true;
|
||||
if (!is_top_client_type) {
|
||||
if (top_hate->GetSpecialAbility(ALLOW_TO_TANK)) {
|
||||
is_top_client_type = true;
|
||||
top_client_type_in_range = top_hate;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTopClientType)
|
||||
if (!is_top_client_type) {
|
||||
return top_client_type_in_range ? top_client_type_in_range : nullptr;
|
||||
}
|
||||
|
||||
return top_hate ? top_hate : nullptr;
|
||||
}
|
||||
else {
|
||||
if (top_hate == nullptr && skipped_count > 0) {
|
||||
} else {
|
||||
if (!top_hate && skipped_count > 0) {
|
||||
return center->GetTarget() ? center->GetTarget() : nullptr;
|
||||
}
|
||||
|
||||
return top_hate ? top_hate : nullptr;
|
||||
}
|
||||
}
|
||||
else{
|
||||
auto iterator = list.begin();
|
||||
int skipped_count = 0;
|
||||
while (iterator != list.end())
|
||||
{
|
||||
} else {
|
||||
auto iterator = list.begin();
|
||||
int skipped_count = 0;
|
||||
while (iterator != list.end()) {
|
||||
struct_HateList *cur = (*iterator);
|
||||
if (cur) {
|
||||
if (cur->entity_on_hatelist == skip) {
|
||||
Mob *m = cur->entity_on_hatelist;
|
||||
|
||||
if (!m) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skip_mezzed && cur->entity_on_hatelist->IsMezzed()) {
|
||||
if (m == skip) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cur->entity_on_hatelist != nullptr && ((cur->stored_hate_amount > hate) || cur->is_entity_frenzy))
|
||||
{
|
||||
top_hate = cur->entity_on_hatelist;
|
||||
hate = cur->stored_hate_amount;
|
||||
if (skip_mezzed && m->IsMezzed()) {
|
||||
++iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((cur->stored_hate_amount > hate) || cur->is_entity_frenzy) {
|
||||
top_hate = m;
|
||||
hate = cur->stored_hate_amount;
|
||||
}
|
||||
}
|
||||
++iterator;
|
||||
}
|
||||
if (top_hate == nullptr && skipped_count > 0) {
|
||||
|
||||
if (!top_hate && skipped_count > 0) {
|
||||
return center->GetTarget() ? center->GetTarget() : nullptr;
|
||||
}
|
||||
|
||||
return top_hate ? top_hate : nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mob *HateList::GetEntWithMostHateOnList(bool skip_mezzed){
|
||||
Mob *HateList::GetMobWithMostHateOnList(bool skip_mezzed){
|
||||
Mob* top = nullptr;
|
||||
int64 hate = -1;
|
||||
|
||||
@@ -539,7 +563,7 @@ Mob *HateList::GetEntWithMostHateOnList(bool skip_mezzed){
|
||||
|
||||
if (cur) {
|
||||
LogHateDetail(
|
||||
"Looping GetEntWithMostHateOnList1 [{}] cur [{}] hate [{}] calc [{}]",
|
||||
"Looping GetMobWithMostHateOnList1 [{}] cur [{}] hate [{}] calc [{}]",
|
||||
cur->entity_on_hatelist->GetMobDescription(),
|
||||
cur->stored_hate_amount,
|
||||
hate,
|
||||
@@ -549,7 +573,7 @@ Mob *HateList::GetEntWithMostHateOnList(bool skip_mezzed){
|
||||
if (cur->entity_on_hatelist != nullptr && (cur->stored_hate_amount > hate))
|
||||
{
|
||||
LogHateDetail(
|
||||
"Looping GetEntWithMostHateOnList2 [{}]",
|
||||
"Looping GetMobWithMostHateOnList2 [{}]",
|
||||
cur->entity_on_hatelist->GetMobDescription()
|
||||
);
|
||||
|
||||
@@ -565,53 +589,45 @@ Mob *HateList::GetEntWithMostHateOnList(bool skip_mezzed){
|
||||
}
|
||||
|
||||
|
||||
Mob *HateList::GetRandomEntOnHateList(bool skip_mezzed)
|
||||
Mob *HateList::GetRandomMobOnHateList(EntityFilterType filter_type)
|
||||
{
|
||||
int count = list.size();
|
||||
if (count <= 0) //If we don't have any entries it'll crash getting a random 0, -1 position.
|
||||
return nullptr;
|
||||
|
||||
if (count == 1) //No need to do all that extra work if we only have one hate entry
|
||||
{
|
||||
if (*list.begin() && (!skip_mezzed || !(*list.begin())->entity_on_hatelist->IsMezzed())) // Just in case tHateEntry is invalidated somehow...
|
||||
return (*list.begin())->entity_on_hatelist;
|
||||
const auto &l = GetFilteredHateList(filter_type);
|
||||
|
||||
int count = l.size();
|
||||
if (count <= 0) { // If we don't have any entries it'll crash getting a random 0, -1 position.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (skip_mezzed) {
|
||||
|
||||
for (auto iter : list) {
|
||||
if (iter->entity_on_hatelist->IsMezzed()) {
|
||||
--count;
|
||||
if (count == 1) { // No need to do all that extra work if we only have one hate entry
|
||||
auto c = *l.begin();
|
||||
if (c) {
|
||||
Mob *m = c->entity_on_hatelist;
|
||||
if (!m) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
if (count <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int random = zone->random.Int(0, count - 1);
|
||||
int counter = 0;
|
||||
auto r = l.begin();
|
||||
int random_index = rand() % count;
|
||||
|
||||
for (auto iter : list) {
|
||||
std::advance(r, random_index);
|
||||
|
||||
if (skip_mezzed && iter->entity_on_hatelist->IsMezzed()) {
|
||||
continue;
|
||||
}
|
||||
if (counter < random) {
|
||||
auto e = *r;
|
||||
|
||||
++counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
return iter->entity_on_hatelist;
|
||||
Mob *m = e->entity_on_hatelist;
|
||||
if (m) {
|
||||
return m;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Mob *HateList::GetEscapingEntOnHateList(Mob *center, float range, bool first) {
|
||||
Mob *HateList::GetEscapingMobOnHateList(Mob *center, float range, bool first) {
|
||||
// function is still in design stage
|
||||
|
||||
if (!center)
|
||||
@@ -853,153 +869,6 @@ void HateList::RemoveStaleEntries(int time_ms, float dist)
|
||||
}
|
||||
}
|
||||
|
||||
Bot* HateList::GetRandomBotOnHateList(bool skip_mezzed)
|
||||
{
|
||||
int count = list.size();
|
||||
if (count <= 0) { //If we don't have any entries it'll crash getting a random 0, -1 position.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (count == 1) { //No need to do all that extra work if we only have one hate entry
|
||||
if (*list.begin() && (*list.begin())->entity_on_hatelist->IsBot() && (!skip_mezzed || !(*list.begin())->entity_on_hatelist->IsMezzed())) {
|
||||
return (*list.begin())->entity_on_hatelist->CastToBot();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (skip_mezzed) {
|
||||
for (auto iter : list) {
|
||||
if (iter->entity_on_hatelist->IsMezzed()) {
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
if (count <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int random = zone->random.Int(0, count - 1);
|
||||
int counter = 0;
|
||||
|
||||
for (auto iter : list) {
|
||||
if (!iter->entity_on_hatelist->IsBot()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skip_mezzed && iter->entity_on_hatelist->IsMezzed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (counter < random) {
|
||||
++counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
return iter->entity_on_hatelist->CastToBot();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Client* HateList::GetRandomClientOnHateList(bool skip_mezzed)
|
||||
{
|
||||
int count = list.size();
|
||||
if (count <= 0) { //If we don't have any entries it'll crash getting a random 0, -1 position.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (count == 1) { //No need to do all that extra work if we only have one hate entry
|
||||
if (*list.begin() && (*list.begin())->entity_on_hatelist->IsClient() && (!skip_mezzed || !(*list.begin())->entity_on_hatelist->IsMezzed())) {
|
||||
return (*list.begin())->entity_on_hatelist->CastToClient();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (skip_mezzed) {
|
||||
for (auto iter : list) {
|
||||
if (iter->entity_on_hatelist->IsMezzed()) {
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
if (count <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int random = zone->random.Int(0, count - 1);
|
||||
int counter = 0;
|
||||
|
||||
for (auto iter : list) {
|
||||
if (!iter->entity_on_hatelist->IsClient()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skip_mezzed && iter->entity_on_hatelist->IsMezzed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (counter < random) {
|
||||
++counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
return iter->entity_on_hatelist->CastToClient();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
NPC* HateList::GetRandomNPCOnHateList(bool skip_mezzed)
|
||||
{
|
||||
int count = list.size();
|
||||
if (count <= 0) { //If we don't have any entries it'll crash getting a random 0, -1 position.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (count == 1) { //No need to do all that extra work if we only have one hate entry
|
||||
if (*list.begin() && (*list.begin())->entity_on_hatelist->IsNPC() && (!skip_mezzed || !(*list.begin())->entity_on_hatelist->IsMezzed())) {
|
||||
return (*list.begin())->entity_on_hatelist->CastToNPC();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (skip_mezzed) {
|
||||
for (auto iter : list) {
|
||||
if (iter->entity_on_hatelist->IsMezzed()) {
|
||||
--count;
|
||||
}
|
||||
}
|
||||
|
||||
if (count <= 0) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int random = zone->random.Int(0, count - 1);
|
||||
int counter = 0;
|
||||
|
||||
for (auto iter : list) {
|
||||
if (!iter->entity_on_hatelist->IsNPC()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (skip_mezzed && iter->entity_on_hatelist->IsMezzed()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (counter < random) {
|
||||
++counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
return iter->entity_on_hatelist->CastToNPC();
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void HateList::DamageHateList(int64 damage, uint32 distance, EntityFilterType filter_type, bool is_percentage)
|
||||
{
|
||||
if (damage <= 0) {
|
||||
|
||||
+5
-9
@@ -41,16 +41,12 @@ public:
|
||||
HateList();
|
||||
~HateList();
|
||||
|
||||
Mob *GetClosestEntOnHateList(Mob *hater, bool skip_mezzed = false, EntityFilterType entity_type = EntityFilterType::All);
|
||||
Mob *GetClosestEntOnHateList(Mob *hater, bool skip_mezzed = false, EntityFilterType filter_type = EntityFilterType::All);
|
||||
Mob *GetDamageTopOnHateList(Mob *hater); // didn't add 'skip_mezzed' due to calls being in ::Death()
|
||||
Mob *GetEntWithMostHateOnList(Mob *center, Mob *skip = nullptr, bool skip_mezzed = false);
|
||||
Mob *GetRandomEntOnHateList(bool skip_mezzed = false);
|
||||
Mob *GetEntWithMostHateOnList(bool skip_mezzed = false);
|
||||
Mob *GetEscapingEntOnHateList(Mob *center, float range = 0.0f, bool first = false);
|
||||
|
||||
Bot* GetRandomBotOnHateList(bool skip_mezzed = false);
|
||||
Client *GetRandomClientOnHateList(bool skip_mezzed = false);
|
||||
NPC *GetRandomNPCOnHateList(bool skip_mezzed = false);
|
||||
Mob *GetMobWithMostHateOnList(Mob *center, Mob *skip = nullptr, bool skip_mezzed = false, EntityFilterType filter_type = EntityFilterType::All);
|
||||
Mob *GetRandomMobOnHateList(EntityFilterType filter_type = EntityFilterType::All);
|
||||
Mob *GetMobWithMostHateOnList(bool skip_mezzed = false);
|
||||
Mob *GetEscapingMobOnHateList(Mob *center, float range = 0.0f, bool first = false);
|
||||
|
||||
bool IsEntOnHateList(Mob *mob);
|
||||
bool IsHateListEmpty();
|
||||
|
||||
@@ -4739,3 +4739,81 @@ bool Client::IsAugmentRestricted(uint8 item_type, uint32 augment_restriction)
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Client::SummonItemIntoInventory(
|
||||
uint32 item_id,
|
||||
int16 charges,
|
||||
uint32 aug1,
|
||||
uint32 aug2,
|
||||
uint32 aug3,
|
||||
uint32 aug4,
|
||||
uint32 aug5,
|
||||
uint32 aug6,
|
||||
bool is_attuned
|
||||
)
|
||||
{
|
||||
auto *inst = database.CreateItem(
|
||||
item_id,
|
||||
charges,
|
||||
aug1,
|
||||
aug2,
|
||||
aug3,
|
||||
aug4,
|
||||
aug5,
|
||||
aug6,
|
||||
is_attuned
|
||||
);
|
||||
|
||||
if (!inst) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bool is_arrow = inst->GetItem()->ItemType == EQ::item::ItemTypeArrow;
|
||||
const int16 slot_id = m_inv.FindFreeSlot(
|
||||
inst->IsClassBag(),
|
||||
true,
|
||||
inst->GetItem()->Size,
|
||||
is_arrow
|
||||
);
|
||||
|
||||
SummonItem(
|
||||
item_id,
|
||||
charges,
|
||||
aug1,
|
||||
aug2,
|
||||
aug3,
|
||||
aug4,
|
||||
aug5,
|
||||
aug6,
|
||||
is_attuned,
|
||||
slot_id
|
||||
);
|
||||
}
|
||||
|
||||
bool Client::HasItemOnCorpse(uint32 item_id)
|
||||
{
|
||||
const uint32 corpse_count = GetCorpseCount();
|
||||
if (!corpse_count) {
|
||||
return EQ::invslot::SLOT_INVALID;
|
||||
}
|
||||
|
||||
for (int i = 0; i < corpse_count; i++) {
|
||||
const uint32 corpse_id = GetCorpseID(i);
|
||||
|
||||
for (int16 slot_id = EQ::invslot::POSSESSIONS_BEGIN; slot_id < EQ::invslot::POSSESSIONS_END; slot_id++) {
|
||||
const uint32 current_item_id = GetCorpseItemAt(corpse_id, slot_id);
|
||||
if (current_item_id && current_item_id == item_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int16 slot_id = EQ::invbag::GENERAL_BAGS_BEGIN; slot_id < EQ::invbag::GENERAL_BAGS_END; slot_id++) {
|
||||
const uint32 current_item_id = GetCorpseItemAt(corpse_id, slot_id);
|
||||
if (current_item_id && current_item_id == item_id) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
+234
-215
@@ -27,24 +27,37 @@
|
||||
#include "zonedb.h"
|
||||
#include "global_loot_manager.h"
|
||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||
#include "quest_parser_collection.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
// Queries the loottable: adds item & coin to the npc
|
||||
void ZoneDatabase::AddLootTableToNPC(NPC* npc, uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) {
|
||||
const LootTable_Struct* lts = nullptr;
|
||||
// global loot passes nullptr for these
|
||||
bool bGlobal = copper == nullptr && silver == nullptr && gold == nullptr && plat == nullptr;
|
||||
if (!bGlobal) {
|
||||
void ZoneDatabase::AddLootTableToNPC(
|
||||
NPC *npc,
|
||||
uint32 loottable_id,
|
||||
ItemList *itemlist,
|
||||
uint32 *copper,
|
||||
uint32 *silver,
|
||||
uint32 *gold,
|
||||
uint32 *plat
|
||||
)
|
||||
{
|
||||
const bool is_global = (
|
||||
copper == nullptr &&
|
||||
silver == nullptr &&
|
||||
gold == nullptr &&
|
||||
plat == nullptr
|
||||
);
|
||||
if (!is_global) {
|
||||
*copper = 0;
|
||||
*silver = 0;
|
||||
*gold = 0;
|
||||
*plat = 0;
|
||||
}
|
||||
|
||||
lts = database.GetLootTable(loottable_id);
|
||||
const auto *lts = database.GetLootTable(loottable_id);
|
||||
if (!lts) {
|
||||
return;
|
||||
}
|
||||
@@ -55,19 +68,23 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc, uint32 loottable_id, ItemList* it
|
||||
|
||||
uint32 min_cash = lts->mincash;
|
||||
uint32 max_cash = lts->maxcash;
|
||||
if(min_cash > max_cash) {
|
||||
uint32 t = min_cash;
|
||||
if (min_cash > max_cash) {
|
||||
const uint32 t = min_cash;
|
||||
min_cash = max_cash;
|
||||
max_cash = t;
|
||||
}
|
||||
|
||||
uint32 cash = 0;
|
||||
if (!bGlobal) {
|
||||
if(max_cash > 0 && lts->avgcoin > 0 && EQ::ValueWithin(lts->avgcoin, min_cash, max_cash)) {
|
||||
float upper_chance = (float)(lts->avgcoin - min_cash) / (float)(max_cash - min_cash);
|
||||
float avg_cash_roll = (float)zone->random.Real(0.0, 1.0);
|
||||
if (!is_global) {
|
||||
if (
|
||||
max_cash > 0 &&
|
||||
lts->avgcoin > 0 &&
|
||||
EQ::ValueWithin(lts->avgcoin, min_cash, max_cash)
|
||||
) {
|
||||
const float upper_chance = static_cast<float>(lts->avgcoin - min_cash) / static_cast<float>(max_cash - min_cash);
|
||||
const float avg_cash_roll = static_cast<float>(zone->random.Real(0.0, 1.0));
|
||||
|
||||
if(avg_cash_roll < upper_chance) {
|
||||
if (avg_cash_roll < upper_chance) {
|
||||
cash = zone->random.Int(lts->avgcoin, max_cash);
|
||||
} else {
|
||||
cash = zone->random.Int(min_cash, lts->avgcoin);
|
||||
@@ -77,7 +94,7 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc, uint32 loottable_id, ItemList* it
|
||||
}
|
||||
}
|
||||
|
||||
if(cash != 0) {
|
||||
if (cash != 0) {
|
||||
*plat = cash / 1000;
|
||||
cash -= *plat * 1000;
|
||||
|
||||
@@ -90,25 +107,23 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc, uint32 loottable_id, ItemList* it
|
||||
*copper = cash;
|
||||
}
|
||||
|
||||
uint32 global_loot_multiplier = RuleI(Zone, GlobalLootMultiplier);
|
||||
const uint32 global_loot_multiplier = RuleI(Zone, GlobalLootMultiplier);
|
||||
|
||||
// Do items
|
||||
for (uint32 i=0; i<lts->NumEntries; i++) {
|
||||
for (uint32 i = 0; i < lts->NumEntries; i++) {
|
||||
for (uint32 k = 1; k <= (lts->Entries[i].multiplier * global_loot_multiplier); k++) {
|
||||
uint8 droplimit = lts->Entries[i].droplimit;
|
||||
uint8 mindrop = lts->Entries[i].mindrop;
|
||||
const uint8 drop_limit = lts->Entries[i].droplimit;
|
||||
const uint8 minimum_drop = lts->Entries[i].mindrop;
|
||||
|
||||
//LootTable Entry probability
|
||||
float ltchance = 0.0f;
|
||||
ltchance = lts->Entries[i].probability;
|
||||
const float probability = lts->Entries[i].probability;
|
||||
|
||||
float drop_chance = 0.0f;
|
||||
if(ltchance > 0.0 && ltchance < 100.0) {
|
||||
drop_chance = (float)zone->random.Real(0.0, 100.0);
|
||||
if (EQ::ValueWithin(probability, 0.0f, 100.0f)) {
|
||||
drop_chance = static_cast<float>(zone->random.Real(0.0, 100.0));
|
||||
}
|
||||
|
||||
if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance <= ltchance)) {
|
||||
AddLootDropToNPC(npc, lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop);
|
||||
if (probability != 0.0 && (probability == 100.0 || drop_chance <= probability)) {
|
||||
AddLootDropToNPC(npc, lts->Entries[i].lootdrop_id, itemlist, drop_limit, minimum_drop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,31 +133,29 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc, uint32 loottable_id, ItemList* it
|
||||
// maxdrops = size of the array npcd
|
||||
void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item_list, uint8 droplimit, uint8 mindrop)
|
||||
{
|
||||
const LootDrop_Struct *loot_drop = GetLootDrop(lootdrop_id);
|
||||
if (!loot_drop) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (loot_drop->NumEntries == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!content_service.DoesPassContentFiltering(loot_drop->content_flags)) {
|
||||
const auto *lds = GetLootDrop(lootdrop_id);
|
||||
if (
|
||||
!lds ||
|
||||
lds->NumEntries == 0 ||
|
||||
!content_service.DoesPassContentFiltering(lds->content_flags)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if this lootdrop is droplimit=0 and mindrop 0, scan list once and return
|
||||
if (droplimit == 0 && mindrop == 0) {
|
||||
for (uint32 i = 0; i < loot_drop->NumEntries; ++i) {
|
||||
int charges = loot_drop->Entries[i].multiplier;
|
||||
for (int j = 0; j < charges; ++j) {
|
||||
if (zone->random.Real(0.0, 100.0) <= loot_drop->Entries[i].chance &&
|
||||
npc->MeetsLootDropLevelRequirements(loot_drop->Entries[i], true)) {
|
||||
const EQ::ItemData *database_item = GetItem(loot_drop->Entries[i].item_id);
|
||||
for (uint32 i = 0; i < lds->NumEntries; ++i) {
|
||||
const uint8 charges = lds->Entries[i].multiplier;
|
||||
for (int j = 0; j < charges; ++j) {
|
||||
if (
|
||||
zone->random.Real(0.0, 100.0) <= lds->Entries[i].chance &&
|
||||
npc->MeetsLootDropLevelRequirements(lds->Entries[i], true)
|
||||
) {
|
||||
const EQ::ItemData *database_item = GetItem(lds->Entries[i].item_id);
|
||||
npc->AddLootDrop(
|
||||
database_item,
|
||||
item_list,
|
||||
loot_drop->Entries[i]
|
||||
lds->Entries[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -150,7 +163,7 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item
|
||||
return;
|
||||
}
|
||||
|
||||
if (loot_drop->NumEntries > 100 && droplimit == 0) {
|
||||
if (lds->NumEntries > 100 && droplimit == 0) {
|
||||
droplimit = 10;
|
||||
}
|
||||
|
||||
@@ -163,16 +176,17 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item
|
||||
bool roll_table_chance_bypass = false;
|
||||
bool active_item_list = false;
|
||||
|
||||
for (uint32 i = 0; i < loot_drop->NumEntries; ++i) {
|
||||
const EQ::ItemData *db_item = GetItem(loot_drop->Entries[i].item_id);
|
||||
if (db_item && npc->MeetsLootDropLevelRequirements(loot_drop->Entries[i])) {
|
||||
roll_t += loot_drop->Entries[i].chance;
|
||||
if (loot_drop->Entries[i].chance >= 100) {
|
||||
for (uint32 i = 0; i < lds->NumEntries; ++i) {
|
||||
const EQ::ItemData *db_item = GetItem(lds->Entries[i].item_id);
|
||||
if (db_item && npc->MeetsLootDropLevelRequirements(lds->Entries[i])) {
|
||||
roll_t += lds->Entries[i].chance;
|
||||
|
||||
if (lds->Entries[i].chance >= 100) {
|
||||
roll_table_chance_bypass = true;
|
||||
} else {
|
||||
no_loot_prob *= (100 - lds->Entries[i].chance) / 100.0f;
|
||||
}
|
||||
else {
|
||||
no_loot_prob *= (100 - loot_drop->Entries[i].chance) / 100.0f;
|
||||
}
|
||||
|
||||
active_item_list = true;
|
||||
}
|
||||
}
|
||||
@@ -191,41 +205,40 @@ void ZoneDatabase::AddLootDropToNPC(NPC *npc, uint32 lootdrop_id, ItemList *item
|
||||
for (int i = 0; i < droplimit; ++i) {
|
||||
if (drops < mindrop || roll_table_chance_bypass || (float) zone->random.Real(0.0, 1.0) >= no_loot_prob) {
|
||||
float roll = (float) zone->random.Real(0.0, roll_t);
|
||||
for (uint32 j = 0; j < loot_drop->NumEntries; ++j) {
|
||||
const EQ::ItemData *db_item = GetItem(loot_drop->Entries[j].item_id);
|
||||
for (uint32 j = 0; j < lds->NumEntries; ++j) {
|
||||
const auto *db_item = GetItem(lds->Entries[j].item_id);
|
||||
if (db_item) {
|
||||
// if it doesn't meet the requirements do nothing
|
||||
if (!npc->MeetsLootDropLevelRequirements(loot_drop->Entries[j])) {
|
||||
if (!npc->MeetsLootDropLevelRequirements(lds->Entries[j])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (roll < loot_drop->Entries[j].chance) {
|
||||
if (roll < lds->Entries[j].chance) {
|
||||
npc->AddLootDrop(
|
||||
db_item,
|
||||
item_list,
|
||||
loot_drop->Entries[j]
|
||||
lds->Entries[j]
|
||||
);
|
||||
drops++;
|
||||
|
||||
int charges = (int) loot_drop->Entries[i].multiplier;
|
||||
charges = EQ::ClampLower(charges, 1);
|
||||
uint8 charges = lds->Entries[i].multiplier;
|
||||
charges = EQ::ClampLower(charges, static_cast<uint8>(1));
|
||||
|
||||
for (int k = 1; k < charges; ++k) {
|
||||
float c_roll = (float) zone->random.Real(0.0, 100.0);
|
||||
if (c_roll <= loot_drop->Entries[i].chance) {
|
||||
float c_roll = static_cast<float>(zone->random.Real(0.0, 100.0));
|
||||
if (c_roll <= lds->Entries[i].chance) {
|
||||
npc->AddLootDrop(
|
||||
db_item,
|
||||
item_list,
|
||||
loot_drop->Entries[i]
|
||||
lds->Entries[i]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
j = loot_drop->NumEntries;
|
||||
j = lds->NumEntries;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
roll -= loot_drop->Entries[j].chance;
|
||||
} else {
|
||||
roll -= lds->Entries[j].chance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -291,19 +304,18 @@ void NPC::AddLootDrop(
|
||||
ItemList *itemlist,
|
||||
LootDropEntries_Struct loot_drop,
|
||||
bool wear_change,
|
||||
uint32 aug1,
|
||||
uint32 aug2,
|
||||
uint32 aug3,
|
||||
uint32 aug4,
|
||||
uint32 aug5,
|
||||
uint32 aug6
|
||||
uint32 augment_one,
|
||||
uint32 augment_two,
|
||||
uint32 augment_three,
|
||||
uint32 augment_four,
|
||||
uint32 augment_five,
|
||||
uint32 augment_six
|
||||
)
|
||||
{
|
||||
if (!item2) {
|
||||
return;
|
||||
}
|
||||
|
||||
//make sure we are doing something...
|
||||
if (!itemlist && !wear_change) {
|
||||
return;
|
||||
}
|
||||
@@ -340,18 +352,17 @@ void NPC::AddLootDrop(
|
||||
|
||||
item->item_id = item2->ID;
|
||||
item->charges = loot_drop.item_charges;
|
||||
item->aug_1 = aug1;
|
||||
item->aug_2 = aug2;
|
||||
item->aug_3 = aug3;
|
||||
item->aug_4 = aug4;
|
||||
item->aug_5 = aug5;
|
||||
item->aug_6 = aug6;
|
||||
item->attuned = 0;
|
||||
item->aug_1 = augment_one;
|
||||
item->aug_2 = augment_two;
|
||||
item->aug_3 = augment_three;
|
||||
item->aug_4 = augment_four;
|
||||
item->aug_5 = augment_five;
|
||||
item->aug_6 = augment_six;
|
||||
item->attuned = false;
|
||||
item->trivial_min_level = loot_drop.trivial_min_level;
|
||||
item->trivial_max_level = loot_drop.trivial_max_level;
|
||||
item->equip_slot = EQ::invslot::SLOT_INVALID;
|
||||
|
||||
|
||||
// unsure if required to equip, YOLO for now
|
||||
if (item2->ItemType == EQ::item::ItemTypeBow) {
|
||||
SetBowEquipped(true);
|
||||
@@ -363,17 +374,17 @@ void NPC::AddLootDrop(
|
||||
|
||||
bool found = false; // track if we found an empty slot we fit into
|
||||
|
||||
int foundslot = INVALID_INDEX; // for multi-slot items
|
||||
int found_slot = INVALID_INDEX; // for multi-slot items
|
||||
|
||||
const auto* inst = database.CreateItem(
|
||||
auto *inst = database.CreateItem(
|
||||
item2->ID,
|
||||
loot_drop.item_charges,
|
||||
aug1,
|
||||
aug2,
|
||||
aug3,
|
||||
aug4,
|
||||
aug5,
|
||||
aug6
|
||||
augment_one,
|
||||
augment_two,
|
||||
augment_three,
|
||||
augment_four,
|
||||
augment_five,
|
||||
augment_six
|
||||
);
|
||||
|
||||
if (!inst) {
|
||||
@@ -381,9 +392,8 @@ void NPC::AddLootDrop(
|
||||
}
|
||||
|
||||
if (loot_drop.equip_item > 0) {
|
||||
uint8 eslot = 0xFF;
|
||||
char newid[20];
|
||||
const EQ::ItemData* compitem = nullptr;
|
||||
uint8 equipment_slot = UINT8_MAX;
|
||||
const EQ::ItemData *compitem = nullptr;
|
||||
|
||||
// Equip rules are as follows:
|
||||
// If the item has the NoPet flag set it will not be equipped.
|
||||
@@ -395,141 +405,136 @@ void NPC::AddLootDrop(
|
||||
// it is an improvement.
|
||||
|
||||
if (!item2->NoPet) {
|
||||
for (int i = EQ::invslot::EQUIPMENT_BEGIN; !found && i <= EQ::invslot::EQUIPMENT_END; i++) {
|
||||
uint32 slots = (1 << i);
|
||||
for (
|
||||
int i = EQ::invslot::EQUIPMENT_BEGIN;
|
||||
!found && i <= EQ::invslot::EQUIPMENT_END;
|
||||
i++
|
||||
) {
|
||||
const uint32 slots = (1 << i);
|
||||
if (item2->Slots & slots) {
|
||||
if(equipment[i])
|
||||
{
|
||||
if (equipment[i]) {
|
||||
compitem = database.GetItem(equipment[i]);
|
||||
if (item2->AC > compitem->AC ||
|
||||
(item2->AC == compitem->AC && item2->HP > compitem->HP))
|
||||
{
|
||||
if (
|
||||
item2->AC > compitem->AC ||
|
||||
(item2->AC == compitem->AC && item2->HP > compitem->HP)
|
||||
) {
|
||||
// item would be an upgrade
|
||||
// check if we're multi-slot, if yes then we have to keep
|
||||
// looking in case any of the other slots we can fit into are empty.
|
||||
if (item2->Slots != slots) {
|
||||
foundslot = i;
|
||||
}
|
||||
else {
|
||||
found_slot = i;
|
||||
} else {
|
||||
// Unequip old item
|
||||
auto* olditem = GetItem(i);
|
||||
auto *old_item = GetItem(i);
|
||||
|
||||
olditem->equip_slot = EQ::invslot::SLOT_INVALID;
|
||||
old_item->equip_slot = EQ::invslot::SLOT_INVALID;
|
||||
|
||||
equipment[i] = item2->ID;
|
||||
|
||||
foundslot = i;
|
||||
found = true;
|
||||
found_slot = i;
|
||||
found = true;
|
||||
}
|
||||
} // end if ac
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
} else {
|
||||
equipment[i] = item2->ID;
|
||||
foundslot = i;
|
||||
found = true;
|
||||
|
||||
found_slot = i;
|
||||
found = true;
|
||||
}
|
||||
} // end if (slots)
|
||||
} // end for
|
||||
} // end if NoPet
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Possible slot was found but not selected. Pick it now.
|
||||
if (!found && foundslot >= 0) {
|
||||
equipment[foundslot] = item2->ID;
|
||||
if (!found && found_slot >= 0) {
|
||||
equipment[found_slot] = item2->ID;
|
||||
|
||||
found = true;
|
||||
}
|
||||
|
||||
// @merth: IDFile size has been increased, this needs to change
|
||||
uint16 emat;
|
||||
if(item2->Material <= 0
|
||||
|| (item2->Slots & ((1 << EQ::invslot::slotPrimary) | (1 << EQ::invslot::slotSecondary)))) {
|
||||
memset(newid, 0, sizeof(newid));
|
||||
for(int i=0;i<7;i++){
|
||||
if (!isalpha(item2->IDFile[i])){
|
||||
strn0cpy(newid, &item2->IDFile[i],6);
|
||||
i=8;
|
||||
uint32 equipment_material;
|
||||
if (
|
||||
item2->Material <= 0 ||
|
||||
(
|
||||
item2->Slots & (
|
||||
(1 << EQ::invslot::slotPrimary) |
|
||||
(1 << EQ::invslot::slotSecondary)
|
||||
)
|
||||
)
|
||||
) {
|
||||
equipment_material = Strings::ToUnsignedInt(&item2->IDFile[2]);
|
||||
} else {
|
||||
equipment_material = item2->Material;
|
||||
}
|
||||
|
||||
if (found_slot == EQ::invslot::slotPrimary) {
|
||||
equipment_slot = EQ::textures::weaponPrimary;
|
||||
|
||||
if (item2->Damage > 0) {
|
||||
SendAddPlayerState(PlayerState::PrimaryWeaponEquipped);
|
||||
|
||||
if (!RuleB(Combat, ClassicNPCBackstab)) {
|
||||
SetFacestab(true);
|
||||
}
|
||||
}
|
||||
|
||||
emat = Strings::ToInt(newid);
|
||||
} else {
|
||||
emat = item2->Material;
|
||||
}
|
||||
|
||||
if (foundslot == EQ::invslot::slotPrimary) {
|
||||
|
||||
eslot = EQ::textures::weaponPrimary;
|
||||
if (item2->Damage > 0) {
|
||||
SendAddPlayerState(PlayerState::PrimaryWeaponEquipped);
|
||||
if (!RuleB(Combat, ClassicNPCBackstab))
|
||||
SetFacestab(true);
|
||||
}
|
||||
if (item2->IsType2HWeapon())
|
||||
if (item2->IsType2HWeapon()) {
|
||||
SetTwoHanderEquipped(true);
|
||||
}
|
||||
else if (foundslot == EQ::invslot::slotSecondary
|
||||
&& (GetOwner() != nullptr || (CanThisClassDualWield() && zone->random.Roll(NPC_DW_CHANCE)) || (item2->Damage==0)) &&
|
||||
(item2->IsType1HWeapon() || item2->ItemType == EQ::item::ItemTypeShield || item2->ItemType == EQ::item::ItemTypeLight))
|
||||
{
|
||||
|
||||
eslot = EQ::textures::weaponSecondary;
|
||||
if (item2->Damage > 0)
|
||||
SendAddPlayerState(PlayerState::SecondaryWeaponEquipped);
|
||||
}
|
||||
else if (foundslot == EQ::invslot::slotHead) {
|
||||
eslot = EQ::textures::armorHead;
|
||||
}
|
||||
else if (foundslot == EQ::invslot::slotChest) {
|
||||
eslot = EQ::textures::armorChest;
|
||||
}
|
||||
else if (foundslot == EQ::invslot::slotArms) {
|
||||
eslot = EQ::textures::armorArms;
|
||||
}
|
||||
else if (foundslot == EQ::invslot::slotWrist1 || foundslot == EQ::invslot::slotWrist2) {
|
||||
eslot = EQ::textures::armorWrist;
|
||||
}
|
||||
else if (foundslot == EQ::invslot::slotHands) {
|
||||
eslot = EQ::textures::armorHands;
|
||||
}
|
||||
else if (foundslot == EQ::invslot::slotLegs) {
|
||||
eslot = EQ::textures::armorLegs;
|
||||
}
|
||||
else if (foundslot == EQ::invslot::slotFeet) {
|
||||
eslot = EQ::textures::armorFeet;
|
||||
}
|
||||
|
||||
/*
|
||||
what was this about???
|
||||
|
||||
if (((npc->GetRace()==127) && (npc->CastToMob()->GetOwnerID()!=0)) && (item2->Slots==24576) || (item2->Slots==8192) || (item2->Slots==16384)){
|
||||
npc->d_melee_texture2=Strings::ToInt(newid);
|
||||
wc->wear_slot_id=8;
|
||||
if (item2->Material >0)
|
||||
wc->material=item2->Material;
|
||||
else
|
||||
wc->material=Strings::ToInt(newid);
|
||||
npc->AC+=item2->AC;
|
||||
npc->STR+=item2->STR;
|
||||
npc->INT+=item2->INT;
|
||||
}
|
||||
*/
|
||||
|
||||
//if we found an open slot it goes in...
|
||||
if(eslot != 0xFF) {
|
||||
if(wear_change) {
|
||||
p_wear_change_struct->wear_slot_id = eslot;
|
||||
p_wear_change_struct->material = emat;
|
||||
}
|
||||
} else if (
|
||||
found_slot == EQ::invslot::slotSecondary &&
|
||||
(
|
||||
GetOwner() ||
|
||||
(CanThisClassDualWield() && zone->random.Roll(NPC_DW_CHANCE)) ||
|
||||
item2->Damage == 0
|
||||
) &&
|
||||
(
|
||||
item2->IsType1HWeapon() ||
|
||||
item2->ItemType == EQ::item::ItemTypeShield ||
|
||||
item2->ItemType == EQ::item::ItemTypeLight
|
||||
)
|
||||
) {
|
||||
equipment_slot = EQ::textures::weaponSecondary;
|
||||
|
||||
if (item2->Damage > 0) {
|
||||
SendAddPlayerState(PlayerState::SecondaryWeaponEquipped);
|
||||
}
|
||||
} else if (found_slot == EQ::invslot::slotHead) {
|
||||
equipment_slot = EQ::textures::armorHead;
|
||||
} else if (found_slot == EQ::invslot::slotChest) {
|
||||
equipment_slot = EQ::textures::armorChest;
|
||||
} else if (found_slot == EQ::invslot::slotArms) {
|
||||
equipment_slot = EQ::textures::armorArms;
|
||||
} else if (EQ::ValueWithin(found_slot, EQ::invslot::slotWrist1, EQ::invslot::slotWrist2)) {
|
||||
equipment_slot = EQ::textures::armorWrist;
|
||||
} else if (found_slot == EQ::invslot::slotHands) {
|
||||
equipment_slot = EQ::textures::armorHands;
|
||||
} else if (found_slot == EQ::invslot::slotLegs) {
|
||||
equipment_slot = EQ::textures::armorLegs;
|
||||
} else if (found_slot == EQ::invslot::slotFeet) {
|
||||
equipment_slot = EQ::textures::armorFeet;
|
||||
}
|
||||
|
||||
if (equipment_slot != UINT8_MAX) {
|
||||
if (wear_change) {
|
||||
p_wear_change_struct->wear_slot_id = equipment_slot;
|
||||
p_wear_change_struct->material = equipment_material;
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
item->equip_slot = foundslot;
|
||||
item->equip_slot = found_slot;
|
||||
}
|
||||
}
|
||||
|
||||
if (itemlist) {
|
||||
if (foundslot != INVALID_INDEX) {
|
||||
GetInv().PutItem(foundslot, *inst);
|
||||
if (found_slot != INVALID_INDEX) {
|
||||
GetInv().PutItem(found_slot, *inst);
|
||||
}
|
||||
|
||||
if (parse->HasQuestSub(GetNPCTypeID(), EVENT_LOOT_ADDED)) {
|
||||
std::vector<std::any> args = { inst };
|
||||
parse->EventNPC(EVENT_LOOT_ADDED, this, nullptr, "", 0, &args);
|
||||
}
|
||||
|
||||
itemlist->push_back(item);
|
||||
@@ -551,55 +556,69 @@ void NPC::AddLootDrop(
|
||||
}
|
||||
|
||||
UpdateEquipmentLight();
|
||||
|
||||
if (UpdateActiveLight()) {
|
||||
SendAppearancePacket(AT_Light, GetActiveLightType());
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::AddItem(const EQ::ItemData *item, uint16 charges, bool equipitem)
|
||||
void NPC::AddItem(const EQ::ItemData *item, uint16 charges, bool equip_item)
|
||||
{
|
||||
//slot isnt needed, its determined from the item.
|
||||
auto loot_drop_entry = NPC::NewLootDropEntry();
|
||||
loot_drop_entry.equip_item = static_cast<uint8>(equipitem ? 1 : 0);
|
||||
|
||||
loot_drop_entry.equip_item = static_cast<uint8>(equip_item ? 1 : 0);
|
||||
loot_drop_entry.item_charges = charges;
|
||||
|
||||
AddLootDrop(item, &itemlist, loot_drop_entry, true);
|
||||
}
|
||||
|
||||
void NPC::AddItem(
|
||||
uint32 itemid,
|
||||
uint32 item_id,
|
||||
uint16 charges,
|
||||
bool equipitem,
|
||||
uint32 aug1,
|
||||
uint32 aug2,
|
||||
uint32 aug3,
|
||||
uint32 aug4,
|
||||
uint32 aug5,
|
||||
uint32 aug6
|
||||
bool equip_item,
|
||||
uint32 augment_one,
|
||||
uint32 augment_two,
|
||||
uint32 augment_three,
|
||||
uint32 augment_four,
|
||||
uint32 augment_five,
|
||||
uint32 augment_six
|
||||
)
|
||||
{
|
||||
//slot isnt needed, its determined from the item.
|
||||
const EQ::ItemData *i = database.GetItem(itemid);
|
||||
if (i == nullptr) {
|
||||
const auto *item = database.GetItem(item_id);
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto loot_drop_entry = NPC::NewLootDropEntry();
|
||||
loot_drop_entry.equip_item = static_cast<uint8>(equipitem ? 1 : 0);
|
||||
|
||||
loot_drop_entry.equip_item = static_cast<uint8>(equip_item ? 1 : 0);
|
||||
loot_drop_entry.item_charges = charges;
|
||||
|
||||
AddLootDrop(i, &itemlist, loot_drop_entry, true, aug1, aug2, aug3, aug4, aug5, aug6);
|
||||
AddLootDrop(
|
||||
item,
|
||||
&itemlist,
|
||||
loot_drop_entry,
|
||||
true,
|
||||
augment_one,
|
||||
augment_two,
|
||||
augment_three,
|
||||
augment_four,
|
||||
augment_five,
|
||||
augment_six
|
||||
);
|
||||
}
|
||||
|
||||
void NPC::AddLootTable() {
|
||||
void NPC::AddLootTable()
|
||||
{
|
||||
if (npctype_id != 0) { // check if it's a GM spawn
|
||||
database.AddLootTableToNPC(this,loottable_id, &itemlist, &copper, &silver, &gold, &platinum);
|
||||
database.AddLootTableToNPC(this, loottable_id, &itemlist, &copper, &silver, &gold, &platinum);
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::AddLootTable(uint32 ldid) {
|
||||
void NPC::AddLootTable(uint32 loottable_id)
|
||||
{
|
||||
if (npctype_id != 0) { // check if it's a GM spawn
|
||||
database.AddLootTableToNPC(this,ldid, &itemlist, &copper, &silver, &gold, &platinum);
|
||||
database.AddLootTableToNPC(this, loottable_id, &itemlist, &copper, &silver, &gold, &platinum);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+109
-1
@@ -389,6 +389,96 @@ int Lua_Bot::GetRawItemAC() {
|
||||
return self->GetRawItemAC();
|
||||
}
|
||||
|
||||
void Lua_Bot::ClearDisciplineReuseTimer() {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->ClearDisciplineReuseTimer();
|
||||
}
|
||||
|
||||
void Lua_Bot::ClearDisciplineReuseTimer(uint16 spell_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->ClearDisciplineReuseTimer(spell_id);
|
||||
}
|
||||
|
||||
void Lua_Bot::ClearItemReuseTimer() {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->ClearItemReuseTimer();
|
||||
}
|
||||
|
||||
void Lua_Bot::ClearItemReuseTimer(uint32 item_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->ClearItemReuseTimer(item_id);
|
||||
}
|
||||
|
||||
void Lua_Bot::ClearSpellRecastTimer() {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->ClearSpellRecastTimer();
|
||||
}
|
||||
|
||||
void Lua_Bot::ClearSpellRecastTimer(uint16 spell_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->ClearSpellRecastTimer(spell_id);
|
||||
}
|
||||
|
||||
uint32 Lua_Bot::GetDisciplineReuseTimer() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetDisciplineReuseRemainingTime();
|
||||
}
|
||||
|
||||
uint32 Lua_Bot::GetDisciplineReuseTimer(uint16 spell_id) {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetDisciplineReuseRemainingTime(spell_id);
|
||||
}
|
||||
|
||||
uint32 Lua_Bot::GetItemReuseTimer() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetItemReuseRemainingTime();
|
||||
}
|
||||
|
||||
uint32 Lua_Bot::GetItemReuseTimer(uint32 item_id) {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetItemReuseRemainingTime(item_id);
|
||||
}
|
||||
|
||||
uint32 Lua_Bot::GetSpellRecastTimer() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetSpellRecastRemainingTime();
|
||||
}
|
||||
|
||||
uint32 Lua_Bot::GetSpellRecastTimer(uint16 spell_id) {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetSpellRecastRemainingTime(spell_id);
|
||||
}
|
||||
|
||||
void Lua_Bot::SetDisciplineReuseTimer(uint16 spell_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetDisciplineReuseTimer(spell_id);
|
||||
}
|
||||
|
||||
void Lua_Bot::SetDisciplineReuseTimer(uint16 spell_id, uint32 reuse_timer) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetDisciplineReuseTimer(spell_id, reuse_timer);
|
||||
}
|
||||
|
||||
void Lua_Bot::SetItemReuseTimer(uint32 item_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetItemReuseTimer(item_id);
|
||||
}
|
||||
|
||||
void Lua_Bot::SetItemReuseTimer(uint32 item_id, uint32 reuse_timer) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetItemReuseTimer(item_id, reuse_timer);
|
||||
}
|
||||
|
||||
void Lua_Bot::SetSpellRecastTimer(uint16 spell_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetSpellRecastTimer(spell_id);
|
||||
}
|
||||
|
||||
void Lua_Bot::SetSpellRecastTimer(uint16 spell_id, uint32 recast_delay) {
|
||||
Lua_Safe_Call_Void();
|
||||
return self->SetSpellRecastTimer(spell_id, recast_delay);
|
||||
}
|
||||
|
||||
bool Lua_Bot::IsGrouped() {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->IsGrouped();
|
||||
@@ -600,6 +690,12 @@ luabind::scope lua_register_bot() {
|
||||
.def("ApplySpellRaid", (void(Lua_Bot::*)(int,int,int,bool,bool))&Lua_Bot::ApplySpellRaid)
|
||||
.def("Camp", (void(Lua_Bot::*)(void))&Lua_Bot::Camp)
|
||||
.def("Camp", (void(Lua_Bot::*)(bool))&Lua_Bot::Camp)
|
||||
.def("ClearDisciplineReuseTimer", (void(Lua_Bot::*)())&Lua_Bot::ClearDisciplineReuseTimer)
|
||||
.def("ClearDisciplineReuseTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::ClearDisciplineReuseTimer)
|
||||
.def("ClearItemReuseTimer", (void(Lua_Bot::*)())&Lua_Bot::ClearItemReuseTimer)
|
||||
.def("ClearItemReuseTimer", (void(Lua_Bot::*)(uint32))&Lua_Bot::ClearItemReuseTimer)
|
||||
.def("ClearSpellRecastTimer", (void(Lua_Bot::*)())&Lua_Bot::ClearSpellRecastTimer)
|
||||
.def("ClearSpellRecastTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::ClearSpellRecastTimer)
|
||||
.def("CountBotItem", (uint32(Lua_Bot::*)(uint32))&Lua_Bot::CountBotItem)
|
||||
.def("CountItemEquippedByID", (int(Lua_Bot::*)(uint32))&Lua_Bot::CountItemEquippedByID)
|
||||
.def("DeleteBucket", (void(Lua_Bot::*)(std::string))&Lua_Bot::DeleteBucket)
|
||||
@@ -623,6 +719,8 @@ luabind::scope lua_register_bot() {
|
||||
.def("GetBotID", (uint32(Lua_Bot::*)(void))&Lua_Bot::GetBotID)
|
||||
.def("GetBotItem", (Lua_ItemInst(Lua_Bot::*)(uint16))&Lua_Bot::GetBotItem)
|
||||
.def("GetBotItemIDBySlot", (uint32(Lua_Bot::*)(uint16))&Lua_Bot::GetBotItemIDBySlot)
|
||||
.def("GetDisciplineReuseTimer", (uint32(Lua_Bot::*)())&Lua_Bot::GetDisciplineReuseTimer)
|
||||
.def("GetDisciplineReuseTimer", (uint32(Lua_Bot::*)(uint16))&Lua_Bot::GetDisciplineReuseTimer)
|
||||
.def("GetBucket", (std::string(Lua_Bot::*)(std::string))&Lua_Bot::GetBucket)
|
||||
.def("GetBucketExpires", (std::string(Lua_Bot::*)(std::string))&Lua_Bot::GetBucketExpires)
|
||||
.def("GetBucketRemaining", (std::string(Lua_Bot::*)(std::string))&Lua_Bot::GetBucketRemaining)
|
||||
@@ -633,13 +731,17 @@ luabind::scope lua_register_bot() {
|
||||
.def("GetInstrumentMod", (int(Lua_Bot::*)(int))&Lua_Bot::GetInstrumentMod)
|
||||
.def("GetItemAt", (Lua_ItemInst(Lua_Bot::*)(int16))&Lua_Bot::GetItemAt)
|
||||
.def("GetItemIDAt", (int(Lua_Bot::*)(int16))&Lua_Bot::GetItemIDAt)
|
||||
.def("GetItemReuseTimer", (uint32(Lua_Bot::*)())&Lua_Bot::GetItemReuseTimer)
|
||||
.def("GetItemReuseTimer", (uint32(Lua_Bot::*)(uint32))&Lua_Bot::GetItemReuseTimer)
|
||||
.def("GetOwner", (Lua_Mob(Lua_Bot::*)(void))&Lua_Bot::GetOwner)
|
||||
.def("GetRaceAbbreviation", (std::string(Lua_Bot::*)(void))&Lua_Bot::GetRaceAbbreviation)
|
||||
.def("GetRawItemAC", (int(Lua_Bot::*)(void))&Lua_Bot::GetRawItemAC)
|
||||
.def("GetSpellDamage", (int(Lua_Bot::*)(void))&Lua_Bot::GetSpellDamage)
|
||||
.def("GetSpellRecastTimer", (uint32(Lua_Bot::*)())&Lua_Bot::GetSpellRecastTimer)
|
||||
.def("GetSpellRecastTimer", (uint32(Lua_Bot::*)(uint16))&Lua_Bot::GetSpellRecastTimer)
|
||||
.def("HasAugmentEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasAugmentEquippedByID)
|
||||
.def("HasBotItem", (int16(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
|
||||
.def("HasBotSpellEntry", (bool(Lua_Bot::*)(uint16)) & Lua_Bot::HasBotSpellEntry)
|
||||
.def("HasBotSpellEntry", (bool(Lua_Bot::*)(uint16))&Lua_Bot::HasBotSpellEntry)
|
||||
.def("HasItemEquippedByID", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasItemEquippedByID)
|
||||
.def("IsGrouped", (bool(Lua_Bot::*)(void))&Lua_Bot::IsGrouped)
|
||||
.def("IsSitting", (bool(Lua_Bot::*)(void))&Lua_Bot::IsSitting)
|
||||
@@ -655,6 +757,10 @@ luabind::scope lua_register_bot() {
|
||||
.def("SetBucket", (void(Lua_Bot::*)(std::string,std::string,std::string))&Lua_Bot::SetBucket)
|
||||
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int))&Lua_Bot::SetExpansionBitmask)
|
||||
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int,bool))&Lua_Bot::SetExpansionBitmask)
|
||||
.def("SetDisciplineReuseTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::SetDisciplineReuseTimer)
|
||||
.def("SetDisciplineReuseTimer", (void(Lua_Bot::*)(uint16, uint32))&Lua_Bot::SetDisciplineReuseTimer)
|
||||
.def("SetItemReuseTimer", (void(Lua_Bot::*)(uint32))&Lua_Bot::SetItemReuseTimer)
|
||||
.def("SetItemReuseTimer", (void(Lua_Bot::*)(uint32, uint32))&Lua_Bot::SetItemReuseTimer)
|
||||
.def("SetSpellDuration", (void(Lua_Bot::*)(int))&Lua_Bot::SetSpellDuration)
|
||||
.def("SetSpellDuration", (void(Lua_Bot::*)(int,int))&Lua_Bot::SetSpellDuration)
|
||||
.def("SetSpellDuration", (void(Lua_Bot::*)(int,int,int))&Lua_Bot::SetSpellDuration)
|
||||
@@ -668,6 +774,8 @@ luabind::scope lua_register_bot() {
|
||||
.def("SetSpellDurationRaid", (void(Lua_Bot::*)(int,int,int))&Lua_Bot::SetSpellDurationRaid)
|
||||
.def("SetSpellDurationRaid", (void(Lua_Bot::*)(int,int,int,bool))&Lua_Bot::SetSpellDurationRaid)
|
||||
.def("SetSpellDurationRaid", (void(Lua_Bot::*)(int,int,int,bool,bool))&Lua_Bot::SetSpellDurationRaid)
|
||||
.def("SetSpellRecastTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::SetSpellRecastTimer)
|
||||
.def("SetSpellRecastTimer", (void(Lua_Bot::*)(uint16, uint32))&Lua_Bot::SetSpellRecastTimer)
|
||||
.def("SendPayload", (void(Lua_Bot::*)(int))&Lua_Bot::SendPayload)
|
||||
.def("SendPayload", (void(Lua_Bot::*)(int,std::string))&Lua_Bot::SendPayload)
|
||||
.def("Signal", (void(Lua_Bot::*)(int))&Lua_Bot::Signal)
|
||||
|
||||
@@ -107,6 +107,25 @@ public:
|
||||
void SetSpellDurationRaid(int spell_id, int duration, int level, bool allow_pets);
|
||||
void SetSpellDurationRaid(int spell_id, int duration, int level, bool allow_pets, bool is_raid_group_only);
|
||||
|
||||
void ClearDisciplineReuseTimer();
|
||||
void ClearDisciplineReuseTimer(uint16 spell_id);
|
||||
void ClearItemReuseTimer();
|
||||
void ClearItemReuseTimer(uint32 item_id);
|
||||
void ClearSpellRecastTimer();
|
||||
void ClearSpellRecastTimer(uint16 spell_id);
|
||||
uint32 GetDisciplineReuseTimer();
|
||||
uint32 GetDisciplineReuseTimer(uint16 spell_id);
|
||||
uint32 GetItemReuseTimer();
|
||||
uint32 GetItemReuseTimer(uint32 item_id);
|
||||
uint32 GetSpellRecastTimer();
|
||||
uint32 GetSpellRecastTimer(uint16 spell_id);
|
||||
void SetDisciplineReuseTimer(uint16 spell_id);
|
||||
void SetDisciplineReuseTimer(uint16 spell_id, uint32 reuse_timer);
|
||||
void SetItemReuseTimer(uint32 item_id);
|
||||
void SetItemReuseTimer(uint32 item_id, uint32 reuse_timer);
|
||||
void SetSpellRecastTimer(uint16 spell_id);
|
||||
void SetSpellRecastTimer(uint16 spell_id, uint32 reuse_timer);
|
||||
|
||||
int CountAugmentEquippedByID(uint32 item_id);
|
||||
int CountItemEquippedByID(uint32 item_id);
|
||||
bool HasAugmentEquippedByID(uint32 item_id);
|
||||
|
||||
+89
-7
@@ -1211,7 +1211,24 @@ void Lua_Client::AddPVPPoints(uint32 points) {
|
||||
|
||||
void Lua_Client::AddCrystals(uint32 radiant, uint32 ebon) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->AddCrystals(radiant, ebon);
|
||||
|
||||
if (ebon != 0) {
|
||||
if (ebon > 0) {
|
||||
self->AddEbonCrystals(ebon);
|
||||
return;
|
||||
}
|
||||
|
||||
self->RemoveEbonCrystals(ebon);
|
||||
}
|
||||
|
||||
if (radiant != 0) {
|
||||
if (radiant > 0) {
|
||||
self->AddRadiantCrystals(radiant);
|
||||
return;
|
||||
}
|
||||
|
||||
self->RemoveRadiantCrystals(radiant);
|
||||
}
|
||||
}
|
||||
|
||||
void Lua_Client::SetEbonCrystals(uint32 value) {
|
||||
@@ -1486,15 +1503,15 @@ void Lua_Client::Signal(int signal_id) {
|
||||
|
||||
void Lua_Client::AddAlternateCurrencyValue(uint32 currency, int amount) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->AddAlternateCurrencyValue(currency, amount, 1);
|
||||
self->AddAlternateCurrencyValue(currency, amount, true);
|
||||
}
|
||||
|
||||
void Lua_Client::SetAlternateCurrencyValue(uint32 currency, int amount) {
|
||||
void Lua_Client::SetAlternateCurrencyValue(uint32 currency, uint32 amount) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->SetAlternateCurrencyValue(currency, amount);
|
||||
}
|
||||
|
||||
int Lua_Client::GetAlternateCurrencyValue(uint32 currency) {
|
||||
uint32 Lua_Client::GetAlternateCurrencyValue(uint32 currency) {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetAlternateCurrencyValue(currency);
|
||||
}
|
||||
@@ -3176,6 +3193,65 @@ void Lua_Client::GrantAllAAPoints(uint8 unlock_level)
|
||||
self->GrantAllAAPoints(unlock_level);
|
||||
}
|
||||
|
||||
void Lua_Client::AddEbonCrystals(uint32 amount)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->AddEbonCrystals(amount);
|
||||
}
|
||||
|
||||
void Lua_Client::AddRadiantCrystals(uint32 amount)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->AddRadiantCrystals(amount);
|
||||
}
|
||||
|
||||
void Lua_Client::RemoveEbonCrystals(uint32 amount)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->RemoveEbonCrystals(amount);
|
||||
}
|
||||
|
||||
void Lua_Client::RemoveRadiantCrystals(uint32 amount)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->RemoveRadiantCrystals(amount);
|
||||
}
|
||||
|
||||
void Lua_Client::SummonItemIntoInventory(luabind::object item_table) {
|
||||
Lua_Safe_Call_Void();
|
||||
if (luabind::type(item_table) != LUA_TTABLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32 item_id = luabind::object_cast<uint32>(item_table["item_id"]);
|
||||
const int16 charges = luabind::object_cast<uint32>(item_table["charges"]);
|
||||
const uint32 augment_one = luabind::type(item_table["augment_one"]) != LUA_TNIL ? luabind::object_cast<uint32>(item_table["augment_one"]) : 0;
|
||||
const uint32 augment_two = luabind::type(item_table["augment_two"]) != LUA_TNIL ? luabind::object_cast<uint32>(item_table["augment_two"]) : 0;
|
||||
const uint32 augment_three = luabind::type(item_table["augment_three"]) != LUA_TNIL ? luabind::object_cast<uint32>(item_table["augment_three"]) : 0;
|
||||
const uint32 augment_four = luabind::type(item_table["augment_four"]) != LUA_TNIL ? luabind::object_cast<uint32>(item_table["augment_four"]) : 0;
|
||||
const uint32 augment_five = luabind::type(item_table["augment_five"]) != LUA_TNIL ? luabind::object_cast<uint32>(item_table["augment_five"]) : 0;
|
||||
const uint32 augment_six = luabind::type(item_table["augment_six"]) != LUA_TNIL ? luabind::object_cast<uint32>(item_table["augment_six"]) : 0;
|
||||
const bool attuned = luabind::type(item_table["attuned"]) != LUA_TNIL ? luabind::object_cast<bool>(item_table["attuned"]) : false;
|
||||
|
||||
self->SummonItemIntoInventory(
|
||||
item_id,
|
||||
charges,
|
||||
augment_one,
|
||||
augment_two,
|
||||
augment_three,
|
||||
augment_four,
|
||||
augment_five,
|
||||
augment_six,
|
||||
attuned
|
||||
);
|
||||
}
|
||||
|
||||
bool Lua_Client::HasItemOnCorpse(uint32 item_id)
|
||||
{
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->HasItemOnCorpse(item_id);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_client() {
|
||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -3187,6 +3263,7 @@ luabind::scope lua_register_client() {
|
||||
.def("AddEXP", (void(Lua_Client::*)(uint32))&Lua_Client::AddEXP)
|
||||
.def("AddEXP", (void(Lua_Client::*)(uint32,int))&Lua_Client::AddEXP)
|
||||
.def("AddEXP", (void(Lua_Client::*)(uint32,int,bool))&Lua_Client::AddEXP)
|
||||
.def("AddEbonCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::AddEbonCrystals)
|
||||
.def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32))&Lua_Client::AddExpeditionLockout)
|
||||
.def("AddExpeditionLockout", (void(Lua_Client::*)(std::string, std::string, uint32, std::string))&Lua_Client::AddExpeditionLockout)
|
||||
.def("AddExpeditionLockoutDuration", (void(Lua_Client::*)(std::string, std::string, int))&Lua_Client::AddExpeditionLockoutDuration)
|
||||
@@ -3202,6 +3279,7 @@ luabind::scope lua_register_client() {
|
||||
.def("AddPlatinum", (void(Lua_Client::*)(uint32))&Lua_Client::AddPlatinum)
|
||||
.def("AddPlatinum", (void(Lua_Client::*)(uint32,bool))&Lua_Client::AddPlatinum)
|
||||
.def("AddPVPPoints", (void(Lua_Client::*)(uint32))&Lua_Client::AddPVPPoints)
|
||||
.def("AddRadiantCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::AddRadiantCrystals)
|
||||
.def("AddSkill", (void(Lua_Client::*)(int,int))&Lua_Client::AddSkill)
|
||||
.def("Admin", (int16(Lua_Client::*)(void))&Lua_Client::Admin)
|
||||
.def("ApplySpell", (void(Lua_Client::*)(int))&Lua_Client::ApplySpell)
|
||||
@@ -3303,7 +3381,7 @@ luabind::scope lua_register_client() {
|
||||
.def("GetAccountFlags", (luabind::object(Lua_Client::*)(lua_State*))&Lua_Client::GetAccountFlags)
|
||||
.def("GetAggroCount", (uint32(Lua_Client::*)(void))&Lua_Client::GetAggroCount)
|
||||
.def("GetAllMoney", (uint64(Lua_Client::*)(void))&Lua_Client::GetAllMoney)
|
||||
.def("GetAlternateCurrencyValue", (int(Lua_Client::*)(uint32))&Lua_Client::GetAlternateCurrencyValue)
|
||||
.def("GetAlternateCurrencyValue", (uint32(Lua_Client::*)(uint32))&Lua_Client::GetAlternateCurrencyValue)
|
||||
.def("GetAnon", (int(Lua_Client::*)(void))&Lua_Client::GetAnon)
|
||||
.def("GetAugmentIDAt", (int(Lua_Client::*)(int,int))&Lua_Client::GetAugmentIDAt)
|
||||
.def("GetAugmentIDsBySlotID", (luabind::object(Lua_Client::*)(lua_State* L,int16))&Lua_Client::GetAugmentIDsBySlotID)
|
||||
@@ -3436,6 +3514,7 @@ luabind::scope lua_register_client() {
|
||||
.def("HasDisciplineLearned", (bool(Lua_Client::*)(uint16))&Lua_Client::HasDisciplineLearned)
|
||||
.def("HasExpeditionLockout", (bool(Lua_Client::*)(std::string, std::string))&Lua_Client::HasExpeditionLockout)
|
||||
.def("HasItemEquippedByID", (bool(Lua_Client::*)(uint32))&Lua_Client::HasItemEquippedByID)
|
||||
.def("HasItemOnCorpse", (bool(Lua_Client::*)(uint32))&Lua_Client::HasItemOnCorpse)
|
||||
.def("HasPEQZoneFlag", (bool(Lua_Client::*)(uint32))&Lua_Client::HasPEQZoneFlag)
|
||||
.def("HasRecipeLearned", (bool(Lua_Client::*)(uint32))&Lua_Client::HasRecipeLearned)
|
||||
.def("HasSkill", (bool(Lua_Client::*)(int))&Lua_Client::HasSkill)
|
||||
@@ -3554,7 +3633,9 @@ luabind::scope lua_register_client() {
|
||||
.def("ResetCastbarCooldownBySlot", (void(Lua_Client::*)(int))&Lua_Client::ResetCastbarCooldownBySlot)
|
||||
.def("ResetCastbarCooldownBySpellID", (void(Lua_Client::*)(uint32))&Lua_Client::ResetCastbarCooldownBySpellID)
|
||||
.def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer)
|
||||
.def("RemoveEbonCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveEbonCrystals)
|
||||
.def("ResetItemCooldown", (void(Lua_Client::*)(uint32))&Lua_Client::ResetItemCooldown)
|
||||
.def("RemoveRadiantCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveRadiantCrystals)
|
||||
.def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade)
|
||||
.def("RewardFaction", (void(Lua_Client::*)(int,int))&Lua_Client::RewardFaction)
|
||||
.def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save)
|
||||
@@ -3586,7 +3667,7 @@ luabind::scope lua_register_client() {
|
||||
.def("SetAATitle", (void(Lua_Client::*)(std::string,bool))&Lua_Client::SetAATitle)
|
||||
.def("SetAFK", (void(Lua_Client::*)(uint8))&Lua_Client::SetAFK)
|
||||
.def("SetAccountFlag", (void(Lua_Client::*)(const std::string&,const std::string&))&Lua_Client::SetAccountFlag)
|
||||
.def("SetAlternateCurrencyValue", (void(Lua_Client::*)(uint32,int))&Lua_Client::SetAlternateCurrencyValue)
|
||||
.def("SetAlternateCurrencyValue", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::SetAlternateCurrencyValue)
|
||||
.def("SetAnon", (void(Lua_Client::*)(uint8))&Lua_Client::SetAnon)
|
||||
.def("SetBaseClass", (void(Lua_Client::*)(int))&Lua_Client::SetBaseClass)
|
||||
.def("SetBaseGender", (void(Lua_Client::*)(int))&Lua_Client::SetBaseGender)
|
||||
@@ -3605,7 +3686,7 @@ luabind::scope lua_register_client() {
|
||||
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int))&Lua_Client::SetBotSpawnLimit)
|
||||
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotSpawnLimit)
|
||||
.def("SetBucket", (void(Lua_Client::*)(std::string,std::string))&Lua_Client::SetBucket)
|
||||
.def("SetBucketExpires", (void(Lua_Client::*)(std::string,std::string,std::string))&Lua_Client::SetBucket)
|
||||
.def("SetBucket", (void(Lua_Client::*)(std::string,std::string,std::string))&Lua_Client::SetBucket)
|
||||
.def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel)
|
||||
.def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption)
|
||||
.def("SetDeity", (void(Lua_Client::*)(int))&Lua_Client::SetDeity)
|
||||
@@ -3678,6 +3759,7 @@ luabind::scope lua_register_client() {
|
||||
.def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32,uint32))&Lua_Client::SummonItem)
|
||||
.def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32,uint32,bool))&Lua_Client::SummonItem)
|
||||
.def("SummonItem", (void(Lua_Client::*)(uint32,int,uint32,uint32,uint32,uint32,uint32,bool,int))&Lua_Client::SummonItem)
|
||||
.def("SummonItemIntoInventory", (void(Lua_Client::*)(luabind::adl::object))&Lua_Client::SummonItemIntoInventory)
|
||||
.def("TGB", (bool(Lua_Client::*)(void))&Lua_Client::TGB)
|
||||
.def("TakeMoneyFromPP", (bool(Lua_Client::*)(uint64))&Lua_Client::TakeMoneyFromPP)
|
||||
.def("TakeMoneyFromPP", (bool(Lua_Client::*)(uint64,bool))&Lua_Client::TakeMoneyFromPP)
|
||||
|
||||
+8
-2
@@ -376,8 +376,8 @@ public:
|
||||
void NotifyNewTitlesAvailable();
|
||||
void Signal(int signal_id);
|
||||
void AddAlternateCurrencyValue(uint32 currency, int amount);
|
||||
void SetAlternateCurrencyValue(uint32 currency, int amount);
|
||||
int GetAlternateCurrencyValue(uint32 currency);
|
||||
void SetAlternateCurrencyValue(uint32 currency, uint32 amount);
|
||||
uint32 GetAlternateCurrencyValue(uint32 currency);
|
||||
void SendWebLink(const char *site);
|
||||
bool HasSpellScribed(int spell_id);
|
||||
void ClearAccountFlag(const std::string& flag);
|
||||
@@ -482,6 +482,12 @@ public:
|
||||
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration);
|
||||
void GrantAllAAPoints();
|
||||
void GrantAllAAPoints(uint8 unlock_level);
|
||||
void AddEbonCrystals(uint32 amount);
|
||||
void AddRadiantCrystals(uint32 amount);
|
||||
void RemoveEbonCrystals(uint32 amount);
|
||||
void RemoveRadiantCrystals(uint32 amount);
|
||||
void SummonItemIntoInventory(luabind::object item_table);
|
||||
bool HasItemOnCorpse(uint32 item_id);
|
||||
|
||||
void ApplySpell(int spell_id);
|
||||
void ApplySpell(int spell_id, int duration);
|
||||
|
||||
@@ -6609,7 +6609,14 @@ luabind::scope lua_register_events() {
|
||||
luabind::value("memorize_spell", static_cast<int>(EVENT_MEMORIZE_SPELL)),
|
||||
luabind::value("unmemorize_spell", static_cast<int>(EVENT_UNMEMORIZE_SPELL)),
|
||||
luabind::value("scribe_spell", static_cast<int>(EVENT_SCRIBE_SPELL)),
|
||||
luabind::value("unscribe_spell", static_cast<int>(EVENT_UNSCRIBE_SPELL))
|
||||
luabind::value("unscribe_spell", static_cast<int>(EVENT_UNSCRIBE_SPELL)),
|
||||
luabind::value("loot_added", static_cast<int>(EVENT_LOOT_ADDED)),
|
||||
luabind::value("ldon_points_gain", static_cast<int>(EVENT_LDON_POINTS_GAIN)),
|
||||
luabind::value("ldon_points_loss", static_cast<int>(EVENT_LDON_POINTS_LOSS)),
|
||||
luabind::value("alt_currency_gain", static_cast<int>(EVENT_ALT_CURRENCY_GAIN)),
|
||||
luabind::value("alt_currency_loss", static_cast<int>(EVENT_ALT_CURRENCY_LOSS)),
|
||||
luabind::value("crystal_gain", static_cast<int>(EVENT_CRYSTAL_GAIN)),
|
||||
luabind::value("crystal_loss", static_cast<int>(EVENT_CRYSTAL_LOSS))
|
||||
)];
|
||||
}
|
||||
|
||||
|
||||
@@ -1015,6 +1015,21 @@ Lua_Mob Lua_Mob::GetHateTop() {
|
||||
return Lua_Mob(self->GetHateTop());
|
||||
}
|
||||
|
||||
Lua_Bot Lua_Mob::GetHateTopBot() {
|
||||
Lua_Safe_Call_Class(Lua_Bot);
|
||||
return Lua_Bot(self->GetHateTopBot());
|
||||
}
|
||||
|
||||
Lua_Client Lua_Mob::GetHateTopClient() {
|
||||
Lua_Safe_Call_Class(Lua_Client);
|
||||
return Lua_Client(self->GetHateTopClient());
|
||||
}
|
||||
|
||||
Lua_NPC Lua_Mob::GetHateTopNPC() {
|
||||
Lua_Safe_Call_Class(Lua_NPC);
|
||||
return Lua_NPC(self->GetHateTopNPC());
|
||||
}
|
||||
|
||||
Lua_Mob Lua_Mob::GetHateDamageTop(Lua_Mob other) {
|
||||
Lua_Safe_Call_Class(Lua_Mob);
|
||||
return Lua_Mob(self->GetHateDamageTop(other));
|
||||
@@ -3456,6 +3471,9 @@ luabind::scope lua_register_mob() {
|
||||
.def("GetHateRandomClient", (Lua_Client(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomClient)
|
||||
.def("GetHateRandomNPC", (Lua_NPC(Lua_Mob::*)(void))&Lua_Mob::GetHateRandomNPC)
|
||||
.def("GetHateTop", (Lua_Mob(Lua_Mob::*)(void))&Lua_Mob::GetHateTop)
|
||||
.def("GetHateTopBot", (Lua_Bot(Lua_Mob::*)(void))&Lua_Mob::GetHateTopBot)
|
||||
.def("GetHateTopClient", (Lua_Client(Lua_Mob::*)(void))&Lua_Mob::GetHateTopClient)
|
||||
.def("GetHateTopNPC", (Lua_NPC(Lua_Mob::*)(void))&Lua_Mob::GetHateTopNPC)
|
||||
.def("GetHeading", &Lua_Mob::GetHeading)
|
||||
.def("GetHelmTexture", &Lua_Mob::GetHelmTexture)
|
||||
.def("GetHerosForgeModel", (int32(Lua_Mob::*)(uint8))&Lua_Mob::GetHerosForgeModel)
|
||||
|
||||
@@ -225,6 +225,9 @@ public:
|
||||
Lua_HateList GetHateListByDistance();
|
||||
Lua_HateList GetHateListByDistance(uint32 distance);
|
||||
Lua_Mob GetHateTop();
|
||||
Lua_Bot GetHateTopBot();
|
||||
Lua_Client GetHateTopClient();
|
||||
Lua_NPC GetHateTopNPC();
|
||||
Lua_Mob GetHateDamageTop(Lua_Mob other);
|
||||
Lua_Mob GetHateRandom();
|
||||
Lua_Bot GetHateRandomBot();
|
||||
|
||||
@@ -813,6 +813,18 @@ void Lua_NPC::SetBucket(std::string bucket_name, std::string bucket_value, std::
|
||||
self->SetBucket(bucket_name, bucket_value, expiration);
|
||||
}
|
||||
|
||||
bool Lua_NPC::GetNPCAggro()
|
||||
{
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->GetNPCAggro();
|
||||
}
|
||||
|
||||
void Lua_NPC::SetNPCAggro(bool in_npc_aggro)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->SetNPCAggro(in_npc_aggro);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_npc() {
|
||||
return luabind::class_<Lua_NPC, Lua_Mob>("NPC")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -872,6 +884,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("GetLDoNLockedSkill", (uint16(Lua_NPC::*)(void))&Lua_NPC::GetLDoNLockedSkill)
|
||||
.def("GetLDoNTrapType", (uint8(Lua_NPC::*)(void))&Lua_NPC::GetLDoNTrapType)
|
||||
.def("GetLDoNTrapSpellID", (uint16(Lua_NPC::*)(void))&Lua_NPC::GetLDoNTrapSpellID)
|
||||
.def("GetNPCAggro", (bool(Lua_NPC::*)(void))&Lua_NPC::GetNPCAggro)
|
||||
.def("GetNPCFactionID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCFactionID)
|
||||
.def("GetNPCHate", (int64(Lua_NPC::*)(Lua_Mob))&Lua_NPC::GetNPCHate)
|
||||
.def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID)
|
||||
@@ -949,6 +962,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("SetLDoNTrapDetected", (void(Lua_NPC::*)(bool))&Lua_NPC::SetLDoNTrapDetected)
|
||||
.def("SetLDoNTrapSpellID", (void(Lua_NPC::*)(uint16))&Lua_NPC::SetLDoNTrapSpellID)
|
||||
.def("SetLDoNTrapType", (void(Lua_NPC::*)(uint8))&Lua_NPC::SetLDoNTrapType)
|
||||
.def("SetNPCAggro", (void(Lua_NPC::*)(bool))&Lua_NPC::SetNPCAggro)
|
||||
.def("SetNPCFactionID", (void(Lua_NPC::*)(int))&Lua_NPC::SetNPCFactionID)
|
||||
.def("SetPetSpellID", (void(Lua_NPC::*)(int))&Lua_NPC::SetPetSpellID)
|
||||
.def("SetPlatinum", (void(Lua_NPC::*)(uint32))&Lua_NPC::SetPlatinum)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user