mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-31 17:26:30 +00:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5c0bdfdc4c | |||
| 6130e10831 | |||
| c3e1c531d2 | |||
| b52719a535 | |||
| 1af252466f | |||
| 699d22fc28 | |||
| 5d1fe68906 | |||
| 52dcf35425 | |||
| a7550fbd9e | |||
| cc0171dfe1 | |||
| 913c5da70f | |||
| 40fecbfaf5 | |||
| b1646381b0 | |||
| bb1578796b | |||
| 0e5a38f072 | |||
| 39876ab858 | |||
| ff16a76481 | |||
| ffd68eb63d | |||
| 76c1da1aad | |||
| a91e03fa43 | |||
| 453106439f | |||
| 3da24fffa4 | |||
| 8d8ef6d480 | |||
| 1f9c4b3a22 | |||
| 7dfda95d86 | |||
| 40738b29e3 | |||
| 080865faa2 | |||
| e2b545991a | |||
| b7f8d0f179 | |||
| e3588781aa | |||
| e9b84f4d11 | |||
| 4f03970fd1 | |||
| 4979da6932 | |||
| 9987029791 | |||
| eece0a92e3 | |||
| 057f96796a | |||
| f475cecdb1 | |||
| 6296ed6d41 | |||
| ac0f729aa2 | |||
| 2937852cf9 | |||
| 2cf5bae571 | |||
| 2feb05be18 | |||
| 421767e1e5 | |||
| 6e9ff52dce | |||
| aa700f8960 | |||
| 2ef959c5ed | |||
| e49ab924cc | |||
| fc3c691588 | |||
| d465a3deba | |||
| 40c9c8044b | |||
| 70a96ea098 | |||
| d5cbec714e | |||
| 6903205484 | |||
| 4c81321847 | |||
| e5cea73e0c | |||
| 23308192b5 | |||
| 29fdf7e2ae | |||
| 098498dedd | |||
| b6fb8daae8 | |||
| 563f7d5564 | |||
| 1e5abc456b | |||
| 3b0fa015a7 | |||
| c73a1e8bea | |||
| 3bfdc0cf71 | |||
| a23ac4628f | |||
| 5ef4612249 | |||
| 17f66c5d60 | |||
| 51eb95ed31 | |||
| 080abaede1 | |||
| 97e332819d | |||
| 1e41c5517e | |||
| c7a88af11a | |||
| d8ddd0aab9 | |||
| 95cbadade5 | |||
| a85f4fb703 | |||
| e63f34638b |
+149
@@ -1,3 +1,152 @@
|
|||||||
|
## [22.56.3] 9/23/2024
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix issue with Client::SaveDisciplines() not specifying character ID ([#4481](https://github.com/EQEmu/Server/pull/4477)) @Kinglykrab 2024-09-23
|
||||||
|
|
||||||
|
## [22.56.2] 9/20/2024
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix Issue with Database::ReserveName ([#4477](https://github.com/EQEmu/Server/pull/4477)) @Kinglykrab 2024-09-20
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add GrantAllAAPoints() Overload To Perl/Lua ([#4474](https://github.com/EQEmu/Server/pull/4474)) @Kinglykrab 2024-09-20
|
||||||
|
|
||||||
|
## [22.56.1] 9/20/2024
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Fix Untrained Disciplines in Client::SaveDisciplines() ([#4472](https://github.com/EQEmu/Server/pull/4472)) @Kinglykrab 2024-09-13
|
||||||
|
* Fix Infinite Loop in Adventure::Finished() ([#4473](https://github.com/EQEmu/Server/pull/4473)) @oddx2k 2024-09-13
|
||||||
|
|
||||||
|
## [22.56.0] 9/12/2024
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Add IsCloseToBanker method ([#4462](https://github.com/EQEmu/Server/pull/4462)) @Akkadius 2024-08-27
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Add Rule to Limit Task Update Messages ([#4459](https://github.com/EQEmu/Server/pull/4459)) @Kinglykrab 2024-08-28
|
||||||
|
* Allow NPCs to cast Sacrifice ([#4470](https://github.com/EQEmu/Server/pull/4470)) @fuzzlecutter 2024-09-12
|
||||||
|
* Lazy Load Bank Contents ([#4453](https://github.com/EQEmu/Server/pull/4453)) @catapultam-habeo 2024-08-27
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Add RULE_STRING to RuleManager::ResetRules ([#4467](https://github.com/EQEmu/Server/pull/4467)) @Kinglykrab 2024-09-07
|
||||||
|
* Fix Bard Effect in Migration 9237 ([#4468](https://github.com/EQEmu/Server/pull/4468)) @Kinglykrab 2024-09-09
|
||||||
|
* ModernAAScalingEnabled() Calculation Error ([#4469](https://github.com/EQEmu/Server/pull/4469)) @carolus21rex 2024-09-11
|
||||||
|
|
||||||
|
### Performance
|
||||||
|
|
||||||
|
* Move Discipline Loading to Client::CompleteConnect() ([#4466](https://github.com/EQEmu/Server/pull/4466)) @Kinglykrab 2024-09-09
|
||||||
|
|
||||||
|
### Rules
|
||||||
|
|
||||||
|
* Add a Bandolier Swap Delay Rule ([#4465](https://github.com/EQEmu/Server/pull/4465)) @Kinglykrab 2024-09-08
|
||||||
|
|
||||||
|
## [22.55.1] 8/26/2024
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Remove unused methods ([#4449](https://github.com/EQEmu/Server/pull/4449)) @Kinglykrab 2024-08-22
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Add Character:DefaultGuildRank Rule ([#4438](https://github.com/EQEmu/Server/pull/4438)) @Kinglykrab 2024-08-04
|
||||||
|
* Add Optional Return to EVENT_DAMAGE_TAKEN ([#4454](https://github.com/EQEmu/Server/pull/4454)) @Kinglykrab 2024-08-27
|
||||||
|
* Extend Spell Buckets Functionality ([#4441](https://github.com/EQEmu/Server/pull/4441)) @Kinglykrab 2024-08-22
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Apply Race & Class restrictions to Auto-Combines ([#4452](https://github.com/EQEmu/Server/pull/4452)) @catapultam-habeo 2024-08-20
|
||||||
|
* Attune Augments when Equipped ([#4446](https://github.com/EQEmu/Server/pull/4446)) @fryguy503 2024-08-10
|
||||||
|
* Correct missed maxlevel reference in exp.cpp ([#4463](https://github.com/EQEmu/Server/pull/4463)) @N0ctrnl 2024-08-27
|
||||||
|
* Ensure close of Tribute Item search ([#4439](https://github.com/EQEmu/Server/pull/4439)) @joligario 2024-08-04
|
||||||
|
* Fix AddCrystals() in Perl/Lua ([#4445](https://github.com/EQEmu/Server/pull/4445)) @Kinglykrab 2024-08-10
|
||||||
|
* Fix Bot Spell Entries IDs Capping at 32,767 ([#4444](https://github.com/EQEmu/Server/pull/4444)) @Kinglykrab 2024-08-27
|
||||||
|
* Fix Character ID of 0 being inserted into character_stats_record ([#4458](https://github.com/EQEmu/Server/pull/4458)) @Kinglykrab 2024-08-22
|
||||||
|
* Fix Issue with Removed #setfaction Command ([#4448](https://github.com/EQEmu/Server/pull/4448)) @Kinglykrab 2024-08-11
|
||||||
|
* Fix Lua Client FilteredMessage ([#4437](https://github.com/EQEmu/Server/pull/4437)) @Kinglykrab 2024-07-31
|
||||||
|
* Fix client hotbar exchanging items when zoning ([#4460](https://github.com/EQEmu/Server/pull/4460)) @neckkola 2024-08-27
|
||||||
|
* Fix issue with killed mob coordinates ([#4457](https://github.com/EQEmu/Server/pull/4457)) @Kinglykrab 2024-08-22
|
||||||
|
* Imitate Death should also clear zone feign aggro ([#4436](https://github.com/EQEmu/Server/pull/4436)) @fryguy503 2024-07-31
|
||||||
|
* client_max_level allow leveling to end of level ([#4455](https://github.com/EQEmu/Server/pull/4455)) @fryguy503 2024-08-20
|
||||||
|
|
||||||
|
### Improvement
|
||||||
|
|
||||||
|
* Filtered Messages Extension ([#4435](https://github.com/EQEmu/Server/pull/4435)) @fryguy503 2024-07-31
|
||||||
|
|
||||||
|
### Quest API
|
||||||
|
|
||||||
|
* Add AreTasksCompleted() to Perl/Lua. ([#4456](https://github.com/EQEmu/Server/pull/4456)) @Kinglykrab 2024-08-23
|
||||||
|
* Add Area-Based Quest Methods to Perl/Lua ([#4447](https://github.com/EQEmu/Server/pull/4447)) @Kinglykrab 2024-08-27
|
||||||
|
* Add Several Door Methods to Perl/Lua ([#4451](https://github.com/EQEmu/Server/pull/4451)) @Kinglykrab 2024-08-16
|
||||||
|
|
||||||
|
### World
|
||||||
|
|
||||||
|
* Fix slow world bootup bug ([#4461](https://github.com/EQEmu/Server/pull/4461)) @Akkadius 2024-08-27
|
||||||
|
|
||||||
|
## [22.54.0] 7/30/2024
|
||||||
|
|
||||||
|
### Code
|
||||||
|
|
||||||
|
* Cleanup Client File Exporting ([#4348](https://github.com/EQEmu/Server/pull/4348)) @Kinglykrab 2024-07-31
|
||||||
|
* Cleanup Stance Code ([#4368](https://github.com/EQEmu/Server/pull/4368)) @Kinglykrab 2024-07-03
|
||||||
|
* Mask GM Show Buff message behind EntityVariable ([#4419](https://github.com/EQEmu/Server/pull/4419)) @nytmyr 2024-07-22
|
||||||
|
|
||||||
|
### Commands
|
||||||
|
|
||||||
|
* Extend #devtools Functionality ([#4425](https://github.com/EQEmu/Server/pull/4425)) @Kinglykrab 2024-07-23
|
||||||
|
|
||||||
|
### Databuckets
|
||||||
|
|
||||||
|
* Remove memory reserve from bulk load ([#4427](https://github.com/EQEmu/Server/pull/4427)) @Akkadius 2024-07-23
|
||||||
|
|
||||||
|
### Feature
|
||||||
|
|
||||||
|
* Add Barter/Buyer Features ([#4405](https://github.com/EQEmu/Server/pull/4405)) @neckkola 2024-07-30
|
||||||
|
* Add Parcel notification for online players when using the Quest API ([#4418](https://github.com/EQEmu/Server/pull/4418)) @neckkola 2024-07-22
|
||||||
|
* Implement Move Multiple Items ([#4259](https://github.com/EQEmu/Server/pull/4259)) @catapultam-habeo 2024-07-30
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
|
||||||
|
* Aegolism Spell line stacking ([#4399](https://github.com/EQEmu/Server/pull/4399)) @KayenEQ 2024-07-07
|
||||||
|
* AllowRaidTargetBlind logic backwards ([#4400](https://github.com/EQEmu/Server/pull/4400)) @fryguy503 2024-07-01
|
||||||
|
* AutoSplit unknown bug and cleanup. ([#4401](https://github.com/EQEmu/Server/pull/4401)) @fryguy503 2024-07-07
|
||||||
|
* Corpse Call removing Resurrection Effects ([#4410](https://github.com/EQEmu/Server/pull/4410)) @fryguy503 2024-07-22
|
||||||
|
* Fix #parcels add subcommand ([#4431](https://github.com/EQEmu/Server/pull/4431)) @neckkola 2024-07-29
|
||||||
|
* Fix #setlevel Allowing Skills Above Max ([#4423](https://github.com/EQEmu/Server/pull/4423)) @Kinglykrab 2024-07-23
|
||||||
|
* Fix Bot::SetBotStance ([#4426](https://github.com/EQEmu/Server/pull/4426)) @Kinglykrab 2024-07-23
|
||||||
|
* Fix Client::RemoveTitle ([#4421](https://github.com/EQEmu/Server/pull/4421)) @Kinglykrab 2024-07-23
|
||||||
|
* Fix EVENT_USE_SKILL with Sense Heading ([#4424](https://github.com/EQEmu/Server/pull/4424)) @Kinglykrab 2024-07-23
|
||||||
|
* Fix for random disconnects when a large number of guild members zone or disconnect ([#4402](https://github.com/EQEmu/Server/pull/4402)) @neckkola 2024-07-10
|
||||||
|
* Fix issue with quest::echo and quest::me ([#4433](https://github.com/EQEmu/Server/pull/4433)) @Kinglykrab 2024-07-30
|
||||||
|
* Personal tributes for bard items were not applying correctly ([#4416](https://github.com/EQEmu/Server/pull/4416)) @neckkola 2024-07-16
|
||||||
|
* Potential fix for some undesired ranged explotative behavior. ([#4413](https://github.com/EQEmu/Server/pull/4413)) @fryguy503 2024-07-22
|
||||||
|
* Proximity Aggro for Frustrated and Undead ([#4411](https://github.com/EQEmu/Server/pull/4411)) @fryguy503 2024-07-22
|
||||||
|
* Slay Adjustments ([#4389](https://github.com/EQEmu/Server/pull/4389)) @fryguy503 2024-07-07
|
||||||
|
* Stop DOSing ourselves with OP_WearChange ([#4432](https://github.com/EQEmu/Server/pull/4432)) @catapultam-habeo 2024-07-30
|
||||||
|
* [Quest API] Fix getraididbycharid and getgroupidbycharid ([#4417](https://github.com/EQEmu/Server/pull/4417)) @nytmyr 2024-07-16
|
||||||
|
|
||||||
|
### Improvement
|
||||||
|
|
||||||
|
* Flee Overhaul ([#4407](https://github.com/EQEmu/Server/pull/4407)) @fryguy503 2024-07-30
|
||||||
|
|
||||||
|
### Rules
|
||||||
|
|
||||||
|
* Add HasteCap and Hastev3Cap rules for NPCs, Bots and Mercs ([#4406](https://github.com/EQEmu/Server/pull/4406)) @nytmyr 2024-07-22
|
||||||
|
|
||||||
|
### Zone Instances
|
||||||
|
|
||||||
|
* Revert " Handle routing to instances when using evac/succor " (#4429) ([#4297](https://github.com/EQEmu/Server/pull/4297)) @Akkadius 2024-07-30
|
||||||
|
|
||||||
|
### Zoning
|
||||||
|
|
||||||
|
* Improve zone routing ([#4428](https://github.com/EQEmu/Server/pull/4428)) @Akkadius 2024-07-30
|
||||||
|
|
||||||
## [22.53.1] 6/16/2024
|
## [22.53.1] 6/16/2024
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|||||||
+40
-141
@@ -29,7 +29,10 @@
|
|||||||
#include "../../common/content/world_content_service.h"
|
#include "../../common/content/world_content_service.h"
|
||||||
#include "../../common/zone_store.h"
|
#include "../../common/zone_store.h"
|
||||||
#include "../../common/path_manager.h"
|
#include "../../common/path_manager.h"
|
||||||
|
#include "../../common/repositories/base_data_repository.h"
|
||||||
|
#include "../../common/repositories/db_str_repository.h"
|
||||||
#include "../../common/repositories/skill_caps_repository.h"
|
#include "../../common/repositories/skill_caps_repository.h"
|
||||||
|
#include "../../common/repositories/spells_new_repository.h"
|
||||||
#include "../../common/file.h"
|
#include "../../common/file.h"
|
||||||
#include "../../common/events/player_event_logs.h"
|
#include "../../common/events/player_event_logs.h"
|
||||||
#include "../../common/skill_caps.h"
|
#include "../../common/skill_caps.h"
|
||||||
@@ -99,25 +102,22 @@ int main(int argc, char **argv)
|
|||||||
->LoadLogDatabaseSettings()
|
->LoadLogDatabaseSettings()
|
||||||
->StartFileLogs();
|
->StartFileLogs();
|
||||||
|
|
||||||
std::string arg_1;
|
std::string export_type;
|
||||||
|
|
||||||
if (argv[1]) {
|
if (argv[1]) {
|
||||||
arg_1 = argv[1];
|
export_type = argv[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (arg_1 == "spells") {
|
if (Strings::EqualFold(export_type, "spells")) {
|
||||||
ExportSpells(&content_db);
|
ExportSpells(&content_db);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} else if (Strings::EqualFold(export_type, "skills")) {
|
||||||
if (arg_1 == "skills") {
|
|
||||||
ExportSkillCaps(&content_db);
|
ExportSkillCaps(&content_db);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} else if (Strings::EqualFold(export_type, "basedata") || Strings::EqualFold(export_type, "base_data")) {
|
||||||
if (arg_1 == "basedata") {
|
|
||||||
ExportBaseData(&content_db);
|
ExportBaseData(&content_db);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
} else if (Strings::EqualFold(export_type, "dbstr") || Strings::EqualFold(export_type, "dbstring")) {
|
||||||
if (arg_1 == "dbstring") {
|
|
||||||
ExportDBStrings(&database);
|
ExportDBStrings(&database);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -134,178 +134,77 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
void ExportSpells(SharedDatabase* db)
|
void ExportSpells(SharedDatabase* db)
|
||||||
{
|
{
|
||||||
LogInfo("Exporting Spells");
|
std::ofstream file(fmt::format("{}/export/spells_us.txt", path.GetServerPath()));
|
||||||
|
if (!file || !file.is_open()) {
|
||||||
std::string file = fmt::format("{}/export/spells_us.txt", path.GetServerPath());
|
|
||||||
FILE *f = fopen(file.c_str(), "w");
|
|
||||||
if (!f) {
|
|
||||||
LogError("Unable to open export/spells_us.txt to write, skipping.");
|
LogError("Unable to open export/spells_us.txt to write, skipping.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string query = "SELECT * FROM spells_new ORDER BY id";
|
const auto& lines = SpellsNewRepository::GetSpellFileLines(*db);
|
||||||
auto results = db->QueryDatabase(query);
|
|
||||||
|
|
||||||
if (results.Success()) {
|
const std::string& file_string = Strings::Implode("\n", lines);
|
||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
|
||||||
std::string line;
|
|
||||||
unsigned int fields = results.ColumnCount();
|
|
||||||
for (unsigned int i = 0; i < fields; ++i) {
|
|
||||||
if (i != 0) {
|
|
||||||
line.push_back('^');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row[i] != nullptr) {
|
file << file_string;
|
||||||
line += row[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(f, "%s\n", line.c_str());
|
file.close();
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
LogInfo("Exported [{}] Spell{}", lines.size(), lines.size() != 1 ? "s" : "");
|
||||||
}
|
|
||||||
|
|
||||||
bool SkillUsable(SharedDatabase* db, int skill_id, int class_id)
|
|
||||||
{
|
|
||||||
const auto& l = SkillCapsRepository::GetWhere(
|
|
||||||
*db,
|
|
||||||
fmt::format(
|
|
||||||
"`class_id` = {} AND `skill_id` = {} ORDER BY `cap` DESC LIMIT 1",
|
|
||||||
class_id,
|
|
||||||
skill_id
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return !l.empty();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32 GetSkill(SharedDatabase* db, int skill_id, int class_id, int level)
|
|
||||||
{
|
|
||||||
const auto& l = SkillCapsRepository::GetWhere(
|
|
||||||
*db,
|
|
||||||
fmt::format(
|
|
||||||
"`class_id` = {} AND `skill_id` = {} AND `level` = {}",
|
|
||||||
class_id,
|
|
||||||
skill_id,
|
|
||||||
level
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (l.empty()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto e = l.front();
|
|
||||||
|
|
||||||
return e.cap;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportSkillCaps(SharedDatabase* db)
|
void ExportSkillCaps(SharedDatabase* db)
|
||||||
{
|
{
|
||||||
LogInfo("Exporting Skill Caps");
|
|
||||||
|
|
||||||
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", path.GetServerPath()));
|
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", path.GetServerPath()));
|
||||||
if (!file || !file.is_open()) {
|
if (!file || !file.is_open()) {
|
||||||
LogError("Unable to open export/SkillCaps.txt to write, skipping.");
|
LogError("Unable to open export/SkillCaps.txt to write, skipping.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (uint8 class_id = Class::Warrior; class_id <= Class::Berserker; class_id++) {
|
const auto& lines = SkillCapsRepository::GetSkillCapFileLines(*db);
|
||||||
for (uint8 skill_id = EQ::skills::Skill1HBlunt; skill_id <= EQ::skills::Skill2HPiercing; skill_id++) {
|
|
||||||
if (SkillUsable(db, skill_id, class_id)) {
|
|
||||||
uint32 previous_cap = 0;
|
|
||||||
|
|
||||||
for (
|
const std::string& file_string = Strings::Implode("\n", lines);
|
||||||
uint8 level = 1;
|
|
||||||
level <= SkillCaps::GetSkillCapMaxLevel(class_id, static_cast<EQ::skills::SkillType>(skill_id));
|
|
||||||
level++
|
|
||||||
) {
|
|
||||||
uint32 cap = GetSkill(db, skill_id, class_id, level);
|
|
||||||
if (cap < previous_cap) {
|
|
||||||
cap = previous_cap;
|
|
||||||
}
|
|
||||||
|
|
||||||
file << fmt::format("{}^{}^{}^{}^0", class_id, skill_id, level, cap) << std::endl;
|
file << file_string;
|
||||||
|
|
||||||
previous_cap = cap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
|
||||||
|
LogInfo("Exported [{}] Skill Cap{}", lines.size(), lines.size() != 1 ? "s" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportBaseData(SharedDatabase *db)
|
void ExportBaseData(SharedDatabase *db)
|
||||||
{
|
{
|
||||||
LogInfo("Exporting Base Data");
|
std::ofstream file(fmt::format("{}/export/BaseData.txt", path.GetServerPath()));
|
||||||
|
if (!file || !file.is_open()) {
|
||||||
std::string file = fmt::format("{}/export/BaseData.txt", path.GetServerPath());
|
|
||||||
FILE *f = fopen(file.c_str(), "w");
|
|
||||||
if (!f) {
|
|
||||||
LogError("Unable to open export/BaseData.txt to write, skipping.");
|
LogError("Unable to open export/BaseData.txt to write, skipping.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string query = "SELECT * FROM base_data ORDER BY level, class";
|
const auto& lines = BaseDataRepository::GetBaseDataFileLines(*db);
|
||||||
auto results = db->QueryDatabase(query);
|
|
||||||
if (results.Success()) {
|
|
||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
|
||||||
std::string line;
|
|
||||||
unsigned int fields = results.ColumnCount();
|
|
||||||
for (unsigned int rowIndex = 0; rowIndex < fields; ++rowIndex) {
|
|
||||||
if (rowIndex != 0) {
|
|
||||||
line.push_back('^');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row[rowIndex] != nullptr) {
|
const std::string& file_string = Strings::Implode("\n", lines);
|
||||||
line += row[rowIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(f, "%s\n", line.c_str());
|
file << file_string;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
file.close();
|
||||||
|
|
||||||
|
LogInfo("Exported [{}] Base Data Entr{}", lines.size(), lines.size() != 1 ? "ies" : "y");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExportDBStrings(SharedDatabase *db)
|
void ExportDBStrings(SharedDatabase *db)
|
||||||
{
|
{
|
||||||
LogInfo("Exporting DB Strings");
|
std::ofstream file(fmt::format("{}/export/dbstr_us.txt", path.GetServerPath()));
|
||||||
|
if (!file || !file.is_open()) {
|
||||||
std::string file = fmt::format("{}/export/dbstr_us.txt", path.GetServerPath());
|
|
||||||
FILE *f = fopen(file.c_str(), "w");
|
|
||||||
if (!f) {
|
|
||||||
LogError("Unable to open export/dbstr_us.txt to write, skipping.");
|
LogError("Unable to open export/dbstr_us.txt to write, skipping.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(f, "Major^Minor^String(New)\n");
|
const auto& lines = DbStrRepository::GetDBStrFileLines(*db);
|
||||||
const std::string query = "SELECT * FROM db_str ORDER BY id, type";
|
|
||||||
auto results = db->QueryDatabase(query);
|
const std::string& file_string = Strings::Implode("\n", lines);
|
||||||
if (results.Success()) {
|
|
||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
file << file_string;
|
||||||
std::string line;
|
|
||||||
unsigned int fields = results.ColumnCount();
|
file.close();
|
||||||
for (unsigned int rowIndex = 0; rowIndex < fields; ++rowIndex) {
|
|
||||||
if (rowIndex != 0) {
|
LogInfo("Exported [{}] Database String{}", lines.size(), lines.size() != 1 ? "s" : "");
|
||||||
line.push_back('^');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row[rowIndex] != nullptr) {
|
|
||||||
line += row[rowIndex];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fprintf(f, "%s\n", line.c_str());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ SET(repositories
|
|||||||
repositories/base/base_bugs_repository.h
|
repositories/base/base_bugs_repository.h
|
||||||
repositories/base/base_bug_reports_repository.h
|
repositories/base/base_bug_reports_repository.h
|
||||||
repositories/base/base_buyer_repository.h
|
repositories/base/base_buyer_repository.h
|
||||||
|
repositories/base/base_buyer_trade_items_repository.h
|
||||||
repositories/base/base_character_activities_repository.h
|
repositories/base/base_character_activities_repository.h
|
||||||
repositories/base/base_character_alternate_abilities_repository.h
|
repositories/base/base_character_alternate_abilities_repository.h
|
||||||
repositories/base/base_character_alt_currency_repository.h
|
repositories/base/base_character_alt_currency_repository.h
|
||||||
@@ -339,7 +340,8 @@ SET(repositories
|
|||||||
repositories/books_repository.h
|
repositories/books_repository.h
|
||||||
repositories/bugs_repository.h
|
repositories/bugs_repository.h
|
||||||
repositories/bug_reports_repository.h
|
repositories/bug_reports_repository.h
|
||||||
repositories/buyer_repository.h
|
repositories/buyer_buy_lines_repository.h
|
||||||
|
repositories/buyer_trade_items_repository.h
|
||||||
repositories/character_activities_repository.h
|
repositories/character_activities_repository.h
|
||||||
repositories/character_alternate_abilities_repository.h
|
repositories/character_alternate_abilities_repository.h
|
||||||
repositories/character_alt_currency_repository.h
|
repositories/character_alt_currency_repository.h
|
||||||
|
|||||||
+1
-1
@@ -47,7 +47,7 @@ Bazaar::GetSearchResults(
|
|||||||
search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id));
|
search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id));
|
||||||
}
|
}
|
||||||
if (search.min_cost != 0) {
|
if (search.min_cost != 0) {
|
||||||
search_criteria_trader.append(fmt::format(" AND trader.item_cost >= {}", search.min_cost));
|
search_criteria_trader.append(fmt::format(" AND trader.item_cost >= {}", search.min_cost * 1000));
|
||||||
}
|
}
|
||||||
if (search.max_cost != 0) {
|
if (search.max_cost != 0) {
|
||||||
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
|
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "../rulesys.h"
|
#include "../rulesys.h"
|
||||||
#include "../eqemu_logsys.h"
|
#include "../eqemu_logsys.h"
|
||||||
#include "../repositories/instance_list_repository.h"
|
#include "../repositories/instance_list_repository.h"
|
||||||
|
#include "../zone_store.h"
|
||||||
|
|
||||||
|
|
||||||
WorldContentService::WorldContentService()
|
WorldContentService::WorldContentService()
|
||||||
@@ -183,8 +184,8 @@ void WorldContentService::ReloadContentFlags()
|
|||||||
}
|
}
|
||||||
|
|
||||||
SetContentFlags(set_content_flags);
|
SetContentFlags(set_content_flags);
|
||||||
LoadZones();
|
|
||||||
LoadStaticGlobalZoneInstances();
|
LoadStaticGlobalZoneInstances();
|
||||||
|
zone_store.LoadZones(*m_content_database);
|
||||||
}
|
}
|
||||||
|
|
||||||
Database *WorldContentService::GetDatabase() const
|
Database *WorldContentService::GetDatabase() const
|
||||||
@@ -236,18 +237,6 @@ void WorldContentService::SetContentFlag(const std::string &content_flag_name, b
|
|||||||
ReloadContentFlags();
|
ReloadContentFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
// HandleZoneRoutingMiddleware is meant to handle content and context aware zone routing
|
|
||||||
//
|
|
||||||
// example # 1
|
|
||||||
// lavastorm (pre-don) version 0 (classic)
|
|
||||||
// lavastorm (don) version 1
|
|
||||||
// we want to route players to the correct version of lavastorm based on the current server side expansion
|
|
||||||
// in order to do that the simplest and cleanest way we intercept the zoning process and route players to an "instance" of the zone
|
|
||||||
// the reason why we're doing this is because all of the zoning logic already is handled by two keys "zone_id" and "instance_id"
|
|
||||||
// we can leverage static, never expires instances to handle this but to the client they don't see it any other way than a public normal zone
|
|
||||||
// scripts handle all the same way, you don't have to think about instances, the middleware will handle the magic
|
|
||||||
// the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags
|
|
||||||
// we decide to route the client to the correct version of the zone based on the current server side expansion
|
|
||||||
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||||
{
|
{
|
||||||
auto r = FindZone(zc->zoneID, zc->instanceID);
|
auto r = FindZone(zc->zoneID, zc->instanceID);
|
||||||
@@ -263,63 +252,59 @@ void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
|||||||
// these are used commonly in v1/v2/v3 versions of the same zone for expansion routing
|
// these are used commonly in v1/v2/v3 versions of the same zone for expansion routing
|
||||||
WorldContentService *WorldContentService::LoadStaticGlobalZoneInstances()
|
WorldContentService *WorldContentService::LoadStaticGlobalZoneInstances()
|
||||||
{
|
{
|
||||||
m_zone_instances = InstanceListRepository::GetWhere(*GetDatabase(), fmt::format("never_expires = 1 AND is_global = 1"));
|
m_zone_static_instances = InstanceListRepository::GetWhere(
|
||||||
|
*GetDatabase(),
|
||||||
LogInfo("Loaded [{}] zone_instances", m_zone_instances.size());
|
fmt::format("never_expires = 1 AND is_global = 1")
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadZones sets the zones for the world content service
|
|
||||||
// this is used for zone routing middleware
|
|
||||||
// we pull the zone list from the zone repository and feed from the zone store for now
|
|
||||||
// we're holding a copy in the content service - but we're talking 250kb of data in memory to handle routing of zoning
|
|
||||||
WorldContentService * WorldContentService::LoadZones()
|
|
||||||
{
|
|
||||||
m_zones = ZoneRepository::All(*GetContentDatabase());
|
|
||||||
|
|
||||||
LogInfo("Loaded [{}] zones", m_zones.size());
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FindZone is critical to the zone routing middleware and any logic that needs to route players to the correct zone
|
|
||||||
// era contextual routing, multiple version of zones, etc
|
|
||||||
WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id, uint32 instance_id)
|
|
||||||
{
|
|
||||||
// if there's an active dynamic instance, we don't need to route
|
|
||||||
if (instance_id > 0) {
|
|
||||||
auto inst = InstanceListRepository::FindOne(*GetDatabase(), instance_id);
|
|
||||||
if (inst.id != 0 && !inst.is_global && !inst.never_expires) {
|
|
||||||
return WorldContentService::FindZoneResult{
|
|
||||||
.zone_id = 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto &z: m_zones) {
|
|
||||||
if (z.zoneidnumber == zone_id) {
|
|
||||||
auto f = ContentFlags{
|
|
||||||
.min_expansion = z.min_expansion,
|
|
||||||
.max_expansion = z.max_expansion,
|
|
||||||
.content_flags = z.content_flags,
|
|
||||||
.content_flags_disabled = z.content_flags_disabled
|
|
||||||
};
|
|
||||||
|
|
||||||
if (DoesPassContentFiltering(f)) {
|
|
||||||
LogInfo(
|
|
||||||
"Attempting to route player to zone [{}] ({}) version [{}] long_name [{}]",
|
|
||||||
z.short_name,
|
|
||||||
z.zoneidnumber,
|
|
||||||
z.version,
|
|
||||||
z.long_name
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// first pass, explicit match on public static global zone instances
|
LogInfo("Loaded [{}] zone_instances", m_zone_static_instances.size());
|
||||||
for (auto &i: m_zone_instances) {
|
|
||||||
if (i.zone == zone_id && i.version == z.version) {
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindZone handles content and context aware zone routing (middleware)
|
||||||
|
//
|
||||||
|
// this is a middleware function that is meant to be used in the zone change process
|
||||||
|
// this hooks all core zone changes within the server and routes the player to the correct zone
|
||||||
|
// returning a zone_id of non-zero means the middleware will route the player
|
||||||
|
// returning a zone_id of 0 means the middleware will not route the player
|
||||||
|
// this is useful for handling multiple versions of the same zone
|
||||||
|
//
|
||||||
|
// implementation >
|
||||||
|
// the zoning and process spawning logic already is handled by two keys "zone_id" and "instance_id"
|
||||||
|
// we leverage static, never expires instances to handle this and client still sees it as a normal zone
|
||||||
|
//
|
||||||
|
// content awareness >
|
||||||
|
// simply use the zone_id, server content settings and the middleware will handle the rest
|
||||||
|
// you don't have to think about instances in any data tables (use instance_id 0)
|
||||||
|
// you don't have to keep track of instance ids in scripts (use instance_id 0)
|
||||||
|
// the versions of zones are represented by two zone entries that have potentially different min/max expansion and/or different content flags
|
||||||
|
// we decide to route the client to the correct version of the zone based on the current server side expansion
|
||||||
|
//
|
||||||
|
// example >
|
||||||
|
// we want to route players to the correct version of lavastorm based on the current server side expansion (DoesZonePassContentFiltering)
|
||||||
|
// lavastorm (pre-don) version 0 (classic)
|
||||||
|
// zone table entry for version = 0, min_expansion = 0, max_expansion = 8
|
||||||
|
// instance_list table entry for lavastorm has version = 0, is_global = 1, never_expires = 1
|
||||||
|
// lavastorm (don) version 1
|
||||||
|
// zone table entry for version = 1, min_expansion = 9, max_expansion = 99
|
||||||
|
// instance_list table entry for lavastorm has version = 1, is_global = 1, never_expires = 1
|
||||||
|
WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id, uint32 instance_id)
|
||||||
|
{
|
||||||
|
for (const auto &z: zone_store.GetZones()) {
|
||||||
|
for (auto &i: m_zone_static_instances) {
|
||||||
|
if (
|
||||||
|
z.zoneidnumber == zone_id &&
|
||||||
|
DoesZonePassContentFiltering(z) &&
|
||||||
|
i.zone == zone_id &&
|
||||||
|
i.version == z.version) {
|
||||||
|
|
||||||
|
if (instance_id > 0 && i.id != instance_id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
LogInfo(
|
LogInfo(
|
||||||
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
"Routed player to public static instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||||
i.id,
|
i.id,
|
||||||
z.short_name,
|
z.short_name,
|
||||||
z.zoneidnumber,
|
z.zoneidnumber,
|
||||||
@@ -335,23 +320,6 @@ WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LogInfo(
|
|
||||||
"Routed player to non-instance zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
|
||||||
z.short_name,
|
|
||||||
z.zoneidnumber,
|
|
||||||
z.version,
|
|
||||||
z.long_name,
|
|
||||||
z.note
|
|
||||||
);
|
|
||||||
|
|
||||||
return WorldContentService::FindZoneResult{
|
|
||||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
|
||||||
.instance = InstanceListRepository::NewEntity(),
|
|
||||||
.zone = z
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return WorldContentService::FindZoneResult{.zone_id = 0};
|
return WorldContentService::FindZoneResult{.zone_id = 0};
|
||||||
@@ -359,7 +327,7 @@ WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_id
|
|||||||
|
|
||||||
bool WorldContentService::IsInPublicStaticInstance(uint32 instance_id)
|
bool WorldContentService::IsInPublicStaticInstance(uint32 instance_id)
|
||||||
{
|
{
|
||||||
for (auto &i: m_zone_instances) {
|
for (auto &i: m_zone_static_instances) {
|
||||||
if (i.id == instance_id) {
|
if (i.id == instance_id) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -367,3 +335,15 @@ bool WorldContentService::IsInPublicStaticInstance(uint32 instance_id)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WorldContentService::DoesZonePassContentFiltering(const ZoneRepository::Zone &z)
|
||||||
|
{
|
||||||
|
auto f = ContentFlags{
|
||||||
|
.min_expansion = z.min_expansion,
|
||||||
|
.max_expansion = z.max_expansion,
|
||||||
|
.content_flags = z.content_flags,
|
||||||
|
.content_flags_disabled = z.content_flags_disabled
|
||||||
|
};
|
||||||
|
|
||||||
|
return DoesPassContentFiltering(f);
|
||||||
|
}
|
||||||
|
|||||||
@@ -160,6 +160,7 @@ public:
|
|||||||
WorldContentService * SetExpansionContext();
|
WorldContentService * SetExpansionContext();
|
||||||
|
|
||||||
bool DoesPassContentFiltering(const ContentFlags& f);
|
bool DoesPassContentFiltering(const ContentFlags& f);
|
||||||
|
bool DoesZonePassContentFiltering(const ZoneRepository::Zone& z);
|
||||||
|
|
||||||
WorldContentService * SetDatabase(Database *database);
|
WorldContentService * SetDatabase(Database *database);
|
||||||
Database *GetDatabase() const;
|
Database *GetDatabase() const;
|
||||||
@@ -189,10 +190,8 @@ private:
|
|||||||
Database *m_content_database;
|
Database *m_content_database;
|
||||||
|
|
||||||
// holds a record of the zone table from the database
|
// holds a record of the zone table from the database
|
||||||
std::vector<ZoneRepository::Zone> m_zones = {};
|
|
||||||
WorldContentService *LoadStaticGlobalZoneInstances();
|
WorldContentService *LoadStaticGlobalZoneInstances();
|
||||||
std::vector<InstanceListRepository::InstanceList> m_zone_instances;
|
std::vector<InstanceListRepository::InstanceList> m_zone_static_instances;
|
||||||
WorldContentService * LoadZones();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern WorldContentService content_service;
|
extern WorldContentService content_service;
|
||||||
|
|||||||
+48
-11
@@ -66,6 +66,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "database.h"
|
#include "database.h"
|
||||||
|
#include "data_verification.h"
|
||||||
#include "eq_packet_structs.h"
|
#include "eq_packet_structs.h"
|
||||||
#include "extprofile.h"
|
#include "extprofile.h"
|
||||||
#include "strings.h"
|
#include "strings.h"
|
||||||
@@ -78,6 +79,7 @@
|
|||||||
#include "repositories/merchantlist_temp_repository.h"
|
#include "repositories/merchantlist_temp_repository.h"
|
||||||
#include "repositories/bot_data_repository.h"
|
#include "repositories/bot_data_repository.h"
|
||||||
#include "repositories/trader_repository.h"
|
#include "repositories/trader_repository.h"
|
||||||
|
#include "repositories/buyer_repository.h"
|
||||||
|
|
||||||
extern Client client;
|
extern Client client;
|
||||||
|
|
||||||
@@ -283,16 +285,31 @@ bool Database::SetAccountStatus(const std::string& account_name, int16 status)
|
|||||||
|
|
||||||
bool Database::ReserveName(uint32 account_id, const std::string& name)
|
bool Database::ReserveName(uint32 account_id, const std::string& name)
|
||||||
{
|
{
|
||||||
const auto& l = CharacterDataRepository::GetWhere(
|
const std::string& where_filter = fmt::format(
|
||||||
*this,
|
|
||||||
fmt::format(
|
|
||||||
"`name` = '{}'",
|
"`name` = '{}'",
|
||||||
Strings::Escape(name)
|
Strings::Escape(name)
|
||||||
)
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!l.empty()) {
|
if (RuleB(Bots, Enabled)) {
|
||||||
LogInfo("Account: [{}] tried to request name: [{}], but it is already taken", account_id, name);
|
const auto& b = BotDataRepository::GetWhere(*this, where_filter);
|
||||||
|
|
||||||
|
if (!b.empty()) {
|
||||||
|
LogInfo("Account [{}] requested name [{}] but name is already taken by a bot", account_id, name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& c = CharacterDataRepository::GetWhere(*this, where_filter);
|
||||||
|
|
||||||
|
if (!c.empty()) {
|
||||||
|
LogInfo("Account [{}] requested name [{}] but name is already taken by a character", account_id, name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& n = NpcTypesRepository::GetWhere(*this, where_filter);
|
||||||
|
|
||||||
|
if (!n.empty()) {
|
||||||
|
LogInfo("Account [{}] requested name [{}] but name is already taken by an NPC", account_id, name);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -307,13 +324,15 @@ bool Database::ReserveName(uint32 account_id, const std::string& name)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int guild_id = RuleI(Character, DefaultGuild);
|
const uint32 guild_id = RuleI(Character, DefaultGuild);
|
||||||
|
const uint8 guild_rank = EQ::Clamp(RuleI(Character, DefaultGuildRank), 0, 8);
|
||||||
if (guild_id != 0) {
|
if (guild_id != 0) {
|
||||||
if (e.id) {
|
if (e.id) {
|
||||||
auto g = GuildMembersRepository::NewEntity();
|
auto g = GuildMembersRepository::NewEntity();
|
||||||
|
|
||||||
g.char_id = e.id;
|
g.char_id = e.id;
|
||||||
g.guild_id = guild_id;
|
g.guild_id = guild_id;
|
||||||
|
g.rank_ = guild_rank;
|
||||||
|
|
||||||
GuildMembersRepository::InsertOne(*this, g);
|
GuildMembersRepository::InsertOne(*this, g);
|
||||||
}
|
}
|
||||||
@@ -1629,16 +1648,29 @@ uint32 Database::GetGuildIDByCharID(uint32 character_id)
|
|||||||
|
|
||||||
uint32 Database::GetGroupIDByCharID(uint32 character_id)
|
uint32 Database::GetGroupIDByCharID(uint32 character_id)
|
||||||
{
|
{
|
||||||
const auto& e = GroupIdRepository::FindOne(*this, character_id);
|
const auto& e = GroupIdRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`character_id` = {}",
|
||||||
|
character_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
return e.character_id ? e.group_id : 0;
|
return e.size() == 1 ? e.front().group_id : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 Database::GetRaidIDByCharID(uint32 character_id)
|
uint32 Database::GetRaidIDByCharID(uint32 character_id)
|
||||||
{
|
{
|
||||||
const auto& e = RaidMembersRepository::FindOne(*this, character_id);
|
|
||||||
|
|
||||||
return e.charid ? e.raidid : 0;
|
const auto& e = RaidMembersRepository::GetWhere(
|
||||||
|
*this,
|
||||||
|
fmt::format(
|
||||||
|
"`charid` = {}",
|
||||||
|
character_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return e.size() == 1 ? e.front().raidid : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int64 Database::CountInvSnapshots()
|
int64 Database::CountInvSnapshots()
|
||||||
@@ -2110,3 +2142,8 @@ void Database::ClearTraderDetails()
|
|||||||
{
|
{
|
||||||
TraderRepository::Truncate(*this);
|
TraderRepository::Truncate(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Database::ClearBuyerDetails()
|
||||||
|
{
|
||||||
|
BuyerRepository::DeleteBuyer(*this, 0);
|
||||||
|
}
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ public:
|
|||||||
void PurgeAllDeletedDataBuckets();
|
void PurgeAllDeletedDataBuckets();
|
||||||
void ClearGuildOnlineStatus();
|
void ClearGuildOnlineStatus();
|
||||||
void ClearTraderDetails();
|
void ClearTraderDetails();
|
||||||
|
void ClearBuyerDetails();
|
||||||
|
|
||||||
|
|
||||||
/* Database Variables */
|
/* Database Variables */
|
||||||
|
|||||||
@@ -4947,7 +4947,7 @@ UPDATE `aa_ability` SET `auto_grant_enabled` = 1 WHERE `grant_only` = 0 AND `cha
|
|||||||
.version = 9237,
|
.version = 9237,
|
||||||
.description = "2023_10_15_import_13th_floor.sql",
|
.description = "2023_10_15_import_13th_floor.sql",
|
||||||
.check = "SHOW COLUMNS FROM `items` LIKE 'bardeffect';",
|
.check = "SHOW COLUMNS FROM `items` LIKE 'bardeffect';",
|
||||||
.condition = "contains",
|
.condition = "missing",
|
||||||
.match = "mediumint",
|
.match = "mediumint",
|
||||||
.sql = R"(
|
.sql = R"(
|
||||||
ALTER TABLE `items`
|
ALTER TABLE `items`
|
||||||
@@ -5660,6 +5660,92 @@ ALTER TABLE `trader`
|
|||||||
DROP PRIMARY KEY,
|
DROP PRIMARY KEY,
|
||||||
ADD PRIMARY KEY (`id`),
|
ADD PRIMARY KEY (`id`),
|
||||||
ADD INDEX `charid_slotid` (`char_id`, `slot_id`);
|
ADD INDEX `charid_slotid` (`char_id`, `slot_id`);
|
||||||
|
)"
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9281,
|
||||||
|
.description = "2024_06_24_update_buyer_support.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `buyer` LIKE 'id'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `buyer`
|
||||||
|
ADD COLUMN `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
|
||||||
|
CHANGE COLUMN `charid` `char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `id`,
|
||||||
|
ADD COLUMN `char_entity_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_id`,
|
||||||
|
ADD COLUMN `char_name` VARCHAR(64) NULL DEFAULT NULL AFTER `char_entity_id`,
|
||||||
|
ADD COLUMN `char_zone_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_name`,
|
||||||
|
ADD COLUMN `char_zone_instance_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_zone_id`,
|
||||||
|
ADD COLUMN `transaction_date` DATETIME NULL DEFAULT NULL AFTER `char_zone_instance_id`,
|
||||||
|
ADD COLUMN `welcome_message` VARCHAR(256) NULL DEFAULT NULL AFTER `transaction_date`,
|
||||||
|
DROP COLUMN `buyslot`,
|
||||||
|
DROP COLUMN `itemid`,
|
||||||
|
DROP COLUMN `itemname`,
|
||||||
|
DROP COLUMN `quantity`,
|
||||||
|
DROP COLUMN `price`,
|
||||||
|
DROP PRIMARY KEY,
|
||||||
|
ADD PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
ADD INDEX `charid` (`char_id`);
|
||||||
|
|
||||||
|
CREATE TABLE `buyer_buy_lines` (
|
||||||
|
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`buyer_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`buy_slot_id` INT(11) NOT NULL DEFAULT '0',
|
||||||
|
`item_id` INT(11) NOT NULL DEFAULT '0',
|
||||||
|
`item_qty` INT(11) NOT NULL DEFAULT '0',
|
||||||
|
`item_price` INT(11) NOT NULL DEFAULT '0',
|
||||||
|
`item_icon` INT(11) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`item_name` VARCHAR(64) NOT NULL DEFAULT '' COLLATE 'latin1_swedish_ci',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
INDEX `buyerid_charid_buyslotid` (`buyer_id`, `char_id`, `buy_slot_id`) USING BTREE
|
||||||
|
)
|
||||||
|
COLLATE='latin1_swedish_ci'
|
||||||
|
ENGINE=InnoDB
|
||||||
|
AUTO_INCREMENT=1;
|
||||||
|
|
||||||
|
CREATE TABLE `buyer_trade_items` (
|
||||||
|
`id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`buyer_buy_lines_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT '0',
|
||||||
|
`item_id` INT(11) NOT NULL DEFAULT '0',
|
||||||
|
`item_qty` INT(11) NOT NULL DEFAULT '0',
|
||||||
|
`item_icon` INT(11) NOT NULL DEFAULT '0',
|
||||||
|
`item_name` VARCHAR(64) NOT NULL DEFAULT '0' COLLATE 'latin1_swedish_ci',
|
||||||
|
PRIMARY KEY (`id`) USING BTREE,
|
||||||
|
INDEX `buyerbuylinesid` (`buyer_buy_lines_id`) USING BTREE
|
||||||
|
)
|
||||||
|
COLLATE='latin1_swedish_ci'
|
||||||
|
ENGINE=InnoDB
|
||||||
|
AUTO_INCREMENT=1;
|
||||||
|
)"
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9282,
|
||||||
|
.description = "2024_08_02_spell_buckets_comparison.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `spell_buckets` LIKE 'bucket_comparison'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `spell_buckets`
|
||||||
|
CHANGE COLUMN `spellid` `spell_id` int UNSIGNED NOT NULL FIRST,
|
||||||
|
CHANGE COLUMN `key` `bucket_name` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `spell_id`,
|
||||||
|
CHANGE COLUMN `value` `bucket_value` varchar(100) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `bucket_name`,
|
||||||
|
ADD COLUMN `bucket_comparison` tinyint UNSIGNED NOT NULL DEFAULT 0 AFTER `bucket_value`,
|
||||||
|
DROP PRIMARY KEY,
|
||||||
|
ADD PRIMARY KEY (`spell_id`) USING BTREE;
|
||||||
|
)"
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9283,
|
||||||
|
.description = "2024_08_05_fix_client_hotbar",
|
||||||
|
.check = "SHOW COLUMNS FROM `inventory` LIKE 'guid'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `inventory`
|
||||||
|
ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`;
|
||||||
|
ALTER TABLE `inventory_snapshots`
|
||||||
|
ADD COLUMN `guid` BIGINT UNSIGNED NULL DEFAULT '0' AFTER `ornament_hero_model`;
|
||||||
)"
|
)"
|
||||||
}
|
}
|
||||||
// -- template; copy/paste this when you need to create a new entry
|
// -- template; copy/paste this when you need to create a new entry
|
||||||
|
|||||||
@@ -150,6 +150,17 @@ ADD COLUMN `augment_six` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five
|
|||||||
.sql = R"(
|
.sql = R"(
|
||||||
ALTER TABLE `bot_data`
|
ALTER TABLE `bot_data`
|
||||||
ADD COLUMN `extra_haste` mediumint(8) NOT NULL DEFAULT 0 AFTER `wis`;
|
ADD COLUMN `extra_haste` mediumint(8) NOT NULL DEFAULT 0 AFTER `wis`;
|
||||||
|
)"
|
||||||
|
},
|
||||||
|
ManifestEntry{
|
||||||
|
.version = 9045,
|
||||||
|
.description = "2024_08_05_bot_spells_entries_unsigned_spell_id.sql",
|
||||||
|
.check = "SHOW COLUMNS FROM `bot_spells_entries` LIKE 'spell_id'",
|
||||||
|
.condition = "empty",
|
||||||
|
.match = "",
|
||||||
|
.sql = R"(
|
||||||
|
ALTER TABLE `bot_spells_entries`
|
||||||
|
CHANGE COLUMN `spellid` `spell_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_spells_id`;
|
||||||
)"
|
)"
|
||||||
}
|
}
|
||||||
// -- template; copy/paste this when you need to create a new entry
|
// -- template; copy/paste this when you need to create a new entry
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ namespace DatabaseSchema {
|
|||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
{"adventure_stats", "player_id"},
|
{"adventure_stats", "player_id"},
|
||||||
{"buyer", "charid"},
|
|
||||||
{"char_recipe_list", "char_id"},
|
{"char_recipe_list", "char_id"},
|
||||||
{"character_activities", "charid"},
|
{"character_activities", "charid"},
|
||||||
{"character_alt_currency", "char_id"},
|
{"character_alt_currency", "char_id"},
|
||||||
@@ -107,6 +106,8 @@ namespace DatabaseSchema {
|
|||||||
"adventure_details",
|
"adventure_details",
|
||||||
"adventure_stats",
|
"adventure_stats",
|
||||||
"buyer",
|
"buyer",
|
||||||
|
"buyer_buy_lines",
|
||||||
|
"buyer_trade_items",
|
||||||
"char_recipe_list",
|
"char_recipe_list",
|
||||||
"character_activities",
|
"character_activities",
|
||||||
"character_alt_currency",
|
"character_alt_currency",
|
||||||
@@ -325,6 +326,9 @@ namespace DatabaseSchema {
|
|||||||
"banned_ips",
|
"banned_ips",
|
||||||
"bug_reports",
|
"bug_reports",
|
||||||
"bugs",
|
"bugs",
|
||||||
|
"buyer",
|
||||||
|
"buyer_buy_lines",
|
||||||
|
"buyer_trade_items",
|
||||||
"completed_shared_task_activity_state",
|
"completed_shared_task_activity_state",
|
||||||
"completed_shared_task_members",
|
"completed_shared_task_members",
|
||||||
"completed_shared_tasks",
|
"completed_shared_tasks",
|
||||||
|
|||||||
@@ -80,39 +80,19 @@ bool Bug::IsValid(uint32 category_id)
|
|||||||
return bug_category_names.find(category_id) != bug_category_names.end();
|
return bug_category_names.find(category_id) != bug_category_names.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *EQ::constants::GetStanceName(StanceType stance_type) {
|
std::string Stance::GetName(uint8 stance_id)
|
||||||
switch (stance_type) {
|
{
|
||||||
case stanceUnknown:
|
return IsValid(stance_id) ? stance_names[stance_id] : "UNKNOWN STANCE";
|
||||||
return "Unknown";
|
|
||||||
case stancePassive:
|
|
||||||
return "Passive";
|
|
||||||
case stanceBalanced:
|
|
||||||
return "Balanced";
|
|
||||||
case stanceEfficient:
|
|
||||||
return "Efficient";
|
|
||||||
case stanceReactive:
|
|
||||||
return "Reactive";
|
|
||||||
case stanceAggressive:
|
|
||||||
return "Aggressive";
|
|
||||||
case stanceAssist:
|
|
||||||
return "Assist";
|
|
||||||
case stanceBurn:
|
|
||||||
return "Burn";
|
|
||||||
case stanceEfficient2:
|
|
||||||
return "Efficient2";
|
|
||||||
case stanceBurnAE:
|
|
||||||
return "BurnAE";
|
|
||||||
default:
|
|
||||||
return "Invalid";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int EQ::constants::ConvertStanceTypeToIndex(StanceType stance_type) {
|
bool Stance::IsValid(uint8 stance_id)
|
||||||
if (EQ::ValueWithin(stance_type, EQ::constants::stancePassive, EQ::constants::stanceBurnAE)) {
|
{
|
||||||
return (stance_type - EQ::constants::stancePassive);
|
return stance_names.find(stance_id) != stance_names.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
uint8 Stance::GetIndex(uint8 stance_id)
|
||||||
|
{
|
||||||
|
return IsValid(stance_id) ? (stance_id - Stance::Passive) : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::map<uint8, std::string>& EQ::constants::GetLanguageMap()
|
const std::map<uint8, std::string>& EQ::constants::GetLanguageMap()
|
||||||
|
|||||||
+36
-21
@@ -274,19 +274,6 @@ namespace EQ
|
|||||||
const size_t SAY_LINK_CLOSER_SIZE = 1;
|
const size_t SAY_LINK_CLOSER_SIZE = 1;
|
||||||
const size_t SAY_LINK_MAXIMUM_SIZE = (SAY_LINK_OPENER_SIZE + SAY_LINK_BODY_SIZE + SAY_LINK_TEXT_SIZE + SAY_LINK_CLOSER_SIZE);
|
const size_t SAY_LINK_MAXIMUM_SIZE = (SAY_LINK_OPENER_SIZE + SAY_LINK_BODY_SIZE + SAY_LINK_TEXT_SIZE + SAY_LINK_CLOSER_SIZE);
|
||||||
|
|
||||||
enum StanceType : int {
|
|
||||||
stanceUnknown = 0,
|
|
||||||
stancePassive,
|
|
||||||
stanceBalanced,
|
|
||||||
stanceEfficient,
|
|
||||||
stanceReactive,
|
|
||||||
stanceAggressive,
|
|
||||||
stanceAssist,
|
|
||||||
stanceBurn,
|
|
||||||
stanceEfficient2,
|
|
||||||
stanceBurnAE
|
|
||||||
};
|
|
||||||
|
|
||||||
enum BotSpellIDs : int {
|
enum BotSpellIDs : int {
|
||||||
Warrior = 3001,
|
Warrior = 3001,
|
||||||
Cleric,
|
Cleric,
|
||||||
@@ -362,9 +349,6 @@ namespace EQ
|
|||||||
Proximity
|
Proximity
|
||||||
};
|
};
|
||||||
|
|
||||||
const char *GetStanceName(StanceType stance_type);
|
|
||||||
int ConvertStanceTypeToIndex(StanceType stance_type);
|
|
||||||
|
|
||||||
extern const std::map<uint8, std::string>& GetLanguageMap();
|
extern const std::map<uint8, std::string>& GetLanguageMap();
|
||||||
std::string GetLanguageName(uint8 language_id);
|
std::string GetLanguageName(uint8 language_id);
|
||||||
|
|
||||||
@@ -401,10 +385,6 @@ namespace EQ
|
|||||||
extern const std::map<uint32, std::string>& GetConsiderColorMap();
|
extern const std::map<uint32, std::string>& GetConsiderColorMap();
|
||||||
std::string GetConsiderColorName(uint32 consider_color);
|
std::string GetConsiderColorName(uint32 consider_color);
|
||||||
|
|
||||||
const int STANCE_TYPE_FIRST = stancePassive;
|
|
||||||
const int STANCE_TYPE_LAST = stanceBurnAE;
|
|
||||||
const int STANCE_TYPE_COUNT = stanceBurnAE;
|
|
||||||
|
|
||||||
} /*constants*/
|
} /*constants*/
|
||||||
|
|
||||||
namespace profile {
|
namespace profile {
|
||||||
@@ -471,7 +451,7 @@ namespace EQ
|
|||||||
Raid,
|
Raid,
|
||||||
Guild
|
Guild
|
||||||
};
|
};
|
||||||
}; // namespace consent
|
};
|
||||||
} /*EQEmu*/
|
} /*EQEmu*/
|
||||||
|
|
||||||
enum ServerLockType : int {
|
enum ServerLockType : int {
|
||||||
@@ -741,4 +721,39 @@ static std::map<uint32, std::string> bug_category_names = {
|
|||||||
{ Bug::Category::Mercenaries, "Mercenaries" }
|
{ Bug::Category::Mercenaries, "Mercenaries" }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace Stance {
|
||||||
|
constexpr uint32 Unknown = 0;
|
||||||
|
constexpr uint32 Passive = 1;
|
||||||
|
constexpr uint32 Balanced = 2;
|
||||||
|
constexpr uint32 Efficient = 3;
|
||||||
|
constexpr uint32 Reactive = 4;
|
||||||
|
constexpr uint32 Aggressive = 5;
|
||||||
|
constexpr uint32 Assist = 6;
|
||||||
|
constexpr uint32 Burn = 7;
|
||||||
|
constexpr uint32 Efficient2 = 8;
|
||||||
|
constexpr uint32 AEBurn = 9;
|
||||||
|
|
||||||
|
std::string GetName(uint8 stance_id);
|
||||||
|
uint8 GetIndex(uint8 stance_id);
|
||||||
|
bool IsValid(uint8 stance_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::map<uint32, std::string> stance_names = {
|
||||||
|
{ Stance::Unknown, "Unknown" },
|
||||||
|
{ Stance::Passive, "Passive" },
|
||||||
|
{ Stance::Balanced, "Balanced" },
|
||||||
|
{ Stance::Efficient, "Efficient" },
|
||||||
|
{ Stance::Reactive, "Reactive" },
|
||||||
|
{ Stance::Aggressive, "Aggressive" },
|
||||||
|
{ Stance::Assist, "Assist" },
|
||||||
|
{ Stance::Burn, "Burn" },
|
||||||
|
{ Stance::Efficient2, "Efficient" },
|
||||||
|
{ Stance::AEBurn, "AE Burn" }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace PCNPCOnlyFlagType {
|
||||||
|
constexpr int PC = 1;
|
||||||
|
constexpr int NPC = 2;
|
||||||
|
}
|
||||||
|
|
||||||
#endif /*COMMON_EMU_CONSTANTS_H*/
|
#endif /*COMMON_EMU_CONSTANTS_H*/
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ N(OP_Buff),
|
|||||||
N(OP_BuffCreate),
|
N(OP_BuffCreate),
|
||||||
N(OP_BuffRemoveRequest),
|
N(OP_BuffRemoveRequest),
|
||||||
N(OP_Bug),
|
N(OP_Bug),
|
||||||
|
N(OP_BuyerItems),
|
||||||
N(OP_CameraEffect),
|
N(OP_CameraEffect),
|
||||||
N(OP_Camp),
|
N(OP_Camp),
|
||||||
N(OP_CancelSneakHide),
|
N(OP_CancelSneakHide),
|
||||||
|
|||||||
@@ -758,10 +758,10 @@ typedef enum {
|
|||||||
FilterFocusEffects = 22, //0=show, 1=hide
|
FilterFocusEffects = 22, //0=show, 1=hide
|
||||||
FilterPetSpells = 23, //0=show, 1=hide
|
FilterPetSpells = 23, //0=show, 1=hide
|
||||||
FilterHealOverTime = 24, //0=show, 1=mine only, 2=hide
|
FilterHealOverTime = 24, //0=show, 1=mine only, 2=hide
|
||||||
FilterUnknown25 = 25,
|
FilterItemSpeech = 25, //0=show, 1=hide // RoF2 Confirmed
|
||||||
FilterUnknown26 = 26,
|
FilterStrikethrough = 26, //0=show, 1=hide // RoF2 Confirmed
|
||||||
FilterUnknown27 = 27,
|
FilterStuns = 27, //0=show, 1=hide // RoF2 Confirmed
|
||||||
FilterUnknown28 = 28,
|
FilterBardSongsOnPets = 28, //0=show, 1=hide // RoF2 Confirmed
|
||||||
_FilterCount
|
_FilterCount
|
||||||
} eqFilterType;
|
} eqFilterType;
|
||||||
|
|
||||||
@@ -1129,4 +1129,7 @@ enum ExpSource
|
|||||||
#define PARCEL_LIMIT 5
|
#define PARCEL_LIMIT 5
|
||||||
#define PARCEL_BEGIN_SLOT 1
|
#define PARCEL_BEGIN_SLOT 1
|
||||||
|
|
||||||
|
namespace DoorType {
|
||||||
|
constexpr uint32 BuyerStall = 155;
|
||||||
|
}
|
||||||
#endif /*COMMON_EQ_CONSTANTS_H*/
|
#endif /*COMMON_EQ_CONSTANTS_H*/
|
||||||
|
|||||||
+367
-48
@@ -323,6 +323,7 @@ union
|
|||||||
bool show_name;
|
bool show_name;
|
||||||
bool guild_show;
|
bool guild_show;
|
||||||
bool trader;
|
bool trader;
|
||||||
|
bool buyer;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PlayerState_Struct {
|
struct PlayerState_Struct {
|
||||||
@@ -1620,6 +1621,32 @@ struct MoveItem_Struct
|
|||||||
/*0012*/
|
/*0012*/
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// New for RoF2 - Size: 12
|
||||||
|
struct InventorySlot_Struct
|
||||||
|
{
|
||||||
|
/*000*/ int16 Type; // Worn and Normal inventory = 0, Bank = 1, Shared Bank = 2, Delete Item = -1
|
||||||
|
/*002*/ int16 Unknown02;
|
||||||
|
/*004*/ int16 Slot;
|
||||||
|
/*006*/ int16 SubIndex;
|
||||||
|
/*008*/ int16 AugIndex; // Guessing - Seen 0xffff
|
||||||
|
/*010*/ int16 Unknown01; // Normally 0 - Seen 13262 when deleting an item, but didn't match item ID
|
||||||
|
/*012*/
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MultiMoveItemSub_Struct
|
||||||
|
{
|
||||||
|
/*0000*/ InventorySlot_Struct from_slot;
|
||||||
|
/*0012*/ InventorySlot_Struct to_slot;
|
||||||
|
/*0024*/ uint32 number_in_stack;
|
||||||
|
/*0028*/ uint8 unknown[8];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MultiMoveItem_Struct
|
||||||
|
{
|
||||||
|
/*0000*/ uint32 count;
|
||||||
|
/*0004*/ MultiMoveItemSub_Struct moves[0];
|
||||||
|
};
|
||||||
|
|
||||||
// both MoveItem_Struct/DeleteItem_Struct server structures will be changing to a structure-based slot format..this will
|
// both MoveItem_Struct/DeleteItem_Struct server structures will be changing to a structure-based slot format..this will
|
||||||
// be used for handling SoF/SoD/etc... time stamps sent using the MoveItem_Struct format. (nothing will be done with this
|
// be used for handling SoF/SoD/etc... time stamps sent using the MoveItem_Struct format. (nothing will be done with this
|
||||||
// info at the moment..but, it is forwarded on to the server for handling/future use)
|
// info at the moment..but, it is forwarded on to the server for handling/future use)
|
||||||
@@ -3113,11 +3140,14 @@ struct BazaarSearchResults_Struct {
|
|||||||
// Barter/Buyer
|
// Barter/Buyer
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
enum {
|
#define MAX_BUYER_COMPENSATION_ITEMS 10
|
||||||
|
|
||||||
|
enum BarterBuyerActions {
|
||||||
Barter_BuyerSearch = 0,
|
Barter_BuyerSearch = 0,
|
||||||
Barter_SellerSearch = 1,
|
Barter_SellerSearch = 1,
|
||||||
Barter_BuyerModeOn = 2,
|
Barter_BuyerModeOn = 2,
|
||||||
Barter_BuyerModeOff = 3,
|
Barter_BuyerModeOff = 3,
|
||||||
|
Barter_BuyerItemStart = 4,
|
||||||
Barter_BuyerItemUpdate = 5,
|
Barter_BuyerItemUpdate = 5,
|
||||||
Barter_BuyerItemRemove = 6,
|
Barter_BuyerItemRemove = 6,
|
||||||
Barter_SellItem = 7,
|
Barter_SellItem = 7,
|
||||||
@@ -3132,41 +3162,347 @@ enum {
|
|||||||
Barter_BuyerSearchResults = 16,
|
Barter_BuyerSearchResults = 16,
|
||||||
Barter_Welcome = 17,
|
Barter_Welcome = 17,
|
||||||
Barter_WelcomeMessageUpdate = 19,
|
Barter_WelcomeMessageUpdate = 19,
|
||||||
|
Barter_Greeting = 20,
|
||||||
Barter_BuyerItemInspect = 21,
|
Barter_BuyerItemInspect = 21,
|
||||||
Barter_Unknown23 = 23
|
Barter_OpenBarterWindow = 23,
|
||||||
|
Barter_AddToBarterWindow = 26,
|
||||||
|
Barter_RemoveFromBarterWindow = 27,
|
||||||
|
Barter_RemoveFromMerchantWindow = 50, //Not a client item. Used for internal communications.
|
||||||
|
Barter_FailedTransaction = 51,
|
||||||
|
Barter_BuyerCouldNotBeFound = 52,
|
||||||
|
Barter_FailedBuyerChecks = 53,
|
||||||
|
Barter_SellerCouldNotBeFound = 54,
|
||||||
|
Barter_FailedSellerChecks = 55
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BarterBuyerSubActions {
|
||||||
|
Barter_Success = 0,
|
||||||
|
Barter_Failure = 1,
|
||||||
|
Barter_DataOutOfDate = 4,
|
||||||
|
Barter_SellerDoesNotHaveItem = 6,
|
||||||
|
Barter_SameZone = 8
|
||||||
|
};
|
||||||
|
|
||||||
|
enum BuyerBarter {
|
||||||
|
Off = 0,
|
||||||
|
On = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerRemoveItem_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 buy_slot_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerRemoveItemFromMerchantWindow_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 unknown_004;
|
||||||
|
uint32 buy_slot_id;
|
||||||
|
uint32 unknown_012;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerGeneric_Struct {
|
||||||
|
uint32 action;
|
||||||
|
char payload[];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerMessaging_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 sub_action;
|
||||||
|
uint32 zone_id;
|
||||||
|
uint32 buyer_id;
|
||||||
|
uint32 buyer_entity_id;
|
||||||
|
char buyer_name[64];
|
||||||
|
uint32 buy_item_id;
|
||||||
|
uint32 buy_item_qty;
|
||||||
|
uint64 buy_item_cost;
|
||||||
|
uint32 buy_item_icon;
|
||||||
|
uint32 seller_entity_id;
|
||||||
|
char seller_name[64];
|
||||||
|
char item_name[64];
|
||||||
|
uint32 slot;
|
||||||
|
uint32 seller_quantity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerAddBuyertoBarterWindow_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 zone_id;
|
||||||
|
uint32 buyer_id;
|
||||||
|
uint32 buyer_entity_id;
|
||||||
|
char buyer_name[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerRemoveBuyerFromBarterWindow_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 buyer_id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerBrowsing_Struct {
|
||||||
|
uint32 action;
|
||||||
|
char char_name[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerGreeting_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 buyer_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuyerWelcomeMessageUpdate_Struct {
|
struct BuyerWelcomeMessageUpdate_Struct {
|
||||||
/*000*/ uint32 Action;
|
uint32 action;
|
||||||
/*004*/ char WelcomeMessage[256];
|
char welcome_message[256];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuyerItemSearch_Struct {
|
struct BuyerLineTradeItems_Struct {
|
||||||
/*000*/ uint32 Unknown000;
|
uint32 item_id;
|
||||||
/*004*/ char SearchString[64];
|
uint32 item_quantity;
|
||||||
|
uint32 item_icon;
|
||||||
|
std::string item_name;
|
||||||
|
|
||||||
|
void operator*=(uint32 multiplier)
|
||||||
|
{
|
||||||
|
this->item_quantity *= multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &archive)
|
||||||
|
{
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(item_id),
|
||||||
|
CEREAL_NVP(item_quantity),
|
||||||
|
CEREAL_NVP(item_icon),
|
||||||
|
CEREAL_NVP(item_name)
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BuyerItemSearchResultEntry_Struct {
|
struct BuyerLineItems_Struct {
|
||||||
/*000*/ char ItemName[64];
|
uint32 slot;
|
||||||
/*064*/ uint32 ItemID;
|
uint8 enabled;
|
||||||
/*068*/ uint32 Unknown068;
|
uint32 item_id;
|
||||||
/*072*/ uint32 Unknown072;
|
std::string item_name;
|
||||||
|
uint32 item_icon;
|
||||||
|
uint32 item_quantity;
|
||||||
|
uint8 item_toggle;
|
||||||
|
uint32 item_cost;
|
||||||
|
std::vector<BuyerLineTradeItems_Struct> trade_items;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &archive)
|
||||||
|
{
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(slot),
|
||||||
|
CEREAL_NVP(enabled),
|
||||||
|
CEREAL_NVP(item_id),
|
||||||
|
CEREAL_NVP(item_name),
|
||||||
|
CEREAL_NVP(item_icon),
|
||||||
|
CEREAL_NVP(item_quantity),
|
||||||
|
CEREAL_NVP(item_toggle),
|
||||||
|
CEREAL_NVP(item_cost),
|
||||||
|
CEREAL_NVP(trade_items)
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MAX_BUYER_ITEMSEARCH_RESULTS 200
|
struct BuyerBuyLines_Struct {
|
||||||
|
uint32 action;
|
||||||
|
union {
|
||||||
|
uint32 no_items;
|
||||||
|
uint32 string_length;
|
||||||
|
};
|
||||||
|
std::vector<BuyerLineItems_Struct> buy_lines;
|
||||||
|
|
||||||
struct BuyerItemSearchResults_Struct {
|
template<class Archive>
|
||||||
uint32 Action;
|
void serialize(Archive &archive)
|
||||||
uint32 ResultCount;
|
{
|
||||||
BuyerItemSearchResultEntry_Struct Results[MAX_BUYER_ITEMSEARCH_RESULTS];
|
archive(
|
||||||
|
CEREAL_NVP(action),
|
||||||
|
CEREAL_NVP(no_items),
|
||||||
|
CEREAL_NVP(buy_lines)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerLineSellItem_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 sub_action;
|
||||||
|
uint32 error_code;
|
||||||
|
uint32 purchase_method; // 0 direct merchant, 1 via /barter window
|
||||||
|
uint32 buyer_entity_id;
|
||||||
|
uint32 buyer_id;
|
||||||
|
std::string buyer_name;
|
||||||
|
uint32 seller_entity_id;
|
||||||
|
std::string seller_name;
|
||||||
|
uint32 slot;
|
||||||
|
uint8 enabled;
|
||||||
|
uint32 item_id;
|
||||||
|
char item_name[64];
|
||||||
|
uint32 item_icon;
|
||||||
|
uint32 item_quantity;
|
||||||
|
uint8 item_toggle;
|
||||||
|
uint32 item_cost;
|
||||||
|
uint32 no_trade_items;
|
||||||
|
std::vector<BuyerLineTradeItems_Struct> trade_items;
|
||||||
|
uint32 seller_quantity;
|
||||||
|
uint32 zone_id;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &archive)
|
||||||
|
{
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(action),
|
||||||
|
CEREAL_NVP(sub_action),
|
||||||
|
CEREAL_NVP(error_code),
|
||||||
|
CEREAL_NVP(purchase_method),
|
||||||
|
CEREAL_NVP(buyer_entity_id),
|
||||||
|
CEREAL_NVP(buyer_id),
|
||||||
|
CEREAL_NVP(buyer_name),
|
||||||
|
CEREAL_NVP(seller_entity_id),
|
||||||
|
CEREAL_NVP(seller_name),
|
||||||
|
CEREAL_NVP(slot),
|
||||||
|
CEREAL_NVP(enabled),
|
||||||
|
CEREAL_NVP(item_id),
|
||||||
|
CEREAL_NVP(item_name),
|
||||||
|
CEREAL_NVP(item_icon),
|
||||||
|
CEREAL_NVP(item_quantity),
|
||||||
|
CEREAL_NVP(item_toggle),
|
||||||
|
CEREAL_NVP(item_cost),
|
||||||
|
CEREAL_NVP(no_trade_items),
|
||||||
|
CEREAL_NVP(trade_items),
|
||||||
|
CEREAL_NVP(seller_quantity),
|
||||||
|
CEREAL_NVP(zone_id)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerLineItemsSearch_Struct {
|
||||||
|
uint32 slot;
|
||||||
|
uint8 enabled;
|
||||||
|
uint32 item_id;
|
||||||
|
char item_name[64];
|
||||||
|
uint32 item_icon;
|
||||||
|
uint32 item_quantity;
|
||||||
|
uint8 item_toggle;
|
||||||
|
uint32 item_cost;
|
||||||
|
uint32 buyer_id;
|
||||||
|
uint32 buyer_entity_id;
|
||||||
|
uint32 buyer_zone_id;
|
||||||
|
uint32 buyer_zone_instance_id;
|
||||||
|
std::string buyer_name;
|
||||||
|
std::vector<BuyerLineTradeItems_Struct> trade_items;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &archive)
|
||||||
|
{
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(slot),
|
||||||
|
CEREAL_NVP(enabled),
|
||||||
|
CEREAL_NVP(item_id),
|
||||||
|
CEREAL_NVP(item_name),
|
||||||
|
CEREAL_NVP(item_icon),
|
||||||
|
CEREAL_NVP(item_quantity),
|
||||||
|
CEREAL_NVP(item_toggle),
|
||||||
|
CEREAL_NVP(item_cost),
|
||||||
|
CEREAL_NVP(buyer_id),
|
||||||
|
CEREAL_NVP(buyer_entity_id),
|
||||||
|
CEREAL_NVP(buyer_zone_id),
|
||||||
|
CEREAL_NVP(buyer_zone_instance_id),
|
||||||
|
CEREAL_NVP(buyer_name),
|
||||||
|
CEREAL_NVP(trade_items)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerLineSearch_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 no_items;
|
||||||
|
std::string search_string;
|
||||||
|
uint32 transaction_id;
|
||||||
|
std::vector<BuyerLineItemsSearch_Struct> buy_line;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &archive)
|
||||||
|
{
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(action),
|
||||||
|
CEREAL_NVP(no_items),
|
||||||
|
CEREAL_NVP(search_string),
|
||||||
|
CEREAL_NVP(transaction_id),
|
||||||
|
CEREAL_NVP(buy_line)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerSetAppearance_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 entity_id;
|
||||||
|
uint32 status; // 0 off 1 on
|
||||||
|
char buyer_name[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BarterItemSearchLinkRequest_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 searcher_id;
|
||||||
|
uint32 unknown_008;
|
||||||
|
uint32 unknown_012;
|
||||||
|
uint32 item_id;
|
||||||
|
uint32 unknown_020;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerInspectRequest_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 buyer_id;
|
||||||
|
uint32 approval;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BarterSearchRequest_Struct {
|
struct BarterSearchRequest_Struct {
|
||||||
uint32 Action;
|
uint32 action;
|
||||||
char SearchString[64];
|
char search_string[64];
|
||||||
uint32 SearchID;
|
uint32 transaction_id;
|
||||||
|
uint32 unknown_072;
|
||||||
|
uint32 buyer_id;
|
||||||
|
uint8 search_scope; //0 All Buyers, 1 Local Buyers
|
||||||
|
uint16 zone_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BuyerItemSearch_Struct {
|
||||||
|
uint32 action;
|
||||||
|
char search_string[64];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerItemSearchResultEntry_Struct {
|
||||||
|
char item_name[64];
|
||||||
|
uint32 item_id;
|
||||||
|
uint32 item_icon;
|
||||||
|
uint32 unknown_072;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &archive)
|
||||||
|
{
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(item_name),
|
||||||
|
CEREAL_NVP(item_id),
|
||||||
|
CEREAL_NVP(item_icon),
|
||||||
|
CEREAL_NVP(unknown_072)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerItemSearchResults_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 result_count;
|
||||||
|
std::vector<BuyerItemSearchResultEntry_Struct> results;
|
||||||
|
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &archive)
|
||||||
|
{
|
||||||
|
archive(
|
||||||
|
CEREAL_NVP(action),
|
||||||
|
CEREAL_NVP(result_count),
|
||||||
|
CEREAL_NVP(results)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//old below here
|
||||||
struct BuyerItemSearchLinkRequest_Struct {
|
struct BuyerItemSearchLinkRequest_Struct {
|
||||||
/*000*/ uint32 Action; // 0x00000015
|
/*000*/ uint32 Action; // 0x00000015
|
||||||
/*004*/ uint32 ItemID;
|
/*004*/ uint32 ItemID;
|
||||||
@@ -3174,31 +3510,6 @@ struct BuyerItemSearchLinkRequest_Struct {
|
|||||||
/*012*/ uint32 Unknown012;
|
/*012*/ uint32 Unknown012;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BarterItemSearchLinkRequest_Struct {
|
|
||||||
/*000*/ uint32 Action; // 0x0000000E
|
|
||||||
/*004*/ uint32 SearcherID;
|
|
||||||
/*008*/ uint32 Unknown008;
|
|
||||||
/*012*/ uint32 Unknown012;
|
|
||||||
/*016*/ uint32 ItemID;
|
|
||||||
/*020*/ uint32 Unknown020;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BuyerInspectRequest_Struct {
|
|
||||||
uint32 Action;
|
|
||||||
uint32 BuyerID;
|
|
||||||
uint32 Approval;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BuyerBrowsing_Struct {
|
|
||||||
uint32 Action;
|
|
||||||
char PlayerName[64];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BuyerRemoveItem_Struct {
|
|
||||||
uint32 Action;
|
|
||||||
uint32 BuySlot;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ServerSideFilters_Struct {
|
struct ServerSideFilters_Struct {
|
||||||
uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group
|
uint8 clientattackfilters; // 0) No, 1) All (players) but self, 2) All (players) but group
|
||||||
uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group)
|
uint8 npcattackfilters; // 0) No, 1) Ignore NPC misses (all), 2) Ignore NPC Misses + Attacks (all but self), 3) Ignores NPC Misses + Attacks (all but group)
|
||||||
@@ -6040,9 +6351,12 @@ enum BazaarTraderBarterActions {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum BazaarPurchaseActions {
|
enum BazaarPurchaseActions {
|
||||||
ByVendor = 0,
|
BazaarByVendor = 0,
|
||||||
ByParcel = 1,
|
BazaarByParcel = 1,
|
||||||
ByDirectToInventory = 2
|
BazaarByDirectToInventory = 2,
|
||||||
|
BarterByVendor = 0,
|
||||||
|
BarterInBazaar = 1,
|
||||||
|
BarterOutsideBazaar = 2
|
||||||
};
|
};
|
||||||
|
|
||||||
enum BazaarPurchaseSubActions {
|
enum BazaarPurchaseSubActions {
|
||||||
@@ -6116,6 +6430,11 @@ struct BazaarSearchMessaging_Struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BuylineItemDetails_Struct {
|
||||||
|
uint64 item_cost;
|
||||||
|
uint32 item_quantity;
|
||||||
|
};
|
||||||
|
|
||||||
// Restore structure packing to default
|
// Restore structure packing to default
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
|
||||||
|
|||||||
@@ -706,6 +706,7 @@ void PlayerEventLogs::SetSettingsDefaults()
|
|||||||
m_settings[PlayerEvent::PARCEL_SEND].event_enabled = 1;
|
m_settings[PlayerEvent::PARCEL_SEND].event_enabled = 1;
|
||||||
m_settings[PlayerEvent::PARCEL_RETRIEVE].event_enabled = 1;
|
m_settings[PlayerEvent::PARCEL_RETRIEVE].event_enabled = 1;
|
||||||
m_settings[PlayerEvent::PARCEL_DELETE].event_enabled = 1;
|
m_settings[PlayerEvent::PARCEL_DELETE].event_enabled = 1;
|
||||||
|
m_settings[PlayerEvent::BARTER_TRANSACTION].event_enabled = 1;
|
||||||
|
|
||||||
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) {
|
||||||
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
|
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ namespace PlayerEvent {
|
|||||||
PARCEL_SEND,
|
PARCEL_SEND,
|
||||||
PARCEL_RETRIEVE,
|
PARCEL_RETRIEVE,
|
||||||
PARCEL_DELETE,
|
PARCEL_DELETE,
|
||||||
|
BARTER_TRANSACTION,
|
||||||
MAX // dont remove
|
MAX // dont remove
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -122,7 +123,8 @@ namespace PlayerEvent {
|
|||||||
"Guild Tribute Donate Platinum",
|
"Guild Tribute Donate Platinum",
|
||||||
"Parcel Item Sent",
|
"Parcel Item Sent",
|
||||||
"Parcel Item Retrieved",
|
"Parcel Item Retrieved",
|
||||||
"Parcel Prune Routine"
|
"Parcel Prune Routine",
|
||||||
|
"Barter Transaction"
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic struct used by all events
|
// Generic struct used by all events
|
||||||
@@ -1081,6 +1083,32 @@ namespace PlayerEvent {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct BarterTransaction {
|
||||||
|
std::string status;
|
||||||
|
uint32 item_id;
|
||||||
|
uint32 item_quantity;
|
||||||
|
std::string item_name;
|
||||||
|
std::vector<BuyerLineTradeItems_Struct> trade_items;
|
||||||
|
std::string buyer_name;
|
||||||
|
std::string seller_name;
|
||||||
|
uint64 total_cost;
|
||||||
|
// cereal
|
||||||
|
template<class Archive>
|
||||||
|
void serialize(Archive &ar)
|
||||||
|
{
|
||||||
|
ar(
|
||||||
|
CEREAL_NVP(status),
|
||||||
|
CEREAL_NVP(item_id),
|
||||||
|
CEREAL_NVP(item_quantity),
|
||||||
|
CEREAL_NVP(item_name),
|
||||||
|
CEREAL_NVP(trade_items),
|
||||||
|
CEREAL_NVP(buyer_name),
|
||||||
|
CEREAL_NVP(seller_name),
|
||||||
|
CEREAL_NVP(total_cost)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif //EQEMU_PLAYER_EVENTS_H
|
#endif //EQEMU_PLAYER_EVENTS_H
|
||||||
|
|||||||
@@ -1743,3 +1743,68 @@ std::vector<uint32> EQ::InventoryProfile::GetAugmentIDsBySlotID(int16 slot_id)
|
|||||||
|
|
||||||
return augments;
|
return augments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<int16> EQ::InventoryProfile::FindAllFreeSlotsThatFitItem(const EQ::ItemData *item_data)
|
||||||
|
{
|
||||||
|
std::vector<int16> free_slots{};
|
||||||
|
for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) {
|
||||||
|
if ((((uint64) 1 << i) & GetLookup()->PossessionsBitmask) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
EQ::ItemInstance *inv_item = GetItem(i);
|
||||||
|
|
||||||
|
if (!inv_item) {
|
||||||
|
// Found available slot in personal inventory
|
||||||
|
free_slots.push_back(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inv_item->IsClassBag() &&
|
||||||
|
EQ::InventoryProfile::CanItemFitInContainer(item_data, inv_item->GetItem())) {
|
||||||
|
|
||||||
|
int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(i, EQ::invbag::SLOT_BEGIN);
|
||||||
|
uint8 bag_size = inv_item->GetItem()->BagSlots;
|
||||||
|
|
||||||
|
for (uint8 bag_slot = EQ::invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) {
|
||||||
|
auto bag_item = GetItem(base_slot_id + bag_slot);
|
||||||
|
if (!bag_item) {
|
||||||
|
// Found available slot within bag
|
||||||
|
free_slots.push_back(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return free_slots;
|
||||||
|
}
|
||||||
|
|
||||||
|
int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *item_data)
|
||||||
|
{
|
||||||
|
for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) {
|
||||||
|
if ((((uint64) 1 << i) & GetLookup()->PossessionsBitmask) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
EQ::ItemInstance *inv_item = GetItem(i);
|
||||||
|
|
||||||
|
if (!inv_item) {
|
||||||
|
// Found available slot in personal inventory
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inv_item->IsClassBag() &&
|
||||||
|
EQ::InventoryProfile::CanItemFitInContainer(item_data, inv_item->GetItem())) {
|
||||||
|
|
||||||
|
int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(i, EQ::invbag::SLOT_BEGIN);
|
||||||
|
uint8 bag_size = inv_item->GetItem()->BagSlots;
|
||||||
|
|
||||||
|
for (uint8 bag_slot = EQ::invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) {
|
||||||
|
auto bag_item = GetItem(base_slot_id + bag_slot);
|
||||||
|
if (!bag_item) {
|
||||||
|
// Found available slot within bag
|
||||||
|
return base_slot_id + bag_slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -176,6 +176,8 @@ namespace EQ
|
|||||||
// Locate an available inventory slot
|
// Locate an available inventory slot
|
||||||
int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false);
|
int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false);
|
||||||
int16 FindFreeSlotForTradeItem(const ItemInstance* inst, int16 general_start = invslot::GENERAL_BEGIN, uint8 bag_start = invbag::SLOT_BEGIN);
|
int16 FindFreeSlotForTradeItem(const ItemInstance* inst, int16 general_start = invslot::GENERAL_BEGIN, uint8 bag_start = invbag::SLOT_BEGIN);
|
||||||
|
std::vector<int16> FindAllFreeSlotsThatFitItem(const EQ::ItemData *inst);
|
||||||
|
int16 FindFirstFreeSlotThatFitsItem(const EQ::ItemData *inst);
|
||||||
|
|
||||||
// Calculate slot_id for an item within a bag
|
// Calculate slot_id for an item within a bag
|
||||||
static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id
|
static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id
|
||||||
|
|||||||
+53
-19
@@ -81,45 +81,79 @@ bool IpUtil::IsIpInPrivateRfc1918(const std::string &ip)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets local address - pings google to inspect what interface was used locally
|
#ifdef _WIN32
|
||||||
* @return
|
#include <winsock2.h>
|
||||||
*/
|
#include <ws2tcpip.h>
|
||||||
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
#else
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
std::string IpUtil::GetLocalIPAddress()
|
std::string IpUtil::GetLocalIPAddress()
|
||||||
{
|
{
|
||||||
char my_ip_address[16];
|
#ifdef _WIN32
|
||||||
unsigned int my_port;
|
WSADATA wsaData;
|
||||||
|
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
char my_ip_address[INET_ADDRSTRLEN];
|
||||||
struct sockaddr_in server_address{};
|
struct sockaddr_in server_address{};
|
||||||
struct sockaddr_in my_address{};
|
struct sockaddr_in my_address{};
|
||||||
int sockfd;
|
int sockfd;
|
||||||
|
|
||||||
// Connect to server
|
// Create a UDP socket
|
||||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
#ifdef _WIN32
|
||||||
|
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (sockfd == INVALID_SOCKET) {
|
||||||
|
WSACleanup();
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (sockfd < 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Set server_addr
|
// Set server_addr (dummy address)
|
||||||
memset(&server_address, 0, sizeof(server_address));
|
memset(&server_address, 0, sizeof(server_address));
|
||||||
server_address.sin_family = AF_INET;
|
server_address.sin_family = AF_INET;
|
||||||
server_address.sin_addr.s_addr = inet_addr("172.217.160.99");
|
server_address.sin_addr.s_addr = inet_addr("8.8.8.8"); // Google DNS
|
||||||
server_address.sin_port = htons(80);
|
server_address.sin_port = htons(53); // DNS port
|
||||||
|
|
||||||
// Connect to server
|
// Perform a dummy connection to the server (UDP)
|
||||||
if (connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) {
|
connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address));
|
||||||
close(sockfd);
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get my ip address and port
|
// Get my IP address
|
||||||
memset(&my_address, 0, sizeof(my_address));
|
memset(&my_address, 0, sizeof(my_address));
|
||||||
socklen_t len = sizeof(my_address);
|
socklen_t len = sizeof(my_address);
|
||||||
getsockname(sockfd, (struct sockaddr *) &my_address, &len);
|
getsockname(sockfd, (struct sockaddr *) &my_address, &len);
|
||||||
inet_ntop(AF_INET, &my_address.sin_addr, my_ip_address, sizeof(my_ip_address));
|
inet_ntop(AF_INET, &my_address.sin_addr, my_ip_address, sizeof(my_ip_address));
|
||||||
my_port = ntohs(my_address.sin_port);
|
|
||||||
|
|
||||||
return fmt::format("{}", my_ip_address);
|
#ifdef _WIN32
|
||||||
|
closesocket(sockfd);
|
||||||
|
WSACleanup();
|
||||||
|
#else
|
||||||
|
close(sockfd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
LogInfo("Local IP Address [{}]", my_ip_address);
|
||||||
|
|
||||||
|
return std::string(my_ip_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets public address
|
* Gets public address
|
||||||
* Uses various websites as options to return raw public IP back to the client
|
* Uses various websites as options to return raw public IP back to the client
|
||||||
|
|||||||
@@ -32,10 +32,11 @@
|
|||||||
|
|
||||||
//#include <iostream>
|
//#include <iostream>
|
||||||
|
|
||||||
int32 NextItemInstSerialNumber = 1;
|
int32 next_item_serial_number = 1;
|
||||||
|
std::unordered_set<uint64> guids{};
|
||||||
static inline int32 GetNextItemInstSerialNumber() {
|
|
||||||
|
|
||||||
|
static inline int32 GetNextItemInstSerialNumber()
|
||||||
|
{
|
||||||
// The Bazaar relies on each item a client has up for Trade having a unique
|
// The Bazaar relies on each item a client has up for Trade having a unique
|
||||||
// identifier. This 'SerialNumber' is sent in Serialized item packets and
|
// identifier. This 'SerialNumber' is sent in Serialized item packets and
|
||||||
// is used in Bazaar packets to identify the item a player is buying or inspecting.
|
// is used in Bazaar packets to identify the item a player is buying or inspecting.
|
||||||
@@ -46,12 +47,18 @@ static inline int32 GetNextItemInstSerialNumber() {
|
|||||||
// NextItemInstSerialNumber is the next one to hand out.
|
// NextItemInstSerialNumber is the next one to hand out.
|
||||||
//
|
//
|
||||||
// It is very unlikely to reach 2,147,483,647. Maybe we should call abort(), rather than wrapping back to 1.
|
// It is very unlikely to reach 2,147,483,647. Maybe we should call abort(), rather than wrapping back to 1.
|
||||||
if(NextItemInstSerialNumber >= INT_MAX)
|
if (next_item_serial_number >= INT32_MAX) {
|
||||||
NextItemInstSerialNumber = 1;
|
next_item_serial_number = 1;
|
||||||
else
|
}
|
||||||
NextItemInstSerialNumber++;
|
else {
|
||||||
|
next_item_serial_number++;
|
||||||
|
}
|
||||||
|
|
||||||
return NextItemInstSerialNumber;
|
while (guids.contains(next_item_serial_number)) {
|
||||||
|
next_item_serial_number++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next_item_serial_number;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -1935,6 +1942,15 @@ int EQ::ItemInstance::GetItemSkillsStat(EQ::skills::SkillType skill, bool augmen
|
|||||||
return stat;
|
return stat;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EQ::ItemInstance::AddGUIDToMap(uint64 existing_serial_number)
|
||||||
|
{
|
||||||
|
guids.emplace(existing_serial_number);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EQ::ItemInstance::ClearGUIDMap()
|
||||||
|
{
|
||||||
|
guids.clear();
|
||||||
|
}
|
||||||
//
|
//
|
||||||
// class EvolveInfo
|
// class EvolveInfo
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -309,6 +309,8 @@ namespace EQ
|
|||||||
int GetItemSkillsStat(EQ::skills::SkillType skill, bool augments = false) const;
|
int GetItemSkillsStat(EQ::skills::SkillType skill, bool augments = false) const;
|
||||||
uint32 GetItemGuildFavor() const;
|
uint32 GetItemGuildFavor() const;
|
||||||
std::vector<uint32> GetAugmentIDs() const;
|
std::vector<uint32> GetAugmentIDs() const;
|
||||||
|
static void AddGUIDToMap(uint64 existing_serial_number);
|
||||||
|
static void ClearGUIDMap();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|||||||
+512
-21
@@ -359,38 +359,88 @@ namespace RoF2
|
|||||||
EQApplicationPacket *in = *p;
|
EQApplicationPacket *in = *p;
|
||||||
*p = nullptr;
|
*p = nullptr;
|
||||||
|
|
||||||
char *Buffer = (char *)in->pBuffer;
|
char *buffer = (char *) in->pBuffer;
|
||||||
|
uint32 sub_action = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
|
||||||
uint32 SubAction = VARSTRUCT_DECODE_TYPE(uint32, Buffer);
|
switch (sub_action) {
|
||||||
|
case Barter_BuyerAppearance: {
|
||||||
|
auto emu = (BuyerInspectRequest_Struct *) in->pBuffer;
|
||||||
|
|
||||||
if (SubAction != Barter_BuyerAppearance)
|
auto outapp = new EQApplicationPacket(OP_Barter, sizeof(structs::Buyer_SetAppearance_Struct));
|
||||||
{
|
auto eq = (structs::Buyer_SetAppearance_Struct *) outapp->pBuffer;
|
||||||
dest->FastQueuePacket(&in, ack_req);
|
|
||||||
|
|
||||||
return;
|
eq->action = structs::RoF2BuyerActions::BuyerAppearance;
|
||||||
|
eq->entity_id = emu->buyer_id;
|
||||||
|
eq->enabled = emu->approval;
|
||||||
|
|
||||||
|
dest->FastQueuePacket(&outapp);
|
||||||
|
safe_delete(in);
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case Barter_BuyerItemRemove: {
|
||||||
|
auto emu = (BuyerRemoveItem_Struct *) in->pBuffer;
|
||||||
|
|
||||||
unsigned char *__emu_buffer = in->pBuffer;
|
auto outapp = new EQApplicationPacket(OP_BuyerItems, sizeof(structs::BuyerRemoveItem_Struct));
|
||||||
|
auto eq = (structs::BuyerRemoveItem_Struct *) outapp->pBuffer;
|
||||||
|
|
||||||
in->size = 80;
|
eq->action = structs::RoF2BuyerActions::BuyerModifyBuyLine;
|
||||||
|
eq->slot_id = emu->buy_slot_id;
|
||||||
|
eq->toggle = 0;
|
||||||
|
|
||||||
in->pBuffer = new unsigned char[in->size];
|
dest->FastQueuePacket(&outapp);
|
||||||
|
safe_delete(in);
|
||||||
|
|
||||||
char *OutBuffer = (char *)in->pBuffer;
|
break;
|
||||||
|
}
|
||||||
|
case Barter_BuyerInspectBegin: {
|
||||||
|
*(uint32 *) in->pBuffer = structs::RoF2BuyerActions::BuyerInspectBegin;
|
||||||
|
dest->FastQueuePacket(&in);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_BuyerInspectEnd: {
|
||||||
|
*(uint32 *) in->pBuffer = structs::RoF2BuyerActions::BuyerInspectEnd;
|
||||||
|
dest->FastQueuePacket(&in);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_SellerBrowsing: {
|
||||||
|
*(uint32 *) in->pBuffer = structs::RoF2BuyerActions::BuyerBrowsingBuyLine;
|
||||||
|
dest->FastQueuePacket(&in);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_BuyerSearchResults: {
|
||||||
|
BuyerItemSearchResults_Struct bisr{};
|
||||||
|
auto emu = (BuyerGeneric_Struct *) in->pBuffer;
|
||||||
|
EQ::Util::MemoryStreamReader ss(
|
||||||
|
reinterpret_cast<char *>(emu->payload),
|
||||||
|
in->size - sizeof(BuyerGeneric_Struct)
|
||||||
|
);
|
||||||
|
cereal::BinaryInputArchive ar(ss);
|
||||||
|
ar(bisr);
|
||||||
|
|
||||||
char Name[64];
|
LogTradingDetail("Sending item search results <green>[{}]", bisr.result_count);
|
||||||
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction);
|
uint32 packet_size = bisr.result_count * sizeof(structs::BuyerItemSearchResultEntry_Struct) + 8;
|
||||||
uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer);
|
auto outapp = std::make_unique<EQApplicationPacket>(OP_Barter, packet_size);
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID);
|
auto eq = (char *) outapp->pBuffer;
|
||||||
uint8 Toggle = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
|
|
||||||
VARSTRUCT_DECODE_STRING(Name, Buffer);
|
|
||||||
VARSTRUCT_ENCODE_STRING(OutBuffer, Name);
|
|
||||||
OutBuffer = (char *)in->pBuffer + 72;
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint8, OutBuffer, Toggle);
|
|
||||||
|
|
||||||
delete[] __emu_buffer;
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSearchResults);
|
||||||
dest->FastQueuePacket(&in, ack_req);
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bisr.result_count);
|
||||||
|
for (auto const &i: bisr.results) {
|
||||||
|
strn0cpy(eq, i.item_name, 64);
|
||||||
|
eq += 64;
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_id);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
|
||||||
|
VARSTRUCT_SKIP_TYPE(uint32, eq);
|
||||||
|
}
|
||||||
|
dest->QueuePacket(outapp.get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
LogTradingDetail("Unhandled action <red>[{}]", sub_action);
|
||||||
|
dest->FastQueuePacket(&in);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ENCODE(OP_BazaarSearch)
|
ENCODE(OP_BazaarSearch)
|
||||||
@@ -682,6 +732,243 @@ namespace RoF2
|
|||||||
FINISH_ENCODE();
|
FINISH_ENCODE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ENCODE(OP_BuyerItems)
|
||||||
|
{
|
||||||
|
EQApplicationPacket *inapp = *p;
|
||||||
|
*p = nullptr;
|
||||||
|
|
||||||
|
auto action = *(uint32 *) inapp->pBuffer;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case Barter_BuyerItemUpdate: {
|
||||||
|
BuyerLineItems_Struct bl{};
|
||||||
|
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
|
||||||
|
EQ::Util::MemoryStreamReader ss(
|
||||||
|
reinterpret_cast<char *>(emu->payload),
|
||||||
|
inapp->size - sizeof(BuyerGeneric_Struct)
|
||||||
|
);
|
||||||
|
cereal::BinaryInputArchive ar(ss);
|
||||||
|
ar(bl);
|
||||||
|
|
||||||
|
//packet size
|
||||||
|
auto packet_size = bl.item_name.length() + 1 + 34;
|
||||||
|
for (auto const &b: bl.trade_items) {
|
||||||
|
packet_size += b.item_name.length() + 1;
|
||||||
|
packet_size += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuyerItems, packet_size);
|
||||||
|
char *eq = (char *) outapp->pBuffer;
|
||||||
|
auto no_trade_items = bl.trade_items.size();
|
||||||
|
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerModifyBuyLine);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.slot);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint8, eq, bl.enabled ? 1 : 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_id);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, bl.item_name.c_str());
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_icon);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_quantity);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint8, eq, bl.item_toggle ? 1 : 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.item_cost);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, no_trade_items);
|
||||||
|
|
||||||
|
for (int i = 0; i < no_trade_items; i++) {
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.trade_items[i].item_id);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.trade_items[i].item_quantity);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bl.trade_items[i].item_icon);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, bl.trade_items[i].item_name.c_str());
|
||||||
|
}
|
||||||
|
dest->QueuePacket(outapp.get());
|
||||||
|
safe_delete(inapp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_BuyerInspectBegin: {
|
||||||
|
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
|
||||||
|
|
||||||
|
BuyerLineItems_Struct bli{};
|
||||||
|
EQ::Util::MemoryStreamReader ss(
|
||||||
|
reinterpret_cast<char *>(emu->payload),
|
||||||
|
inapp->size - sizeof(BuyerGeneric_Struct)
|
||||||
|
);
|
||||||
|
cereal::BinaryInputArchive ar(ss);
|
||||||
|
ar(bli);
|
||||||
|
|
||||||
|
//packet size
|
||||||
|
auto packet_size = bli.item_name.length() + 1 + 34;
|
||||||
|
for (auto const &b: bli.trade_items) {
|
||||||
|
packet_size += b.item_name.length() + 1;
|
||||||
|
packet_size += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto packet = std::make_unique<EQApplicationPacket>(OP_BuyerItems, packet_size);
|
||||||
|
char *eq = (char *) packet->pBuffer;
|
||||||
|
auto no_trade_items = bli.trade_items.size();
|
||||||
|
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSendBuyLine);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.slot);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.slot);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint8, eq, bli.enabled ? 1 : 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_id);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, bli.item_name.c_str());
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_icon);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_quantity);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint8, eq, bli.item_toggle ? 1 : 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bli.item_cost);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, no_trade_items);
|
||||||
|
|
||||||
|
for (auto const &i: bli.trade_items) {
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_id);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_quantity);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, i.item_name.c_str());
|
||||||
|
}
|
||||||
|
dest->QueuePacket(packet.get());
|
||||||
|
safe_delete(inapp);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_BuyerSearch: {
|
||||||
|
BuyerLineSearch_Struct bls{};
|
||||||
|
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
|
||||||
|
EQ::Util::MemoryStreamReader ss(
|
||||||
|
reinterpret_cast<char *>(emu->payload),
|
||||||
|
inapp->size - sizeof(BuyerGeneric_Struct)
|
||||||
|
);
|
||||||
|
cereal::BinaryInputArchive ar(ss);
|
||||||
|
ar(bls);
|
||||||
|
LogTrading("(RoF2) Barter_BuyerSearch action <green>[{}]", emu->action);
|
||||||
|
|
||||||
|
//Calculate size of packet
|
||||||
|
auto p_size = 0;
|
||||||
|
p_size += 5 * sizeof(uint32) + 1 * sizeof(uint8);
|
||||||
|
p_size += bls.search_string.length() + 1;
|
||||||
|
for (auto const &b: bls.buy_line) {
|
||||||
|
p_size += 6 * sizeof(uint32) + 2 * sizeof(uint8);
|
||||||
|
p_size += strlen(b.item_name) + 1;
|
||||||
|
p_size += b.buyer_name.length() + 1;
|
||||||
|
for (auto const &d: b.trade_items) {
|
||||||
|
if (d.item_id != 0) {
|
||||||
|
p_size += d.item_name.length() + 1;
|
||||||
|
p_size += 3 * sizeof(uint32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p_size += 3 * sizeof(uint32);
|
||||||
|
}
|
||||||
|
|
||||||
|
BuyerBuyLines_Struct bl{};
|
||||||
|
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuyerItems, p_size);
|
||||||
|
auto eq = (char *) outapp->pBuffer;
|
||||||
|
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 1);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, bls.search_string.c_str());
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bls.transaction_id);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint8, eq, 1);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, bls.no_items);
|
||||||
|
for (auto const &b: bls.buy_line) {
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.slot);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint8, eq, 1);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_id);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, b.item_name);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_icon);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_quantity);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint8, eq, 1);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.item_cost);
|
||||||
|
auto no_sub_items = b.trade_items.size();
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, no_sub_items);
|
||||||
|
for (auto const &i: b.trade_items) {
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_id);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_quantity);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_icon);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, i.item_name.c_str());
|
||||||
|
}
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.buyer_entity_id);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, b.buyer_id);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint16, eq, b.buyer_zone_id);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint16, eq, b.buyer_zone_instance_id);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, b.buyer_name.c_str());
|
||||||
|
}
|
||||||
|
dest->QueuePacket(outapp.get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_RemoveFromMerchantWindow: {
|
||||||
|
auto emu = (BuyerRemoveItemFromMerchantWindow_Struct *) inapp->pBuffer;
|
||||||
|
|
||||||
|
emu->action = structs::RoF2BuyerActions::BuyerSendBuyLine;
|
||||||
|
dest->FastQueuePacket(&inapp);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_BuyerTransactionComplete:
|
||||||
|
case Barter_SellerTransactionComplete: {
|
||||||
|
BuyerLineSellItem_Struct blsi{};
|
||||||
|
auto emu = (BuyerGeneric_Struct *) inapp->pBuffer;
|
||||||
|
EQ::Util::MemoryStreamReader ss(
|
||||||
|
reinterpret_cast<char *>(emu->payload),
|
||||||
|
inapp->size - sizeof(BuyerGeneric_Struct)
|
||||||
|
);
|
||||||
|
cereal::BinaryInputArchive ar(ss);
|
||||||
|
ar(blsi);
|
||||||
|
|
||||||
|
//packet size
|
||||||
|
auto packet_size = strlen(blsi.item_name) * 2 + 2 + 48 + 30 + blsi.seller_name.length() + 1 +
|
||||||
|
blsi.buyer_name.length() + 1;
|
||||||
|
for (auto const &b: blsi.trade_items) {
|
||||||
|
packet_size += b.item_name.length() + 1;
|
||||||
|
packet_size += 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto outapp = std::make_unique<EQApplicationPacket>(OP_BuyerItems, packet_size);
|
||||||
|
auto eq = (char *) outapp->pBuffer;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case Barter_BuyerTransactionComplete: {
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerBuyItem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_SellerTransactionComplete: {
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSellItem);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.sub_action);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.error_code);
|
||||||
|
eq += 16;
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, blsi.buyer_name.c_str());
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, blsi.item_name);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, blsi.seller_name.c_str());
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0xFFFFFFFF);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0xFFFFFFFF);
|
||||||
|
eq += 1;
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, blsi.item_name);
|
||||||
|
eq += 9;
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.item_cost);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.trade_items.size());
|
||||||
|
|
||||||
|
for (auto const &i: blsi.trade_items) {
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, i.item_quantity);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||||
|
VARSTRUCT_ENCODE_STRING(eq, i.item_name.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0xFFFFFF);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, 0);
|
||||||
|
VARSTRUCT_ENCODE_TYPE(uint32, eq, blsi.seller_quantity);
|
||||||
|
|
||||||
|
dest->QueuePacket(outapp.get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
dest->FastQueuePacket(&inapp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ENCODE(OP_CancelTrade)
|
ENCODE(OP_CancelTrade)
|
||||||
{
|
{
|
||||||
ENCODE_LENGTH_EXACT(CancelTrade_Struct);
|
ENCODE_LENGTH_EXACT(CancelTrade_Struct);
|
||||||
@@ -4348,6 +4635,9 @@ namespace RoF2
|
|||||||
if (emu->DestructibleObject) {
|
if (emu->DestructibleObject) {
|
||||||
OtherData = OtherData | 0xe1; // Live has 0xe1 for OtherData
|
OtherData = OtherData | 0xe1; // Live has 0xe1 for OtherData
|
||||||
}
|
}
|
||||||
|
if (emu->buyer) {
|
||||||
|
OtherData = OtherData | 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData);
|
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData);
|
||||||
// float EmitterScalingRadius
|
// float EmitterScalingRadius
|
||||||
@@ -4664,6 +4954,66 @@ namespace RoF2
|
|||||||
FINISH_DIRECT_DECODE();
|
FINISH_DIRECT_DECODE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DECODE(OP_Barter)
|
||||||
|
{
|
||||||
|
auto action = *(uint32 *) __packet->pBuffer;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case structs::RoF2BuyerActions::BuyerRemoveItem: {
|
||||||
|
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||||
|
emu->action = Barter_BuyerItemRemove;
|
||||||
|
LogTradingDetail("(RoF2) Buyer Remove Item");
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case structs::RoF2BuyerActions::BuyerInspectBegin: {
|
||||||
|
LogTradingDetail("(RoF2) Buyer Inspect Begin Item");
|
||||||
|
|
||||||
|
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||||
|
emu->action = Barter_BuyerInspectBegin;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case structs::RoF2BuyerActions::BuyerInspectEnd: {
|
||||||
|
LogTradingDetail("(RoF2) Buyer Inspect End Item ");
|
||||||
|
|
||||||
|
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||||
|
emu->action = Barter_BuyerInspectEnd;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case structs::RoF2BuyerActions::BuyerWelcomeMessage: {
|
||||||
|
LogTradingDetail("(RoF2) Buyer Welcome Message Update");
|
||||||
|
SETUP_DIRECT_DECODE(BuyerWelcomeMessageUpdate_Struct, structs::BuyerWelcomeMessageUpdate_Struct);
|
||||||
|
|
||||||
|
emu->action = Barter_WelcomeMessageUpdate;
|
||||||
|
strn0cpy(emu->welcome_message, eq->welcome_message, sizeof(emu->welcome_message));
|
||||||
|
|
||||||
|
FINISH_DIRECT_DECODE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case structs::RoF2BuyerActions::BuyerItemInspect: {
|
||||||
|
SETUP_DIRECT_DECODE(BarterItemSearchLinkRequest_Struct, structs::BarterItemSearchLinkRequest_Struct);
|
||||||
|
LogTradingDetail("(RoF2) Seller ID <green>[{}] Inspecting Item <green>[{}] from Buyer ID <green>[{}] ",
|
||||||
|
eq->seller_id,
|
||||||
|
eq->item_id,
|
||||||
|
eq->buyer_id
|
||||||
|
);
|
||||||
|
|
||||||
|
emu->action = Barter_BarterItemInspect;
|
||||||
|
emu->item_id = eq->item_id;
|
||||||
|
emu->searcher_id = eq->seller_id;
|
||||||
|
|
||||||
|
FINISH_DIRECT_DECODE();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||||
|
LogTradingDetail("(RoF2) Pass thru OP_Barter packet action <red>[{}]", emu->action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DECODE(OP_BazaarSearch)
|
DECODE(OP_BazaarSearch)
|
||||||
{
|
{
|
||||||
char *Buffer = (char *)__packet->pBuffer;
|
char *Buffer = (char *)__packet->pBuffer;
|
||||||
@@ -4742,6 +5092,147 @@ namespace RoF2
|
|||||||
FINISH_DIRECT_DECODE();
|
FINISH_DIRECT_DECODE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DECODE(OP_BuyerItems)
|
||||||
|
{
|
||||||
|
auto action = *(uint32 *) __packet->pBuffer;
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case structs::RoF2BuyerActions::BuyerModifyBuyLine:
|
||||||
|
case structs::RoF2BuyerActions::BuyerBuyLine: {
|
||||||
|
BuyerBuyLines_Struct buyer_buy_lines{};
|
||||||
|
auto buffer = (char *) __packet->pBuffer;
|
||||||
|
|
||||||
|
buyer_buy_lines.action = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
|
||||||
|
buyer_buy_lines.no_items = 1;
|
||||||
|
if (action == structs::RoF2BuyerActions::BuyerBuyLine) {
|
||||||
|
buyer_buy_lines.no_items = VARSTRUCT_DECODE_TYPE(uint16, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
buyer_buy_lines.buy_lines.reserve(buyer_buy_lines.no_items);
|
||||||
|
for (int i = 0; i < buyer_buy_lines.no_items; i++) {
|
||||||
|
BuyerLineItems_Struct b{};
|
||||||
|
b.slot = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
b.enabled = VARSTRUCT_DECODE_TYPE(uint8, buffer);
|
||||||
|
b.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
b.item_name = std::string(buffer, strlen(buffer));
|
||||||
|
buffer += strlen(buffer) + 1;
|
||||||
|
b.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
b.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
b.item_toggle = VARSTRUCT_DECODE_TYPE(uint8, buffer);
|
||||||
|
b.item_cost = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
auto trade_items = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
buyer_buy_lines.buy_lines.push_back(b);
|
||||||
|
|
||||||
|
if (trade_items > 0) {
|
||||||
|
buyer_buy_lines.buy_lines[i].trade_items.reserve(trade_items);
|
||||||
|
for (int x = 0; x < trade_items; x++) {
|
||||||
|
BuyerLineTradeItems_Struct blti{};
|
||||||
|
blti.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
blti.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
blti.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
blti.item_name = std::string(buffer, strlen(buffer));
|
||||||
|
buffer += strlen(buffer) + 1;
|
||||||
|
buyer_buy_lines.buy_lines[i].trade_items.push_back(blti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer += 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = nullptr;
|
||||||
|
std::stringstream ss{};
|
||||||
|
cereal::BinaryOutputArchive ar(ss);
|
||||||
|
{
|
||||||
|
ar(buyer_buy_lines);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto new_size = sizeof(BuyerGeneric_Struct) + ss.str().length();
|
||||||
|
auto new_packet = new unsigned char[new_size];
|
||||||
|
__packet->size = new_size;
|
||||||
|
__packet->pBuffer = new_packet;
|
||||||
|
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||||
|
emu->action = Barter_BuyerItemUpdate;
|
||||||
|
|
||||||
|
if (action == structs::RoF2BuyerActions::BuyerBuyLine) {
|
||||||
|
emu->action = Barter_BuyerItemStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(emu->payload, ss.str().data(), ss.str().length());
|
||||||
|
__packet->SetOpcode(OP_Barter);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case structs::RoF2BuyerActions::BuyerSellItem: {
|
||||||
|
BuyerLineSellItem_Struct sell_item{};
|
||||||
|
|
||||||
|
char *buffer = (char *) __packet->pBuffer;
|
||||||
|
|
||||||
|
sell_item.action = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
sell_item.purchase_method = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
buffer += 4;
|
||||||
|
sell_item.buyer_entity_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
sell_item.buyer_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
buffer += 11;
|
||||||
|
sell_item.slot = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
sell_item.enabled = VARSTRUCT_DECODE_TYPE(uint8, buffer);
|
||||||
|
sell_item.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
VARSTRUCT_DECODE_STRING(sell_item.item_name, buffer);
|
||||||
|
sell_item.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
sell_item.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
sell_item.item_toggle = VARSTRUCT_DECODE_TYPE(uint8, buffer);
|
||||||
|
sell_item.item_cost = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
sell_item.no_trade_items = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
|
||||||
|
if (sell_item.no_trade_items > 0) {
|
||||||
|
sell_item.trade_items.reserve(sell_item.no_trade_items);
|
||||||
|
for (int x = 0; x < sell_item.no_trade_items; x++) {
|
||||||
|
BuyerLineTradeItems_Struct blti{};
|
||||||
|
blti.item_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
blti.item_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
blti.item_icon = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
blti.item_name = std::string(buffer, strlen(buffer));
|
||||||
|
buffer += strlen(buffer) + 1;
|
||||||
|
sell_item.trade_items.push_back(blti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sell_item.purchase_method) {
|
||||||
|
sell_item.buyer_entity_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
sell_item.buyer_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
sell_item.zone_id = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
sell_item.buyer_name = std::string(buffer, strlen(buffer));
|
||||||
|
buffer += sell_item.buyer_name.length() + 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buffer += 13;
|
||||||
|
}
|
||||||
|
|
||||||
|
sell_item.seller_quantity = VARSTRUCT_DECODE_TYPE(uint32, buffer);
|
||||||
|
|
||||||
|
buffer += 4;
|
||||||
|
|
||||||
|
buffer = nullptr;
|
||||||
|
std::stringstream ss{};
|
||||||
|
cereal::BinaryOutputArchive ar(ss);
|
||||||
|
{
|
||||||
|
ar(sell_item);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto new_size = sizeof(BuyerGeneric_Struct) + ss.str().length();
|
||||||
|
auto new_packet = new unsigned char[new_size];
|
||||||
|
__packet->size = new_size;
|
||||||
|
__packet->pBuffer = new_packet;
|
||||||
|
auto emu = (BuyerGeneric_Struct *) __packet->pBuffer;
|
||||||
|
emu->action = Barter_SellItem;
|
||||||
|
|
||||||
|
memcpy(emu->payload, ss.str().data(), ss.str().length());
|
||||||
|
__packet->SetOpcode(OP_Barter);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
DECODE(OP_CastSpell)
|
DECODE(OP_CastSpell)
|
||||||
{
|
{
|
||||||
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
DECODE_LENGTH_EXACT(structs::CastSpell_Struct);
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ E(OP_BeginCast)
|
|||||||
E(OP_BlockedBuffs)
|
E(OP_BlockedBuffs)
|
||||||
E(OP_Buff)
|
E(OP_Buff)
|
||||||
E(OP_BuffCreate)
|
E(OP_BuffCreate)
|
||||||
|
E(OP_BuyerItems)
|
||||||
E(OP_CancelTrade)
|
E(OP_CancelTrade)
|
||||||
E(OP_CastSpell)
|
E(OP_CastSpell)
|
||||||
E(OP_ChannelMessage)
|
E(OP_ChannelMessage)
|
||||||
@@ -149,11 +150,13 @@ D(OP_Animation)
|
|||||||
D(OP_ApplyPoison)
|
D(OP_ApplyPoison)
|
||||||
D(OP_AugmentInfo)
|
D(OP_AugmentInfo)
|
||||||
D(OP_AugmentItem)
|
D(OP_AugmentItem)
|
||||||
|
D(OP_Barter)
|
||||||
D(OP_BazaarSearch)
|
D(OP_BazaarSearch)
|
||||||
D(OP_BlockedBuffs)
|
D(OP_BlockedBuffs)
|
||||||
D(OP_BookButton)
|
D(OP_BookButton)
|
||||||
D(OP_Buff)
|
D(OP_Buff)
|
||||||
D(OP_BuffRemoveRequest)
|
D(OP_BuffRemoveRequest)
|
||||||
|
D(OP_BuyerItems)
|
||||||
D(OP_CastSpell)
|
D(OP_CastSpell)
|
||||||
D(OP_ChannelMessage)
|
D(OP_ChannelMessage)
|
||||||
D(OP_CharacterCreate)
|
D(OP_CharacterCreate)
|
||||||
|
|||||||
@@ -354,15 +354,15 @@ struct Spawn_Struct_Bitfields
|
|||||||
/*29*/ unsigned showname:1;
|
/*29*/ unsigned showname:1;
|
||||||
/*30*/ unsigned idleanimationsoff:1; // what we called statue?
|
/*30*/ unsigned idleanimationsoff:1; // what we called statue?
|
||||||
/*31*/ unsigned untargetable:1; // bClickThrough
|
/*31*/ unsigned untargetable:1; // bClickThrough
|
||||||
/* do these later
|
// byte 5
|
||||||
32 unsigned buyer:1;
|
/*32 unsigned buyer:1;
|
||||||
33 unsigned offline:1;
|
/*33 unsigned offline:1;
|
||||||
34 unsigned interactiveobject:1;
|
/*34 unsigned interactiveobject:1;
|
||||||
35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables
|
/*35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables
|
||||||
36 unsigned title:1;
|
/*36 unsigned title:1;
|
||||||
37 unsigned suffix:1;
|
/*37 unsigned suffix:1;
|
||||||
38 unsigned padding1:1;
|
/*38 unsigned padding1:1;
|
||||||
39 unsigned padding2:1;
|
/*39 unsigned padding2:1;
|
||||||
40 unsinged padding3:1;
|
40 unsinged padding3:1;
|
||||||
*/
|
*/
|
||||||
/*
|
/*
|
||||||
@@ -3122,6 +3122,126 @@ enum RoF2BazaarTraderBuyerActions {
|
|||||||
ReconcileItems = 20
|
ReconcileItems = 20
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum RoF2BuyerActions {
|
||||||
|
BuyerSearchResults = 0x00,
|
||||||
|
BuyerBuyLine = 0x06,
|
||||||
|
BuyerModifyBuyLine = 0x07,
|
||||||
|
BuyerRemoveItem = 0x08,
|
||||||
|
BuyerSellItem = 0x09,
|
||||||
|
BuyerBuyItem = 0x0a,
|
||||||
|
BuyerInspectBegin = 0x0b,
|
||||||
|
BuyerInspectEnd = 0x0c,
|
||||||
|
BuyerAppearance = 0x0d,
|
||||||
|
BuyerSendBuyLine = 0x0e,
|
||||||
|
BuyerItemInspect = 0x0f,
|
||||||
|
BuyerBrowsingBuyLine = 0x10,
|
||||||
|
BarterWelcomeMessage = 0x11,
|
||||||
|
BuyerWelcomeMessage = 0x13,
|
||||||
|
BuyerGreeting = 0x14,
|
||||||
|
BuyerInventoryFull = 0x16
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BarterItemSearchLinkRequest_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 unknown_004;
|
||||||
|
uint32 seller_id;
|
||||||
|
uint32 buyer_id;
|
||||||
|
uint32 unknown_016;
|
||||||
|
uint32 slot_id; // 0xffffffff main buy line 0x0 trade_item_1, 0x1 trade_item_2
|
||||||
|
uint32 item_id;
|
||||||
|
uint32 unknown_028;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerWelcomeMessageUpdate_Struct {
|
||||||
|
uint32 action;
|
||||||
|
char unknown_004[64];
|
||||||
|
uint32 unknown_068;
|
||||||
|
char welcome_message[256];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Buyer_SetAppearance_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 entity_id;
|
||||||
|
char unknown[64];
|
||||||
|
uint32 enabled;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerRemoveItem_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 unknown004;
|
||||||
|
uint32 slot_id;
|
||||||
|
uint32 toggle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerLineSellItem_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 purchase_method; // 0 direct merchant, 1 via /barter window
|
||||||
|
uint32 unknown008;
|
||||||
|
uint32 buyer_entity_id;
|
||||||
|
uint32 seller_entity_id;
|
||||||
|
char unknown[15];
|
||||||
|
uint32 slot;
|
||||||
|
uint8 enabled;
|
||||||
|
uint32 item_id;
|
||||||
|
char item_name[64];
|
||||||
|
uint32 item_icon;
|
||||||
|
uint32 item_quantity;
|
||||||
|
uint8 item_toggle;
|
||||||
|
uint32 item_cost;
|
||||||
|
uint32 no_trade_items;
|
||||||
|
BuyerLineTradeItems_Struct trade_items[10];
|
||||||
|
char unknown2[13];
|
||||||
|
uint32 seller_quantity;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerLineItemsSearch_Struct {
|
||||||
|
uint32 slot;
|
||||||
|
uint8 enabled;
|
||||||
|
uint32 item_id;
|
||||||
|
char item_name[64];
|
||||||
|
uint32 item_icon;
|
||||||
|
uint32 item_quantity;
|
||||||
|
uint8 item_toggle;
|
||||||
|
uint32 item_cost;
|
||||||
|
uint32 buyer_id;
|
||||||
|
BuyerLineTradeItems_Struct trade_items[MAX_BUYER_COMPENSATION_ITEMS];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerLineSearch_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 no_items;
|
||||||
|
std::vector<BuyerLineItemsSearch_Struct> buy_line;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerStart_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint16 no_buyer_lines;
|
||||||
|
uint32 slot;
|
||||||
|
uint8 enabled;
|
||||||
|
uint32 item_id;
|
||||||
|
char item_name[1]; // vary length
|
||||||
|
uint32 item_icon;
|
||||||
|
uint32 item_quantity;
|
||||||
|
uint8 toggle;
|
||||||
|
uint32 item_cost;
|
||||||
|
uint32 no_trade_items;
|
||||||
|
BuyerLineTradeItems_Struct trade_items[1]; // size is actually no_trade_items. If 0, then this is not in packet
|
||||||
|
char unknown[13];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerItemSearchResultEntry_Struct {
|
||||||
|
char item_name[64];
|
||||||
|
uint32 item_id;
|
||||||
|
uint32 item_icon;
|
||||||
|
uint32 unknown_072;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BuyerItemSearchResults_Struct {
|
||||||
|
uint32 action;
|
||||||
|
uint32 result_count;
|
||||||
|
BuyerItemSearchResultEntry_Struct results[];
|
||||||
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
BazaarTrader_StartTraderMode = 1,
|
BazaarTrader_StartTraderMode = 1,
|
||||||
BazaarTrader_EndTraderMode = 2,
|
BazaarTrader_EndTraderMode = 2,
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public:
|
|||||||
struct BotSpellsEntries {
|
struct BotSpellsEntries {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
int32_t npc_spells_id;
|
int32_t npc_spells_id;
|
||||||
int16_t spellid;
|
uint16_t spell_id;
|
||||||
uint32_t type;
|
uint32_t type;
|
||||||
uint8_t minlevel;
|
uint8_t minlevel;
|
||||||
uint8_t maxlevel;
|
uint8_t maxlevel;
|
||||||
@@ -46,7 +46,7 @@ public:
|
|||||||
return {
|
return {
|
||||||
"id",
|
"id",
|
||||||
"npc_spells_id",
|
"npc_spells_id",
|
||||||
"spellid",
|
"spell_id",
|
||||||
"type",
|
"type",
|
||||||
"minlevel",
|
"minlevel",
|
||||||
"maxlevel",
|
"maxlevel",
|
||||||
@@ -67,7 +67,7 @@ public:
|
|||||||
return {
|
return {
|
||||||
"id",
|
"id",
|
||||||
"npc_spells_id",
|
"npc_spells_id",
|
||||||
"spellid",
|
"spell_id",
|
||||||
"type",
|
"type",
|
||||||
"minlevel",
|
"minlevel",
|
||||||
"maxlevel",
|
"maxlevel",
|
||||||
@@ -122,7 +122,7 @@ public:
|
|||||||
|
|
||||||
e.id = 0;
|
e.id = 0;
|
||||||
e.npc_spells_id = 0;
|
e.npc_spells_id = 0;
|
||||||
e.spellid = 0;
|
e.spell_id = 0;
|
||||||
e.type = 0;
|
e.type = 0;
|
||||||
e.minlevel = 0;
|
e.minlevel = 0;
|
||||||
e.maxlevel = 255;
|
e.maxlevel = 255;
|
||||||
@@ -173,7 +173,7 @@ public:
|
|||||||
|
|
||||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||||
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.spellid = row[2] ? static_cast<int16_t>(atoi(row[2])) : 0;
|
e.spell_id = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
|
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
|
||||||
@@ -220,7 +220,7 @@ public:
|
|||||||
auto columns = Columns();
|
auto columns = Columns();
|
||||||
|
|
||||||
v.push_back(columns[1] + " = " + std::to_string(e.npc_spells_id));
|
v.push_back(columns[1] + " = " + std::to_string(e.npc_spells_id));
|
||||||
v.push_back(columns[2] + " = " + std::to_string(e.spellid));
|
v.push_back(columns[2] + " = " + std::to_string(e.spell_id));
|
||||||
v.push_back(columns[3] + " = " + std::to_string(e.type));
|
v.push_back(columns[3] + " = " + std::to_string(e.type));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.minlevel));
|
v.push_back(columns[4] + " = " + std::to_string(e.minlevel));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.maxlevel));
|
v.push_back(columns[5] + " = " + std::to_string(e.maxlevel));
|
||||||
@@ -256,7 +256,7 @@ public:
|
|||||||
|
|
||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.npc_spells_id));
|
v.push_back(std::to_string(e.npc_spells_id));
|
||||||
v.push_back(std::to_string(e.spellid));
|
v.push_back(std::to_string(e.spell_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.minlevel));
|
v.push_back(std::to_string(e.minlevel));
|
||||||
v.push_back(std::to_string(e.maxlevel));
|
v.push_back(std::to_string(e.maxlevel));
|
||||||
@@ -300,7 +300,7 @@ public:
|
|||||||
|
|
||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.npc_spells_id));
|
v.push_back(std::to_string(e.npc_spells_id));
|
||||||
v.push_back(std::to_string(e.spellid));
|
v.push_back(std::to_string(e.spell_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.minlevel));
|
v.push_back(std::to_string(e.minlevel));
|
||||||
v.push_back(std::to_string(e.maxlevel));
|
v.push_back(std::to_string(e.maxlevel));
|
||||||
@@ -348,7 +348,7 @@ public:
|
|||||||
|
|
||||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||||
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.spellid = row[2] ? static_cast<int16_t>(atoi(row[2])) : 0;
|
e.spell_id = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
|
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
|
||||||
@@ -387,7 +387,7 @@ public:
|
|||||||
|
|
||||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||||
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||||
e.spellid = row[2] ? static_cast<int16_t>(atoi(row[2])) : 0;
|
e.spell_id = row[2] ? static_cast<uint16_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
e.type = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
e.minlevel = row[4] ? static_cast<uint8_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
|
e.maxlevel = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 255;
|
||||||
@@ -476,7 +476,7 @@ public:
|
|||||||
|
|
||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.npc_spells_id));
|
v.push_back(std::to_string(e.npc_spells_id));
|
||||||
v.push_back(std::to_string(e.spellid));
|
v.push_back(std::to_string(e.spell_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.minlevel));
|
v.push_back(std::to_string(e.minlevel));
|
||||||
v.push_back(std::to_string(e.maxlevel));
|
v.push_back(std::to_string(e.maxlevel));
|
||||||
@@ -513,7 +513,7 @@ public:
|
|||||||
|
|
||||||
v.push_back(std::to_string(e.id));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.npc_spells_id));
|
v.push_back(std::to_string(e.npc_spells_id));
|
||||||
v.push_back(std::to_string(e.spellid));
|
v.push_back(std::to_string(e.spell_id));
|
||||||
v.push_back(std::to_string(e.type));
|
v.push_back(std::to_string(e.type));
|
||||||
v.push_back(std::to_string(e.minlevel));
|
v.push_back(std::to_string(e.minlevel));
|
||||||
v.push_back(std::to_string(e.maxlevel));
|
v.push_back(std::to_string(e.maxlevel));
|
||||||
|
|||||||
@@ -0,0 +1,475 @@
|
|||||||
|
/**
|
||||||
|
* DO NOT MODIFY THIS FILE
|
||||||
|
*
|
||||||
|
* This repository was automatically generated and is NOT to be modified directly.
|
||||||
|
* Any repository modifications are meant to be made to the repository extending the base.
|
||||||
|
* Any modifications to base repositories are to be made by the generator only
|
||||||
|
*
|
||||||
|
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||||
|
* @docs https://docs.eqemu.io/developer/repositories
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EQEMU_BASE_BUYER_BUY_LINES_REPOSITORY_H
|
||||||
|
#define EQEMU_BASE_BUYER_BUY_LINES_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../../database.h"
|
||||||
|
#include "../../strings.h"
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
class BaseBuyerBuyLinesRepository {
|
||||||
|
public:
|
||||||
|
struct BuyerBuyLines {
|
||||||
|
uint64_t id;
|
||||||
|
uint64_t buyer_id;
|
||||||
|
uint32_t char_id;
|
||||||
|
int32_t buy_slot_id;
|
||||||
|
int32_t item_id;
|
||||||
|
int32_t item_qty;
|
||||||
|
int32_t item_price;
|
||||||
|
uint32_t item_icon;
|
||||||
|
std::string item_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string PrimaryKey()
|
||||||
|
{
|
||||||
|
return std::string("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> Columns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"buyer_id",
|
||||||
|
"char_id",
|
||||||
|
"buy_slot_id",
|
||||||
|
"item_id",
|
||||||
|
"item_qty",
|
||||||
|
"item_price",
|
||||||
|
"item_icon",
|
||||||
|
"item_name",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> SelectColumns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"buyer_id",
|
||||||
|
"char_id",
|
||||||
|
"buy_slot_id",
|
||||||
|
"item_id",
|
||||||
|
"item_qty",
|
||||||
|
"item_price",
|
||||||
|
"item_icon",
|
||||||
|
"item_name",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", Columns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string SelectColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string TableName()
|
||||||
|
{
|
||||||
|
return std::string("buyer_buy_lines");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseSelect()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"SELECT {} FROM {}",
|
||||||
|
SelectColumnsRaw(),
|
||||||
|
TableName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseInsert()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"INSERT INTO {} ({}) ",
|
||||||
|
TableName(),
|
||||||
|
ColumnsRaw()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuyerBuyLines NewEntity()
|
||||||
|
{
|
||||||
|
BuyerBuyLines e{};
|
||||||
|
|
||||||
|
e.id = 0;
|
||||||
|
e.buyer_id = 0;
|
||||||
|
e.char_id = 0;
|
||||||
|
e.buy_slot_id = 0;
|
||||||
|
e.item_id = 0;
|
||||||
|
e.item_qty = 0;
|
||||||
|
e.item_price = 0;
|
||||||
|
e.item_icon = 0;
|
||||||
|
e.item_name = "";
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuyerBuyLines GetBuyerBuyLines(
|
||||||
|
const std::vector<BuyerBuyLines> &buyer_buy_liness,
|
||||||
|
int buyer_buy_lines_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (auto &buyer_buy_lines : buyer_buy_liness) {
|
||||||
|
if (buyer_buy_lines.id == buyer_buy_lines_id) {
|
||||||
|
return buyer_buy_lines;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuyerBuyLines FindOne(
|
||||||
|
Database& db,
|
||||||
|
int buyer_buy_lines_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {} = {} LIMIT 1",
|
||||||
|
BaseSelect(),
|
||||||
|
PrimaryKey(),
|
||||||
|
buyer_buy_lines_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto row = results.begin();
|
||||||
|
if (results.RowCount() == 1) {
|
||||||
|
BuyerBuyLines e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.buyer_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||||
|
e.char_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.buy_slot_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||||
|
e.item_id = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.item_qty = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.item_price = row[6] ? static_cast<int32_t>(atoi(row[6])) : 0;
|
||||||
|
e.item_icon = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.item_name = row[8] ? row[8] : "";
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteOne(
|
||||||
|
Database& db,
|
||||||
|
int buyer_buy_lines_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
PrimaryKey(),
|
||||||
|
buyer_buy_lines_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int UpdateOne(
|
||||||
|
Database& db,
|
||||||
|
const BuyerBuyLines &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto columns = Columns();
|
||||||
|
|
||||||
|
v.push_back(columns[1] + " = " + std::to_string(e.buyer_id));
|
||||||
|
v.push_back(columns[2] + " = " + std::to_string(e.char_id));
|
||||||
|
v.push_back(columns[3] + " = " + std::to_string(e.buy_slot_id));
|
||||||
|
v.push_back(columns[4] + " = " + std::to_string(e.item_id));
|
||||||
|
v.push_back(columns[5] + " = " + std::to_string(e.item_qty));
|
||||||
|
v.push_back(columns[6] + " = " + std::to_string(e.item_price));
|
||||||
|
v.push_back(columns[7] + " = " + std::to_string(e.item_icon));
|
||||||
|
v.push_back(columns[8] + " = '" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"UPDATE {} SET {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
Strings::Implode(", ", v),
|
||||||
|
PrimaryKey(),
|
||||||
|
e.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuyerBuyLines InsertOne(
|
||||||
|
Database& db,
|
||||||
|
BuyerBuyLines e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.buyer_id));
|
||||||
|
v.push_back(std::to_string(e.char_id));
|
||||||
|
v.push_back(std::to_string(e.buy_slot_id));
|
||||||
|
v.push_back(std::to_string(e.item_id));
|
||||||
|
v.push_back(std::to_string(e.item_qty));
|
||||||
|
v.push_back(std::to_string(e.item_price));
|
||||||
|
v.push_back(std::to_string(e.item_icon));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES ({})",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", v)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (results.Success()) {
|
||||||
|
e.id = results.LastInsertedID();
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = NewEntity();
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int InsertMany(
|
||||||
|
Database& db,
|
||||||
|
const std::vector<BuyerBuyLines> &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.buyer_id));
|
||||||
|
v.push_back(std::to_string(e.char_id));
|
||||||
|
v.push_back(std::to_string(e.buy_slot_id));
|
||||||
|
v.push_back(std::to_string(e.item_id));
|
||||||
|
v.push_back(std::to_string(e.item_qty));
|
||||||
|
v.push_back(std::to_string(e.item_price));
|
||||||
|
v.push_back(std::to_string(e.item_icon));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES {}",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", insert_chunks)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<BuyerBuyLines> All(Database& db)
|
||||||
|
{
|
||||||
|
std::vector<BuyerBuyLines> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{}",
|
||||||
|
BaseSelect()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
BuyerBuyLines e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.buyer_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||||
|
e.char_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.buy_slot_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||||
|
e.item_id = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.item_qty = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.item_price = row[6] ? static_cast<int32_t>(atoi(row[6])) : 0;
|
||||||
|
e.item_icon = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.item_name = row[8] ? row[8] : "";
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<BuyerBuyLines> GetWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
std::vector<BuyerBuyLines> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {}",
|
||||||
|
BaseSelect(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
BuyerBuyLines e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.buyer_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||||
|
e.char_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
|
e.buy_slot_id = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||||
|
e.item_id = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.item_qty = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||||
|
e.item_price = row[6] ? static_cast<int32_t>(atoi(row[6])) : 0;
|
||||||
|
e.item_icon = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||||
|
e.item_name = row[8] ? row[8] : "";
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {}",
|
||||||
|
TableName(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Truncate(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"TRUNCATE TABLE {}",
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 GetMaxId(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||||
|
PrimaryKey(),
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COUNT(*) FROM {} {}",
|
||||||
|
TableName(),
|
||||||
|
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
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 BuyerBuyLines &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.buyer_id));
|
||||||
|
v.push_back(std::to_string(e.char_id));
|
||||||
|
v.push_back(std::to_string(e.buy_slot_id));
|
||||||
|
v.push_back(std::to_string(e.item_id));
|
||||||
|
v.push_back(std::to_string(e.item_qty));
|
||||||
|
v.push_back(std::to_string(e.item_price));
|
||||||
|
v.push_back(std::to_string(e.item_icon));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
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<BuyerBuyLines> &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.buyer_id));
|
||||||
|
v.push_back(std::to_string(e.char_id));
|
||||||
|
v.push_back(std::to_string(e.buy_slot_id));
|
||||||
|
v.push_back(std::to_string(e.item_id));
|
||||||
|
v.push_back(std::to_string(e.item_qty));
|
||||||
|
v.push_back(std::to_string(e.item_price));
|
||||||
|
v.push_back(std::to_string(e.item_icon));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
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_BUYER_BUY_LINES_REPOSITORY_H
|
||||||
@@ -19,40 +19,46 @@
|
|||||||
class BaseBuyerRepository {
|
class BaseBuyerRepository {
|
||||||
public:
|
public:
|
||||||
struct Buyer {
|
struct Buyer {
|
||||||
int32_t charid;
|
uint64_t id;
|
||||||
int32_t buyslot;
|
uint32_t char_id;
|
||||||
int32_t itemid;
|
uint32_t char_entity_id;
|
||||||
std::string itemname;
|
std::string char_name;
|
||||||
int32_t quantity;
|
uint32_t char_zone_id;
|
||||||
int32_t price;
|
uint32_t char_zone_instance_id;
|
||||||
|
time_t transaction_date;
|
||||||
|
std::string welcome_message;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string PrimaryKey()
|
static std::string PrimaryKey()
|
||||||
{
|
{
|
||||||
return std::string("charid");
|
return std::string("id");
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::string> Columns()
|
static std::vector<std::string> Columns()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
"charid",
|
"id",
|
||||||
"buyslot",
|
"char_id",
|
||||||
"itemid",
|
"char_entity_id",
|
||||||
"itemname",
|
"char_name",
|
||||||
"quantity",
|
"char_zone_id",
|
||||||
"price",
|
"char_zone_instance_id",
|
||||||
|
"transaction_date",
|
||||||
|
"welcome_message",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::string> SelectColumns()
|
static std::vector<std::string> SelectColumns()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
"charid",
|
"id",
|
||||||
"buyslot",
|
"char_id",
|
||||||
"itemid",
|
"char_entity_id",
|
||||||
"itemname",
|
"char_name",
|
||||||
"quantity",
|
"char_zone_id",
|
||||||
"price",
|
"char_zone_instance_id",
|
||||||
|
"UNIX_TIMESTAMP(transaction_date)",
|
||||||
|
"welcome_message",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,12 +99,14 @@ public:
|
|||||||
{
|
{
|
||||||
Buyer e{};
|
Buyer e{};
|
||||||
|
|
||||||
e.charid = 0;
|
e.id = 0;
|
||||||
e.buyslot = 0;
|
e.char_id = 0;
|
||||||
e.itemid = 0;
|
e.char_entity_id = 0;
|
||||||
e.itemname = "";
|
e.char_name = "";
|
||||||
e.quantity = 0;
|
e.char_zone_id = 0;
|
||||||
e.price = 0;
|
e.char_zone_instance_id = 0;
|
||||||
|
e.transaction_date = 0;
|
||||||
|
e.welcome_message = "";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -109,7 +117,7 @@ public:
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
for (auto &buyer : buyers) {
|
for (auto &buyer : buyers) {
|
||||||
if (buyer.charid == buyer_id) {
|
if (buyer.id == buyer_id) {
|
||||||
return buyer;
|
return buyer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -135,12 +143,14 @@ public:
|
|||||||
if (results.RowCount() == 1) {
|
if (results.RowCount() == 1) {
|
||||||
Buyer e{};
|
Buyer e{};
|
||||||
|
|
||||||
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.itemname = row[3] ? row[3] : "";
|
e.char_name = row[3] ? row[3] : "";
|
||||||
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
e.char_zone_instance_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
|
e.transaction_date = strtoll(row[6] ? row[6] : "-1", nullptr, 10);
|
||||||
|
e.welcome_message = row[7] ? row[7] : "";
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -174,12 +184,13 @@ public:
|
|||||||
|
|
||||||
auto columns = Columns();
|
auto columns = Columns();
|
||||||
|
|
||||||
v.push_back(columns[0] + " = " + std::to_string(e.charid));
|
v.push_back(columns[1] + " = " + std::to_string(e.char_id));
|
||||||
v.push_back(columns[1] + " = " + std::to_string(e.buyslot));
|
v.push_back(columns[2] + " = " + std::to_string(e.char_entity_id));
|
||||||
v.push_back(columns[2] + " = " + std::to_string(e.itemid));
|
v.push_back(columns[3] + " = '" + Strings::Escape(e.char_name) + "'");
|
||||||
v.push_back(columns[3] + " = '" + Strings::Escape(e.itemname) + "'");
|
v.push_back(columns[4] + " = " + std::to_string(e.char_zone_id));
|
||||||
v.push_back(columns[4] + " = " + std::to_string(e.quantity));
|
v.push_back(columns[5] + " = " + std::to_string(e.char_zone_instance_id));
|
||||||
v.push_back(columns[5] + " = " + std::to_string(e.price));
|
v.push_back(columns[6] + " = FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||||
|
v.push_back(columns[7] + " = '" + Strings::Escape(e.welcome_message) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -187,7 +198,7 @@ public:
|
|||||||
TableName(),
|
TableName(),
|
||||||
Strings::Implode(", ", v),
|
Strings::Implode(", ", v),
|
||||||
PrimaryKey(),
|
PrimaryKey(),
|
||||||
e.charid
|
e.id
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -201,12 +212,14 @@ public:
|
|||||||
{
|
{
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
v.push_back(std::to_string(e.charid));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.buyslot));
|
v.push_back(std::to_string(e.char_id));
|
||||||
v.push_back(std::to_string(e.itemid));
|
v.push_back(std::to_string(e.char_entity_id));
|
||||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||||
v.push_back(std::to_string(e.quantity));
|
v.push_back(std::to_string(e.char_zone_id));
|
||||||
v.push_back(std::to_string(e.price));
|
v.push_back(std::to_string(e.char_zone_instance_id));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||||
|
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -217,7 +230,7 @@ public:
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (results.Success()) {
|
if (results.Success()) {
|
||||||
e.charid = results.LastInsertedID();
|
e.id = results.LastInsertedID();
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,12 +249,14 @@ public:
|
|||||||
for (auto &e: entries) {
|
for (auto &e: entries) {
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
v.push_back(std::to_string(e.charid));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.buyslot));
|
v.push_back(std::to_string(e.char_id));
|
||||||
v.push_back(std::to_string(e.itemid));
|
v.push_back(std::to_string(e.char_entity_id));
|
||||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||||
v.push_back(std::to_string(e.quantity));
|
v.push_back(std::to_string(e.char_zone_id));
|
||||||
v.push_back(std::to_string(e.price));
|
v.push_back(std::to_string(e.char_zone_instance_id));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||||
|
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
}
|
}
|
||||||
@@ -275,12 +290,14 @@ public:
|
|||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
Buyer e{};
|
Buyer e{};
|
||||||
|
|
||||||
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.itemname = row[3] ? row[3] : "";
|
e.char_name = row[3] ? row[3] : "";
|
||||||
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
e.char_zone_instance_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
|
e.transaction_date = strtoll(row[6] ? row[6] : "-1", nullptr, 10);
|
||||||
|
e.welcome_message = row[7] ? row[7] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -305,12 +322,14 @@ public:
|
|||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
Buyer e{};
|
Buyer e{};
|
||||||
|
|
||||||
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||||
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||||
e.itemname = row[3] ? row[3] : "";
|
e.char_name = row[3] ? row[3] : "";
|
||||||
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||||
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
e.char_zone_instance_id = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||||
|
e.transaction_date = strtoll(row[6] ? row[6] : "-1", nullptr, 10);
|
||||||
|
e.welcome_message = row[7] ? row[7] : "";
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -385,12 +404,14 @@ public:
|
|||||||
{
|
{
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
v.push_back(std::to_string(e.charid));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.buyslot));
|
v.push_back(std::to_string(e.char_id));
|
||||||
v.push_back(std::to_string(e.itemid));
|
v.push_back(std::to_string(e.char_entity_id));
|
||||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||||
v.push_back(std::to_string(e.quantity));
|
v.push_back(std::to_string(e.char_zone_id));
|
||||||
v.push_back(std::to_string(e.price));
|
v.push_back(std::to_string(e.char_zone_instance_id));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||||
|
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -413,12 +434,14 @@ public:
|
|||||||
for (auto &e: entries) {
|
for (auto &e: entries) {
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
v.push_back(std::to_string(e.charid));
|
v.push_back(std::to_string(e.id));
|
||||||
v.push_back(std::to_string(e.buyslot));
|
v.push_back(std::to_string(e.char_id));
|
||||||
v.push_back(std::to_string(e.itemid));
|
v.push_back(std::to_string(e.char_entity_id));
|
||||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||||
v.push_back(std::to_string(e.quantity));
|
v.push_back(std::to_string(e.char_zone_id));
|
||||||
v.push_back(std::to_string(e.price));
|
v.push_back(std::to_string(e.char_zone_instance_id));
|
||||||
|
v.push_back("FROM_UNIXTIME(" + (e.transaction_date > 0 ? std::to_string(e.transaction_date) : "null") + ")");
|
||||||
|
v.push_back("'" + Strings::Escape(e.welcome_message) + "'");
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,439 @@
|
|||||||
|
/**
|
||||||
|
* DO NOT MODIFY THIS FILE
|
||||||
|
*
|
||||||
|
* This repository was automatically generated and is NOT to be modified directly.
|
||||||
|
* Any repository modifications are meant to be made to the repository extending the base.
|
||||||
|
* Any modifications to base repositories are to be made by the generator only
|
||||||
|
*
|
||||||
|
* @generator ./utils/scripts/generators/repository-generator.pl
|
||||||
|
* @docs https://docs.eqemu.io/developer/repositories
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef EQEMU_BASE_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||||
|
#define EQEMU_BASE_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../../database.h"
|
||||||
|
#include "../../strings.h"
|
||||||
|
#include <ctime>
|
||||||
|
|
||||||
|
class BaseBuyerTradeItemsRepository {
|
||||||
|
public:
|
||||||
|
struct BuyerTradeItems {
|
||||||
|
uint64_t id;
|
||||||
|
uint64_t buyer_buy_lines_id;
|
||||||
|
int32_t item_id;
|
||||||
|
int32_t item_qty;
|
||||||
|
int32_t item_icon;
|
||||||
|
std::string item_name;
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::string PrimaryKey()
|
||||||
|
{
|
||||||
|
return std::string("id");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> Columns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"buyer_buy_lines_id",
|
||||||
|
"item_id",
|
||||||
|
"item_qty",
|
||||||
|
"item_icon",
|
||||||
|
"item_name",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<std::string> SelectColumns()
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
"id",
|
||||||
|
"buyer_buy_lines_id",
|
||||||
|
"item_id",
|
||||||
|
"item_qty",
|
||||||
|
"item_icon",
|
||||||
|
"item_name",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string ColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", Columns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string SelectColumnsRaw()
|
||||||
|
{
|
||||||
|
return std::string(Strings::Implode(", ", SelectColumns()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string TableName()
|
||||||
|
{
|
||||||
|
return std::string("buyer_trade_items");
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseSelect()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"SELECT {} FROM {}",
|
||||||
|
SelectColumnsRaw(),
|
||||||
|
TableName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string BaseInsert()
|
||||||
|
{
|
||||||
|
return fmt::format(
|
||||||
|
"INSERT INTO {} ({}) ",
|
||||||
|
TableName(),
|
||||||
|
ColumnsRaw()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuyerTradeItems NewEntity()
|
||||||
|
{
|
||||||
|
BuyerTradeItems e{};
|
||||||
|
|
||||||
|
e.id = 0;
|
||||||
|
e.buyer_buy_lines_id = 0;
|
||||||
|
e.item_id = 0;
|
||||||
|
e.item_qty = 0;
|
||||||
|
e.item_icon = 0;
|
||||||
|
e.item_name = "0";
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuyerTradeItems GetBuyerTradeItems(
|
||||||
|
const std::vector<BuyerTradeItems> &buyer_trade_itemss,
|
||||||
|
int buyer_trade_items_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (auto &buyer_trade_items : buyer_trade_itemss) {
|
||||||
|
if (buyer_trade_items.id == buyer_trade_items_id) {
|
||||||
|
return buyer_trade_items;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuyerTradeItems FindOne(
|
||||||
|
Database& db,
|
||||||
|
int buyer_trade_items_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {} = {} LIMIT 1",
|
||||||
|
BaseSelect(),
|
||||||
|
PrimaryKey(),
|
||||||
|
buyer_trade_items_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto row = results.begin();
|
||||||
|
if (results.RowCount() == 1) {
|
||||||
|
BuyerTradeItems e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||||
|
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
|
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||||
|
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.item_name = row[5] ? row[5] : "0";
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NewEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteOne(
|
||||||
|
Database& db,
|
||||||
|
int buyer_trade_items_id
|
||||||
|
)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
PrimaryKey(),
|
||||||
|
buyer_trade_items_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int UpdateOne(
|
||||||
|
Database& db,
|
||||||
|
const BuyerTradeItems &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto columns = Columns();
|
||||||
|
|
||||||
|
v.push_back(columns[1] + " = " + std::to_string(e.buyer_buy_lines_id));
|
||||||
|
v.push_back(columns[2] + " = " + std::to_string(e.item_id));
|
||||||
|
v.push_back(columns[3] + " = " + std::to_string(e.item_qty));
|
||||||
|
v.push_back(columns[4] + " = " + std::to_string(e.item_icon));
|
||||||
|
v.push_back(columns[5] + " = '" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"UPDATE {} SET {} WHERE {} = {}",
|
||||||
|
TableName(),
|
||||||
|
Strings::Implode(", ", v),
|
||||||
|
PrimaryKey(),
|
||||||
|
e.id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuyerTradeItems InsertOne(
|
||||||
|
Database& db,
|
||||||
|
BuyerTradeItems e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.buyer_buy_lines_id));
|
||||||
|
v.push_back(std::to_string(e.item_id));
|
||||||
|
v.push_back(std::to_string(e.item_qty));
|
||||||
|
v.push_back(std::to_string(e.item_icon));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES ({})",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", v)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (results.Success()) {
|
||||||
|
e.id = results.LastInsertedID();
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = NewEntity();
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int InsertMany(
|
||||||
|
Database& db,
|
||||||
|
const std::vector<BuyerTradeItems> &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.buyer_buy_lines_id));
|
||||||
|
v.push_back(std::to_string(e.item_id));
|
||||||
|
v.push_back(std::to_string(e.item_qty));
|
||||||
|
v.push_back(std::to_string(e.item_icon));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} VALUES {}",
|
||||||
|
BaseInsert(),
|
||||||
|
Strings::Implode(",", insert_chunks)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<BuyerTradeItems> All(Database& db)
|
||||||
|
{
|
||||||
|
std::vector<BuyerTradeItems> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{}",
|
||||||
|
BaseSelect()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
BuyerTradeItems e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||||
|
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
|
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||||
|
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.item_name = row[5] ? row[5] : "0";
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<BuyerTradeItems> GetWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
std::vector<BuyerTradeItems> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"{} WHERE {}",
|
||||||
|
BaseSelect(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
BuyerTradeItems e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||||
|
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
|
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||||
|
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.item_name = row[5] ? row[5] : "0";
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int DeleteWhere(Database& db, const std::string &where_filter)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"DELETE FROM {} WHERE {}",
|
||||||
|
TableName(),
|
||||||
|
where_filter
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int Truncate(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"TRUNCATE TABLE {}",
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() ? results.RowsAffected() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 GetMaxId(Database& db)
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COALESCE(MAX({}), 0) FROM {}",
|
||||||
|
PrimaryKey(),
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int64 Count(Database& db, const std::string &where_filter = "")
|
||||||
|
{
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT COUNT(*) FROM {} {}",
|
||||||
|
TableName(),
|
||||||
|
(where_filter.empty() ? "" : "WHERE " + where_filter)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
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 BuyerTradeItems &e
|
||||||
|
)
|
||||||
|
{
|
||||||
|
std::vector<std::string> v;
|
||||||
|
|
||||||
|
v.push_back(std::to_string(e.id));
|
||||||
|
v.push_back(std::to_string(e.buyer_buy_lines_id));
|
||||||
|
v.push_back(std::to_string(e.item_id));
|
||||||
|
v.push_back(std::to_string(e.item_qty));
|
||||||
|
v.push_back(std::to_string(e.item_icon));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
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<BuyerTradeItems> &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.buyer_buy_lines_id));
|
||||||
|
v.push_back(std::to_string(e.item_id));
|
||||||
|
v.push_back(std::to_string(e.item_qty));
|
||||||
|
v.push_back(std::to_string(e.item_icon));
|
||||||
|
v.push_back("'" + Strings::Escape(e.item_name) + "'");
|
||||||
|
|
||||||
|
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_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||||
@@ -35,6 +35,7 @@ public:
|
|||||||
uint32_t ornamenticon;
|
uint32_t ornamenticon;
|
||||||
uint32_t ornamentidfile;
|
uint32_t ornamentidfile;
|
||||||
int32_t ornament_hero_model;
|
int32_t ornament_hero_model;
|
||||||
|
uint64_t guid;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string PrimaryKey()
|
static std::string PrimaryKey()
|
||||||
@@ -61,6 +62,7 @@ public:
|
|||||||
"ornamenticon",
|
"ornamenticon",
|
||||||
"ornamentidfile",
|
"ornamentidfile",
|
||||||
"ornament_hero_model",
|
"ornament_hero_model",
|
||||||
|
"guid",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,6 +85,7 @@ public:
|
|||||||
"ornamenticon",
|
"ornamenticon",
|
||||||
"ornamentidfile",
|
"ornamentidfile",
|
||||||
"ornament_hero_model",
|
"ornament_hero_model",
|
||||||
|
"guid",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,6 +142,7 @@ public:
|
|||||||
e.ornamenticon = 0;
|
e.ornamenticon = 0;
|
||||||
e.ornamentidfile = 0;
|
e.ornamentidfile = 0;
|
||||||
e.ornament_hero_model = 0;
|
e.ornament_hero_model = 0;
|
||||||
|
e.guid = 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -191,6 +195,7 @@ public:
|
|||||||
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||||
|
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -240,6 +245,7 @@ public:
|
|||||||
v.push_back(columns[13] + " = " + std::to_string(e.ornamenticon));
|
v.push_back(columns[13] + " = " + std::to_string(e.ornamenticon));
|
||||||
v.push_back(columns[14] + " = " + std::to_string(e.ornamentidfile));
|
v.push_back(columns[14] + " = " + std::to_string(e.ornamentidfile));
|
||||||
v.push_back(columns[15] + " = " + std::to_string(e.ornament_hero_model));
|
v.push_back(columns[15] + " = " + std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(columns[16] + " = " + std::to_string(e.guid));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -277,6 +283,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.ornamenticon));
|
v.push_back(std::to_string(e.ornamenticon));
|
||||||
v.push_back(std::to_string(e.ornamentidfile));
|
v.push_back(std::to_string(e.ornamentidfile));
|
||||||
v.push_back(std::to_string(e.ornament_hero_model));
|
v.push_back(std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(std::to_string(e.guid));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -322,6 +329,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.ornamenticon));
|
v.push_back(std::to_string(e.ornamenticon));
|
||||||
v.push_back(std::to_string(e.ornamentidfile));
|
v.push_back(std::to_string(e.ornamentidfile));
|
||||||
v.push_back(std::to_string(e.ornament_hero_model));
|
v.push_back(std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(std::to_string(e.guid));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
}
|
}
|
||||||
@@ -371,6 +379,7 @@ public:
|
|||||||
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||||
|
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -411,6 +420,7 @@ public:
|
|||||||
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
e.ornamenticon = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||||
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
e.ornamentidfile = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
e.ornament_hero_model = row[15] ? static_cast<int32_t>(atoi(row[15])) : 0;
|
||||||
|
e.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -501,6 +511,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.ornamenticon));
|
v.push_back(std::to_string(e.ornamenticon));
|
||||||
v.push_back(std::to_string(e.ornamentidfile));
|
v.push_back(std::to_string(e.ornamentidfile));
|
||||||
v.push_back(std::to_string(e.ornament_hero_model));
|
v.push_back(std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(std::to_string(e.guid));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -539,6 +550,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.ornamenticon));
|
v.push_back(std::to_string(e.ornamenticon));
|
||||||
v.push_back(std::to_string(e.ornamentidfile));
|
v.push_back(std::to_string(e.ornamentidfile));
|
||||||
v.push_back(std::to_string(e.ornament_hero_model));
|
v.push_back(std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(std::to_string(e.guid));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ public:
|
|||||||
uint32_t ornamenticon;
|
uint32_t ornamenticon;
|
||||||
uint32_t ornamentidfile;
|
uint32_t ornamentidfile;
|
||||||
int32_t ornament_hero_model;
|
int32_t ornament_hero_model;
|
||||||
|
uint64_t guid;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string PrimaryKey()
|
static std::string PrimaryKey()
|
||||||
@@ -63,6 +64,7 @@ public:
|
|||||||
"ornamenticon",
|
"ornamenticon",
|
||||||
"ornamentidfile",
|
"ornamentidfile",
|
||||||
"ornament_hero_model",
|
"ornament_hero_model",
|
||||||
|
"guid",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,6 +88,7 @@ public:
|
|||||||
"ornamenticon",
|
"ornamenticon",
|
||||||
"ornamentidfile",
|
"ornamentidfile",
|
||||||
"ornament_hero_model",
|
"ornament_hero_model",
|
||||||
|
"guid",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,6 +146,7 @@ public:
|
|||||||
e.ornamenticon = 0;
|
e.ornamenticon = 0;
|
||||||
e.ornamentidfile = 0;
|
e.ornamentidfile = 0;
|
||||||
e.ornament_hero_model = 0;
|
e.ornament_hero_model = 0;
|
||||||
|
e.guid = 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -196,6 +200,7 @@ public:
|
|||||||
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -246,6 +251,7 @@ public:
|
|||||||
v.push_back(columns[14] + " = " + std::to_string(e.ornamenticon));
|
v.push_back(columns[14] + " = " + std::to_string(e.ornamenticon));
|
||||||
v.push_back(columns[15] + " = " + std::to_string(e.ornamentidfile));
|
v.push_back(columns[15] + " = " + std::to_string(e.ornamentidfile));
|
||||||
v.push_back(columns[16] + " = " + std::to_string(e.ornament_hero_model));
|
v.push_back(columns[16] + " = " + std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(columns[17] + " = " + std::to_string(e.guid));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -284,6 +290,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.ornamenticon));
|
v.push_back(std::to_string(e.ornamenticon));
|
||||||
v.push_back(std::to_string(e.ornamentidfile));
|
v.push_back(std::to_string(e.ornamentidfile));
|
||||||
v.push_back(std::to_string(e.ornament_hero_model));
|
v.push_back(std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(std::to_string(e.guid));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -330,6 +337,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.ornamenticon));
|
v.push_back(std::to_string(e.ornamenticon));
|
||||||
v.push_back(std::to_string(e.ornamentidfile));
|
v.push_back(std::to_string(e.ornamentidfile));
|
||||||
v.push_back(std::to_string(e.ornament_hero_model));
|
v.push_back(std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(std::to_string(e.guid));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
}
|
}
|
||||||
@@ -380,6 +388,7 @@ public:
|
|||||||
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -421,6 +430,7 @@ public:
|
|||||||
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
e.ornamenticon = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||||
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
e.ornamentidfile = row[15] ? static_cast<uint32_t>(strtoul(row[15], nullptr, 10)) : 0;
|
||||||
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
e.ornament_hero_model = row[16] ? static_cast<int32_t>(atoi(row[16])) : 0;
|
||||||
|
e.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -512,6 +522,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.ornamenticon));
|
v.push_back(std::to_string(e.ornamenticon));
|
||||||
v.push_back(std::to_string(e.ornamentidfile));
|
v.push_back(std::to_string(e.ornamentidfile));
|
||||||
v.push_back(std::to_string(e.ornament_hero_model));
|
v.push_back(std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(std::to_string(e.guid));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -551,6 +562,7 @@ public:
|
|||||||
v.push_back(std::to_string(e.ornamenticon));
|
v.push_back(std::to_string(e.ornamenticon));
|
||||||
v.push_back(std::to_string(e.ornamentidfile));
|
v.push_back(std::to_string(e.ornamentidfile));
|
||||||
v.push_back(std::to_string(e.ornament_hero_model));
|
v.push_back(std::to_string(e.ornament_hero_model));
|
||||||
|
v.push_back(std::to_string(e.guid));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,31 +19,34 @@
|
|||||||
class BaseSpellBucketsRepository {
|
class BaseSpellBucketsRepository {
|
||||||
public:
|
public:
|
||||||
struct SpellBuckets {
|
struct SpellBuckets {
|
||||||
uint64_t spellid;
|
uint32_t spell_id;
|
||||||
std::string key_;
|
std::string bucket_name;
|
||||||
std::string value;
|
std::string bucket_value;
|
||||||
|
uint8_t bucket_comparison;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::string PrimaryKey()
|
static std::string PrimaryKey()
|
||||||
{
|
{
|
||||||
return std::string("spellid");
|
return std::string("spell_id");
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::string> Columns()
|
static std::vector<std::string> Columns()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
"spellid",
|
"spell_id",
|
||||||
"`key`",
|
"bucket_name",
|
||||||
"value",
|
"bucket_value",
|
||||||
|
"bucket_comparison",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::vector<std::string> SelectColumns()
|
static std::vector<std::string> SelectColumns()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
"spellid",
|
"spell_id",
|
||||||
"`key`",
|
"bucket_name",
|
||||||
"value",
|
"bucket_value",
|
||||||
|
"bucket_comparison",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -84,9 +87,10 @@ public:
|
|||||||
{
|
{
|
||||||
SpellBuckets e{};
|
SpellBuckets e{};
|
||||||
|
|
||||||
e.spellid = 0;
|
e.spell_id = 0;
|
||||||
e.key_ = "";
|
e.bucket_name = "";
|
||||||
e.value = "";
|
e.bucket_value = "";
|
||||||
|
e.bucket_comparison = 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -97,7 +101,7 @@ public:
|
|||||||
)
|
)
|
||||||
{
|
{
|
||||||
for (auto &spell_buckets : spell_bucketss) {
|
for (auto &spell_buckets : spell_bucketss) {
|
||||||
if (spell_buckets.spellid == spell_buckets_id) {
|
if (spell_buckets.spell_id == spell_buckets_id) {
|
||||||
return spell_buckets;
|
return spell_buckets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -123,9 +127,10 @@ public:
|
|||||||
if (results.RowCount() == 1) {
|
if (results.RowCount() == 1) {
|
||||||
SpellBuckets e{};
|
SpellBuckets e{};
|
||||||
|
|
||||||
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||||
e.key_ = row[1] ? row[1] : "";
|
e.bucket_name = row[1] ? row[1] : "";
|
||||||
e.value = row[2] ? row[2] : "";
|
e.bucket_value = row[2] ? row[2] : "";
|
||||||
|
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
@@ -159,9 +164,10 @@ public:
|
|||||||
|
|
||||||
auto columns = Columns();
|
auto columns = Columns();
|
||||||
|
|
||||||
v.push_back(columns[0] + " = " + std::to_string(e.spellid));
|
v.push_back(columns[0] + " = " + std::to_string(e.spell_id));
|
||||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'");
|
v.push_back(columns[1] + " = '" + Strings::Escape(e.bucket_name) + "'");
|
||||||
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
|
v.push_back(columns[2] + " = '" + Strings::Escape(e.bucket_value) + "'");
|
||||||
|
v.push_back(columns[3] + " = " + std::to_string(e.bucket_comparison));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -169,7 +175,7 @@ public:
|
|||||||
TableName(),
|
TableName(),
|
||||||
Strings::Implode(", ", v),
|
Strings::Implode(", ", v),
|
||||||
PrimaryKey(),
|
PrimaryKey(),
|
||||||
e.spellid
|
e.spell_id
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -183,9 +189,10 @@ public:
|
|||||||
{
|
{
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
v.push_back(std::to_string(e.spellid));
|
v.push_back(std::to_string(e.spell_id));
|
||||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||||
|
v.push_back(std::to_string(e.bucket_comparison));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -196,7 +203,7 @@ public:
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (results.Success()) {
|
if (results.Success()) {
|
||||||
e.spellid = results.LastInsertedID();
|
e.spell_id = results.LastInsertedID();
|
||||||
return e;
|
return e;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -215,9 +222,10 @@ public:
|
|||||||
for (auto &e: entries) {
|
for (auto &e: entries) {
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
v.push_back(std::to_string(e.spellid));
|
v.push_back(std::to_string(e.spell_id));
|
||||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||||
|
v.push_back(std::to_string(e.bucket_comparison));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
}
|
}
|
||||||
@@ -251,9 +259,10 @@ public:
|
|||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
SpellBuckets e{};
|
SpellBuckets e{};
|
||||||
|
|
||||||
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||||
e.key_ = row[1] ? row[1] : "";
|
e.bucket_name = row[1] ? row[1] : "";
|
||||||
e.value = row[2] ? row[2] : "";
|
e.bucket_value = row[2] ? row[2] : "";
|
||||||
|
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -278,9 +287,10 @@ public:
|
|||||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
SpellBuckets e{};
|
SpellBuckets e{};
|
||||||
|
|
||||||
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||||
e.key_ = row[1] ? row[1] : "";
|
e.bucket_name = row[1] ? row[1] : "";
|
||||||
e.value = row[2] ? row[2] : "";
|
e.bucket_value = row[2] ? row[2] : "";
|
||||||
|
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||||
|
|
||||||
all_entries.push_back(e);
|
all_entries.push_back(e);
|
||||||
}
|
}
|
||||||
@@ -355,9 +365,10 @@ public:
|
|||||||
{
|
{
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
v.push_back(std::to_string(e.spellid));
|
v.push_back(std::to_string(e.spell_id));
|
||||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||||
|
v.push_back(std::to_string(e.bucket_comparison));
|
||||||
|
|
||||||
auto results = db.QueryDatabase(
|
auto results = db.QueryDatabase(
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -380,9 +391,10 @@ public:
|
|||||||
for (auto &e: entries) {
|
for (auto &e: entries) {
|
||||||
std::vector<std::string> v;
|
std::vector<std::string> v;
|
||||||
|
|
||||||
v.push_back(std::to_string(e.spellid));
|
v.push_back(std::to_string(e.spell_id));
|
||||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||||
|
v.push_back(std::to_string(e.bucket_comparison));
|
||||||
|
|
||||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,24 @@ public:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Custom extended repository methods here
|
// Custom extended repository methods here
|
||||||
|
static std::vector<std::string> GetBaseDataFileLines(Database& db)
|
||||||
|
{
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT CONCAT_WS('^', {}) FROM {} ORDER BY `level`, `class` ASC",
|
||||||
|
ColumnsRaw(),
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto row : results) {
|
||||||
|
lines.emplace_back(row[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_BASE_DATA_REPOSITORY_H
|
#endif //EQEMU_BASE_DATA_REPOSITORY_H
|
||||||
|
|||||||
@@ -0,0 +1,356 @@
|
|||||||
|
#ifndef EQEMU_BUYER_BUY_LINES_REPOSITORY_H
|
||||||
|
#define EQEMU_BUYER_BUY_LINES_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../database.h"
|
||||||
|
#include "../strings.h"
|
||||||
|
#include "base/base_buyer_buy_lines_repository.h"
|
||||||
|
#include "buyer_trade_items_repository.h"
|
||||||
|
#include "character_data_repository.h"
|
||||||
|
#include "buyer_repository.h"
|
||||||
|
|
||||||
|
#include "../eq_packet_structs.h"
|
||||||
|
|
||||||
|
class BuyerBuyLinesRepository: public BaseBuyerBuyLinesRepository {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file was auto generated and can be modified and extended upon
|
||||||
|
*
|
||||||
|
* Base repository methods are automatically
|
||||||
|
* generated in the "base" version of this repository. The base repository
|
||||||
|
* is immutable and to be left untouched, while methods in this class
|
||||||
|
* are used as extension methods for more specific persistence-layer
|
||||||
|
* accessors or mutators.
|
||||||
|
*
|
||||||
|
* Base Methods (Subject to be expanded upon in time)
|
||||||
|
*
|
||||||
|
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||||
|
*
|
||||||
|
* InsertOne
|
||||||
|
* UpdateOne
|
||||||
|
* DeleteOne
|
||||||
|
* FindOne
|
||||||
|
* GetWhere(std::string where_filter)
|
||||||
|
* DeleteWhere(std::string where_filter)
|
||||||
|
* InsertMany
|
||||||
|
* All
|
||||||
|
*
|
||||||
|
* Example custom methods in a repository
|
||||||
|
*
|
||||||
|
* BuyerBuyLinesRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||||
|
* BuyerBuyLinesRepository::GetWhereNeverExpires()
|
||||||
|
* BuyerBuyLinesRepository::GetWhereXAndY()
|
||||||
|
* BuyerBuyLinesRepository::DeleteWhereXAndY()
|
||||||
|
*
|
||||||
|
* Most of the above could be covered by base methods, but if you as a developer
|
||||||
|
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||||
|
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||||
|
* method and encapsulate filters there
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Custom extended repository methods here
|
||||||
|
|
||||||
|
struct WelcomeData_Struct {
|
||||||
|
uint32 count_of_buyers;
|
||||||
|
uint32 count_of_items;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int CreateBuyLine(Database& db, const BuyerLineItems_Struct b, uint32 char_id)
|
||||||
|
{
|
||||||
|
auto buyer = BuyerRepository::GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1", char_id));
|
||||||
|
if (buyer.empty()){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BuyerBuyLinesRepository::BuyerBuyLines buy_lines{};
|
||||||
|
buy_lines.id = 0;
|
||||||
|
buy_lines.buyer_id = buyer.front().id;
|
||||||
|
buy_lines.char_id = char_id;
|
||||||
|
buy_lines.buy_slot_id = b.slot;
|
||||||
|
buy_lines.item_id = b.item_id;
|
||||||
|
buy_lines.item_name = b.item_name;
|
||||||
|
buy_lines.item_icon = b.item_icon;
|
||||||
|
buy_lines.item_qty = b.item_quantity;
|
||||||
|
buy_lines.item_price = b.item_cost;
|
||||||
|
auto e = InsertOne(db, buy_lines);
|
||||||
|
|
||||||
|
std::vector<BuyerTradeItemsRepository::BuyerTradeItems> queue;
|
||||||
|
|
||||||
|
for (auto const &r: b.trade_items) {
|
||||||
|
BuyerTradeItemsRepository::BuyerTradeItems bti{};
|
||||||
|
bti.id = 0;
|
||||||
|
bti.buyer_buy_lines_id = e.id;
|
||||||
|
bti.item_id = r.item_id;
|
||||||
|
bti.item_qty = r.item_quantity;
|
||||||
|
bti.item_icon = r.item_icon;
|
||||||
|
bti.item_name = r.item_name;
|
||||||
|
|
||||||
|
if (bti.item_id) {
|
||||||
|
queue.push_back(bti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queue.empty()) {
|
||||||
|
BuyerTradeItemsRepository::InsertMany(db, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int ModifyBuyLine(Database& db, const BuyerLineItems_Struct b, uint32 char_id)
|
||||||
|
{
|
||||||
|
auto b_lines = GetWhere(db, fmt::format("`char_id` = '{}' AND `buy_slot_id` = '{}';", char_id, b.slot));
|
||||||
|
if (b_lines.empty() || b_lines.size() > 1){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto b_line = b_lines.front();
|
||||||
|
|
||||||
|
b_line.item_qty = b.item_quantity;
|
||||||
|
b_line.item_price = b.item_cost;
|
||||||
|
b_line.item_icon = b.item_icon;
|
||||||
|
auto e = UpdateOne(db, b_line);
|
||||||
|
|
||||||
|
std::vector<BuyerTradeItemsRepository::BuyerTradeItems> queue;
|
||||||
|
|
||||||
|
BuyerTradeItemsRepository::DeleteWhere(db, fmt::format("`buyer_buy_lines_id` = '{}';", b_line.id));
|
||||||
|
for (auto const &r: b.trade_items) {
|
||||||
|
BuyerTradeItemsRepository::BuyerTradeItems bti{};
|
||||||
|
bti.id = 0;
|
||||||
|
bti.buyer_buy_lines_id = b_line.id;
|
||||||
|
bti.item_id = r.item_id;
|
||||||
|
bti.item_qty = r.item_quantity;
|
||||||
|
bti.item_icon = r.item_icon;
|
||||||
|
bti.item_name = r.item_name;
|
||||||
|
|
||||||
|
if (bti.item_id) {
|
||||||
|
queue.push_back(bti);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!queue.empty()) {
|
||||||
|
BuyerTradeItemsRepository::InsertMany(db, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DeleteBuyLine(Database &db, uint32 char_id, int32 slot_id = 0xffffffff)
|
||||||
|
{
|
||||||
|
std::vector<BuyerBuyLines> buylines{};
|
||||||
|
if (slot_id == 0xffffffff) {
|
||||||
|
auto buylines = GetWhere(db, fmt::format("`char_id` = '{}'", char_id));
|
||||||
|
DeleteWhere(db, fmt::format("`char_id` = '{}'", char_id));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto buylines = GetWhere(db, fmt::format("`char_id` = '{}' AND `buy_slot_id` = '{}'", char_id, slot_id));
|
||||||
|
DeleteWhere(db, fmt::format("`char_id` = '{}' AND `buy_slot_id` = '{}'", char_id, slot_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buylines.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> buyline_ids{};
|
||||||
|
for (auto const &bl: buylines) {
|
||||||
|
buyline_ids.push_back((std::to_string(bl.id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!buyline_ids.empty()) {
|
||||||
|
BuyerTradeItemsRepository::DeleteWhere(
|
||||||
|
db,
|
||||||
|
fmt::format(
|
||||||
|
"`buyer_buy_lines_id` IN({})",
|
||||||
|
Strings::Implode(", ", buyline_ids)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<BuyerLineItems_Struct> GetBuyLines(Database &db, uint32 char_id)
|
||||||
|
{
|
||||||
|
std::vector<BuyerLineItems_Struct> all_entries{};
|
||||||
|
|
||||||
|
auto buy_line = GetWhere(db, fmt::format("`char_id` = '{}';", char_id));
|
||||||
|
if (buy_line.empty()){
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buy_line_trade_items = BuyerTradeItemsRepository::GetWhere(
|
||||||
|
db,
|
||||||
|
fmt::format(
|
||||||
|
"`buyer_buy_lines_id` IN (SELECT b.id FROM buyer_buy_lines AS b WHERE b.char_id = '{}')",
|
||||||
|
char_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(buy_line.size());
|
||||||
|
|
||||||
|
for (auto const &l: buy_line) {
|
||||||
|
BuyerLineItems_Struct bli{};
|
||||||
|
bli.item_id = l.item_id;
|
||||||
|
bli.item_cost = l.item_price;
|
||||||
|
bli.item_quantity = l.item_qty;
|
||||||
|
bli.slot = l.buy_slot_id;
|
||||||
|
bli.item_name = l.item_name;
|
||||||
|
|
||||||
|
for (auto const &i: GetSubIDs(buy_line_trade_items, l.id)) {
|
||||||
|
BuyerLineTradeItems_Struct blti{};
|
||||||
|
blti.item_id = i.item_id;
|
||||||
|
blti.item_icon = i.item_icon;
|
||||||
|
blti.item_quantity = i.item_qty;
|
||||||
|
blti.item_id = i.item_id;
|
||||||
|
blti.item_name = i.item_name;
|
||||||
|
bli.trade_items.push_back(blti);
|
||||||
|
}
|
||||||
|
all_entries.push_back(bli);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BuyerLineSearch_Struct SearchBuyLines(
|
||||||
|
Database &db,
|
||||||
|
std::string &search_string,
|
||||||
|
uint32 char_id = 0,
|
||||||
|
uint32 zone_id = 0,
|
||||||
|
uint32 zone_instance_id = 0
|
||||||
|
)
|
||||||
|
{
|
||||||
|
BuyerLineSearch_Struct all_entries{};
|
||||||
|
std::string where_clause(fmt::format("`item_name` LIKE \"%{}%\" ", search_string));
|
||||||
|
|
||||||
|
if (char_id) {
|
||||||
|
where_clause += fmt::format("AND `char_id` = '{}' ", char_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zone_id) {
|
||||||
|
auto buyers = BuyerRepository::GetWhere(
|
||||||
|
db,
|
||||||
|
fmt::format(
|
||||||
|
"`char_zone_id` = '{}' AND char_zone_instance_id = '{}'",
|
||||||
|
zone_id,
|
||||||
|
zone_instance_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
std::vector<std::string> char_ids{};
|
||||||
|
for (auto const &bl : buyers) {
|
||||||
|
char_ids.push_back((std::to_string(bl.char_id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
where_clause += fmt::format("AND `char_id` IN ({}) ", Strings::Implode(", ", char_ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buy_line = GetWhere(db, where_clause);
|
||||||
|
if (buy_line.empty()){
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> ids{};
|
||||||
|
std::vector<std::string> char_ids{};
|
||||||
|
for (auto const &bl : buy_line) {
|
||||||
|
if (std::find(ids.begin(), ids.end(), std::to_string(bl.id)) == std::end(ids)) {
|
||||||
|
ids.push_back(std::to_string(bl.id));
|
||||||
|
}
|
||||||
|
if (std::find(char_ids.begin(), char_ids.end(), std::to_string(bl.char_id)) == std::end(char_ids)) {
|
||||||
|
char_ids.push_back((std::to_string(bl.char_id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buy_line_trade_items = BuyerTradeItemsRepository::GetWhere(
|
||||||
|
db,
|
||||||
|
fmt::format(
|
||||||
|
"`buyer_buy_lines_id` IN ({});",
|
||||||
|
Strings::Implode(", ", ids)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
auto char_names = BuyerRepository::GetWhere(
|
||||||
|
db,
|
||||||
|
fmt::format(
|
||||||
|
"`char_id` IN ({});",
|
||||||
|
Strings::Implode(", ", char_ids)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.no_items = buy_line.size();
|
||||||
|
for (auto const &l: buy_line) {
|
||||||
|
BuyerLineItemsSearch_Struct blis{};
|
||||||
|
blis.slot = l.buy_slot_id;
|
||||||
|
blis.item_id = l.item_id;
|
||||||
|
blis.item_cost = l.item_price;
|
||||||
|
blis.item_icon = l.item_icon;
|
||||||
|
blis.item_quantity = l.item_qty;
|
||||||
|
blis.buyer_id = l.char_id;
|
||||||
|
auto it = std::find_if(
|
||||||
|
char_names.cbegin(),
|
||||||
|
char_names.cend(),
|
||||||
|
[&](BuyerRepository::Buyer e) { return e.char_id == l.char_id; }
|
||||||
|
);
|
||||||
|
blis.buyer_name = it != char_names.end() ? it->char_name : std::string("");
|
||||||
|
blis.buyer_entity_id = it != char_names.end() ? it->char_entity_id : 0;
|
||||||
|
blis.buyer_zone_id = it != char_names.end() ? it->char_zone_id : 0;
|
||||||
|
blis.buyer_zone_instance_id = it != char_names.end() ? it->char_zone_instance_id : 0;
|
||||||
|
strn0cpy(blis.item_name, l.item_name.c_str(), sizeof(blis.item_name));
|
||||||
|
|
||||||
|
for (auto const &i: GetSubIDs(buy_line_trade_items, l.id)) {
|
||||||
|
BuyerLineTradeItems_Struct e{};
|
||||||
|
e.item_id = i.item_id;
|
||||||
|
e.item_icon = i.item_icon;
|
||||||
|
e.item_quantity = i.item_qty;
|
||||||
|
e.item_id = i.item_id;
|
||||||
|
e.item_name = i.item_name;
|
||||||
|
|
||||||
|
blis.trade_items.push_back(e);
|
||||||
|
}
|
||||||
|
all_entries.buy_line.push_back(blis);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<BuyerTradeItemsRepository::BuyerTradeItems>
|
||||||
|
GetSubIDs(std::vector<BuyerTradeItemsRepository::BuyerTradeItems> &in, uint64 id)
|
||||||
|
{
|
||||||
|
std::vector<BuyerTradeItemsRepository::BuyerTradeItems> results{};
|
||||||
|
std::vector<uint64> indices{};
|
||||||
|
|
||||||
|
auto it = in.begin();
|
||||||
|
while ((it = std::find_if(
|
||||||
|
it,
|
||||||
|
in.end(),
|
||||||
|
[&](BuyerTradeItemsRepository::BuyerTradeItems const &e) {
|
||||||
|
return e.buyer_buy_lines_id == id;
|
||||||
|
}
|
||||||
|
))
|
||||||
|
!= in.end()
|
||||||
|
) {
|
||||||
|
indices.push_back(std::distance(in.begin(), it));
|
||||||
|
results.push_back(*it);
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
static WelcomeData_Struct GetWelcomeData(Database &db)
|
||||||
|
{
|
||||||
|
WelcomeData_Struct e{};
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase("SELECT COUNT(DISTINCT char_id), COUNT(char_id) FROM buyer;");
|
||||||
|
|
||||||
|
if (!results.RowCount()) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto r = results.begin();
|
||||||
|
e.count_of_buyers = Strings::ToInt(r[0]);
|
||||||
|
e.count_of_items = Strings::ToInt(r[1]);
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //EQEMU_BUYER_BUY_LINES_REPOSITORY_H
|
||||||
@@ -4,6 +4,8 @@
|
|||||||
#include "../database.h"
|
#include "../database.h"
|
||||||
#include "../strings.h"
|
#include "../strings.h"
|
||||||
#include "base/base_buyer_repository.h"
|
#include "base/base_buyer_repository.h"
|
||||||
|
#include "base/base_buyer_trade_items_repository.h"
|
||||||
|
#include "base/base_buyer_buy_lines_repository.h"
|
||||||
|
|
||||||
class BuyerRepository: public BaseBuyerRepository {
|
class BuyerRepository: public BaseBuyerRepository {
|
||||||
public:
|
public:
|
||||||
@@ -45,6 +47,93 @@ public:
|
|||||||
|
|
||||||
// Custom extended repository methods here
|
// Custom extended repository methods here
|
||||||
|
|
||||||
|
static bool UpdateWelcomeMessage(Database& db, uint32 char_id, const char *message) {
|
||||||
|
|
||||||
|
auto const b = GetWhere(db, fmt::format("`char_id` = '{}';", char_id));
|
||||||
|
|
||||||
|
if (b.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buyer = b.front();
|
||||||
|
buyer.welcome_message = message;
|
||||||
|
return UpdateOne(db, buyer);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string GetWelcomeMessage(Database& db, uint32 char_id) {
|
||||||
|
|
||||||
|
auto const b = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
|
||||||
|
if (b.empty()) {
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.front().welcome_message;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int UpdateTransactionDate(Database& db, uint32 char_id, time_t transaction_date) {
|
||||||
|
auto b = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
|
||||||
|
if (b.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto e = b.front();
|
||||||
|
e.transaction_date = transaction_date;
|
||||||
|
|
||||||
|
return UpdateOne(db, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
static time_t GetTransactionDate(Database& db, uint32 char_id) {
|
||||||
|
auto b = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
|
||||||
|
if (b.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto e = b.front();
|
||||||
|
|
||||||
|
return e.transaction_date;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DeleteBuyer(Database &db, uint32 char_id)
|
||||||
|
{
|
||||||
|
if (char_id == 0) {
|
||||||
|
Truncate(db);
|
||||||
|
BaseBuyerBuyLinesRepository::Truncate(db);
|
||||||
|
BaseBuyerTradeItemsRepository::Truncate(db);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
auto buyer = GetWhere(db, fmt::format("`char_id` = '{}' LIMIT 1;", char_id));
|
||||||
|
if (buyer.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto buy_lines = BaseBuyerBuyLinesRepository::GetWhere(
|
||||||
|
db,
|
||||||
|
fmt::format("`buyer_id` = '{}'", buyer.front().id)
|
||||||
|
);
|
||||||
|
if (buy_lines.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> buy_line_ids{};
|
||||||
|
for (auto const &bl: buy_lines) {
|
||||||
|
buy_line_ids.push_back(std::to_string(bl.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteWhere(db, fmt::format("`char_id` = '{}';", char_id));
|
||||||
|
BaseBuyerBuyLinesRepository::DeleteWhere(
|
||||||
|
db,
|
||||||
|
fmt::format("`id` IN({})", Strings::Implode(", ", buy_line_ids))
|
||||||
|
);
|
||||||
|
BaseBuyerTradeItemsRepository::DeleteWhere(
|
||||||
|
db,
|
||||||
|
fmt::format(
|
||||||
|
"`buyer_buy_lines_id` IN({})",
|
||||||
|
Strings::Implode(", ", buy_line_ids))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_BUYER_REPOSITORY_H
|
#endif //EQEMU_BUYER_REPOSITORY_H
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
#ifndef EQEMU_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||||
|
#define EQEMU_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||||
|
|
||||||
|
#include "../database.h"
|
||||||
|
#include "../strings.h"
|
||||||
|
#include "base/base_buyer_trade_items_repository.h"
|
||||||
|
|
||||||
|
class BuyerTradeItemsRepository: public BaseBuyerTradeItemsRepository {
|
||||||
|
public:
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This file was auto generated and can be modified and extended upon
|
||||||
|
*
|
||||||
|
* Base repository methods are automatically
|
||||||
|
* generated in the "base" version of this repository. The base repository
|
||||||
|
* is immutable and to be left untouched, while methods in this class
|
||||||
|
* are used as extension methods for more specific persistence-layer
|
||||||
|
* accessors or mutators.
|
||||||
|
*
|
||||||
|
* Base Methods (Subject to be expanded upon in time)
|
||||||
|
*
|
||||||
|
* Note: Not all tables are designed appropriately to fit functionality with all base methods
|
||||||
|
*
|
||||||
|
* InsertOne
|
||||||
|
* UpdateOne
|
||||||
|
* DeleteOne
|
||||||
|
* FindOne
|
||||||
|
* GetWhere(std::string where_filter)
|
||||||
|
* DeleteWhere(std::string where_filter)
|
||||||
|
* InsertMany
|
||||||
|
* All
|
||||||
|
*
|
||||||
|
* Example custom methods in a repository
|
||||||
|
*
|
||||||
|
* BuyerTradeItemsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||||
|
* BuyerTradeItemsRepository::GetWhereNeverExpires()
|
||||||
|
* BuyerTradeItemsRepository::GetWhereXAndY()
|
||||||
|
* BuyerTradeItemsRepository::DeleteWhereXAndY()
|
||||||
|
*
|
||||||
|
* Most of the above could be covered by base methods, but if you as a developer
|
||||||
|
* find yourself re-using logic for other parts of the code, its best to just make a
|
||||||
|
* method that can be re-used easily elsewhere especially if it can use a base repository
|
||||||
|
* method and encapsulate filters there
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Custom extended repository methods here
|
||||||
|
|
||||||
|
static std::vector<BuyerTradeItems> GetTradeItems(Database& db, const uint32 char_id)
|
||||||
|
{
|
||||||
|
std::vector<BuyerTradeItems> all_entries;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT bti.* "
|
||||||
|
"FROM buyer_trade_items AS bti "
|
||||||
|
"INNER JOIN buyer_buy_lines AS bbl ON bti.buyer_buy_lines_id = bbl.id "
|
||||||
|
"WHERE bbl.char_id = '{}';",
|
||||||
|
char_id
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
all_entries.reserve(results.RowCount());
|
||||||
|
|
||||||
|
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||||
|
BuyerTradeItems e{};
|
||||||
|
|
||||||
|
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||||
|
e.buyer_buy_lines_id = row[1] ? strtoull(row[1], nullptr, 10) : 0;
|
||||||
|
e.item_id = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||||
|
e.item_qty = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||||
|
e.item_icon = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||||
|
e.item_name = row[5] ? row[5] : "0";
|
||||||
|
|
||||||
|
all_entries.push_back(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return all_entries;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //EQEMU_BUYER_TRADE_ITEMS_REPOSITORY_H
|
||||||
@@ -66,6 +66,7 @@ public:
|
|||||||
{.parent_command = "find", .sub_command = "recipe", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findrecipe"},
|
{.parent_command = "find", .sub_command = "recipe", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findrecipe"},
|
||||||
{.parent_command = "find", .sub_command = "skill", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findskill"},
|
{.parent_command = "find", .sub_command = "skill", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findskill"},
|
||||||
{.parent_command = "find", .sub_command = "special_ability", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fsa|findspecialability"},
|
{.parent_command = "find", .sub_command = "special_ability", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fsa|findspecialability"},
|
||||||
|
{.parent_command = "find", .sub_command = "stance", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findstance"},
|
||||||
{.parent_command = "find", .sub_command = "spell", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fs|findspell"},
|
{.parent_command = "find", .sub_command = "spell", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fs|findspell"},
|
||||||
{.parent_command = "find", .sub_command = "task", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findtask"},
|
{.parent_command = "find", .sub_command = "task", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findtask"},
|
||||||
{.parent_command = "find", .sub_command = "zone", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fz|findzone"},
|
{.parent_command = "find", .sub_command = "zone", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fz|findzone"},
|
||||||
@@ -84,7 +85,6 @@ public:
|
|||||||
{.parent_command = "set", .sub_command = "endurance", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setendurance"},
|
{.parent_command = "set", .sub_command = "endurance", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setendurance"},
|
||||||
{.parent_command = "set", .sub_command = "endurance_full", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "endurance"},
|
{.parent_command = "set", .sub_command = "endurance_full", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "endurance"},
|
||||||
{.parent_command = "set", .sub_command = "exp", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setxp|setexp"},
|
{.parent_command = "set", .sub_command = "exp", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setxp|setexp"},
|
||||||
{.parent_command = "set", .sub_command = "faction", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "setfaction"},
|
|
||||||
{.parent_command = "set", .sub_command = "flymode", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "flymode"},
|
{.parent_command = "set", .sub_command = "flymode", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "flymode"},
|
||||||
{.parent_command = "set", .sub_command = "freeze", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "freeze|unfreeze"},
|
{.parent_command = "set", .sub_command = "freeze", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "freeze|unfreeze"},
|
||||||
{.parent_command = "set", .sub_command = "gender", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "gender"},
|
{.parent_command = "set", .sub_command = "gender", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "gender"},
|
||||||
|
|||||||
@@ -44,7 +44,24 @@ public:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Custom extended repository methods here
|
// Custom extended repository methods here
|
||||||
|
static std::vector<std::string> GetDBStrFileLines(Database& db)
|
||||||
|
{
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT CONCAT(CONCAT_WS('^', {}), '^0') FROM {} ORDER BY `id`, `type` ASC",
|
||||||
|
ColumnsRaw(),
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto row : results) {
|
||||||
|
lines.emplace_back(row[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_DB_STR_REPOSITORY_H
|
#endif //EQEMU_DB_STR_REPOSITORY_H
|
||||||
|
|||||||
@@ -44,7 +44,23 @@ public:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Custom extended repository methods here
|
// Custom extended repository methods here
|
||||||
|
static std::vector<std::string> GetSkillCapFileLines(Database& db)
|
||||||
|
{
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT CONCAT_WS('^', `class_id`, `skill_id`, `level`, `cap`, `class_`) FROM {} ORDER BY `class_id`, `skill_id`, `level` ASC",
|
||||||
|
TableName()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto row : results) {
|
||||||
|
lines.emplace_back(row[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_SKILL_CAPS_REPOSITORY_H
|
#endif //EQEMU_SKILL_CAPS_REPOSITORY_H
|
||||||
|
|||||||
@@ -44,7 +44,25 @@ public:
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// Custom extended repository methods here
|
// Custom extended repository methods here
|
||||||
|
static std::vector<std::string> GetSpellFileLines(Database& db)
|
||||||
|
{
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
|
||||||
|
auto results = db.QueryDatabase(
|
||||||
|
fmt::format(
|
||||||
|
"SELECT CONCAT_WS('^', {}) FROM {} ORDER BY {} ASC",
|
||||||
|
ColumnsRaw(),
|
||||||
|
TableName(),
|
||||||
|
PrimaryKey()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
for (auto row : results) {
|
||||||
|
lines.emplace_back(row[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //EQEMU_SPELLS_NEW_REPOSITORY_H
|
#endif //EQEMU_SPELLS_NEW_REPOSITORY_H
|
||||||
|
|||||||
@@ -189,6 +189,8 @@ void RuleManager::ResetRules(bool reload) {
|
|||||||
m_RuleRealValues[ Real__##rule_name ] = default_value;
|
m_RuleRealValues[ Real__##rule_name ] = default_value;
|
||||||
#define RULE_BOOL(category_name, rule_name, default_value, notes) \
|
#define RULE_BOOL(category_name, rule_name, default_value, notes) \
|
||||||
m_RuleBoolValues[ Bool__##rule_name ] = default_value;
|
m_RuleBoolValues[ Bool__##rule_name ] = default_value;
|
||||||
|
#define RULE_STRING(category_name, rule_name, default_value, notes) \
|
||||||
|
m_RuleStringValues[ String__##rule_name ] = default_value;
|
||||||
#include "ruletypes.h"
|
#include "ruletypes.h"
|
||||||
|
|
||||||
// restore these rules to their pre-reset values
|
// restore these rules to their pre-reset values
|
||||||
|
|||||||
+18
-1
@@ -178,6 +178,7 @@ RULE_BOOL(Character, NoSkillsOnHorse, false, "Enabling this will prevent Bind Wo
|
|||||||
RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing")
|
RULE_BOOL(Character, UseNoJunkFishing, false, "Disregards junk items when fishing")
|
||||||
RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted")
|
RULE_BOOL(Character, SoftDeletes, true, "When characters are deleted in character select, they are only soft deleted")
|
||||||
RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated")
|
RULE_INT(Character, DefaultGuild, 0, "If not 0, new characters placed into the guild # indicated")
|
||||||
|
RULE_INT(Character, DefaultGuildRank, 8, "Default guild rank when Character:DefaultGuild is a non-0 value, default is 8 (Recruit)")
|
||||||
RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared")
|
RULE_BOOL(Character, ProcessFearedProximity, false, "Processes proximity checks when feared")
|
||||||
RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.")
|
RULE_BOOL(Character, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.")
|
||||||
RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.")
|
RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.")
|
||||||
@@ -228,6 +229,7 @@ RULE_BOOL(Character, GroupInvitesRequireTarget, false, "Enable to require player
|
|||||||
RULE_BOOL(Character, PlayerTradingLoreFeedback, true, "If enabled, during a player to player trade, if lore items exist, it will output which items.")
|
RULE_BOOL(Character, PlayerTradingLoreFeedback, true, "If enabled, during a player to player trade, if lore items exist, it will output which items.")
|
||||||
RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will always succeed its skill check. Default: 199")
|
RULE_INT(Character, MendAlwaysSucceedValue, 199, "Value at which mend will always succeed its skill check. Default: 199")
|
||||||
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
|
RULE_BOOL(Character, SneakAlwaysSucceedOver100, false, "When sneak skill is over 100, always succeed sneak/hide. Default: false")
|
||||||
|
RULE_INT(Character, BandolierSwapDelay, 0, "Bandolier swap delay in milliseconds, default is 0")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Mercs)
|
RULE_CATEGORY(Mercs)
|
||||||
@@ -242,6 +244,9 @@ RULE_INT(Mercs, AggroRadiusPuller, 25, "Determines the distance from which a mer
|
|||||||
RULE_INT(Mercs, ResurrectRadius, 50, "Determines the distance from which a healer merc will attempt to resurrect a group member's corpse")
|
RULE_INT(Mercs, ResurrectRadius, 50, "Determines the distance from which a healer merc will attempt to resurrect a group member's corpse")
|
||||||
RULE_INT(Mercs, ScaleRate, 100, "Merc scale factor")
|
RULE_INT(Mercs, ScaleRate, 100, "Merc scale factor")
|
||||||
RULE_BOOL(Mercs, AllowMercSuspendInCombat, true, "Allow merc suspend in combat")
|
RULE_BOOL(Mercs, AllowMercSuspendInCombat, true, "Allow merc suspend in combat")
|
||||||
|
RULE_BOOL(Mercs, MercsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
|
||||||
|
RULE_INT(Mercs, MercsHasteCap, 100, "Haste cap for non-v3(over haste) haste")
|
||||||
|
RULE_INT(Mercs, MercsHastev3Cap, 25, "Haste cap for v3(over haste) haste")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Guild)
|
RULE_CATEGORY(Guild)
|
||||||
@@ -619,7 +624,8 @@ RULE_INT(Combat, PCAccuracyAvoidanceMod2Scale, 100, "Scale Factor for PC Accurac
|
|||||||
RULE_BOOL(Combat, AllowRaidTargetBlind, false, "Toggle to allow raid targets to be blinded, default is false (Live-like)")
|
RULE_BOOL(Combat, AllowRaidTargetBlind, false, "Toggle to allow raid targets to be blinded, default is false (Live-like)")
|
||||||
RULE_BOOL(Combat, RogueBackstabHasteCorrection, false, "Toggle to enable correction for Haste impacting Backstab DPS too much. DEFAULT: false")
|
RULE_BOOL(Combat, RogueBackstabHasteCorrection, false, "Toggle to enable correction for Haste impacting Backstab DPS too much. DEFAULT: false")
|
||||||
RULE_BOOL(Combat, LegacyComputeDefense, false, "Trim AGI Scaling of defense mostly for lower levels to help compensate for the newer agi based defense system. Default: False")
|
RULE_BOOL(Combat, LegacyComputeDefense, false, "Trim AGI Scaling of defense mostly for lower levels to help compensate for the newer agi based defense system. Default: False")
|
||||||
RULE_REAL(Combat, SlayDamageAdjustment, 0.5, "Slay Damage Adjustment - Multiply final slay damage by this value. Default: 0.5")
|
RULE_REAL(Combat, SlayDamageMultiplier, 1.0, "Slay Damage Adjustment - Multiply final slay damage by this value. Default: 1.0")
|
||||||
|
RULE_REAL(Combat, SlayRateMultiplier, 1.0, "Slay Rate Adjustments - Multiply final slay rate check by this value. Default: 1.0")
|
||||||
RULE_INT(Combat, MaximumLevelStunsCripplingBlow, 55, "Maximum level that Crippling Blows will stun a npc. Default: 55")
|
RULE_INT(Combat, MaximumLevelStunsCripplingBlow, 55, "Maximum level that Crippling Blows will stun a npc. Default: 55")
|
||||||
RULE_INT(Combat, ArcheryBaseDamage, 0, "Archery base damage, default is 0")
|
RULE_INT(Combat, ArcheryBaseDamage, 0, "Archery base damage, default is 0")
|
||||||
RULE_INT(Combat, BackstabBaseDamage, 0, "Backstab base damage, default is 0")
|
RULE_INT(Combat, BackstabBaseDamage, 0, "Backstab base damage, default is 0")
|
||||||
@@ -667,6 +673,9 @@ RULE_REAL(NPC, NPCHealOnGateAmount, 25, "How much the NPC will heal on gate if e
|
|||||||
RULE_BOOL(NPC, AnimalsOpenDoors, true, "Determines or not whether animals open doors or not when they approach them")
|
RULE_BOOL(NPC, AnimalsOpenDoors, true, "Determines or not whether animals open doors or not when they approach them")
|
||||||
RULE_INT(NPC, MaxRaceID, 732, "Maximum Race ID, RoF2 by default supports up to 732")
|
RULE_INT(NPC, MaxRaceID, 732, "Maximum Race ID, RoF2 by default supports up to 732")
|
||||||
RULE_BOOL(NPC, DisableLastNames, false, "Enable to disable NPC Last Names")
|
RULE_BOOL(NPC, DisableLastNames, false, "Enable to disable NPC Last Names")
|
||||||
|
RULE_BOOL(NPC, NPCIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
|
||||||
|
RULE_INT(NPC, NPCHasteCap, 150, "Haste cap for non-v3(over haste) haste")
|
||||||
|
RULE_INT(NPC, NPCHastev3Cap, 25, "Haste cap for v3(over haste) haste")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Aggro)
|
RULE_CATEGORY(Aggro)
|
||||||
@@ -692,6 +701,7 @@ RULE_BOOL(Aggro, UndeadAlwaysAggro, true, "should undead always aggro?")
|
|||||||
RULE_INT(Aggro, BardAggroCap, 40, "per song bard aggro cap.")
|
RULE_INT(Aggro, BardAggroCap, 40, "per song bard aggro cap.")
|
||||||
RULE_INT(Aggro, InitialAggroBonus, 100, "Initial Aggro Bonus, Default: 100")
|
RULE_INT(Aggro, InitialAggroBonus, 100, "Initial Aggro Bonus, Default: 100")
|
||||||
RULE_INT(Aggro, InitialPetAggroBonus, 100, "Initial Pet Aggro Bonus, Default 100")
|
RULE_INT(Aggro, InitialPetAggroBonus, 100, "Initial Pet Aggro Bonus, Default 100")
|
||||||
|
RULE_STRING(Aggro, ExcludedFleeAllyFactionIDs, "0|5013|5014|5023|5032", "Common Faction IDs that are excluded from faction checks in EntityList::FleeAllyCount")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(TaskSystem)
|
RULE_CATEGORY(TaskSystem)
|
||||||
@@ -706,6 +716,7 @@ RULE_BOOL(TaskSystem, ExpRewardsIgnoreLevelBasedEXPMods, false, "Rewarding Level
|
|||||||
RULE_INT(TaskSystem, SharedTasksWorldProcessRate, 6000, "Timer interval (milliseconds) that shared tasks are processed in world")
|
RULE_INT(TaskSystem, SharedTasksWorldProcessRate, 6000, "Timer interval (milliseconds) that shared tasks are processed in world")
|
||||||
RULE_INT(TaskSystem, SharedTasksTerminateTimerMS, 120000, "Delay (milliseconds) until a shared task is terminated if requirements are no longer met after member removal (default: 2 minutes)")
|
RULE_INT(TaskSystem, SharedTasksTerminateTimerMS, 120000, "Delay (milliseconds) until a shared task is terminated if requirements are no longer met after member removal (default: 2 minutes)")
|
||||||
RULE_BOOL(TaskSystem, UpdateOneElementPerTask, true, "If true (live-like) task updates only increment the first matching activity. If false all matching elements will be incremented.")
|
RULE_BOOL(TaskSystem, UpdateOneElementPerTask, true, "If true (live-like) task updates only increment the first matching activity. If false all matching elements will be incremented.")
|
||||||
|
RULE_INT(TaskSystem, MaxUpdateMessages, 50, "Maximum update messages for non-GiveCash activity types in IncrementDoneCount")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Range)
|
RULE_CATEGORY(Range)
|
||||||
@@ -717,6 +728,7 @@ RULE_INT(Range, SpellParticles, 135, "The packet range in which spell particles
|
|||||||
RULE_INT(Range, DamageMessages, 50, "The packet range in which damage messages are sent (non-crit)")
|
RULE_INT(Range, DamageMessages, 50, "The packet range in which damage messages are sent (non-crit)")
|
||||||
RULE_INT(Range, SpellMessages, 75, "The packet range in which spell damage messages are sent")
|
RULE_INT(Range, SpellMessages, 75, "The packet range in which spell damage messages are sent")
|
||||||
RULE_INT(Range, SongMessages, 75, "The packet range in which song messages are sent")
|
RULE_INT(Range, SongMessages, 75, "The packet range in which song messages are sent")
|
||||||
|
RULE_INT(Range, StunMessages, 75, "The packet range in which stun messages are sent")
|
||||||
RULE_INT(Range, ClientPositionUpdates, 300, "Distance in which the own changed position is communicated to other clients")
|
RULE_INT(Range, ClientPositionUpdates, 300, "Distance in which the own changed position is communicated to other clients")
|
||||||
RULE_INT(Range, CriticalDamage, 80, "The packet range in which critical hit messages are sent")
|
RULE_INT(Range, CriticalDamage, 80, "The packet range in which critical hit messages are sent")
|
||||||
RULE_INT(Range, MobCloseScanDistance, 600, "Close scan distance")
|
RULE_INT(Range, MobCloseScanDistance, 600, "Close scan distance")
|
||||||
@@ -758,6 +770,9 @@ RULE_BOOL(Bots, CazicTouchBotsOwner, true, "Default True. Cazic Touch/DT will hi
|
|||||||
RULE_INT(Bots, BotsClickItemsMinLvl, 1, "Minimum level for bots to be able to use ^clickitem. Default 1.")
|
RULE_INT(Bots, BotsClickItemsMinLvl, 1, "Minimum level for bots to be able to use ^clickitem. Default 1.")
|
||||||
RULE_BOOL(Bots, BotsCanClickItems, true, "Enables the ability for bots to click items they have equipped. Default TRUE")
|
RULE_BOOL(Bots, BotsCanClickItems, true, "Enables 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_BOOL(Bots, CanClickMageEpicV1, true, "Whether or not bots are allowed to click Mage Epic 1.0. Default TRUE")
|
||||||
|
RULE_BOOL(Bots, BotsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.")
|
||||||
|
RULE_INT(Bots, BotsHasteCap, 100, "Haste cap for non-v3(over haste) haste")
|
||||||
|
RULE_INT(Bots, BotsHastev3Cap, 25, "Haste cap for v3(over haste) haste")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Chat)
|
RULE_CATEGORY(Chat)
|
||||||
@@ -804,6 +819,7 @@ RULE_INT(Bazaar, MaxBarterSearchResults, 200, "The maximum results returned in t
|
|||||||
RULE_REAL(Bazaar, ParcelDeliveryCostMod, 0.20, "Cost of parcel delivery for a bazaar purchase as a percentage of item cost. Default is 20% of item cost. RoF+ Only.")
|
RULE_REAL(Bazaar, ParcelDeliveryCostMod, 0.20, "Cost of parcel delivery for a bazaar purchase as a percentage of item cost. Default is 20% of item cost. RoF+ Only.")
|
||||||
RULE_INT(Bazaar, VoucherDeliveryCost, 200, "Number of vouchers for direct delivery for a bazaar purchase. Default is 200 vouchers. RoF+ Only.")
|
RULE_INT(Bazaar, VoucherDeliveryCost, 200, "Number of vouchers for direct delivery for a bazaar purchase. Default is 200 vouchers. RoF+ Only.")
|
||||||
RULE_BOOL(Bazaar, EnableParcelDelivery, true, "Enable bazaar purchases via parcel delivery. Default is True.")
|
RULE_BOOL(Bazaar, EnableParcelDelivery, true, "Enable bazaar purchases via parcel delivery. Default is True.")
|
||||||
|
RULE_INT(Bazaar, MaxBuyerInventorySearchResults, 200, "Maximum number of search results when a Buyer searches the global item list. Default is 200. RoF+ Only.")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Mail)
|
RULE_CATEGORY(Mail)
|
||||||
@@ -895,6 +911,7 @@ RULE_BOOL(Inventory, AllowAnyWeaponTransformation, false, "Weapons can use any w
|
|||||||
RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting")
|
RULE_BOOL(Inventory, TransformSummonedBags, false, "Transforms summoned bags into disenchanted ones instead of deleting")
|
||||||
RULE_BOOL(Inventory, AllowMultipleOfSameAugment, false, "Allows multiple of the same augment to be placed in an item via #augmentitem or MQ2, set to true to allow")
|
RULE_BOOL(Inventory, AllowMultipleOfSameAugment, false, "Allows multiple of the same augment to be placed in an item via #augmentitem or MQ2, set to true to allow")
|
||||||
RULE_INT(Inventory, AlternateAugmentationSealer, 53, "Allows RoF+ clients to augment items from a special container type")
|
RULE_INT(Inventory, AlternateAugmentationSealer, 53, "Allows RoF+ clients to augment items from a special container type")
|
||||||
|
RULE_BOOL(Inventory, LazyLoadBank, true, "Don't load bank during zoning, only when in proximinity to a banker. May increase zone speed and stability")
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
RULE_CATEGORY(Client)
|
RULE_CATEGORY(Client)
|
||||||
|
|||||||
@@ -140,6 +140,7 @@
|
|||||||
|
|
||||||
#define ServerOP_TraderMessaging 0x0120
|
#define ServerOP_TraderMessaging 0x0120
|
||||||
#define ServerOP_BazaarPurchase 0x0121
|
#define ServerOP_BazaarPurchase 0x0121
|
||||||
|
#define ServerOP_BuyerMessaging 0x0122
|
||||||
|
|
||||||
#define ServerOP_InstanceUpdateTime 0x014F
|
#define ServerOP_InstanceUpdateTime 0x014F
|
||||||
#define ServerOP_AdventureRequest 0x0150
|
#define ServerOP_AdventureRequest 0x0150
|
||||||
|
|||||||
+96
-64
@@ -46,6 +46,7 @@
|
|||||||
#include "repositories/character_item_recast_repository.h"
|
#include "repositories/character_item_recast_repository.h"
|
||||||
#include "repositories/character_corpses_repository.h"
|
#include "repositories/character_corpses_repository.h"
|
||||||
#include "repositories/skill_caps_repository.h"
|
#include "repositories/skill_caps_repository.h"
|
||||||
|
#include "repositories/inventory_repository.h"
|
||||||
|
|
||||||
namespace ItemField
|
namespace ItemField
|
||||||
{
|
{
|
||||||
@@ -300,15 +301,15 @@ bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance*
|
|||||||
// Update/Insert item
|
// Update/Insert item
|
||||||
const std::string query = StringFormat("REPLACE INTO inventory "
|
const std::string query = StringFormat("REPLACE INTO inventory "
|
||||||
"(charid, slotid, itemid, charges, instnodrop, custom_data, color, "
|
"(charid, slotid, itemid, charges, instnodrop, custom_data, color, "
|
||||||
"augslot1, augslot2, augslot3, augslot4, augslot5, augslot6, ornamenticon, ornamentidfile, ornament_hero_model) "
|
"augslot1, augslot2, augslot3, augslot4, augslot5, augslot6, ornamenticon, ornamentidfile, ornament_hero_model, guid) "
|
||||||
"VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, "
|
"VALUES( %lu, %lu, %lu, %lu, %lu, '%s', %lu, "
|
||||||
"%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)",
|
"%lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu, %lu)",
|
||||||
static_cast<unsigned long>(char_id), static_cast<unsigned long>(slot_id), static_cast<unsigned long>(inst->GetItem()->ID),
|
static_cast<unsigned long>(char_id), static_cast<unsigned long>(slot_id), static_cast<unsigned long>(inst->GetItem()->ID),
|
||||||
static_cast<unsigned long>(charges), static_cast<unsigned long>(inst->IsAttuned() ? 1 : 0),
|
static_cast<unsigned long>(charges), static_cast<unsigned long>(inst->IsAttuned() ? 1 : 0),
|
||||||
inst->GetCustomDataString().c_str(), static_cast<unsigned long>(inst->GetColor()),
|
inst->GetCustomDataString().c_str(), static_cast<unsigned long>(inst->GetColor()),
|
||||||
static_cast<unsigned long>(augslot[0]), static_cast<unsigned long>(augslot[1]), static_cast<unsigned long>(augslot[2]),
|
static_cast<unsigned long>(augslot[0]), static_cast<unsigned long>(augslot[1]), static_cast<unsigned long>(augslot[2]),
|
||||||
static_cast<unsigned long>(augslot[3]), static_cast<unsigned long>(augslot[4]), static_cast<unsigned long>(augslot[5]), static_cast<unsigned long>(inst->GetOrnamentationIcon()),
|
static_cast<unsigned long>(augslot[3]), static_cast<unsigned long>(augslot[4]), static_cast<unsigned long>(augslot[5]), static_cast<unsigned long>(inst->GetOrnamentationIcon()),
|
||||||
static_cast<unsigned long>(inst->GetOrnamentationIDFile()), static_cast<unsigned long>(inst->GetOrnamentHeroModel()));
|
static_cast<unsigned long>(inst->GetOrnamentationIDFile()), static_cast<unsigned long>(inst->GetOrnamentHeroModel()), inst->GetSerialNumber());
|
||||||
const auto results = QueryDatabase(query);
|
const auto results = QueryDatabase(query);
|
||||||
|
|
||||||
// Save bag contents, if slot supports bag contents
|
// Save bag contents, if slot supports bag contents
|
||||||
@@ -651,48 +652,67 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Retrieve character inventory
|
// Retrieve character inventory
|
||||||
const std::string query =
|
auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`;", char_id));
|
||||||
StringFormat("SELECT slotid, itemid, charges, color, augslot1, augslot2, augslot3, augslot4, augslot5, "
|
if (results.empty()) {
|
||||||
"augslot6, instnodrop, custom_data, ornamenticon, ornamentidfile, ornament_hero_model FROM "
|
LogError("Error loading inventory for char_id {} from the database.", char_id);
|
||||||
"inventory WHERE charid = %i ORDER BY slotid",
|
|
||||||
char_id);
|
|
||||||
auto results = QueryDatabase(query);
|
|
||||||
if (!results.Success()) {
|
|
||||||
LogError("If you got an error related to the 'instnodrop' field, run the "
|
|
||||||
"following SQL Queries:\nalter table inventory add instnodrop "
|
|
||||||
"tinyint(1) unsigned default 0 not null;\n");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto timestamps = GetItemRecastTimestamps(char_id);
|
for (auto const &row: results) {
|
||||||
|
if (row.guid != 0) {
|
||||||
|
EQ::ItemInstance::AddGUIDToMap(row.guid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto timestamps = GetItemRecastTimestamps(char_id);
|
||||||
auto cv_conflict = false;
|
auto cv_conflict = false;
|
||||||
const auto pmask = inv->GetLookup()->PossessionsBitmask;
|
const auto pmask = inv->GetLookup()->PossessionsBitmask;
|
||||||
const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank;
|
const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank;
|
||||||
|
|
||||||
for (auto& row = results.begin(); row != results.end(); ++row) {
|
std::vector<InventoryRepository::Inventory> queue{};
|
||||||
int16 slot_id = Strings::ToInt(row[0]);
|
for (auto &row: results) {
|
||||||
|
const int16 slot_id = row.slotid;
|
||||||
|
const uint32 item_id = row.itemid;
|
||||||
|
const uint16 charges = row.charges;
|
||||||
|
const uint32 color = row.color;
|
||||||
|
const bool instnodrop = row.instnodrop;
|
||||||
|
const uint32 ornament_icon = row.ornamenticon;
|
||||||
|
const uint32 ornament_idfile = row.ornamentidfile;
|
||||||
|
const uint32 ornament_hero_model = row.ornament_hero_model;
|
||||||
|
|
||||||
if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) { // Titanium thru UF check
|
uint32 aug[EQ::invaug::SOCKET_COUNT];
|
||||||
|
aug[0] = row.augslot1;
|
||||||
|
aug[1] = row.augslot2;
|
||||||
|
aug[2] = row.augslot3;
|
||||||
|
aug[3] = row.augslot4;
|
||||||
|
aug[4] = row.augslot5;
|
||||||
|
aug[5] = row.augslot6;
|
||||||
|
|
||||||
|
if (slot_id <= EQ::invslot::POSSESSIONS_END && slot_id >= EQ::invslot::POSSESSIONS_BEGIN) {
|
||||||
|
// Titanium thru UF check
|
||||||
if (((static_cast<uint64>(1) << slot_id) & pmask) == 0) {
|
if (((static_cast<uint64>(1) << slot_id) & pmask) == 0) {
|
||||||
cv_conflict = true;
|
cv_conflict = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) { // Titanium thru UF check
|
else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
||||||
const auto parent_slot = EQ::invslot::GENERAL_BEGIN + ((slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
|
// Titanium thru UF check
|
||||||
|
const auto parent_slot = EQ::invslot::GENERAL_BEGIN + (
|
||||||
|
(slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
|
||||||
if (((static_cast<uint64>(1) << parent_slot) & pmask) == 0) {
|
if (((static_cast<uint64>(1) << parent_slot) & pmask) == 0) {
|
||||||
cv_conflict = true;
|
cv_conflict = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) { // Titanium check
|
else if (slot_id <= EQ::invslot::BANK_END && slot_id >= EQ::invslot::BANK_BEGIN) {
|
||||||
|
// Titanium check
|
||||||
if ((slot_id - EQ::invslot::BANK_BEGIN) >= bank_size) {
|
if ((slot_id - EQ::invslot::BANK_BEGIN) >= bank_size) {
|
||||||
cv_conflict = true;
|
cv_conflict = true;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (slot_id <= EQ::invbag::BANK_BAGS_END && slot_id >= EQ::invbag::BANK_BAGS_BEGIN) { // Titanium check
|
else if (slot_id <= EQ::invbag::BANK_BAGS_END && slot_id >= EQ::invbag::BANK_BAGS_BEGIN) {
|
||||||
|
// Titanium check
|
||||||
const auto parent_index = ((slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
|
const auto parent_index = ((slot_id - EQ::invbag::BANK_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
|
||||||
if (parent_index >= bank_size) {
|
if (parent_index >= bank_size) {
|
||||||
cv_conflict = true;
|
cv_conflict = true;
|
||||||
@@ -700,64 +720,55 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 item_id = Strings::ToUnsignedInt(row[1]);
|
auto *item = GetItem(item_id);
|
||||||
const uint16 charges = Strings::ToUnsignedInt(row[2]);
|
|
||||||
const uint32 color = Strings::ToUnsignedInt(row[3]);
|
|
||||||
|
|
||||||
uint32 aug[EQ::invaug::SOCKET_COUNT];
|
|
||||||
|
|
||||||
aug[0] = Strings::ToUnsignedInt(row[4]);
|
|
||||||
aug[1] = Strings::ToUnsignedInt(row[5]);
|
|
||||||
aug[2] = Strings::ToUnsignedInt(row[6]);
|
|
||||||
aug[3] = Strings::ToUnsignedInt(row[7]);
|
|
||||||
aug[4] = Strings::ToUnsignedInt(row[8]);
|
|
||||||
aug[5] = Strings::ToUnsignedInt(row[9]);
|
|
||||||
|
|
||||||
const bool instnodrop = (row[10] && static_cast<uint16>(Strings::ToUnsignedInt(row[10])));
|
|
||||||
|
|
||||||
const uint32 ornament_icon = Strings::ToUnsignedInt(row[12]);
|
|
||||||
const uint32 ornament_idfile = Strings::ToUnsignedInt(row[13]);
|
|
||||||
uint32 ornament_hero_model = Strings::ToUnsignedInt(row[14]);
|
|
||||||
|
|
||||||
const EQ::ItemData *item = GetItem(item_id);
|
|
||||||
|
|
||||||
if (!item) {
|
if (!item) {
|
||||||
LogError("Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]", char_id, item_id,
|
LogError(
|
||||||
slot_id);
|
"Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]",
|
||||||
|
char_id,
|
||||||
|
item_id,
|
||||||
|
slot_id
|
||||||
|
);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
EQ::ItemInstance *inst = CreateBaseItem(item, charges);
|
auto *inst = CreateBaseItem(item, charges);
|
||||||
|
if (!inst) {
|
||||||
if (inst == nullptr)
|
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (row[11]) {
|
if (!row.custom_data.empty()) {
|
||||||
std::string data_str(row[11]);
|
inst->SetCustomDataString(row.custom_data);
|
||||||
inst->SetCustomDataString(data_str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inst->SetOrnamentIcon(ornament_icon);
|
inst->SetOrnamentIcon(ornament_icon);
|
||||||
inst->SetOrnamentationIDFile(ornament_idfile);
|
inst->SetOrnamentationIDFile(ornament_idfile);
|
||||||
inst->SetOrnamentHeroModel(item->HerosForgeModel);
|
inst->SetOrnamentHeroModel(item->HerosForgeModel);
|
||||||
|
|
||||||
if (instnodrop || (inst->GetItem()->Attuneable && slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END))
|
if (instnodrop || (inst->GetItem()->Attuneable && slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <=
|
||||||
|
EQ::invslot::EQUIPMENT_END)) {
|
||||||
inst->SetAttuned(true);
|
inst->SetAttuned(true);
|
||||||
|
}
|
||||||
|
|
||||||
if (color > 0)
|
if (color > 0) {
|
||||||
inst->SetColor(color);
|
inst->SetColor(color);
|
||||||
|
}
|
||||||
|
|
||||||
if (charges == 0x7FFF)
|
if (charges == 0x7FFF) {
|
||||||
inst->SetCharges(-1);
|
inst->SetCharges(-1);
|
||||||
else if (charges == 0 && inst->IsStackable()) // Stackable items need a minimum charge of 1 remain moveable.
|
}
|
||||||
|
else if (charges == 0 && inst->IsStackable()) {
|
||||||
|
// Stackable items need a minimum charge of 1 remain moveable.
|
||||||
inst->SetCharges(1);
|
inst->SetCharges(1);
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
inst->SetCharges(charges);
|
inst->SetCharges(charges);
|
||||||
|
}
|
||||||
|
|
||||||
if (item->RecastDelay) {
|
if (item->RecastDelay) {
|
||||||
if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->RecastType)) {
|
if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->RecastType)) {
|
||||||
inst->SetRecastTimestamp(timestamps.at(item->RecastType));
|
inst->SetRecastTimestamp(timestamps.at(item->RecastType));
|
||||||
} else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) {
|
}
|
||||||
|
else if (item->RecastType == RECAST_TYPE_UNLINKED_ITEM && timestamps.count(item->ID)) {
|
||||||
inst->SetRecastTimestamp(timestamps.at(item->ID));
|
inst->SetRecastTimestamp(timestamps.at(item->ID));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -767,35 +778,50 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
|||||||
|
|
||||||
if (item->IsClassCommon()) {
|
if (item->IsClassCommon()) {
|
||||||
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
for (int i = EQ::invaug::SOCKET_BEGIN; i <= EQ::invaug::SOCKET_END; i++) {
|
||||||
if (aug[i])
|
if (aug[i]) {
|
||||||
inst->PutAugment(this, i, aug[i]);
|
inst->PutAugment(this, i, aug[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int16 put_slot_id;
|
int16 put_slot_id;
|
||||||
if (slot_id >= 8000 && slot_id <= 8999) {
|
if (slot_id >= 8000 && slot_id <= 8999) {
|
||||||
put_slot_id = inv->PushCursor(*inst);
|
put_slot_id = inv->PushCursor(*inst);
|
||||||
} else if (slot_id >= 3111 && slot_id <= 3179) {
|
}
|
||||||
|
else if (slot_id >= 3111 && slot_id <= 3179) {
|
||||||
// Admins: please report any occurrences of this error
|
// Admins: please report any occurrences of this error
|
||||||
LogError("Warning: Defunct location for item in inventory: charid={}, item_id={}, slot_id={} .. pushing to cursor...",
|
LogError(
|
||||||
char_id, item_id, slot_id);
|
"Warning: Defunct location for item in inventory: charid={}, item_id={}, slot_id={} .. pushing to cursor...",
|
||||||
|
char_id,
|
||||||
|
item_id,
|
||||||
|
slot_id
|
||||||
|
);
|
||||||
put_slot_id = inv->PushCursor(*inst);
|
put_slot_id = inv->PushCursor(*inst);
|
||||||
} else {
|
}
|
||||||
|
else {
|
||||||
put_slot_id = inv->PutItem(slot_id, *inst);
|
put_slot_id = inv->PutItem(slot_id, *inst);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
row.guid = inst->GetSerialNumber();
|
||||||
|
queue.push_back(row);
|
||||||
|
|
||||||
safe_delete(inst);
|
safe_delete(inst);
|
||||||
|
|
||||||
// Save ptr to item in inventory
|
// Save ptr to item in inventory
|
||||||
if (put_slot_id == INVALID_INDEX) {
|
if (put_slot_id == INVALID_INDEX) {
|
||||||
LogError("Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]",
|
LogError(
|
||||||
char_id, item_id, slot_id);
|
"Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]",
|
||||||
|
char_id,
|
||||||
|
item_id,
|
||||||
|
slot_id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cv_conflict) {
|
if (cv_conflict) {
|
||||||
const std::string &char_name = GetCharName(char_id);
|
const std::string &char_name = GetCharName(char_id);
|
||||||
LogError("ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
|
LogError(
|
||||||
|
"ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
|
||||||
char_name,
|
char_name,
|
||||||
char_id,
|
char_id,
|
||||||
EQ::versions::MobVersionName(inv->InventoryVersion()),
|
EQ::versions::MobVersionName(inv->InventoryVersion()),
|
||||||
@@ -803,6 +829,12 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!queue.empty()) {
|
||||||
|
InventoryRepository::ReplaceMany(*this, queue);
|
||||||
|
}
|
||||||
|
|
||||||
|
EQ::ItemInstance::ClearGUIDMap();
|
||||||
|
|
||||||
// Retrieve shared inventory
|
// Retrieve shared inventory
|
||||||
return GetSharedBank(char_id, inv, true);
|
return GetSharedBank(char_id, inv, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2325,3 +2325,108 @@ bool IsCastNotStandingSpell(uint16 spell_id) {
|
|||||||
*/
|
*/
|
||||||
return spells[spell_id].cast_not_standing;
|
return spells[spell_id].cast_not_standing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsAegolismSpell(uint16 spell_id) {
|
||||||
|
|
||||||
|
if (!IsValidSpell(spell_id)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_max_hp = false;
|
||||||
|
bool has_current_hp = false;
|
||||||
|
bool has_ac = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < EFFECT_COUNT; ++i) {
|
||||||
|
|
||||||
|
if (i == 0 && spells[spell_id].effect_id[i] != SE_StackingCommand_Block) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 1 && spells[spell_id].effect_id[i] == SE_TotalHP) {
|
||||||
|
has_max_hp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 2 && spells[spell_id].effect_id[i] == SE_CurrentHPOnce) {
|
||||||
|
has_current_hp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 3 && spells[spell_id].effect_id[i] == SE_ArmorClass) {
|
||||||
|
has_ac = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 4 && spells[spell_id].effect_id[i] != SE_StackingCommand_Overwrite) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_max_hp && has_current_hp && has_ac) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool AegolismStackingIsSymbolSpell(uint16 spell_id) {
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is hardcoded to be specific to the type of HP buffs that are removed if a mob has an Aegolism buff.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!IsValidSpell(spell_id)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_max_hp = false;
|
||||||
|
bool has_current_hp = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < EFFECT_COUNT; ++i) {
|
||||||
|
|
||||||
|
if ((i < 2 && spells[spell_id].effect_id[i] != SE_CHA) ||
|
||||||
|
i > 3 && spells[spell_id].effect_id[i] != SE_Blank) {
|
||||||
|
return 0;;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 2 && spells[spell_id].effect_id[i] == SE_TotalHP) {
|
||||||
|
has_max_hp = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 3 && spells[spell_id].effect_id[i] == SE_CurrentHPOnce) {
|
||||||
|
has_current_hp = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_max_hp && has_current_hp) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AegolismStackingIsArmorClassSpell(uint16 spell_id) {
|
||||||
|
/*
|
||||||
|
This is hardcoded to be specific to the type of AC buffs that are removed if a mob has an Aegolism buff.
|
||||||
|
*/
|
||||||
|
if (!IsValidSpell(spell_id)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_ac = false;
|
||||||
|
|
||||||
|
for (int i = 0; i < EFFECT_COUNT; ++i) {
|
||||||
|
|
||||||
|
if ((i < 3 && spells[spell_id].effect_id[i] != SE_CHA) ||
|
||||||
|
i > 3 && spells[spell_id].effect_id[i] != SE_Blank) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == 3 && spells[spell_id].effect_id[i] == SE_ArmorClass) {
|
||||||
|
has_ac = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_ac) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1625,5 +1625,8 @@ bool IsSpellUsableInThisZoneType(uint16 spell_id, uint8 zone_type);
|
|||||||
const char *GetSpellName(uint16 spell_id);
|
const char *GetSpellName(uint16 spell_id);
|
||||||
int GetSpellStatValue(uint16 spell_id, const char* stat_identifier, uint8 slot = 0);
|
int GetSpellStatValue(uint16 spell_id, const char* stat_identifier, uint8 slot = 0);
|
||||||
bool IsCastRestrictedSpell(uint16 spell_id);
|
bool IsCastRestrictedSpell(uint16 spell_id);
|
||||||
|
bool IsAegolismSpell(uint16 spell_id);
|
||||||
|
bool AegolismStackingIsSymbolSpell(uint16 spell_id);
|
||||||
|
bool AegolismStackingIsArmorClassSpell(uint16 spell_id);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+3
-3
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
// Build variables
|
// Build variables
|
||||||
// these get injected during the build pipeline
|
// these get injected during the build pipeline
|
||||||
#define CURRENT_VERSION "22.53.1-dev" // always append -dev to the current version for custom-builds
|
#define CURRENT_VERSION "22.56.3-dev" // always append -dev to the current version for custom-builds
|
||||||
#define LOGIN_VERSION "0.8.0"
|
#define LOGIN_VERSION "0.8.0"
|
||||||
#define COMPILE_DATE __DATE__
|
#define COMPILE_DATE __DATE__
|
||||||
#define COMPILE_TIME __TIME__
|
#define COMPILE_TIME __TIME__
|
||||||
@@ -42,8 +42,8 @@
|
|||||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define CURRENT_BINARY_DATABASE_VERSION 9280
|
#define CURRENT_BINARY_DATABASE_VERSION 9283
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9044
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -224,6 +224,23 @@ void Client::Handle_Login(const char *data, unsigned int size)
|
|||||||
if (server.db->GetLoginDataFromAccountInfo(user, db_loginserver, db_account_password_hash, db_account_id)) {
|
if (server.db->GetLoginDataFromAccountInfo(user, db_loginserver, db_account_password_hash, db_account_id)) {
|
||||||
result = VerifyLoginHash(user, db_loginserver, cred, db_account_password_hash);
|
result = VerifyLoginHash(user, db_loginserver, cred, db_account_password_hash);
|
||||||
|
|
||||||
|
#ifdef LSPX
|
||||||
|
// if user updated their password on the login server, update it here by validating their credentials with the login server
|
||||||
|
if (!result && db_loginserver == "eqemu") {
|
||||||
|
uint32 account_id = AccountManagement::CheckExternalLoginserverUserCredentials(user, cred);
|
||||||
|
if (account_id > 0) {
|
||||||
|
auto encryption_mode = server.options.GetEncryptionMode();
|
||||||
|
server.db->UpdateLoginserverAccountPasswordHash(
|
||||||
|
user,
|
||||||
|
db_loginserver,
|
||||||
|
eqcrypt_hash(user, cred, encryption_mode)
|
||||||
|
);
|
||||||
|
LogInfo("Updating eqemu account [{}] password hash", account_id);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
LogDebug("Success [{0}]", (result ? "true" : "false"));
|
LogDebug("Success [{0}]", (result ? "true" : "false"));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "eqemu-server",
|
"name": "eqemu-server",
|
||||||
"version": "22.53.1",
|
"version": "22.56.3",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/EQEmu/Server.git"
|
"url": "https://github.com/EQEmu/Server.git"
|
||||||
|
|||||||
@@ -433,6 +433,7 @@ OP_TraderShop=0x31df
|
|||||||
OP_TraderBulkSend=0x6a96
|
OP_TraderBulkSend=0x6a96
|
||||||
OP_Trader=0x4ef5
|
OP_Trader=0x4ef5
|
||||||
OP_Barter=0x243a
|
OP_Barter=0x243a
|
||||||
|
OP_BuyerItems=0x1a6a
|
||||||
OP_TraderBuy=0x0000
|
OP_TraderBuy=0x0000
|
||||||
OP_ShopItem=0x0000
|
OP_ShopItem=0x0000
|
||||||
OP_BazaarInspect=0x0000
|
OP_BazaarInspect=0x0000
|
||||||
|
|||||||
@@ -287,6 +287,7 @@ void Adventure::Finished(AdventureWinStatus ws)
|
|||||||
auto character_id = database.GetCharacterID(*iter);
|
auto character_id = database.GetCharacterID(*iter);
|
||||||
|
|
||||||
if (character_id == 0) {
|
if (character_id == 0) {
|
||||||
|
++iter;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -291,6 +291,8 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
|||||||
LogInfo("Loading items");
|
LogInfo("Loading items");
|
||||||
LogInfo("Clearing trader table details");
|
LogInfo("Clearing trader table details");
|
||||||
database.ClearTraderDetails();
|
database.ClearTraderDetails();
|
||||||
|
database.ClearBuyerDetails();
|
||||||
|
LogInfo("Clearing buyer table details");
|
||||||
|
|
||||||
if (!content_db.LoadItems(hotfix_name)) {
|
if (!content_db.LoadItems(hotfix_name)) {
|
||||||
LogError("Error: Could not load item data. But ignoring");
|
LogError("Error: Could not load item data. But ignoring");
|
||||||
|
|||||||
+31
-1
@@ -1742,7 +1742,6 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
zoneserver_list.SendPacketToBootedZones(pack);
|
zoneserver_list.SendPacketToBootedZones(pack);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ServerOP_BazaarPurchase: {
|
case ServerOP_BazaarPurchase: {
|
||||||
@@ -1753,9 +1752,40 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
|||||||
"ServerOP_BazaarPurchase",
|
"ServerOP_BazaarPurchase",
|
||||||
in->trader_buy_struct.trader_id
|
in->trader_buy_struct.trader_id
|
||||||
);
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
zoneserver_list.SendPacket(Zones::BAZAAR, pack);
|
zoneserver_list.SendPacket(Zones::BAZAAR, pack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ServerOP_BuyerMessaging: {
|
||||||
|
auto in = (BuyerMessaging_Struct *)pack->pBuffer;
|
||||||
|
switch (in->action) {
|
||||||
|
case Barter_AddToBarterWindow:
|
||||||
|
case Barter_RemoveFromBarterWindow: {
|
||||||
|
if (in->buyer_id <= 0) {
|
||||||
|
LogTrading("World Message <red>[{}] received with invalid buyer_id <red>[{}]",
|
||||||
|
"ServerOP_BecomeBuyer",
|
||||||
|
in->buyer_id
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
zoneserver_list.SendPacketToBootedZones(pack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_SellItem: {
|
||||||
|
zoneserver_list.SendPacket(Zones::BAZAAR, pack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Barter_FailedTransaction:
|
||||||
|
case Barter_BuyerTransactionComplete: {
|
||||||
|
zoneserver_list.SendPacket(in->zone_id, pack);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);
|
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);
|
||||||
|
|||||||
+5
-1
@@ -2185,7 +2185,7 @@ void Client::AutoGrantAAPoints() {
|
|||||||
SendAlternateAdvancementStats();
|
SendAlternateAdvancementStats();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::GrantAllAAPoints(uint8 unlock_level)
|
void Client::GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only)
|
||||||
{
|
{
|
||||||
//iterate through every AA
|
//iterate through every AA
|
||||||
for (auto& aa : zone->aa_abilities) {
|
for (auto& aa : zone->aa_abilities) {
|
||||||
@@ -2195,6 +2195,10 @@ void Client::GrantAllAAPoints(uint8 unlock_level)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ability->grant_only && skip_grant_only) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
const uint8 level = unlock_level ? unlock_level : GetLevel();
|
const uint8 level = unlock_level ? unlock_level : GetLevel();
|
||||||
|
|
||||||
AA::Rank* rank = ability->first;
|
AA::Rank* rank = ability->first;
|
||||||
|
|||||||
+90
-2
@@ -452,8 +452,26 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't aggro new clients if we are already engaged unless SpecialAbility::ProximityAggro is set
|
// Don't aggro new clients if we are already engaged unless PROX_AGGRO is set
|
||||||
if (IsEngaged() && (!GetSpecialAbility(SpecialAbility::ProximityAggro) || (GetSpecialAbility(SpecialAbility::ProximityAggro) && !CombatRange(mob)))) {
|
// Frustrated mobs (all rooted up with no one to kill)
|
||||||
|
// will engage without PROX_AGGRO ability if someone new is close now.
|
||||||
|
|
||||||
|
const bool is_frustrated = IsRooted() && !CombatRange(target);
|
||||||
|
|
||||||
|
if (
|
||||||
|
!is_frustrated &&
|
||||||
|
IsEngaged() &&
|
||||||
|
(
|
||||||
|
(
|
||||||
|
!GetSpecialAbility(SpecialAbility::ProximityAggro) &&
|
||||||
|
GetBodyType() != BodyType::Undead
|
||||||
|
) ||
|
||||||
|
(
|
||||||
|
GetSpecialAbility(SpecialAbility::ProximityAggro) &&
|
||||||
|
!CombatRange(mob)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) {
|
||||||
LogAggro(
|
LogAggro(
|
||||||
"[{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}]",
|
"[{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}]",
|
||||||
GetName(),
|
GetName(),
|
||||||
@@ -564,6 +582,76 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int EntityList::FleeAllyCount(Mob* attacker, Mob* skipped)
|
||||||
|
{
|
||||||
|
// Return a list of how many NPCs of the same faction or race are within aggro range of the given exclude Mob.
|
||||||
|
if (!attacker) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for (const auto& e : npc_list) {
|
||||||
|
NPC* n = e.second;
|
||||||
|
if (!n || n == skipped) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float aggro_range = n->GetAggroRange();
|
||||||
|
const float assist_range = n->GetAssistRange();
|
||||||
|
|
||||||
|
if (assist_range > aggro_range) {
|
||||||
|
aggro_range = assist_range;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Square it because we will be using DistNoRoot
|
||||||
|
aggro_range *= aggro_range;
|
||||||
|
|
||||||
|
if (DistanceSquared(n->GetPosition(), skipped->GetPosition()) > aggro_range) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& excluded = Strings::Split(RuleS(Aggro, ExcludedFleeAllyFactionIDs));
|
||||||
|
|
||||||
|
const auto& f = std::find_if(
|
||||||
|
excluded.begin(),
|
||||||
|
excluded.end(),
|
||||||
|
[&](std::string x) {
|
||||||
|
return Strings::ToUnsignedInt(x) == skipped->GetPrimaryFaction();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const bool is_excluded = f != excluded.end();
|
||||||
|
|
||||||
|
// If exclude doesn't have a faction, check for buddies based on race.
|
||||||
|
// Also exclude common factions such as noob monsters, indifferent, kos, kos animal
|
||||||
|
if (!is_excluded) {
|
||||||
|
if (n->GetPrimaryFaction() != skipped->GetPrimaryFaction()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (n->GetBaseRace() != skipped->GetBaseRace() || n->IsCharmedPet()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LogFleeDetail(
|
||||||
|
"[{}] on faction [{}] with aggro_range [{}] is at [{}], [{}], [{}] and will count as an ally for [{}]",
|
||||||
|
n->GetName(),
|
||||||
|
n->GetPrimaryFaction(),
|
||||||
|
aggro_range,
|
||||||
|
n->GetX(),
|
||||||
|
n->GetY(),
|
||||||
|
n->GetZ(),
|
||||||
|
skipped->GetName()
|
||||||
|
);
|
||||||
|
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con)
|
int EntityList::GetHatedCount(Mob *attacker, Mob *exclude, bool inc_gray_con)
|
||||||
{
|
{
|
||||||
// Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
|
// Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
|
||||||
|
|||||||
@@ -651,7 +651,6 @@ Json::Value ApiGetClientListDetail(EQ::Net::WebsocketServerConnection *connectio
|
|||||||
row["base_wis"] = client->GetBaseWIS();
|
row["base_wis"] = client->GetBaseWIS();
|
||||||
row["become_npc_level"] = client->GetBecomeNPCLevel();
|
row["become_npc_level"] = client->GetBecomeNPCLevel();
|
||||||
row["boat_id"] = client->GetBoatID();
|
row["boat_id"] = client->GetBoatID();
|
||||||
row["buyer_welcome_message"] = client->GetBuyerWelcomeMessage();
|
|
||||||
row["calc_atk"] = client->CalcATK();
|
row["calc_atk"] = client->CalcATK();
|
||||||
row["calc_base_mana"] = client->CalcBaseMana();
|
row["calc_base_mana"] = client->CalcBaseMana();
|
||||||
row["calc_current_weight"] = client->CalcCurrentWeight();
|
row["calc_current_weight"] = client->CalcCurrentWeight();
|
||||||
|
|||||||
+264
-181
@@ -1478,8 +1478,10 @@ int64 Mob::DoDamageCaps(int64 base_damage)
|
|||||||
//SYNC WITH: tune.cpp, mob.h TuneDoAttack
|
//SYNC WITH: tune.cpp, mob.h TuneDoAttack
|
||||||
void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool FromRiposte)
|
void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool FromRiposte)
|
||||||
{
|
{
|
||||||
if (!other)
|
if (!other) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
LogCombat("[{}]::DoAttack vs [{}] base [{}] min [{}] offense [{}] tohit [{}] skill [{}]", GetName(),
|
LogCombat("[{}]::DoAttack vs [{}] base [{}] min [{}] offense [{}] tohit [{}] skill [{}]", GetName(),
|
||||||
other->GetName(), hit.base_damage, hit.min_damage, hit.offense, hit.tohit, hit.skill);
|
other->GetName(), hit.base_damage, hit.min_damage, hit.offense, hit.tohit, hit.skill);
|
||||||
|
|
||||||
@@ -1491,14 +1493,22 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
|
|||||||
if (!FromRiposte && other->AvoidDamage(this, hit)) {
|
if (!FromRiposte && other->AvoidDamage(this, hit)) {
|
||||||
if (int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough;
|
if (int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough;
|
||||||
strike_through && zone->random.Roll(strike_through)) {
|
strike_through && zone->random.Roll(strike_through)) {
|
||||||
MessageString(Chat::StrikeThrough,
|
|
||||||
STRIKETHROUGH_STRING); // You strike through your opponents defenses!
|
FilteredMessageString(
|
||||||
|
this, /* Sender */
|
||||||
|
Chat::StrikeThrough, /* Type: 339 */
|
||||||
|
FilterStrikethrough, /* FilterType: 12 */
|
||||||
|
STRIKETHROUGH_STRING /* You strike through your opponent's defenses! */
|
||||||
|
);
|
||||||
|
|
||||||
hit.damage_done = 1; // set to one, we will check this to continue
|
hit.damage_done = 1; // set to one, we will check this to continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hit.damage_done == DMG_RIPOSTED) {
|
if (hit.damage_done == DMG_RIPOSTED) {
|
||||||
DoRiposte(other);
|
DoRiposte(other);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogCombat("Avoided/strikethrough damage with code [{}]", hit.damage_done);
|
LogCombat("Avoided/strikethrough damage with code [{}]", hit.damage_done);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1510,9 +1520,19 @@ void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, boo
|
|||||||
int stun_resist2 = other->spellbonuses.FrontalStunResist + other->itembonuses.FrontalStunResist + other->aabonuses.FrontalStunResist;
|
int stun_resist2 = other->spellbonuses.FrontalStunResist + other->itembonuses.FrontalStunResist + other->aabonuses.FrontalStunResist;
|
||||||
int stun_resist = other->spellbonuses.StunResist + other->itembonuses.StunResist + other->aabonuses.StunResist;
|
int stun_resist = other->spellbonuses.StunResist + other->itembonuses.StunResist + other->aabonuses.StunResist;
|
||||||
if (zone->random.Roll(stun_resist2)) {
|
if (zone->random.Roll(stun_resist2)) {
|
||||||
other->MessageString(Chat::Stun, AVOID_STUNNING_BLOW);
|
other->FilteredMessageString(
|
||||||
|
this,
|
||||||
|
Chat::Stun,
|
||||||
|
FilterStuns,
|
||||||
|
AVOID_STUNNING_BLOW
|
||||||
|
);
|
||||||
} else if (zone->random.Roll(stun_resist)) {
|
} else if (zone->random.Roll(stun_resist)) {
|
||||||
other->MessageString(Chat::Stun, SHAKE_OFF_STUN);
|
other->FilteredMessageString(
|
||||||
|
this,
|
||||||
|
Chat::Stun,
|
||||||
|
FilterStuns,
|
||||||
|
SHAKE_OFF_STUN
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
other->Stun(3000); // yuck -- 3 seconds
|
other->Stun(3000); // yuck -- 3 seconds
|
||||||
}
|
}
|
||||||
@@ -2186,6 +2206,19 @@ bool Client::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::Skil
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Client::CheckIfAlreadyDead()
|
||||||
|
{
|
||||||
|
if (!ClientFinishedLoading()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dead) {
|
||||||
|
return false; //cant die more than once...
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
//SYNC WITH: tune.cpp, mob.h TuneNPCAttack
|
//SYNC WITH: tune.cpp, mob.h TuneNPCAttack
|
||||||
bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
|
bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
|
||||||
{
|
{
|
||||||
@@ -2460,11 +2493,6 @@ void NPC::Damage(Mob* other, int64 damage, uint16 spell_id, EQ::skills::SkillTyp
|
|||||||
|
|
||||||
//do a majority of the work...
|
//do a majority of the work...
|
||||||
CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic, special);
|
CommonDamage(other, damage, spell_id, attack_skill, avoidable, buffslot, iBuffTic, special);
|
||||||
|
|
||||||
if (damage > 0) {
|
|
||||||
//see if we are gunna start fleeing
|
|
||||||
if (!IsPet()) CheckFlee();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, KilledByTypes killed_by, bool is_buff_tic)
|
bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillType attack_skill, KilledByTypes killed_by, bool is_buff_tic)
|
||||||
@@ -4132,6 +4160,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
AddToHateList(attacker, 0, damage, true, false, iBuffTic, spell_id);
|
AddToHateList(attacker, 0, damage, true, false, iBuffTic, spell_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool died = false;
|
||||||
if (damage > 0) {
|
if (damage > 0) {
|
||||||
//if there is some damage being done and theres an attacker involved
|
//if there is some damage being done and theres an attacker involved
|
||||||
int previous_hp_ratio = GetHPRatio();
|
int previous_hp_ratio = GetHPRatio();
|
||||||
@@ -4256,7 +4285,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
//final damage has been determined.
|
//final damage has been determined.
|
||||||
SetHP(int64(GetHP() - damage));
|
int old_hp_ratio = (int)GetHPRatio();
|
||||||
|
|
||||||
const auto has_bot_given_event = parse->BotHasQuestSub(EVENT_DAMAGE_GIVEN);
|
const auto has_bot_given_event = parse->BotHasQuestSub(EVENT_DAMAGE_GIVEN);
|
||||||
|
|
||||||
@@ -4303,30 +4332,7 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
);
|
);
|
||||||
|
|
||||||
std::vector<std::any> args;
|
std::vector<std::any> args;
|
||||||
|
int64 damage_override = 0;
|
||||||
if (has_taken_event) {
|
|
||||||
const auto export_string = fmt::format(
|
|
||||||
"{} {} {} {} {} {} {} {} {}",
|
|
||||||
attacker ? attacker->GetID() : 0,
|
|
||||||
damage,
|
|
||||||
spell_id,
|
|
||||||
static_cast<int>(skill_used),
|
|
||||||
FromDamageShield ? 1 : 0,
|
|
||||||
avoidable ? 1 : 0,
|
|
||||||
buffslot,
|
|
||||||
iBuffTic ? 1 : 0,
|
|
||||||
static_cast<int>(special)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (IsBot() && has_bot_taken_event) {
|
|
||||||
parse->EventBot(EVENT_DAMAGE_TAKEN, CastToBot(), attacker ? attacker : nullptr, export_string, 0);
|
|
||||||
} else if (IsClient() && has_player_taken_event) {
|
|
||||||
args.push_back(attacker ? attacker : nullptr);
|
|
||||||
parse->EventPlayer(EVENT_DAMAGE_TAKEN, CastToClient(), export_string, 0, &args);
|
|
||||||
} else if (IsNPC() && has_npc_taken_event) {
|
|
||||||
parse->EventNPC(EVENT_DAMAGE_TAKEN, CastToNPC(), attacker ? attacker : nullptr, export_string, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (has_given_event && attacker) {
|
if (has_given_event && attacker) {
|
||||||
const auto export_string = fmt::format(
|
const auto export_string = fmt::format(
|
||||||
@@ -4352,14 +4358,56 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (has_taken_event) {
|
||||||
|
const auto export_string = fmt::format(
|
||||||
|
"{} {} {} {} {} {} {} {} {}",
|
||||||
|
attacker ? attacker->GetID() : 0,
|
||||||
|
damage,
|
||||||
|
spell_id,
|
||||||
|
static_cast<int>(skill_used),
|
||||||
|
FromDamageShield ? 1 : 0,
|
||||||
|
avoidable ? 1 : 0,
|
||||||
|
buffslot,
|
||||||
|
iBuffTic ? 1 : 0,
|
||||||
|
static_cast<int>(special)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (IsBot() && has_bot_taken_event) {
|
||||||
|
damage_override = parse->EventBot(EVENT_DAMAGE_TAKEN, CastToBot(), attacker ? attacker : nullptr, export_string, 0);
|
||||||
|
} else if (IsClient() && has_player_taken_event) {
|
||||||
|
args.push_back(attacker ? attacker : nullptr);
|
||||||
|
damage_override = parse->EventPlayer(EVENT_DAMAGE_TAKEN, CastToClient(), export_string, 0, &args);
|
||||||
|
} else if (IsNPC() && has_npc_taken_event) {
|
||||||
|
damage_override = parse->EventNPC(EVENT_DAMAGE_TAKEN, CastToNPC(), attacker ? attacker : nullptr, export_string, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (damage_override > 0) {
|
||||||
|
damage = damage_override;
|
||||||
|
} else if (damage_override < 0) {
|
||||||
|
damage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetHP(int64(GetHP() - damage));
|
||||||
|
|
||||||
if (HasDied()) {
|
if (HasDied()) {
|
||||||
bool IsSaved = false;
|
bool IsSaved = false;
|
||||||
|
|
||||||
if (TryDivineSave())
|
if (TryDivineSave()) {
|
||||||
IsSaved = true;
|
IsSaved = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (!IsSaved && !TrySpellOnDeath()) {
|
if (!IsSaved && !TrySpellOnDeath()) {
|
||||||
|
if (IsNPC()) {
|
||||||
|
died = !CastToNPC()->GetDepop();
|
||||||
|
} else if (IsClient()) {
|
||||||
|
died = CastToClient()->CheckIfAlreadyDead();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (died) {
|
||||||
SetHP(-500);
|
SetHP(-500);
|
||||||
|
}
|
||||||
|
|
||||||
// killedByType is clarified in Client::Death if we are client.
|
// killedByType is clarified in Client::Death if we are client.
|
||||||
if (Death(attacker, damage, spell_id, skill_used, KilledByTypes::Killed_NPC, iBuffTic)) {
|
if (Death(attacker, damage, spell_id, skill_used, KilledByTypes::Killed_NPC, iBuffTic)) {
|
||||||
return;
|
return;
|
||||||
@@ -4485,34 +4533,61 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
Stun(RuleI(Combat, StunDuration));
|
Stun(RuleI(Combat, StunDuration));
|
||||||
if (RuleB(Combat, ClientStunMessage) && attacker->IsClient()) {
|
if (RuleB(Combat, ClientStunMessage) && attacker->IsClient()) {
|
||||||
if (attacker) {
|
if (attacker) {
|
||||||
entity_list.MessageClose(this, true, 500, Chat::Emote, "%s is stunned after being bashed by %s.", GetCleanName(), attacker->GetCleanName());
|
entity_list.FilteredMessageClose(
|
||||||
}
|
this,
|
||||||
else {
|
true,
|
||||||
entity_list.MessageClose(this, true, 500, Chat::Emote, "%s is stunned by a bash to the head.", GetCleanName());
|
RuleI(Range, StunMessages),
|
||||||
|
Chat::Stun,
|
||||||
|
FilterStuns,
|
||||||
|
"%s is stunned after being bashed by %s.",
|
||||||
|
GetCleanName(),
|
||||||
|
attacker->GetCleanName()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
entity_list.FilteredMessageClose(
|
||||||
|
this,
|
||||||
|
true,
|
||||||
|
RuleI(Range, StunMessages),
|
||||||
|
Chat::Stun,
|
||||||
|
FilterStuns,
|
||||||
|
"%s is stunned by a bash to the head.",
|
||||||
|
GetCleanName()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// stun resist passed!
|
// stun resist passed!
|
||||||
if (IsClient())
|
if (IsClient()) {
|
||||||
MessageString(Chat::Stun, SHAKE_OFF_STUN);
|
FilteredMessageString(
|
||||||
|
this,
|
||||||
|
Chat::Stun,
|
||||||
|
FilterStuns,
|
||||||
|
SHAKE_OFF_STUN
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
} else {
|
||||||
// stun resist 2 passed!
|
// stun resist 2 passed!
|
||||||
if (IsClient())
|
if (IsClient()) {
|
||||||
MessageString(Chat::Stun, AVOID_STUNNING_BLOW);
|
FilteredMessageString(
|
||||||
|
this,
|
||||||
|
Chat::Stun,
|
||||||
|
FilterStuns,
|
||||||
|
AVOID_STUNNING_BLOW
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
} else {
|
||||||
// main stun failed -- extra interrupt roll
|
// main stun failed -- extra interrupt roll
|
||||||
if (IsCasting() &&
|
// these spells are excluded
|
||||||
!EQ::ValueWithin(casting_spell_id, 859, 1023)) // these spells are excluded
|
|
||||||
// 90% chance >< -- stun immune won't reach this branch though :(
|
// 90% chance >< -- stun immune won't reach this branch though :(
|
||||||
if (zone->random.Int(0, 9) > 1)
|
if (IsCasting() && !EQ::ValueWithin(casting_spell_id, 859, 1023)) {
|
||||||
|
if (zone->random.Int(0, 9) > 1) {
|
||||||
InterruptSpell();
|
InterruptSpell();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (IsValidSpell(spell_id) && !iBuffTic) {
|
if (IsValidSpell(spell_id) && !iBuffTic) {
|
||||||
//see if root will break
|
//see if root will break
|
||||||
@@ -4529,8 +4604,21 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
|||||||
}
|
}
|
||||||
|
|
||||||
//send an HP update if we are hurt
|
//send an HP update if we are hurt
|
||||||
if (GetHP() < GetMaxHP()) {
|
if(GetHP() < GetMaxHP())
|
||||||
SendHPUpdate(); // the OP_Damage actually updates the client in these cases, so we skip the HP update for them
|
{
|
||||||
|
// Don't send a HP update for melee damage unless we've damaged ourself.
|
||||||
|
if (IsNPC()) {
|
||||||
|
int cur_hp_ratio = (int)GetHPRatio();
|
||||||
|
if (cur_hp_ratio != old_hp_ratio) {
|
||||||
|
SendHPUpdate(true);
|
||||||
|
}
|
||||||
|
} else if (!iBuffTic || died) { // Let regen handle buff tics unless this tic killed us.
|
||||||
|
SendHPUpdate(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!died && IsNPC()) {
|
||||||
|
CheckFlee();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} //end `if damage was done`
|
} //end `if damage was done`
|
||||||
|
|
||||||
@@ -5388,101 +5476,6 @@ void Mob::TryPetCriticalHit(Mob *defender, DamageHitInfo &hit)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Mob::RollMeleeCritCheck(Mob *defender, EQ::skills::SkillType skill)
|
|
||||||
{
|
|
||||||
// We either require an innate crit chance or some SPA 169 to crit
|
|
||||||
bool innate_crit = false;
|
|
||||||
int crit_chance = GetCriticalChanceBonus(skill);
|
|
||||||
// Paladin check
|
|
||||||
if (defender->IsUndeadForSlay()) {
|
|
||||||
crit_chance = crit_chance + GetUndeadSlayRate();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GetLevel() >= 12) {
|
|
||||||
if (
|
|
||||||
GetClass() == Class::Warrior ||
|
|
||||||
(GetClass() == Class::Ranger && skill == EQ::skills::SkillArchery) ||
|
|
||||||
(GetClass() == Class::Rogue && skill == EQ::skills::SkillThrowing) ||
|
|
||||||
GetClass() == Class::Berserker
|
|
||||||
) {
|
|
||||||
innate_crit = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we have a chance to crit!
|
|
||||||
if (innate_crit || crit_chance) {
|
|
||||||
int difficulty = 0;
|
|
||||||
|
|
||||||
if (skill == EQ::skills::SkillArchery) {
|
|
||||||
difficulty = RuleI(Combat, ArcheryCritDifficulty);
|
|
||||||
} else if (skill == EQ::skills::SkillThrowing) {
|
|
||||||
difficulty = RuleI(Combat, ThrowingCritDifficulty);
|
|
||||||
} else {
|
|
||||||
difficulty = RuleI(Combat, MeleeCritDifficulty);
|
|
||||||
}
|
|
||||||
|
|
||||||
int roll = zone->random.Int(1, difficulty);
|
|
||||||
|
|
||||||
int dex_bonus = GetDEX();
|
|
||||||
|
|
||||||
if (dex_bonus > 255) {
|
|
||||||
dex_bonus = 255 + ((dex_bonus - 255) / 5);
|
|
||||||
}
|
|
||||||
|
|
||||||
dex_bonus += 45; // chances did not match live without a small boost
|
|
||||||
|
|
||||||
// so if we have an innate crit we have a better chance, except for ber throwing
|
|
||||||
if (!innate_crit || (GetClass() == Class::Berserker && skill == EQ::skills::SkillThrowing)) {
|
|
||||||
dex_bonus = dex_bonus * 3 / 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogCombat("Crit Chance: dex_bonus ({}) * crit_chance ({}) / 100", dex_bonus, crit_chance);
|
|
||||||
|
|
||||||
if (crit_chance) {
|
|
||||||
dex_bonus += dex_bonus * crit_chance / 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if we crited
|
|
||||||
LogCombat("Final Roll! Difficulty = [{}] -- Dex_Bonus = [{}] ", difficulty, dex_bonus);
|
|
||||||
return (roll < dex_bonus);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int Mob::GetUndeadSlayRate()
|
|
||||||
{
|
|
||||||
return aabonuses.SlayUndead[0] + itembonuses.SlayUndead[0] + spellbonuses.SlayUndead[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mob::DoUndeadSlay(DamageHitInfo &hit, int crit_mod)
|
|
||||||
{
|
|
||||||
|
|
||||||
int slay_damage_bonus = std::max(
|
|
||||||
{ aabonuses.SlayUndead[1], itembonuses.SlayUndead[1], spellbonuses.SlayUndead[1] });
|
|
||||||
|
|
||||||
LogCombatDetail("Slayundead bonus [{}]", slay_damage_bonus);
|
|
||||||
|
|
||||||
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
|
||||||
hit.damage_done = (hit.damage_done * slay_damage_bonus * crit_mod) / 100;
|
|
||||||
hit.damage_done = static_cast<int>(hit.damage_done * RuleR(Combat, SlayDamageAdjustment));
|
|
||||||
|
|
||||||
LogCombatDetail("Slayundead damage [{}]", hit.damage_done);
|
|
||||||
|
|
||||||
int slay_sex = GetGender() == Gender::Female ? FEMALE_SLAYUNDEAD : MALE_SLAYUNDEAD;
|
|
||||||
|
|
||||||
entity_list.FilteredMessageString(
|
|
||||||
this, /* Sender */
|
|
||||||
false, /* Skip Sender */
|
|
||||||
Chat::MeleeCrit, /* Type: 301 */
|
|
||||||
FilterMeleeCrits, /* FilterType: 12 */
|
|
||||||
slay_sex, /* MessageFormat: %1's holy blade cleanses her target!(%2) */
|
|
||||||
GetCleanName(), /* Message1 */
|
|
||||||
itoa(hit.damage_done) /* Message2 */
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// a lot of good info: http://giline.versus.jp/shiden/damage_e.htm, http://giline.versus.jp/shiden/su.htm
|
|
||||||
void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts)
|
void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *opts)
|
||||||
{
|
{
|
||||||
#ifdef LUA_EQEMU
|
#ifdef LUA_EQEMU
|
||||||
@@ -5494,8 +5487,9 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (hit.damage_done < 1 || !defender)
|
if (hit.damage_done < 1 || !defender) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// decided to branch this into it's own function since it's going to be duplicating a lot of the
|
// decided to branch this into it's own function since it's going to be duplicating a lot of the
|
||||||
// code in here, but could lead to some confusion otherwise
|
// code in here, but could lead to some confusion otherwise
|
||||||
@@ -5513,43 +5507,127 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 1: Check if we are critting
|
// 1: Try Slay Undead
|
||||||
if (!RollMeleeCritCheck(defender, hit.skill)) {
|
if (defender->GetBodyType() == BodyType::Undead || defender->GetBodyType() == BodyType::SummonedUndead ||
|
||||||
|
defender->GetBodyType() == BodyType::Vampire) {
|
||||||
|
int slay_rate_bonus = aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] + itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] + spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD];
|
||||||
|
|
||||||
|
LogCombatDetail("Slayundead hit rate [{}]", slay_rate_bonus);
|
||||||
|
|
||||||
|
if (slay_rate_bonus) {
|
||||||
|
float slay_chance = ((static_cast<float>(slay_rate_bonus) / 10000.0f) * RuleR(Combat, SlayRateMultiplier));
|
||||||
|
|
||||||
|
if (zone->random.Roll(slay_chance)) {
|
||||||
|
int slay_damage_bonus = aabonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] + itembonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] + spellbonuses.SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD];
|
||||||
|
|
||||||
|
LogCombatDetail("Slayundead damage bonus [{}]", slay_damage_bonus);
|
||||||
|
|
||||||
|
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
||||||
|
hit.damage_done = (hit.damage_done * slay_damage_bonus) / 100;
|
||||||
|
hit.damage_done = static_cast<int>(hit.damage_done * RuleR(Combat, SlayDamageMultiplier));
|
||||||
|
|
||||||
|
int min_slay = (hit.min_damage + 5) * slay_damage_bonus / 100;
|
||||||
|
|
||||||
|
LogCombatDetail(" Calculated Slayundead damage [{}] - Min Slay Undead Damage [{}]", hit.damage_done, min_slay);
|
||||||
|
|
||||||
|
if (hit.damage_done < min_slay) {
|
||||||
|
hit.damage_done = min_slay;
|
||||||
|
}
|
||||||
|
|
||||||
|
LogCombatDetail("Final Slayundead damage [{}]", hit.damage_done);
|
||||||
|
|
||||||
|
int slay_sex = GetGender() == Gender::Female ? FEMALE_SLAYUNDEAD : MALE_SLAYUNDEAD;
|
||||||
|
|
||||||
|
entity_list.FilteredMessageCloseString(
|
||||||
|
this, /* Sender */
|
||||||
|
false, /* Skip Sender */
|
||||||
|
RuleI(Range, CriticalDamage),
|
||||||
|
Chat::MeleeCrit, /* Type: 301 */
|
||||||
|
FilterMeleeCrits, /* FilterType: 12 */
|
||||||
|
slay_sex,
|
||||||
|
0,
|
||||||
|
GetCleanName(), /* Message1 */
|
||||||
|
itoa(hit.damage_done) /* Message2 */
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2: Try Melee Critical
|
||||||
|
// a lot of good info: http://giline.versus.jp/shiden/damage_e.htm, http://giline.versus.jp/shiden/su.htm
|
||||||
|
|
||||||
|
// We either require an innate crit chance or some SPA 169 to crit
|
||||||
|
bool innate_crit = false;
|
||||||
|
int crit_chance = GetCriticalChanceBonus(hit.skill);
|
||||||
|
if ((GetClass() == Class::Warrior || GetClass() == Class::Berserker) && GetLevel() >= 12) {
|
||||||
|
innate_crit = true;
|
||||||
|
} else if (GetClass() == Class::Ranger && GetLevel() >= 12 && hit.skill == EQ::skills::SkillArchery) {
|
||||||
|
innate_crit = true;
|
||||||
|
} else if (GetClass() == Class::Rogue && GetLevel() >= 12 && hit.skill == EQ::skills::SkillThrowing) {
|
||||||
|
innate_crit = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we have a chance to crit!
|
||||||
|
if (innate_crit || crit_chance) {
|
||||||
|
int difficulty = 0;
|
||||||
|
|
||||||
|
if (hit.skill == EQ::skills::SkillArchery) {
|
||||||
|
difficulty = RuleI(Combat, ArcheryCritDifficulty);
|
||||||
|
} else if (hit.skill == EQ::skills::SkillThrowing) {
|
||||||
|
difficulty = RuleI(Combat, ThrowingCritDifficulty);
|
||||||
|
} else {
|
||||||
|
difficulty = RuleI(Combat, MeleeCritDifficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
int roll = zone->random.Int(1, difficulty);
|
||||||
|
int dex_bonus = GetDEX();
|
||||||
|
|
||||||
|
if (dex_bonus > 255) {
|
||||||
|
dex_bonus = 255 + ((dex_bonus - 255) / 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
dex_bonus += 45; // chances did not match live without a small boost
|
||||||
|
|
||||||
|
// so if we have an innate crit we have a better chance, except for ber throwing
|
||||||
|
if (!innate_crit || (GetClass() == Class::Berserker && hit.skill == EQ::skills::SkillThrowing)) {
|
||||||
|
dex_bonus = dex_bonus * 3 / 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (crit_chance) {
|
||||||
|
dex_bonus += dex_bonus * crit_chance / 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if we crited
|
||||||
|
if (roll < dex_bonus) {
|
||||||
|
// step 1: check for finishing blow
|
||||||
|
if (TryFinishingBlow(defender, hit.damage_done)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int crit_mod = EQ::ClampLower((170 + GetCritDmgMod(hit.skill)), 100);
|
// step 2: calculate damage
|
||||||
|
|
||||||
// Step 2: Calculate damage
|
|
||||||
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
||||||
int og_damage = hit.damage_done;
|
int og_damage = hit.damage_done;
|
||||||
|
int crit_mod = 170 + GetCritDmgMod(hit.skill);
|
||||||
|
|
||||||
|
if (crit_mod < 100) {
|
||||||
|
crit_mod = 100;
|
||||||
|
}
|
||||||
|
|
||||||
hit.damage_done = hit.damage_done * crit_mod / 100;
|
hit.damage_done = hit.damage_done * crit_mod / 100;
|
||||||
|
LogCombatDetail("Crit success roll [{}] dex chance [{}] og dmg [{}] crit_mod [{}] new dmg [{}]", roll, dex_bonus, og_damage, crit_mod, hit.damage_done);
|
||||||
|
|
||||||
LogCombatDetail("Crit info: [{}] scaled from: [{}] - IsUndeadForSlay: [{}]", hit.damage_done, og_damage, IsUndeadForSlay() ? "true" : "false");
|
// step 3: check deadly strike
|
||||||
|
if (GetClass() == Class::Rogue && hit.skill == EQ::skills::SkillThrowing) {
|
||||||
// Try Slay Undead
|
if (BehindMob(defender, GetX(), GetY())) {
|
||||||
if (defender->IsUndeadForSlay()) {
|
|
||||||
float chance = GetUndeadSlayRate() / 100.0f;
|
|
||||||
LogCombatDetail("Trying Undead slay: Chance: [{}]", chance);
|
|
||||||
|
|
||||||
if(zone->random.Roll(chance)) {
|
|
||||||
DoUndeadSlay(hit, crit_mod);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Check deadly strike
|
|
||||||
if (GetClass() == Class::Rogue && hit.skill == EQ::skills::SkillThrowing && BehindMob(defender, GetX(), GetY())) {
|
|
||||||
int chance = GetLevel() * 12;
|
int chance = GetLevel() * 12;
|
||||||
if (zone->random.Int(1, 1000) < chance) {
|
if (zone->random.Int(1, 1000) < chance) {
|
||||||
// Check assassinate
|
// step 3a: check assassinate
|
||||||
int assassinate_damage = TryAssassinate(defender, hit.skill);
|
int assassinate_damage = TryAssassinate(defender, hit.skill); // I don't think this is right
|
||||||
|
|
||||||
if (assassinate_damage) {
|
if (assassinate_damage) {
|
||||||
hit.damage_done = assassinate_damage;
|
hit.damage_done = assassinate_damage;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
hit.damage_done = hit.damage_done * 200 / 100;
|
hit.damage_done = hit.damage_done * 200 / 100;
|
||||||
|
|
||||||
entity_list.FilteredMessageCloseString(
|
entity_list.FilteredMessageCloseString(
|
||||||
@@ -5561,22 +5639,25 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
|||||||
DEADLY_STRIKE, /* MessageFormat: %1 scores a Deadly Strike!(%2) */
|
DEADLY_STRIKE, /* MessageFormat: %1 scores a Deadly Strike!(%2) */
|
||||||
0,
|
0,
|
||||||
GetCleanName(), /* Message1 */
|
GetCleanName(), /* Message1 */
|
||||||
itoa(hit.damage_done) /* Message2 */
|
itoa(hit.damage_done + hit.min_damage) /* Message2 */
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Step 4: check cripple
|
// step 4: check crips
|
||||||
|
// this SPA was reused on live ...
|
||||||
bool berserk = spellbonuses.BerserkSPA || itembonuses.BerserkSPA || aabonuses.BerserkSPA;
|
bool berserk = spellbonuses.BerserkSPA || itembonuses.BerserkSPA || aabonuses.BerserkSPA;
|
||||||
|
if (!berserk) {
|
||||||
if (!berserk && zone->random.Roll(GetCrippBlowChance())) {
|
if (zone->random.Roll(GetCrippBlowChance())) {
|
||||||
berserk = true;
|
berserk = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (IsBerserk() || berserk) {
|
if (IsBerserk() || berserk) {
|
||||||
hit.damage_done += og_damage * 119 / 100;
|
hit.damage_done += og_damage * 119 / 100;
|
||||||
LogCombatDetail("Crippling damage [{}]", hit.damage_done);
|
LogCombat("Crip damage [{}]", hit.damage_done);
|
||||||
|
|
||||||
entity_list.FilteredMessageCloseString(
|
entity_list.FilteredMessageCloseString(
|
||||||
this, /* Sender */
|
this, /* Sender */
|
||||||
@@ -5587,13 +5668,13 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
|||||||
CRIPPLING_BLOW, /* MessageFormat: %1 lands a Crippling Blow!(%2) */
|
CRIPPLING_BLOW, /* MessageFormat: %1 lands a Crippling Blow!(%2) */
|
||||||
0,
|
0,
|
||||||
GetCleanName(), /* Message1 */
|
GetCleanName(), /* Message1 */
|
||||||
itoa(hit.damage_done) /* Message2 */
|
itoa(hit.damage_done + hit.min_damage) /* Message2 */
|
||||||
);
|
);
|
||||||
|
|
||||||
// Crippling blows also have a chance to stun
|
// Crippling blows also have a chance to stun
|
||||||
// Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a
|
// Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a
|
||||||
// staggers message.
|
// staggers message.
|
||||||
if (defender->GetLevel() <= RuleI(Combat, MaximumLevelStunsCripplingBlow) && !defender->GetSpecialAbility(SpecialAbility::StunImmunity)) {
|
if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(SpecialAbility::StunImmunity)) {
|
||||||
entity_list.MessageCloseString(
|
entity_list.MessageCloseString(
|
||||||
defender,
|
defender,
|
||||||
true,
|
true,
|
||||||
@@ -5617,9 +5698,11 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
|||||||
CRITICAL_HIT, /* MessageFormat: %1 scores a critical hit! (%2) */
|
CRITICAL_HIT, /* MessageFormat: %1 scores a critical hit! (%2) */
|
||||||
0,
|
0,
|
||||||
GetCleanName(), /* Message1 */
|
GetCleanName(), /* Message1 */
|
||||||
itoa(hit.damage_done) /* Message2 */
|
itoa(hit.damage_done + hit.min_damage) /* Message2 */
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool Mob::TryFinishingBlow(Mob *defender, int64 &damage)
|
bool Mob::TryFinishingBlow(Mob *defender, int64 &damage)
|
||||||
{
|
{
|
||||||
|
|||||||
+14
-8
@@ -182,14 +182,20 @@ void Mob::CalcItemBonuses(StatBonuses* b) {
|
|||||||
SetDualWeaponsEquipped(true);
|
SetDualWeaponsEquipped(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsOfClientBot()) {
|
if (IsClient()) {
|
||||||
for (i = EQ::invslot::TRIBUTE_BEGIN; i <= EQ::invslot::TRIBUTE_END; i++) {
|
if (CastToClient()->GetPP().tribute_active) {
|
||||||
const EQ::ItemInstance* inst = m_inv[i];
|
for (auto const &t: CastToClient()->GetPP().tributes) {
|
||||||
|
auto item_id = CastToClient()->LookupTributeItemID(t.tribute, t.tier);
|
||||||
|
if (item_id) {
|
||||||
|
const EQ::ItemInstance *inst = database.CreateItem(item_id);
|
||||||
if (!inst) {
|
if (!inst) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
AddItemBonuses(inst, b, false, true);
|
AddItemBonuses(inst, b, false, true);
|
||||||
|
safe_delete(inst);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1439,8 +1445,8 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
|||||||
|
|
||||||
case SE_SlayUndead: {
|
case SE_SlayUndead: {
|
||||||
if (newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < base_value) {
|
if (newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < base_value) {
|
||||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = base_value; // Rate
|
newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = base_value; // Rate
|
||||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = limit_value; // Damage Modifier
|
newbon->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = limit_value; // Damage Modifier
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -3360,7 +3366,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case SE_Blind:
|
case SE_Blind:
|
||||||
if (RuleB(Combat, AllowRaidTargetBlind) && IsRaidTarget()) { // do not blind raid targets
|
if (!RuleB(Combat, AllowRaidTargetBlind) && IsRaidTarget()) { // do not blind raid targets
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3589,8 +3595,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
|||||||
|
|
||||||
case SE_SlayUndead: {
|
case SE_SlayUndead: {
|
||||||
if (new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < effect_value) {
|
if (new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < effect_value) {
|
||||||
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = effect_value; // Rate
|
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = limit_value; // Rate
|
||||||
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = limit_value; // Damage Modifier
|
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; // Damage Modifier
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
+11
-15
@@ -204,7 +204,7 @@ Bot::Bot(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
SetTaunting((GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (GetBotStance() == EQ::constants::stanceAggressive));
|
SetTaunting((GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (GetBotStance() == Stance::Aggressive));
|
||||||
SetPauseAI(false);
|
SetPauseAI(false);
|
||||||
|
|
||||||
m_auto_defend_timer.Disable();
|
m_auto_defend_timer.Disable();
|
||||||
@@ -1899,8 +1899,8 @@ void Bot::AI_Process()
|
|||||||
#define NOT_GUARDING (!GetGuardFlag())
|
#define NOT_GUARDING (!GetGuardFlag())
|
||||||
#define HOLDING (GetHoldFlag())
|
#define HOLDING (GetHoldFlag())
|
||||||
#define NOT_HOLDING (!GetHoldFlag())
|
#define NOT_HOLDING (!GetHoldFlag())
|
||||||
#define PASSIVE (GetBotStance() == EQ::constants::stancePassive)
|
#define PASSIVE (GetBotStance() == Stance::Passive)
|
||||||
#define NOT_PASSIVE (GetBotStance() != EQ::constants::stancePassive)
|
#define NOT_PASSIVE (GetBotStance() != Stance::Passive)
|
||||||
|
|
||||||
Client* bot_owner = (GetBotOwner() && GetBotOwner()->IsClient() ? GetBotOwner()->CastToClient() : nullptr);
|
Client* bot_owner = (GetBotOwner() && GetBotOwner()->IsClient() ? GetBotOwner()->CastToClient() : nullptr);
|
||||||
|
|
||||||
@@ -6912,16 +6912,16 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl
|
|||||||
if ((botCasterClass == Class::Paladin || botCasterClass == Class::Beastlord || botCasterClass == Class::Ranger) && (caster->HasGroup() || caster->IsRaidGrouped())) {
|
if ((botCasterClass == Class::Paladin || botCasterClass == Class::Beastlord || botCasterClass == Class::Ranger) && (caster->HasGroup() || caster->IsRaidGrouped())) {
|
||||||
float hpRatioToHeal = 25.0f;
|
float hpRatioToHeal = 25.0f;
|
||||||
switch(caster->GetBotStance()) {
|
switch(caster->GetBotStance()) {
|
||||||
case EQ::constants::stanceReactive:
|
case Stance::Reactive:
|
||||||
case EQ::constants::stanceBalanced:
|
case Stance::Balanced:
|
||||||
hpRatioToHeal = 50.0f;
|
hpRatioToHeal = 50.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceBurn:
|
case Stance::Burn:
|
||||||
case EQ::constants::stanceBurnAE:
|
case Stance::AEBurn:
|
||||||
hpRatioToHeal = 20.0f;
|
hpRatioToHeal = 20.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceAggressive:
|
case Stance::Aggressive:
|
||||||
case EQ::constants::stanceEfficient:
|
case Stance::Efficient:
|
||||||
default:
|
default:
|
||||||
hpRatioToHeal = 25.0f;
|
hpRatioToHeal = 25.0f;
|
||||||
break;
|
break;
|
||||||
@@ -7655,11 +7655,7 @@ bool Bot::HasOrMayGetAggro() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Bot::SetDefaultBotStance() {
|
void Bot::SetDefaultBotStance() {
|
||||||
EQ::constants::StanceType defaultStance = EQ::constants::stanceBalanced;
|
_botStance = GetClass() == Class::Warrior ? Stance::Aggressive : Stance::Balanced;
|
||||||
if (GetClass() == Class::Warrior)
|
|
||||||
defaultStance = EQ::constants::stanceAggressive;
|
|
||||||
|
|
||||||
_botStance = defaultStance;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Bot::BotGroupSay(Mob* speaker, const char* msg, ...) {
|
void Bot::BotGroupSay(Mob* speaker, const char* msg, ...) {
|
||||||
@@ -9233,4 +9229,4 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND] = { 0 };
|
||||||
|
|||||||
+4
-9
@@ -484,7 +484,7 @@ public:
|
|||||||
bool IsOfClientBotMerc() const override { return true; }
|
bool IsOfClientBotMerc() const override { return true; }
|
||||||
|
|
||||||
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
|
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
|
||||||
EQ::constants::StanceType GetBotStance() { return _botStance; }
|
uint8 GetBotStance() { return _botStance; }
|
||||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||||
bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; }
|
bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; }
|
||||||
float GetBotCasterMaxRange(float melee_distance_max);
|
float GetBotCasterMaxRange(float melee_distance_max);
|
||||||
@@ -605,12 +605,7 @@ public:
|
|||||||
void SetPetChooser(bool p) { _petChooser = p; }
|
void SetPetChooser(bool p) { _petChooser = p; }
|
||||||
void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; }
|
void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; }
|
||||||
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == Class::Ranger ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
|
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == Class::Ranger ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
|
||||||
void SetBotStance(EQ::constants::StanceType botStance) {
|
void SetBotStance(uint8 stance_id) { _botStance = Stance::IsValid(stance_id) ? stance_id : Stance::Passive; }
|
||||||
if (botStance >= EQ::constants::stancePassive && botStance <= EQ::constants::stanceBurnAE)
|
|
||||||
_botStance = botStance;
|
|
||||||
else
|
|
||||||
_botStance = EQ::constants::stancePassive;
|
|
||||||
}
|
|
||||||
void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; }
|
void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; }
|
||||||
uint32 GetSpellRecastTimer(uint16 spell_id = 0);
|
uint32 GetSpellRecastTimer(uint16 spell_id = 0);
|
||||||
bool CheckSpellRecastTimer(uint16 spell_id = 0);
|
bool CheckSpellRecastTimer(uint16 spell_id = 0);
|
||||||
@@ -753,7 +748,7 @@ public:
|
|||||||
//Raid additions
|
//Raid additions
|
||||||
Raid* p_raid_instance;
|
Raid* p_raid_instance;
|
||||||
|
|
||||||
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND];
|
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND];
|
||||||
|
|
||||||
bool BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid);
|
bool BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid);
|
||||||
bool BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid);
|
bool BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid);
|
||||||
@@ -870,7 +865,7 @@ private:
|
|||||||
std::string _suffix;
|
std::string _suffix;
|
||||||
uint32 _lastZoneId;
|
uint32 _lastZoneId;
|
||||||
bool _rangerAutoWeaponSelect;
|
bool _rangerAutoWeaponSelect;
|
||||||
EQ::constants::StanceType _botStance;
|
uint8 _botStance;
|
||||||
unsigned int RestRegenHP;
|
unsigned int RestRegenHP;
|
||||||
unsigned int RestRegenMana;
|
unsigned int RestRegenMana;
|
||||||
unsigned int RestRegenEndurance;
|
unsigned int RestRegenEndurance;
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ void bot_command_attack(Client *c, const Seperator *sep)
|
|||||||
sbl.remove(nullptr);
|
sbl.remove(nullptr);
|
||||||
for (auto bot_iter : sbl) {
|
for (auto bot_iter : sbl) {
|
||||||
|
|
||||||
if (bot_iter->GetAppearance() != eaDead && bot_iter->GetBotStance() != EQ::constants::stancePassive) {
|
if (bot_iter->GetAppearance() != eaDead && bot_iter->GetBotStance() != Stance::Passive) {
|
||||||
|
|
||||||
if (!first_attacker) {
|
if (!first_attacker) {
|
||||||
first_attacker = bot_iter;
|
first_attacker = bot_iter;
|
||||||
|
|||||||
+32
-18
@@ -198,7 +198,7 @@ void bot_command_clone(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int clone_stance = EQ::constants::stancePassive;
|
int clone_stance = Stance::Passive;
|
||||||
if (!database.botdb.LoadStance(my_bot->GetBotID(), clone_stance)) {
|
if (!database.botdb.LoadStance(my_bot->GetBotID(), clone_stance)) {
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
@@ -1058,33 +1058,47 @@ void bot_command_stance(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||||
c->Message(Chat::White, "usage: %s [current | value: 1-9] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
|
c->Message(Chat::White, "usage: %s [current | value: 1-9] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
|
||||||
c->Message(Chat::White, "value: %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s)",
|
c->Message(
|
||||||
EQ::constants::stancePassive, EQ::constants::GetStanceName(EQ::constants::stancePassive),
|
Chat::White,
|
||||||
EQ::constants::stanceBalanced, EQ::constants::GetStanceName(EQ::constants::stanceBalanced),
|
fmt::format(
|
||||||
EQ::constants::stanceEfficient, EQ::constants::GetStanceName(EQ::constants::stanceEfficient),
|
"Value: {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({})",
|
||||||
EQ::constants::stanceReactive, EQ::constants::GetStanceName(EQ::constants::stanceReactive),
|
Stance::Passive,
|
||||||
EQ::constants::stanceAggressive, EQ::constants::GetStanceName(EQ::constants::stanceAggressive),
|
Stance::GetName(Stance::Passive),
|
||||||
EQ::constants::stanceAssist, EQ::constants::GetStanceName(EQ::constants::stanceAssist),
|
Stance::Balanced,
|
||||||
EQ::constants::stanceBurn, EQ::constants::GetStanceName(EQ::constants::stanceBurn),
|
Stance::GetName(Stance::Balanced),
|
||||||
EQ::constants::stanceEfficient2, EQ::constants::GetStanceName(EQ::constants::stanceEfficient2),
|
Stance::Efficient,
|
||||||
EQ::constants::stanceBurnAE, EQ::constants::GetStanceName(EQ::constants::stanceBurnAE)
|
Stance::GetName(Stance::Efficient),
|
||||||
|
Stance::Reactive,
|
||||||
|
Stance::GetName(Stance::Reactive),
|
||||||
|
Stance::Aggressive,
|
||||||
|
Stance::GetName(Stance::Aggressive),
|
||||||
|
Stance::Assist,
|
||||||
|
Stance::GetName(Stance::Assist),
|
||||||
|
Stance::Burn,
|
||||||
|
Stance::GetName(Stance::Burn),
|
||||||
|
Stance::Efficient2,
|
||||||
|
Stance::GetName(Stance::Efficient2),
|
||||||
|
Stance::AEBurn,
|
||||||
|
Stance::GetName(Stance::AEBurn)
|
||||||
|
).c_str()
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
|
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
|
||||||
|
|
||||||
bool current_flag = false;
|
bool current_flag = false;
|
||||||
auto bst = EQ::constants::stanceUnknown;
|
uint8 bst = Stance::Unknown;
|
||||||
|
|
||||||
if (!strcasecmp(sep->arg[1], "current"))
|
if (!strcasecmp(sep->arg[1], "current"))
|
||||||
current_flag = true;
|
current_flag = true;
|
||||||
else if (sep->IsNumber(1)) {
|
else if (sep->IsNumber(1)) {
|
||||||
bst = (EQ::constants::StanceType)Strings::ToInt(sep->arg[1]);
|
bst = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[1]));
|
||||||
if (bst < EQ::constants::stanceUnknown || bst > EQ::constants::stanceBurnAE)
|
if (!Stance::IsValid(bst)) {
|
||||||
bst = EQ::constants::stanceUnknown;
|
bst = Stance::Unknown;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!current_flag && bst == EQ::constants::stanceUnknown) {
|
if (!current_flag && bst == Stance::Unknown) {
|
||||||
c->Message(Chat::White, "A [current] argument or valid numeric [value] is required to use this command");
|
c->Message(Chat::White, "A [current] argument or valid numeric [value] is required to use this command");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1106,8 +1120,8 @@ void bot_command_stance(Client *c, const Seperator *sep)
|
|||||||
bot_iter,
|
bot_iter,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"My current stance is {} ({}).",
|
"My current stance is {} ({}).",
|
||||||
EQ::constants::GetStanceName(bot_iter->GetBotStance()),
|
Stance::GetName(bot_iter->GetBotStance()),
|
||||||
static_cast<int>(bot_iter->GetBotStance())
|
bot_iter->GetBotStance()
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
|||||||
Bot* bot_puller = nullptr;
|
Bot* bot_puller = nullptr;
|
||||||
for (auto bot_iter : sbl) {
|
for (auto bot_iter : sbl) {
|
||||||
|
|
||||||
if (bot_iter->GetAppearance() == eaDead || bot_iter->GetBotStance() == EQ::constants::stancePassive) {
|
if (bot_iter->GetAppearance() == eaDead || bot_iter->GetBotStance() == Stance::Passive) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ bool BotDatabase::LoadBotSpellCastingChances()
|
|||||||
if (
|
if (
|
||||||
e.spell_type_index >= Bot::SPELL_TYPE_COUNT ||
|
e.spell_type_index >= Bot::SPELL_TYPE_COUNT ||
|
||||||
!IsPlayerClass(e.class_id) ||
|
!IsPlayerClass(e.class_id) ||
|
||||||
e.stance_index >= EQ::constants::STANCE_TYPE_COUNT
|
e.stance_index >= Stance::AEBurn
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -761,7 +761,7 @@ bool BotDatabase::LoadStance(Bot* b, bool& stance_flag)
|
|||||||
|
|
||||||
auto e = l.front();
|
auto e = l.front();
|
||||||
|
|
||||||
b->SetBotStance(static_cast<EQ::constants::StanceType>(e.stance_id));
|
b->SetBotStance(e.stance_id);
|
||||||
|
|
||||||
stance_flag = true;
|
stance_flag = true;
|
||||||
|
|
||||||
@@ -793,7 +793,7 @@ bool BotDatabase::SaveStance(Bot* b)
|
|||||||
database,
|
database,
|
||||||
BotStancesRepository::BotStances{
|
BotStancesRepository::BotStances{
|
||||||
.bot_id = b->GetBotID(),
|
.bot_id = b->GetBotID(),
|
||||||
.stance_id = static_cast<uint8_t>(b->GetBotStance())
|
.stance_id = b->GetBotStance()
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -2208,7 +2208,7 @@ uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_ind
|
|||||||
if (
|
if (
|
||||||
spell_type_index >= Bot::SPELL_TYPE_COUNT ||
|
spell_type_index >= Bot::SPELL_TYPE_COUNT ||
|
||||||
class_index >= Class::PLAYER_CLASS_COUNT ||
|
class_index >= Class::PLAYER_CLASS_COUNT ||
|
||||||
stance_index >= EQ::constants::STANCE_TYPE_COUNT ||
|
stance_index >= Stance::AEBurn ||
|
||||||
conditional_index >= cntHSND
|
conditional_index >= cntHSND
|
||||||
) {
|
) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
+32
-30
@@ -629,16 +629,16 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
|||||||
float manaRatioToCast = 75.0f;
|
float manaRatioToCast = 75.0f;
|
||||||
|
|
||||||
switch(GetBotStance()) {
|
switch(GetBotStance()) {
|
||||||
case EQ::constants::stanceEfficient:
|
case Stance::Efficient:
|
||||||
manaRatioToCast = 90.0f;
|
manaRatioToCast = 90.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceBalanced:
|
case Stance::Balanced:
|
||||||
case EQ::constants::stanceAggressive:
|
case Stance::Aggressive:
|
||||||
manaRatioToCast = 75.0f;
|
manaRatioToCast = 75.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceReactive:
|
case Stance::Reactive:
|
||||||
case EQ::constants::stanceBurn:
|
case Stance::Burn:
|
||||||
case EQ::constants::stanceBurnAE:
|
case Stance::AEBurn:
|
||||||
manaRatioToCast = 50.0f;
|
manaRatioToCast = 50.0f;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -746,18 +746,18 @@ bool Bot::BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
|
|||||||
float manaRatioToCast = 75.0f;
|
float manaRatioToCast = 75.0f;
|
||||||
|
|
||||||
switch(GetBotStance()) {
|
switch(GetBotStance()) {
|
||||||
case EQ::constants::stanceEfficient:
|
case Stance::Efficient:
|
||||||
manaRatioToCast = 90.0f;
|
manaRatioToCast = 90.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceBalanced:
|
case Stance::Balanced:
|
||||||
manaRatioToCast = 75.0f;
|
manaRatioToCast = 75.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceReactive:
|
case Stance::Reactive:
|
||||||
case EQ::constants::stanceAggressive:
|
case Stance::Aggressive:
|
||||||
manaRatioToCast = 50.0f;
|
manaRatioToCast = 50.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceBurn:
|
case Stance::Burn:
|
||||||
case EQ::constants::stanceBurnAE:
|
case Stance::AEBurn:
|
||||||
manaRatioToCast = 25.0f;
|
manaRatioToCast = 25.0f;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -924,16 +924,16 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
|||||||
float manaRatioToCast = 75.0f;
|
float manaRatioToCast = 75.0f;
|
||||||
|
|
||||||
switch (GetBotStance()) {
|
switch (GetBotStance()) {
|
||||||
case EQ::constants::stanceEfficient:
|
case Stance::Efficient:
|
||||||
manaRatioToCast = 90.0f;
|
manaRatioToCast = 90.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceBalanced:
|
case Stance::Balanced:
|
||||||
case EQ::constants::stanceAggressive:
|
case Stance::Aggressive:
|
||||||
manaRatioToCast = 75.0f;
|
manaRatioToCast = 75.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceReactive:
|
case Stance::Reactive:
|
||||||
case EQ::constants::stanceBurn:
|
case Stance::Burn:
|
||||||
case EQ::constants::stanceBurnAE:
|
case Stance::AEBurn:
|
||||||
manaRatioToCast = 50.0f;
|
manaRatioToCast = 50.0f;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -1088,18 +1088,18 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
|
|||||||
float hpRatioToCast = 0.0f;
|
float hpRatioToCast = 0.0f;
|
||||||
|
|
||||||
switch (GetBotStance()) {
|
switch (GetBotStance()) {
|
||||||
case EQ::constants::stanceEfficient:
|
case Stance::Efficient:
|
||||||
case EQ::constants::stanceAggressive:
|
case Stance::Aggressive:
|
||||||
hpRatioToCast = isPrimaryHealer ? 90.0f : 50.0f;
|
hpRatioToCast = isPrimaryHealer ? 90.0f : 50.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceBalanced:
|
case Stance::Balanced:
|
||||||
hpRatioToCast = isPrimaryHealer ? 95.0f : 75.0f;
|
hpRatioToCast = isPrimaryHealer ? 95.0f : 75.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceReactive:
|
case Stance::Reactive:
|
||||||
hpRatioToCast = isPrimaryHealer ? 100.0f : 90.0f;
|
hpRatioToCast = isPrimaryHealer ? 100.0f : 90.0f;
|
||||||
break;
|
break;
|
||||||
case EQ::constants::stanceBurn:
|
case Stance::Burn:
|
||||||
case EQ::constants::stanceBurnAE:
|
case Stance::AEBurn:
|
||||||
hpRatioToCast = isPrimaryHealer ? 75.0f : 25.0f;
|
hpRatioToCast = isPrimaryHealer ? 75.0f : 25.0f;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -2939,11 +2939,12 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
|||||||
return 0;
|
return 0;
|
||||||
--class_index;
|
--class_index;
|
||||||
|
|
||||||
EQ::constants::StanceType stance_type = GetBotStance();
|
uint32 stance_id = GetBotStance();
|
||||||
if (stance_type < EQ::constants::stancePassive || stance_type > EQ::constants::stanceBurnAE)
|
if (!Stance::IsValid(stance_id)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
uint8 stance_index = EQ::constants::ConvertStanceTypeToIndex(stance_type);
|
uint8 stance_index = Stance::GetIndex(stance_id);
|
||||||
uint8 type_index = nHSND;
|
uint8 type_index = nHSND;
|
||||||
|
|
||||||
if (HasGroup()) {
|
if (HasGroup()) {
|
||||||
@@ -3323,7 +3324,8 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id)
|
|||||||
if (!bse.empty()) {
|
if (!bse.empty()) {
|
||||||
for (const auto& e : bse) {
|
for (const auto& e : bse) {
|
||||||
DBbotspells_entries_Struct entry;
|
DBbotspells_entries_Struct entry;
|
||||||
entry.spellid = e.spellid;
|
|
||||||
|
entry.spellid = e.spell_id;
|
||||||
entry.type = e.type;
|
entry.type = e.type;
|
||||||
entry.minlevel = e.minlevel;
|
entry.minlevel = e.minlevel;
|
||||||
entry.maxlevel = e.maxlevel;
|
entry.maxlevel = e.maxlevel;
|
||||||
@@ -3344,8 +3346,8 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id)
|
|||||||
|
|
||||||
if (e.resist_adjust) {
|
if (e.resist_adjust) {
|
||||||
entry.resist_adjust = e.resist_adjust;
|
entry.resist_adjust = e.resist_adjust;
|
||||||
} else if (IsValidSpell(e.spellid)) {
|
} else if (IsValidSpell(e.spell_id)) {
|
||||||
entry.resist_adjust = spells[e.spellid].resist_difficulty;
|
entry.resist_adjust = spells[e.spell_id].resist_difficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
spell_set.entries.push_back(entry);
|
spell_set.entries.push_back(entry);
|
||||||
|
|||||||
+187
-17
@@ -185,7 +185,9 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
position_update_timer(10000),
|
position_update_timer(10000),
|
||||||
consent_throttle_timer(2000),
|
consent_throttle_timer(2000),
|
||||||
tmSitting(0),
|
tmSitting(0),
|
||||||
parcel_timer(RuleI(Parcel, ParcelDeliveryDelay))
|
parcel_timer(RuleI(Parcel, ParcelDeliveryDelay)),
|
||||||
|
lazy_load_bank_check_timer(1000),
|
||||||
|
bandolier_throttle_timer(0)
|
||||||
{
|
{
|
||||||
for (auto client_filter = FilterNone; client_filter < _FilterCount; client_filter = eqFilterType(client_filter + 1)) {
|
for (auto client_filter = FilterNone; client_filter < _FilterCount; client_filter = eqFilterType(client_filter + 1)) {
|
||||||
SetFilter(client_filter, FilterShow);
|
SetFilter(client_filter, FilterShow);
|
||||||
@@ -203,7 +205,6 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
port = ntohs(eqs->GetRemotePort());
|
port = ntohs(eqs->GetRemotePort());
|
||||||
client_state = CLIENT_CONNECTING;
|
client_state = CLIENT_CONNECTING;
|
||||||
SetTrader(false);
|
SetTrader(false);
|
||||||
Buyer = false;
|
|
||||||
Haste = 0;
|
Haste = 0;
|
||||||
SetCustomerID(0);
|
SetCustomerID(0);
|
||||||
SetTraderID(0);
|
SetTraderID(0);
|
||||||
@@ -215,6 +216,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
guild_id = GUILD_NONE;
|
guild_id = GUILD_NONE;
|
||||||
guildrank = 0;
|
guildrank = 0;
|
||||||
guild_tribute_opt_in = 0;
|
guild_tribute_opt_in = 0;
|
||||||
|
SetGuildListDirty(false);
|
||||||
GuildBanker = false;
|
GuildBanker = false;
|
||||||
memset(lskey, 0, sizeof(lskey));
|
memset(lskey, 0, sizeof(lskey));
|
||||||
strcpy(account_name, "");
|
strcpy(account_name, "");
|
||||||
@@ -240,7 +242,6 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
runmode = false;
|
runmode = false;
|
||||||
linkdead_timer.Disable();
|
linkdead_timer.Disable();
|
||||||
zonesummon_id = 0;
|
zonesummon_id = 0;
|
||||||
zonesummon_instance_id = 0;
|
|
||||||
zonesummon_ignorerestrictions = 0;
|
zonesummon_ignorerestrictions = 0;
|
||||||
bZoning = false;
|
bZoning = false;
|
||||||
m_lock_save_position = false;
|
m_lock_save_position = false;
|
||||||
@@ -285,6 +286,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
memset(&m_epp, 0, sizeof(m_epp));
|
memset(&m_epp, 0, sizeof(m_epp));
|
||||||
PendingTranslocate = false;
|
PendingTranslocate = false;
|
||||||
PendingSacrifice = false;
|
PendingSacrifice = false;
|
||||||
|
sacrifice_caster_id = 0;
|
||||||
controlling_boat_id = 0;
|
controlling_boat_id = 0;
|
||||||
controlled_mob_id = 0;
|
controlled_mob_id = 0;
|
||||||
qGlobals = nullptr;
|
qGlobals = nullptr;
|
||||||
@@ -386,11 +388,12 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
|||||||
m_parcel_merchant_engaged = false;
|
m_parcel_merchant_engaged = false;
|
||||||
m_parcels.clear();
|
m_parcels.clear();
|
||||||
|
|
||||||
|
m_buyer_id = 0;
|
||||||
|
|
||||||
SetBotPulling(false);
|
SetBotPulling(false);
|
||||||
SetBotPrecombat(false);
|
SetBotPrecombat(false);
|
||||||
|
|
||||||
AI_Init();
|
AI_Init();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Client::~Client() {
|
Client::~Client() {
|
||||||
@@ -410,8 +413,12 @@ Client::~Client() {
|
|||||||
zone->ClearEXPModifier(this);
|
zone->ClearEXPModifier(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(IsInAGuild())
|
if (!IsZoning()) {
|
||||||
|
if(IsInAGuild()) {
|
||||||
|
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
||||||
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr));
|
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Mob* horse = entity_list.GetMob(CastToClient()->GetHorseId());
|
Mob* horse = entity_list.GetMob(CastToClient()->GetHorseId());
|
||||||
if (horse)
|
if (horse)
|
||||||
@@ -425,8 +432,9 @@ Client::~Client() {
|
|||||||
TraderEndTrader();
|
TraderEndTrader();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Buyer)
|
if(IsBuyer()) {
|
||||||
ToggleBuyerMode(false);
|
ToggleBuyerMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
if(conn_state != ClientConnectFinished) {
|
if(conn_state != ClientConnectFinished) {
|
||||||
LogDebug("Client [{}] was destroyed before reaching the connected state:", GetName());
|
LogDebug("Client [{}] was destroyed before reaching the connected state:", GetName());
|
||||||
@@ -2165,6 +2173,7 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
|||||||
ns->spawn.gm = GetGM() ? 1 : 0;
|
ns->spawn.gm = GetGM() ? 1 : 0;
|
||||||
ns->spawn.guildID = GuildID();
|
ns->spawn.guildID = GuildID();
|
||||||
ns->spawn.trader = IsTrader();
|
ns->spawn.trader = IsTrader();
|
||||||
|
ns->spawn.buyer = IsBuyer();
|
||||||
// ns->spawn.linkdead = IsLD() ? 1 : 0;
|
// ns->spawn.linkdead = IsLD() ? 1 : 0;
|
||||||
// ns->spawn.pvp = GetPVP(false) ? 1 : 0;
|
// ns->spawn.pvp = GetPVP(false) ? 1 : 0;
|
||||||
ns->spawn.show_name = true;
|
ns->spawn.show_name = true;
|
||||||
@@ -3396,6 +3405,11 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){
|
|||||||
} else { // these clients don't have a 'self only' filter
|
} else { // these clients don't have a 'self only' filter
|
||||||
Filter1(FilterHealOverTime);
|
Filter1(FilterHealOverTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Filter1(FilterItemSpeech);
|
||||||
|
Filter1(FilterStrikethrough);
|
||||||
|
Filter1(FilterStuns);
|
||||||
|
Filter1(FilterBardSongsOnPets);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this version is for messages with no parameters
|
// this version is for messages with no parameters
|
||||||
@@ -3958,7 +3972,7 @@ void Client::SetEndurance(int32 newEnd)
|
|||||||
CheckManaEndUpdate();
|
CheckManaEndUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::SacrificeConfirm(Client *caster)
|
void Client::SacrificeConfirm(Mob *caster)
|
||||||
{
|
{
|
||||||
auto outapp = new EQApplicationPacket(OP_Sacrifice, sizeof(Sacrifice_Struct));
|
auto outapp = new EQApplicationPacket(OP_Sacrifice, sizeof(Sacrifice_Struct));
|
||||||
Sacrifice_Struct *ss = (Sacrifice_Struct *)outapp->pBuffer;
|
Sacrifice_Struct *ss = (Sacrifice_Struct *)outapp->pBuffer;
|
||||||
@@ -3985,14 +3999,14 @@ void Client::SacrificeConfirm(Client *caster)
|
|||||||
ss->Confirm = 0;
|
ss->Confirm = 0;
|
||||||
QueuePacket(outapp);
|
QueuePacket(outapp);
|
||||||
safe_delete(outapp);
|
safe_delete(outapp);
|
||||||
// We store the Caster's name, because when the packet comes back, it only has the victim's entityID in it,
|
// We store the Caster's id, because when the packet comes back, it only has the victim's entityID in it,
|
||||||
// not the caster.
|
// not the caster.
|
||||||
SacrificeCaster += caster->GetName();
|
sacrifice_caster_id = caster->GetID();
|
||||||
PendingSacrifice = true;
|
PendingSacrifice = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Essentially a special case death function
|
//Essentially a special case death function
|
||||||
void Client::Sacrifice(Client *caster)
|
void Client::Sacrifice(Mob *caster)
|
||||||
{
|
{
|
||||||
if (GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)) {
|
if (GetLevel() >= RuleI(Spells, SacrificeMinLevel) && GetLevel() <= RuleI(Spells, SacrificeMaxLevel)) {
|
||||||
int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
|
int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
|
||||||
@@ -4040,8 +4054,11 @@ void Client::Sacrifice(Client *caster)
|
|||||||
}
|
}
|
||||||
Save();
|
Save();
|
||||||
GoToDeath();
|
GoToDeath();
|
||||||
if (caster) // I guess it's possible?
|
if (caster && caster->IsClient()) {
|
||||||
caster->SummonItem(RuleI(Spells, SacrificeItemID));
|
caster->CastToClient()->SummonItem(RuleI(Spells, SacrificeItemID));
|
||||||
|
} else if (caster && caster->IsNPC()) {
|
||||||
|
caster->CastToNPC()->AddItem(RuleI(Spells, SacrificeItemID), 1, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
caster->MessageString(Chat::Red, SAC_TOO_LOW); // This being is not a worthy sacrifice.
|
caster->MessageString(Chat::Red, SAC_TOO_LOW); // This being is not a worthy sacrifice.
|
||||||
@@ -9333,6 +9350,7 @@ void Client::ShowDevToolsMenu()
|
|||||||
std::string menu_reload_eight;
|
std::string menu_reload_eight;
|
||||||
std::string menu_reload_nine;
|
std::string menu_reload_nine;
|
||||||
std::string menu_toggle;
|
std::string menu_toggle;
|
||||||
|
std::string window_toggle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Search entity commands
|
* Search entity commands
|
||||||
@@ -9398,9 +9416,14 @@ void Client::ShowDevToolsMenu()
|
|||||||
/**
|
/**
|
||||||
* Show window status
|
* Show window status
|
||||||
*/
|
*/
|
||||||
menu_toggle = Saylink::Silent("#devtools enable", "Enable");
|
menu_toggle = Saylink::Silent("#devtools menu enable", "Enable");
|
||||||
if (IsDevToolsEnabled()) {
|
if (IsDevToolsEnabled()) {
|
||||||
menu_toggle = Saylink::Silent("#devtools disable", "Disable");
|
menu_toggle = Saylink::Silent("#devtools menu disable", "Disable");
|
||||||
|
}
|
||||||
|
|
||||||
|
window_toggle = Saylink::Silent("#devtools window enable", "Enable");
|
||||||
|
if (GetDisplayMobInfoWindow()) {
|
||||||
|
window_toggle = Saylink::Silent("#devtools window disable", "Disable");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9421,11 +9444,19 @@ void Client::ShowDevToolsMenu()
|
|||||||
Message(
|
Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"Toggle | {}",
|
"Toggle Menu | {}",
|
||||||
menu_toggle
|
menu_toggle
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"Toggle Window | {}",
|
||||||
|
window_toggle
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
Message(
|
Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
@@ -11066,7 +11097,9 @@ void Client::SaveDisciplines()
|
|||||||
{
|
{
|
||||||
std::vector<CharacterDisciplinesRepository::CharacterDisciplines> v;
|
std::vector<CharacterDisciplinesRepository::CharacterDisciplines> v;
|
||||||
|
|
||||||
for (int slot_id = 0; slot_id < MAX_PP_DISCIPLINES; slot_id++) {
|
std::vector<std::string> delete_slots;
|
||||||
|
|
||||||
|
for (uint16 slot_id = 0; slot_id < MAX_PP_DISCIPLINES; slot_id++) {
|
||||||
if (IsValidSpell(m_pp.disciplines.values[slot_id])) {
|
if (IsValidSpell(m_pp.disciplines.values[slot_id])) {
|
||||||
auto e = CharacterDisciplinesRepository::NewEntity();
|
auto e = CharacterDisciplinesRepository::NewEntity();
|
||||||
|
|
||||||
@@ -11075,9 +11108,22 @@ void Client::SaveDisciplines()
|
|||||||
e.disc_id = m_pp.disciplines.values[slot_id];
|
e.disc_id = m_pp.disciplines.values[slot_id];
|
||||||
|
|
||||||
v.emplace_back(e);
|
v.emplace_back(e);
|
||||||
|
} else {
|
||||||
|
delete_slots.emplace_back(std::to_string(slot_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!delete_slots.empty()) {
|
||||||
|
CharacterDisciplinesRepository::DeleteWhere(
|
||||||
|
database,
|
||||||
|
fmt::format(
|
||||||
|
"`id` = {} AND `slot_id` IN ({})",
|
||||||
|
CharacterID(),
|
||||||
|
Strings::Join(delete_slots, ", ")
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (!v.empty()) {
|
if (!v.empty()) {
|
||||||
CharacterDisciplinesRepository::ReplaceMany(database, v);
|
CharacterDisciplinesRepository::ReplaceMany(database, v);
|
||||||
}
|
}
|
||||||
@@ -11975,7 +12021,7 @@ void Client::SendPath(Mob* target)
|
|||||||
target->IsClient() &&
|
target->IsClient() &&
|
||||||
(
|
(
|
||||||
target->CastToClient()->IsTrader() ||
|
target->CastToClient()->IsTrader() ||
|
||||||
target->CastToClient()->Buyer
|
target->CastToClient()->IsBuyer()
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
Message(
|
Message(
|
||||||
@@ -12579,3 +12625,127 @@ void Client::RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Client::AddMoneyToPPWithOverflow(uint64 copper, bool update_client)
|
||||||
|
{
|
||||||
|
//I noticed in the ROF2 client that the client auto updates the currency values using overflow
|
||||||
|
//Therefore, I created this method to ensure that the db matches and clients don't see 10 pp 5 gp
|
||||||
|
//becoming 9pp 15 gold with the current AddMoneyToPP method.
|
||||||
|
|
||||||
|
auto add_pp = copper / 1000;
|
||||||
|
auto add_gp = (copper - add_pp * 1000) / 100;
|
||||||
|
auto add_sp = (copper - add_pp * 1000 - add_gp * 100) / 10;
|
||||||
|
auto add_cp = copper - add_pp * 1000 - add_gp * 100 - add_sp * 10;
|
||||||
|
|
||||||
|
m_pp.copper += add_cp;
|
||||||
|
if (m_pp.copper >= 10) {
|
||||||
|
m_pp.silver += m_pp.copper / 10;
|
||||||
|
m_pp.copper = m_pp.copper % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pp.silver += add_sp;
|
||||||
|
if (m_pp.silver >= 10) {
|
||||||
|
m_pp.gold += m_pp.silver / 10;
|
||||||
|
m_pp.silver = m_pp.silver % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pp.gold += add_gp;
|
||||||
|
if (m_pp.gold >= 10) {
|
||||||
|
m_pp.platinum += m_pp.gold / 10;
|
||||||
|
m_pp.gold = m_pp.gold % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pp.platinum += add_pp;
|
||||||
|
|
||||||
|
if (update_client) {
|
||||||
|
SendMoneyUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
RecalcWeight();
|
||||||
|
SaveCurrency();
|
||||||
|
|
||||||
|
LogDebug("Client::AddMoneyToPPWithOverflow() [{}] should have: plat:[{}] gold:[{}] silver:[{}] copper:[{}]",
|
||||||
|
GetName(),
|
||||||
|
m_pp.platinum,
|
||||||
|
m_pp.gold,
|
||||||
|
m_pp.silver,
|
||||||
|
m_pp.copper
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Client::TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client)
|
||||||
|
{
|
||||||
|
int32 remove_pp = copper / 1000;
|
||||||
|
int32 remove_gp = (copper - remove_pp * 1000) / 100;
|
||||||
|
int32 remove_sp = (copper - remove_pp * 1000 - remove_gp * 100) / 10;
|
||||||
|
int32 remove_cp = copper - remove_pp * 1000 - remove_gp * 100 - remove_sp * 10;
|
||||||
|
|
||||||
|
uint64 current_money = GetCarriedMoney();
|
||||||
|
|
||||||
|
if (copper > current_money) {
|
||||||
|
return false; //client does not have enough money on them
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pp.copper -= remove_cp;
|
||||||
|
if (m_pp.copper < 0) {
|
||||||
|
m_pp.silver -= 1;
|
||||||
|
m_pp.copper = m_pp.copper + 10;
|
||||||
|
if (m_pp.copper >= 10) {
|
||||||
|
m_pp.silver += m_pp.copper / 10;
|
||||||
|
m_pp.copper = m_pp.copper % 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pp.silver -= remove_sp;
|
||||||
|
if (m_pp.silver < 0) {
|
||||||
|
m_pp.gold -= 1;
|
||||||
|
m_pp.silver = m_pp.silver + 10;
|
||||||
|
if (m_pp.silver >= 10) {
|
||||||
|
m_pp.gold += m_pp.silver / 10;
|
||||||
|
m_pp.silver = m_pp.silver % 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pp.gold -= remove_gp;
|
||||||
|
if (m_pp.gold < 0) {
|
||||||
|
m_pp.platinum -= 1;
|
||||||
|
m_pp.gold = m_pp.gold + 10;
|
||||||
|
if (m_pp.gold >= 10) {
|
||||||
|
m_pp.platinum += m_pp.gold / 10;
|
||||||
|
m_pp.gold = m_pp.gold % 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pp.platinum -= remove_pp;
|
||||||
|
|
||||||
|
if (update_client) {
|
||||||
|
SendMoneyUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
SaveCurrency();
|
||||||
|
RecalcWeight();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Client::SendTopLevelInventory()
|
||||||
|
{
|
||||||
|
EQ::ItemInstance* inst = nullptr;
|
||||||
|
|
||||||
|
static const int16 slots[][2] = {
|
||||||
|
{ EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END },
|
||||||
|
{ EQ::invbag::GENERAL_BAGS_BEGIN, EQ::invbag::GENERAL_BAGS_END },
|
||||||
|
{ EQ::invbag::CURSOR_BAG_BEGIN, EQ::invbag::CURSOR_BAG_END }
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto& inv = GetInv();
|
||||||
|
|
||||||
|
const size_t slot_index_count = sizeof(slots) / sizeof(slots[0]);
|
||||||
|
for (int slot_index = 0; slot_index < slot_index_count; ++slot_index) {
|
||||||
|
for (int slot_id = slots[slot_index][0]; slot_id <= slots[slot_index][1]; ++slot_id) {
|
||||||
|
inst = inv.GetItem(slot_id);
|
||||||
|
if (inst) {
|
||||||
|
SendItemPacket(slot_id, inst, ItemPacketType::ItemPacketTrade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+58
-15
@@ -70,6 +70,8 @@ namespace EQ
|
|||||||
#include "../common/data_verification.h"
|
#include "../common/data_verification.h"
|
||||||
#include "../common/repositories/character_parcels_repository.h"
|
#include "../common/repositories/character_parcels_repository.h"
|
||||||
#include "../common/repositories/trader_repository.h"
|
#include "../common/repositories/trader_repository.h"
|
||||||
|
#include "../common/guild_base.h"
|
||||||
|
#include "../common/repositories/buyer_buy_lines_repository.h"
|
||||||
|
|
||||||
#ifdef _WINDOWS
|
#ifdef _WINDOWS
|
||||||
// since windows defines these within windef.h (which windows.h include)
|
// since windows defines these within windef.h (which windows.h include)
|
||||||
@@ -276,6 +278,8 @@ public:
|
|||||||
|
|
||||||
std::vector<Mob*> GetRaidOrGroupOrSelf(bool clients_only = false);
|
std::vector<Mob*> GetRaidOrGroupOrSelf(bool clients_only = false);
|
||||||
|
|
||||||
|
bool CheckIfAlreadyDead();
|
||||||
|
|
||||||
void AI_Init();
|
void AI_Init();
|
||||||
void AI_Start(uint32 iMoveDelay = 0);
|
void AI_Start(uint32 iMoveDelay = 0);
|
||||||
void AI_Stop();
|
void AI_Stop();
|
||||||
@@ -288,6 +292,7 @@ public:
|
|||||||
void TraderPriceUpdate(const EQApplicationPacket *app);
|
void TraderPriceUpdate(const EQApplicationPacket *app);
|
||||||
void SendBazaarDone(uint32 trader_id);
|
void SendBazaarDone(uint32 trader_id);
|
||||||
void SendBulkBazaarTraders();
|
void SendBulkBazaarTraders();
|
||||||
|
void SendBulkBazaarBuyers();
|
||||||
void DoBazaarInspect(const BazaarInspect_Struct &in);
|
void DoBazaarInspect(const BazaarInspect_Struct &in);
|
||||||
void SendBazaarDeliveryCosts();
|
void SendBazaarDeliveryCosts();
|
||||||
static std::string DetermineMoneyString(uint64 copper);
|
static std::string DetermineMoneyString(uint64 copper);
|
||||||
@@ -307,8 +312,10 @@ public:
|
|||||||
bool TryStacking(EQ::ItemInstance* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true);
|
bool TryStacking(EQ::ItemInstance* item, uint8 type = ItemPacketTrade, bool try_worn = true, bool try_cursor = true);
|
||||||
void SendTraderPacket(Client* trader, uint32 Unknown72 = 51);
|
void SendTraderPacket(Client* trader, uint32 Unknown72 = 51);
|
||||||
void SendBuyerPacket(Client* Buyer);
|
void SendBuyerPacket(Client* Buyer);
|
||||||
|
void SendBuyerToBarterWindow(Client* buyer, uint32 action);
|
||||||
GetItems_Struct* GetTraderItems();
|
GetItems_Struct* GetTraderItems();
|
||||||
void SendBazaarWelcome();
|
void SendBazaarWelcome();
|
||||||
|
void SendBarterWelcome();
|
||||||
void DyeArmor(EQ::TintProfile* dye);
|
void DyeArmor(EQ::TintProfile* dye);
|
||||||
void DyeArmorBySlot(uint8 slot, uint8 red, uint8 green, uint8 blue, uint8 use_tint = 0x00);
|
void DyeArmorBySlot(uint8 slot, uint8 red, uint8 green, uint8 blue, uint8 use_tint = 0x00);
|
||||||
uint8 SlotConvert(uint8 slot,bool bracer=false);
|
uint8 SlotConvert(uint8 slot,bool bracer=false);
|
||||||
@@ -375,14 +382,35 @@ public:
|
|||||||
uint32 GetCustomerID() { return customer_id; }
|
uint32 GetCustomerID() { return customer_id; }
|
||||||
void SetCustomerID(uint32 id) { customer_id = id; }
|
void SetCustomerID(uint32 id) { customer_id = id; }
|
||||||
|
|
||||||
void SendBuyerResults(char *SearchQuery, uint32 SearchID);
|
void SetBuyerID(uint32 id) { m_buyer_id = id; }
|
||||||
|
uint32 GetBuyerID() { return m_buyer_id; }
|
||||||
|
bool IsBuyer() { return m_buyer_id != 0 ? true : false; }
|
||||||
|
void SetBarterTime() { m_barter_time = time(nullptr); }
|
||||||
|
uint32 GetBarterTime() { return m_barter_time; }
|
||||||
|
void SetBuyerWelcomeMessage(const char* welcome_message);
|
||||||
|
void SendBuyerGreeting(uint32 char_id);
|
||||||
|
void SendSellerBrowsing(const std::string &browser);
|
||||||
|
void SendBuyerMode(bool status);
|
||||||
|
bool IsInBuyerSpace();
|
||||||
|
void SendBuyLineUpdate(const BuyerLineItems_Struct &buy_line);
|
||||||
|
void CheckIfMovedItemIsPartOfBuyLines(uint32 item_id);
|
||||||
|
|
||||||
|
void SendBuyerResults(BarterSearchRequest_Struct& bsr);
|
||||||
void ShowBuyLines(const EQApplicationPacket *app);
|
void ShowBuyLines(const EQApplicationPacket *app);
|
||||||
void SellToBuyer(const EQApplicationPacket *app);
|
void SellToBuyer(const EQApplicationPacket *app);
|
||||||
void ToggleBuyerMode(bool TurnOn);
|
void ToggleBuyerMode(bool TurnOn);
|
||||||
void UpdateBuyLine(const EQApplicationPacket *app);
|
void ModifyBuyLine(const EQApplicationPacket *app);
|
||||||
|
void CreateStartingBuyLines(const EQApplicationPacket *app);
|
||||||
void BuyerItemSearch(const EQApplicationPacket *app);
|
void BuyerItemSearch(const EQApplicationPacket *app);
|
||||||
void SetBuyerWelcomeMessage(const char* WelcomeMessage) { BuyerWelcomeMessage = WelcomeMessage; }
|
void SendWindowUpdatesToSellerAndBuyer(BuyerLineSellItem_Struct& blsi);
|
||||||
const char* GetBuyerWelcomeMessage() { return BuyerWelcomeMessage.c_str(); }
|
void SendBarterBuyerClientMessage(BuyerLineSellItem_Struct& blsi, BarterBuyerActions action, BarterBuyerSubActions sub_action, BarterBuyerSubActions error_code);
|
||||||
|
bool BuildBuyLineMap(std::map<uint32, BuylineItemDetails_Struct>& item_map, BuyerBuyLines_Struct& bl);
|
||||||
|
bool BuildBuyLineMapFromVector(std::map<uint32, BuylineItemDetails_Struct>& item_map, std::vector<BuyerLineItems_Struct>& bl);
|
||||||
|
void RemoveItemFromBuyLineMap(std::map<uint32, BuylineItemDetails_Struct>& item_map, const BuyerLineItems_Struct& bl);
|
||||||
|
bool ValidateBuyLineItems(std::map<uint32, BuylineItemDetails_Struct>& item_map);
|
||||||
|
int64 ValidateBuyLineCost(std::map<uint32, BuylineItemDetails_Struct>& item_map);
|
||||||
|
bool DoBarterBuyerChecks(BuyerLineSellItem_Struct& sell_line);
|
||||||
|
bool DoBarterSellerChecks(BuyerLineSellItem_Struct& sell_line);
|
||||||
|
|
||||||
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
|
void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
|
||||||
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
|
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
|
||||||
@@ -734,11 +762,12 @@ public:
|
|||||||
void GetRaidAAs(RaidLeadershipAA_Struct *into) const;
|
void GetRaidAAs(RaidLeadershipAA_Struct *into) const;
|
||||||
void ClearGroupAAs();
|
void ClearGroupAAs();
|
||||||
void UpdateGroupAAs(int32 points, uint32 type);
|
void UpdateGroupAAs(int32 points, uint32 type);
|
||||||
void SacrificeConfirm(Client* caster);
|
void SacrificeConfirm(Mob* caster);
|
||||||
void Sacrifice(Client* caster);
|
void Sacrifice(Mob* caster);
|
||||||
void GoToDeath();
|
void GoToDeath();
|
||||||
inline const int32 GetInstanceID() const { return zone->GetInstanceID(); }
|
inline const int32 GetInstanceID() const { return zone->GetInstanceID(); }
|
||||||
void SetZoning(bool in) { bZoning = in; }
|
void SetZoning(bool in) { bZoning = in; }
|
||||||
|
bool IsZoning() { return bZoning; }
|
||||||
|
|
||||||
void ShowSpells(Client* c, ShowSpellType show_spell_type);
|
void ShowSpells(Client* c, ShowSpellType show_spell_type);
|
||||||
|
|
||||||
@@ -825,9 +854,11 @@ public:
|
|||||||
void QuestReadBook(const char* text, uint8 type);
|
void QuestReadBook(const char* text, uint8 type);
|
||||||
void SendMoneyUpdate();
|
void SendMoneyUpdate();
|
||||||
bool TakeMoneyFromPP(uint64 copper, bool update_client = false);
|
bool TakeMoneyFromPP(uint64 copper, bool update_client = false);
|
||||||
|
bool TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client);
|
||||||
bool TakePlatinum(uint32 platinum, bool update_client = false);
|
bool TakePlatinum(uint32 platinum, bool update_client = false);
|
||||||
void AddMoneyToPP(uint64 copper, bool update_client = false);
|
void AddMoneyToPP(uint64 copper, bool update_client = false);
|
||||||
void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool update_client = false);
|
void AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool update_client = false);
|
||||||
|
void AddMoneyToPPWithOverflow(uint64 copper, bool update_client);
|
||||||
void AddPlatinum(uint32 platinu, bool update_client = false);
|
void AddPlatinum(uint32 platinu, bool update_client = false);
|
||||||
bool HasMoney(uint64 copper);
|
bool HasMoney(uint64 copper);
|
||||||
uint64 GetCarriedMoney();
|
uint64 GetCarriedMoney();
|
||||||
@@ -966,6 +997,8 @@ public:
|
|||||||
void ChangeTributeSettings(TributeInfo_Struct *t);
|
void ChangeTributeSettings(TributeInfo_Struct *t);
|
||||||
void SendTributeTimer();
|
void SendTributeTimer();
|
||||||
void ToggleTribute(bool enabled);
|
void ToggleTribute(bool enabled);
|
||||||
|
std::map<uint32, TributeData> GetTributeList();
|
||||||
|
uint32 LookupTributeItemID(uint32 tribute_id, uint32 tier);
|
||||||
void SendPathPacket(const std::vector<FindPerson_Point> &path);
|
void SendPathPacket(const std::vector<FindPerson_Point> &path);
|
||||||
|
|
||||||
inline PTimerList &GetPTimers() { return(p_timers); }
|
inline PTimerList &GetPTimers() { return(p_timers); }
|
||||||
@@ -992,7 +1025,7 @@ public:
|
|||||||
int GetSpentAA() { return m_pp.aapoints_spent; }
|
int GetSpentAA() { return m_pp.aapoints_spent; }
|
||||||
uint32 GetRequiredAAExperience();
|
uint32 GetRequiredAAExperience();
|
||||||
void AutoGrantAAPoints();
|
void AutoGrantAAPoints();
|
||||||
void GrantAllAAPoints(uint8 unlock_level = 0);
|
void GrantAllAAPoints(uint8 unlock_level = 0, bool skip_grant_only = false);
|
||||||
bool HasAlreadyPurchasedRank(AA::Rank* rank);
|
bool HasAlreadyPurchasedRank(AA::Rank* rank);
|
||||||
void ListPurchasedAAs(Client *to, std::string search_criteria = std::string());
|
void ListPurchasedAAs(Client *to, std::string search_criteria = std::string());
|
||||||
|
|
||||||
@@ -1054,6 +1087,8 @@ public:
|
|||||||
int32 GetItemIDAt(int16 slot_id);
|
int32 GetItemIDAt(int16 slot_id);
|
||||||
int32 GetAugmentIDAt(int16 slot_id, uint8 augslot);
|
int32 GetAugmentIDAt(int16 slot_id, uint8 augslot);
|
||||||
bool PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, bool client_update = false);
|
bool PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, bool client_update = false);
|
||||||
|
bool PutItemInInventoryWithStacking(EQ::ItemInstance* inst);
|
||||||
|
bool FindNumberOfFreeInventorySlotsWithSizeCheck(std::vector<BuyerLineTradeItems_Struct> items);
|
||||||
bool PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update = false);
|
bool PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update = false);
|
||||||
void SendCursorBuffer();
|
void SendCursorBuffer();
|
||||||
void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true);
|
void DeleteItemInInventory(int16 slot_id, int16 quantity = 0, bool client_update = false, bool update_db = true);
|
||||||
@@ -1092,7 +1127,6 @@ public:
|
|||||||
uint16 GetTraderID() { return trader_id; }
|
uint16 GetTraderID() { return trader_id; }
|
||||||
void SetTraderID(uint16 id) { trader_id = id; }
|
void SetTraderID(uint16 id) { trader_id = id; }
|
||||||
|
|
||||||
inline bool IsBuyer() const { return(Buyer); }
|
|
||||||
eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; }
|
eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; }
|
||||||
void SetFilter(eqFilterType filter_id, eqFilterMode filter_mode) { ClientFilters[filter_id] = filter_mode; }
|
void SetFilter(eqFilterType filter_id, eqFilterMode filter_mode) { ClientFilters[filter_id] = filter_mode; }
|
||||||
|
|
||||||
@@ -1129,7 +1163,7 @@ public:
|
|||||||
void RemoveNoRent(bool client_update = true);
|
void RemoveNoRent(bool client_update = true);
|
||||||
void RemoveDuplicateLore(bool client_update = true);
|
void RemoveDuplicateLore(bool client_update = true);
|
||||||
void MoveSlotNotAllowed(bool client_update = true);
|
void MoveSlotNotAllowed(bool client_update = true);
|
||||||
virtual void RangedAttack(Mob* other, bool CanDoubleAttack = false);
|
virtual bool RangedAttack(Mob* other, bool CanDoubleAttack = false);
|
||||||
virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false);
|
virtual void ThrowingAttack(Mob* other, bool CanDoubleAttack = false);
|
||||||
void DoClassAttacks(Mob *ca_target, uint16 skill = -1, bool IsRiposte=false);
|
void DoClassAttacks(Mob *ca_target, uint16 skill = -1, bool IsRiposte=false);
|
||||||
|
|
||||||
@@ -1211,7 +1245,7 @@ public:
|
|||||||
bool PendingTranslocate;
|
bool PendingTranslocate;
|
||||||
time_t TranslocateTime;
|
time_t TranslocateTime;
|
||||||
bool PendingSacrifice;
|
bool PendingSacrifice;
|
||||||
std::string SacrificeCaster;
|
uint16 sacrifice_caster_id;
|
||||||
PendingTranslocate_Struct PendingTranslocateData;
|
PendingTranslocate_Struct PendingTranslocateData;
|
||||||
void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID);
|
void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID);
|
||||||
|
|
||||||
@@ -1386,7 +1420,11 @@ public:
|
|||||||
{
|
{
|
||||||
return (task_state ? task_state->EnabledTaskCount(task_set_id) : -1);
|
return (task_state ? task_state->EnabledTaskCount(task_set_id) : -1);
|
||||||
}
|
}
|
||||||
inline int IsTaskCompleted(int task_id) { return (task_state ? task_state->IsTaskCompleted(task_id) : -1); }
|
inline bool IsTaskCompleted(int task_id) { return (task_state ? task_state->IsTaskCompleted(task_id) : false); }
|
||||||
|
inline bool AreTasksCompleted(std::vector<int> task_ids)
|
||||||
|
{
|
||||||
|
return (task_state ? task_state->AreTasksCompleted(task_ids) : false);
|
||||||
|
}
|
||||||
inline void ShowClientTasks(Client *client) { if (task_state) { task_state->ShowClientTasks(this, client); }}
|
inline void ShowClientTasks(Client *client) { if (task_state) { task_state->ShowClientTasks(this, client); }}
|
||||||
inline void CancelAllTasks() { if (task_state) { task_state->CancelAllTasks(this); }}
|
inline void CancelAllTasks() { if (task_state) { task_state->CancelAllTasks(this); }}
|
||||||
inline int GetActiveTaskCount() { return (task_state ? task_state->GetActiveTaskCount() : 0); }
|
inline int GetActiveTaskCount() { return (task_state ? task_state->GetActiveTaskCount() : 0); }
|
||||||
@@ -1909,8 +1947,8 @@ private:
|
|||||||
uint8 firstlogon;
|
uint8 firstlogon;
|
||||||
uint32 mercid; // current merc
|
uint32 mercid; // current merc
|
||||||
uint8 mercSlot; // selected merc slot
|
uint8 mercSlot; // selected merc slot
|
||||||
bool Buyer;
|
uint32 m_buyer_id;
|
||||||
std::string BuyerWelcomeMessage;
|
uint32 m_barter_time;
|
||||||
int32 m_parcel_platinum;
|
int32 m_parcel_platinum;
|
||||||
int32 m_parcel_gold;
|
int32 m_parcel_gold;
|
||||||
int32 m_parcel_silver;
|
int32 m_parcel_silver;
|
||||||
@@ -1974,9 +2012,10 @@ private:
|
|||||||
void ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm);
|
void ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions, ZoneMode zm);
|
||||||
void ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited);
|
void ProcessMovePC(uint32 zoneID, uint32 instance_id, float x, float y, float z, float heading, uint8 ignorerestrictions = 0, ZoneMode zm = ZoneSolicited);
|
||||||
|
|
||||||
|
void SendTopLevelInventory();
|
||||||
|
|
||||||
glm::vec4 m_ZoneSummonLocation;
|
glm::vec4 m_ZoneSummonLocation;
|
||||||
uint16 zonesummon_id;
|
uint16 zonesummon_id;
|
||||||
uint8 zonesummon_instance_id;
|
|
||||||
uint8 zonesummon_ignorerestrictions;
|
uint8 zonesummon_ignorerestrictions;
|
||||||
ZoneMode zone_mode;
|
ZoneMode zone_mode;
|
||||||
|
|
||||||
@@ -2019,6 +2058,11 @@ private:
|
|||||||
Timer task_request_timer;
|
Timer task_request_timer;
|
||||||
Timer pick_lock_timer;
|
Timer pick_lock_timer;
|
||||||
Timer parcel_timer; //Used to limit the number of parcels to one every 30 seconds (default). Changable via rule.
|
Timer parcel_timer; //Used to limit the number of parcels to one every 30 seconds (default). Changable via rule.
|
||||||
|
Timer lazy_load_bank_check_timer;
|
||||||
|
Timer bandolier_throttle_timer;
|
||||||
|
|
||||||
|
bool m_lazy_load_bank = false;
|
||||||
|
int m_lazy_load_sent_bank_slots = 0;
|
||||||
|
|
||||||
glm::vec3 m_Proximity;
|
glm::vec3 m_Proximity;
|
||||||
glm::vec4 last_position_before_bulk_update;
|
glm::vec4 last_position_before_bulk_update;
|
||||||
@@ -2138,7 +2182,6 @@ private:
|
|||||||
bool m_has_quest_compass = false;
|
bool m_has_quest_compass = false;
|
||||||
std::vector<uint32_t> m_dynamic_zone_ids;
|
std::vector<uint32_t> m_dynamic_zone_ids;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum BotOwnerOption : size_t {
|
enum BotOwnerOption : size_t {
|
||||||
booDeathMarquee,
|
booDeathMarquee,
|
||||||
|
|||||||
+233
-95
@@ -65,6 +65,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|||||||
#include "../common/repositories/account_repository.h"
|
#include "../common/repositories/account_repository.h"
|
||||||
#include "../common/repositories/character_corpses_repository.h"
|
#include "../common/repositories/character_corpses_repository.h"
|
||||||
#include "../common/repositories/guild_tributes_repository.h"
|
#include "../common/repositories/guild_tributes_repository.h"
|
||||||
|
#include "../common/repositories/buyer_buy_lines_repository.h"
|
||||||
|
|
||||||
#include "../common/events/player_event_logs.h"
|
#include "../common/events/player_event_logs.h"
|
||||||
#include "../common/repositories/character_stats_record_repository.h"
|
#include "../common/repositories/character_stats_record_repository.h"
|
||||||
@@ -368,13 +369,7 @@ void MapOpcodes()
|
|||||||
ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save;
|
ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save;
|
||||||
ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq;
|
ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq;
|
||||||
ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute;
|
ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute;
|
||||||
|
ConnectedOpcodes[OP_SenseHeading] = &Client::Handle_OP_SenseHeading;
|
||||||
// Use or Ignore sense heading based on rule.
|
|
||||||
bool train = RuleB(Skills, TrainSenseHeading);
|
|
||||||
|
|
||||||
ConnectedOpcodes[OP_SenseHeading] =
|
|
||||||
(train) ? &Client::Handle_OP_SenseHeading : &Client::Handle_OP_Ignore;
|
|
||||||
|
|
||||||
ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps;
|
ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps;
|
||||||
ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD;
|
ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD;
|
||||||
ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode;
|
ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode;
|
||||||
@@ -764,8 +759,6 @@ void Client::CompleteConnect()
|
|||||||
|
|
||||||
entity_list.SendIllusionWearChange(this);
|
entity_list.SendIllusionWearChange(this);
|
||||||
|
|
||||||
entity_list.SendTraders(this);
|
|
||||||
|
|
||||||
SendWearChangeAndLighting(EQ::textures::LastTexture);
|
SendWearChangeAndLighting(EQ::textures::LastTexture);
|
||||||
Mob* pet = GetPet();
|
Mob* pet = GetPet();
|
||||||
if (pet) {
|
if (pet) {
|
||||||
@@ -793,6 +786,8 @@ void Client::CompleteConnect()
|
|||||||
parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0);
|
parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DeleteEntityVariable(SEE_BUFFS_FLAG);
|
||||||
|
|
||||||
// the way that the client deals with positions during the initial spawn struct
|
// the way that the client deals with positions during the initial spawn struct
|
||||||
// is subtly different from how it deals with getting a position update
|
// is subtly different from how it deals with getting a position update
|
||||||
// if a mob is slightly in the wall or slightly clipping a floor they will be
|
// if a mob is slightly in the wall or slightly clipping a floor they will be
|
||||||
@@ -939,6 +934,7 @@ void Client::CompleteConnect()
|
|||||||
}
|
}
|
||||||
|
|
||||||
database.LoadAuras(this); // this ends up spawning them so probably safer to load this later (here)
|
database.LoadAuras(this); // this ends up spawning them so probably safer to load this later (here)
|
||||||
|
database.LoadCharacterDisciplines(this);
|
||||||
|
|
||||||
entity_list.RefreshClientXTargets(this);
|
entity_list.RefreshClientXTargets(this);
|
||||||
|
|
||||||
@@ -1323,7 +1319,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
|||||||
database.LoadCharacterInspectMessage(cid, &m_inspect_message); /* Load Character Inspect Message */
|
database.LoadCharacterInspectMessage(cid, &m_inspect_message); /* Load Character Inspect Message */
|
||||||
database.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */
|
database.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */
|
||||||
database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */
|
database.LoadCharacterMemmedSpells(cid, &m_pp); /* Load Character Memorized Spells */
|
||||||
database.LoadCharacterDisciplines(cid, &m_pp); /* Load Character Disciplines */
|
|
||||||
database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */
|
database.LoadCharacterLanguages(cid, &m_pp); /* Load Character Languages */
|
||||||
database.LoadCharacterLeadershipAbilities(cid, &m_pp); /* Load Character Leadership AA's */
|
database.LoadCharacterLeadershipAbilities(cid, &m_pp); /* Load Character Leadership AA's */
|
||||||
database.LoadCharacterTribute(this); /* Load CharacterTribute */
|
database.LoadCharacterTribute(this); /* Load CharacterTribute */
|
||||||
@@ -3290,6 +3285,10 @@ void Client::Handle_OP_AugmentItem(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (new_aug->GetItem()->Attuneable) {
|
||||||
|
new_aug->SetAttuned(true);
|
||||||
|
}
|
||||||
|
|
||||||
tobe_auged->PutAugment(in_augment->augment_index, *new_aug);
|
tobe_auged->PutAugment(in_augment->augment_index, *new_aug);
|
||||||
tobe_auged->UpdateOrnamentationInfo();
|
tobe_auged->UpdateOrnamentationInfo();
|
||||||
|
|
||||||
@@ -3643,17 +3642,15 @@ void Client::Handle_OP_AutoFire(const EQApplicationPacket *app)
|
|||||||
void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
|
void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
// Although there are three different structs for OP_Bandolier, they are all the same size.
|
// Although there are three different structs for OP_Bandolier, they are all the same size.
|
||||||
//
|
|
||||||
if (app->size != sizeof(BandolierCreate_Struct)) {
|
if (app->size != sizeof(BandolierCreate_Struct)) {
|
||||||
LogDebug("Size mismatch in OP_Bandolier expected [{}] got [{}]", sizeof(BandolierCreate_Struct), app->size);
|
LogDebug("Size mismatch in OP_Bandolier expected [{}] got [{}]", sizeof(BandolierCreate_Struct), app->size);
|
||||||
DumpPacket(app);
|
DumpPacket(app);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer;
|
auto bs = (BandolierCreate_Struct*) app->pBuffer;
|
||||||
|
|
||||||
switch (bs->Action)
|
switch (bs->Action) {
|
||||||
{
|
|
||||||
case bandolierCreate:
|
case bandolierCreate:
|
||||||
CreateBandolier(app);
|
CreateBandolier(app);
|
||||||
break;
|
break;
|
||||||
@@ -3661,6 +3658,18 @@ void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
|
|||||||
RemoveBandolier(app);
|
RemoveBandolier(app);
|
||||||
break;
|
break;
|
||||||
case bandolierSet:
|
case bandolierSet:
|
||||||
|
if (bandolier_throttle_timer.GetDuration() && !bandolier_throttle_timer.Check()) {
|
||||||
|
Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"You may only modify your bandolier once every {}.",
|
||||||
|
Strings::ToLower(Strings::MillisecondsToTime(RuleI(Character, BandolierSwapDelay)))
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
SendTopLevelInventory();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
SetBandolier(app);
|
SetBandolier(app);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@@ -3763,116 +3772,104 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
|
|
||||||
char* Buf = (char *)app->pBuffer;
|
char* Buf = (char *)app->pBuffer;
|
||||||
|
auto in = (BuyerGeneric_Struct *)app->pBuffer;
|
||||||
|
|
||||||
// The first 4 bytes of the packet determine the action. A lot of Barter packets require the
|
// The first 4 bytes of the packet determine the action. A lot of Barter packets require the
|
||||||
// packet the client sent, sent back to it as an acknowledgement.
|
// packet the client sent, sent back to it as an acknowledgement.
|
||||||
//
|
//
|
||||||
uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf);
|
//uint32 Action = VARSTRUCT_DECODE_TYPE(uint32, Buf);
|
||||||
|
|
||||||
|
|
||||||
switch (Action)
|
switch (in->action) {
|
||||||
{
|
|
||||||
|
|
||||||
case Barter_BuyerSearch:
|
case Barter_BuyerSearch: {
|
||||||
{
|
|
||||||
BuyerItemSearch(app);
|
BuyerItemSearch(app);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_SellerSearch:
|
case Barter_SellerSearch: {
|
||||||
{
|
auto bsr = (BarterSearchRequest_Struct *) app->pBuffer;
|
||||||
BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer;
|
SendBuyerResults(*bsr);
|
||||||
SendBuyerResults(bsr->SearchString, bsr->SearchID);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_BuyerModeOn:
|
case Barter_BuyerModeOn: {
|
||||||
{
|
|
||||||
if (!IsTrader()) {
|
if (!IsTrader()) {
|
||||||
ToggleBuyerMode(true);
|
ToggleBuyerMode(true);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Buf = (char *)app->pBuffer;
|
ToggleBuyerMode(false);
|
||||||
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff);
|
|
||||||
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
|
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
|
||||||
}
|
}
|
||||||
QueuePacket(app);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_BuyerModeOff:
|
case Barter_BuyerModeOff: {
|
||||||
{
|
|
||||||
QueuePacket(app);
|
|
||||||
ToggleBuyerMode(false);
|
ToggleBuyerMode(false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_BuyerItemUpdate:
|
case Barter_BuyerItemUpdate: {
|
||||||
{
|
ModifyBuyLine(app);
|
||||||
UpdateBuyLine(app);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_BuyerItemRemove:
|
case Barter_BuyerItemStart: {
|
||||||
{
|
CreateStartingBuyLines(app);
|
||||||
BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer;
|
break;
|
||||||
database.RemoveBuyLine(CharacterID(), bris->BuySlot);
|
}
|
||||||
|
|
||||||
|
case Barter_BuyerItemRemove: {
|
||||||
|
auto bris = (BuyerRemoveItem_Struct *) app->pBuffer;
|
||||||
|
BuyerBuyLinesRepository::DeleteBuyLine(database, CharacterID(), bris->buy_slot_id);
|
||||||
QueuePacket(app);
|
QueuePacket(app);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_SellItem:
|
case Barter_SellItem: {
|
||||||
{
|
|
||||||
SellToBuyer(app);
|
SellToBuyer(app);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_BuyerInspectBegin:
|
case Barter_BuyerInspectBegin: {
|
||||||
{
|
|
||||||
ShowBuyLines(app);
|
ShowBuyLines(app);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_BuyerInspectEnd:
|
case Barter_BuyerInspectEnd: {
|
||||||
{
|
auto bir = (BuyerInspectRequest_Struct *) app->pBuffer;
|
||||||
BuyerInspectRequest_Struct* bir = (BuyerInspectRequest_Struct*)app->pBuffer;
|
auto buyer = entity_list.GetClientByID(bir->buyer_id);
|
||||||
Client *Buyer = entity_list.GetClientByID(bir->BuyerID);
|
if (buyer) {
|
||||||
if (Buyer)
|
buyer->WithCustomer(0);
|
||||||
Buyer->WithCustomer(0);
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case Barter_BarterItemInspect: {
|
||||||
|
auto bislr = (BarterItemSearchLinkRequest_Struct *) app->pBuffer;
|
||||||
|
const EQ::ItemData *item = database.GetItem(bislr->item_id);
|
||||||
|
|
||||||
case Barter_BarterItemInspect:
|
if (!item) {
|
||||||
{
|
|
||||||
BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer;
|
|
||||||
|
|
||||||
const EQ::ItemData* item = database.GetItem(bislr->ItemID);
|
|
||||||
|
|
||||||
if (!item)
|
|
||||||
Message(Chat::Red, "Error: This item does not exist!");
|
Message(Chat::Red, "Error: This item does not exist!");
|
||||||
else
|
return;
|
||||||
{
|
}
|
||||||
|
|
||||||
EQ::ItemInstance *inst = database.CreateItem(item);
|
EQ::ItemInstance *inst = database.CreateItem(item);
|
||||||
if (inst)
|
if (inst) {
|
||||||
{
|
|
||||||
SendItemPacket(0, inst, ItemPacketViewLink);
|
SendItemPacket(0, inst, ItemPacketViewLink);
|
||||||
safe_delete(inst);
|
safe_delete(inst);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_Welcome:
|
case Barter_Welcome:
|
||||||
{
|
{
|
||||||
//SendBazaarWelcome();
|
SendBarterWelcome();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_WelcomeMessageUpdate:
|
case Barter_WelcomeMessageUpdate: {
|
||||||
{
|
auto bwmu = (BuyerWelcomeMessageUpdate_Struct *) app->pBuffer;
|
||||||
BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer;
|
SetBuyerWelcomeMessage(bwmu->welcome_message);
|
||||||
SetBuyerWelcomeMessage(bwmu->WelcomeMessage);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3896,15 +3893,20 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case Barter_Unknown23:
|
case Barter_Greeting:
|
||||||
{
|
{
|
||||||
// Sent by SoD client for no discernible reason.
|
auto data = (BuyerGreeting_Struct *)app->pBuffer;
|
||||||
|
SendBuyerGreeting(data->buyer_id);
|
||||||
|
}
|
||||||
|
case Barter_OpenBarterWindow:
|
||||||
|
{
|
||||||
|
SendBulkBazaarBuyers();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Message(Chat::Red, "Unrecognised Barter action.");
|
Message(Chat::Red, "Unrecognised Barter action.");
|
||||||
LogTrading("Unrecognised Barter Action [{}]", Action);
|
LogTrading("Unrecognised Barter Action [{}]", in->action);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4986,6 +4988,10 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
|
|||||||
TraderEndTrader();
|
TraderEndTrader();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (IsBuyer()) {
|
||||||
|
ToggleBuyerMode(false);
|
||||||
|
}
|
||||||
|
|
||||||
/* Break Hide if moving without sneaking and set rewind timer if moved */
|
/* Break Hide if moving without sneaking and set rewind timer if moved */
|
||||||
if ((hidden || improved_hidden) && !sneaking) {
|
if ((hidden || improved_hidden) && !sneaking) {
|
||||||
hidden = false;
|
hidden = false;
|
||||||
@@ -10514,7 +10520,7 @@ void Client::Handle_OP_MercenaryCommand(const EQApplicationPacket *app)
|
|||||||
//check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance)
|
//check to see if selected option is a valid stance slot (option is the slot the stance is in, not the actual stance)
|
||||||
if (option >= 0 && option < numStances)
|
if (option >= 0 && option < numStances)
|
||||||
{
|
{
|
||||||
merc->SetStance((EQ::constants::StanceType)mercTemplate->Stances[option]);
|
merc->SetStance(mercTemplate->Stances[option]);
|
||||||
GetMercInfo().Stance = mercTemplate->Stances[option];
|
GetMercInfo().Stance = mercTemplate->Stances[option];
|
||||||
|
|
||||||
Log(Logs::General, Logs::Mercenaries, "Set Stance: %u for %s (%s)", merc->GetStance(), merc->GetName(), GetName());
|
Log(Logs::General, Logs::Mercenaries, "Set Stance: %u for %s (%s)", merc->GetStance(), merc->GetName(), GetName());
|
||||||
@@ -10913,7 +10919,121 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app)
|
|||||||
|
|
||||||
void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app)
|
void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
Kick("Unimplemented move multiple items"); // TODO: lets not desync though
|
// This packet is only sent from the client if we ctrl click items in inventory
|
||||||
|
if (m_ClientVersionBit & EQ::versions::maskRoF2AndLater) {
|
||||||
|
if (!CharacterID()) {
|
||||||
|
LinkDead();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app->size < sizeof(MultiMoveItem_Struct)) {
|
||||||
|
LinkDead();
|
||||||
|
return; // Not enough data to be a valid packet
|
||||||
|
}
|
||||||
|
|
||||||
|
const MultiMoveItem_Struct* multi_move = reinterpret_cast<const MultiMoveItem_Struct*>(app->pBuffer);
|
||||||
|
if (app->size != sizeof(MultiMoveItem_Struct) + sizeof(MultiMoveItemSub_Struct) * multi_move->count) {
|
||||||
|
LinkDead();
|
||||||
|
return; // Packet size does not match expected size
|
||||||
|
}
|
||||||
|
|
||||||
|
const int16 from_parent = multi_move->moves[0].from_slot.Slot;
|
||||||
|
const int16 to_parent = multi_move->moves[0].to_slot.Slot;
|
||||||
|
|
||||||
|
// CTRL + left click drops an item into a bag without opening it.
|
||||||
|
// This can be a bag, in which case it tries to fill the target bag with the contents of the bag on the cursor
|
||||||
|
// CTRL + right click swaps the contents of two bags if a bag is on your cursor and you ctrl-right click on another bag.
|
||||||
|
|
||||||
|
// We need to check if this is a swap or just an addition (left click or right click)
|
||||||
|
// Check if any component of this transaction is coming from anywhere other than the cursor
|
||||||
|
bool left_click = true;
|
||||||
|
for (int i = 0; i < multi_move->count; i++) {
|
||||||
|
if (multi_move->moves[i].from_slot.Slot != EQ::invslot::slotCursor) {
|
||||||
|
left_click = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a left click which is purely additive. This should always be cursor object or cursor bag contents into general\bank\whatever bag
|
||||||
|
if (left_click) {
|
||||||
|
for (int i = 0; i < multi_move->count; i++) {
|
||||||
|
MoveItem_Struct* mi = new MoveItem_Struct();
|
||||||
|
mi->from_slot = multi_move->moves[i].from_slot.SubIndex == -1 ? multi_move->moves[i].from_slot.Slot : m_inv.CalcSlotId(multi_move->moves[i].from_slot.Slot, multi_move->moves[i].from_slot.SubIndex);
|
||||||
|
mi->to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot, multi_move->moves[i].to_slot.SubIndex);
|
||||||
|
|
||||||
|
if (multi_move->moves[i].to_slot.Type == EQ::invtype::typeBank) { // Target is bank inventory
|
||||||
|
mi->to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot + EQ::invslot::BANK_BEGIN, multi_move->moves[i].to_slot.SubIndex);
|
||||||
|
} else if (multi_move->moves[i].to_slot.Type == EQ::invtype::typeSharedBank) { // Target is shared bank inventory
|
||||||
|
mi->to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot + EQ::invslot::SHARED_BANK_BEGIN, multi_move->moves[i].to_slot.SubIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This sends '1' as the stack count for unstackable items, which our titanium-era SwapItem blows up
|
||||||
|
if (m_inv.GetItem(mi->from_slot)->IsStackable()) {
|
||||||
|
mi->number_in_stack = multi_move->moves[i].number_in_stack;
|
||||||
|
} else {
|
||||||
|
mi->number_in_stack = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SwapItem(mi) && IsValidSlot(mi->from_slot) && IsValidSlot(mi->to_slot)) {
|
||||||
|
bool error = false;
|
||||||
|
SwapItemResync(mi);
|
||||||
|
InterrogateInventory(this, false, true, false, error, false);
|
||||||
|
if (error) {
|
||||||
|
InterrogateInventory(this, true, false, true, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This is the swap.
|
||||||
|
// Client behavior is just to move stacks without combining them
|
||||||
|
// Items get rearranged to fill the 'top' of the bag first
|
||||||
|
} else {
|
||||||
|
struct MoveInfo {
|
||||||
|
EQ::ItemInstance* item;
|
||||||
|
uint16 to_slot;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<MoveInfo> items;
|
||||||
|
items.reserve(multi_move->count);
|
||||||
|
|
||||||
|
for (int i = 0; i < multi_move->count; i++) {
|
||||||
|
// These are always bags, so we don't need to worry about raw items in slotCursor
|
||||||
|
uint16 from_slot = m_inv.CalcSlotId(multi_move->moves[i].from_slot.Slot, multi_move->moves[i].from_slot.SubIndex);
|
||||||
|
if (multi_move->moves[i].from_slot.Type == EQ::invtype::typeBank) { // Target is bank inventory
|
||||||
|
from_slot = m_inv.CalcSlotId(multi_move->moves[i].from_slot.Slot + EQ::invslot::BANK_BEGIN, multi_move->moves[i].from_slot.SubIndex);
|
||||||
|
} else if (multi_move->moves[i].from_slot.Type == EQ::invtype::typeSharedBank) { // Target is shared bank inventory
|
||||||
|
from_slot = m_inv.CalcSlotId(multi_move->moves[i].from_slot.Slot + EQ::invslot::SHARED_BANK_BEGIN, multi_move->moves[i].from_slot.SubIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16 to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot, multi_move->moves[i].to_slot.SubIndex);
|
||||||
|
if (multi_move->moves[i].to_slot.Type == EQ::invtype::typeBank) { // Target is bank inventory
|
||||||
|
to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot + EQ::invslot::BANK_BEGIN, multi_move->moves[i].to_slot.SubIndex);
|
||||||
|
} else if (multi_move->moves[i].to_slot.Type == EQ::invtype::typeSharedBank) { // Target is shared bank inventory
|
||||||
|
to_slot = m_inv.CalcSlotId(multi_move->moves[i].to_slot.Slot + EQ::invslot::SHARED_BANK_BEGIN, multi_move->moves[i].to_slot.SubIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// I wasn't able to produce any error states on purpose.
|
||||||
|
MoveInfo move{
|
||||||
|
.item = m_inv.PopItem(from_slot), // Don't delete the instance here
|
||||||
|
.to_slot = to_slot
|
||||||
|
};
|
||||||
|
|
||||||
|
if (move.item) {
|
||||||
|
items.push_back(move);
|
||||||
|
database.SaveInventory(CharacterID(), NULL, from_slot); // We have to manually save inventory here.
|
||||||
|
} else {
|
||||||
|
LinkDead();
|
||||||
|
return; // Prevent inventory desync here. Forcing a resync would be better, but we don't have a MoveItem struct to work with.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const MoveInfo& move : items) {
|
||||||
|
PutItemInInventory(move.to_slot, *move.item); // This saves inventory too
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
LinkDead(); // This packet should not be sent by an older client
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app)
|
void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app)
|
||||||
@@ -13561,11 +13681,11 @@ void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ss->Confirm) {
|
if (ss->Confirm) {
|
||||||
Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str());
|
Mob *Caster = entity_list.GetMob(sacrifice_caster_id);
|
||||||
if (Caster) Sacrifice(Caster);
|
if (Caster) Sacrifice(Caster);
|
||||||
}
|
}
|
||||||
PendingSacrifice = false;
|
PendingSacrifice = false;
|
||||||
SacrificeCaster.clear();
|
sacrifice_caster_id = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail)
|
void Client::Handle_OP_SafeFallSuccess(const EQApplicationPacket *app) // bit of a misnomer, sent whenever safe fall is used (success of fail)
|
||||||
@@ -13608,16 +13728,26 @@ void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app)
|
|||||||
|
|
||||||
void Client::Handle_OP_SenseHeading(const EQApplicationPacket *app)
|
void Client::Handle_OP_SenseHeading(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
if (!HasSkill(EQ::skills::SkillSenseHeading))
|
if (!HasSkill(EQ::skills::SkillSenseHeading)) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
int chancemod = 0;
|
if (RuleB(Skills, TrainSenseHeading)) {
|
||||||
|
CheckIncreaseSkill(EQ::skills::SkillSenseHeading, nullptr, 0);
|
||||||
CheckIncreaseSkill(EQ::skills::SkillSenseHeading, nullptr, chancemod);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parse->PlayerHasQuestSub(EVENT_USE_SKILL)) {
|
||||||
|
const auto& export_string = fmt::format(
|
||||||
|
"{} {}",
|
||||||
|
EQ::skills::SkillSenseHeading,
|
||||||
|
GetRawSkill(EQ::skills::SkillSenseHeading)
|
||||||
|
);
|
||||||
|
|
||||||
|
parse->EventPlayer(EVENT_USE_SKILL, this, export_string, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app)
|
void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app)
|
||||||
{
|
{
|
||||||
if (!HasSkill(EQ::skills::SkillSenseTraps))
|
if (!HasSkill(EQ::skills::SkillSenseTraps))
|
||||||
@@ -14868,14 +14998,15 @@ void Client::Handle_OP_Split(const EQApplicationPacket *app)
|
|||||||
Group *group = nullptr;
|
Group *group = nullptr;
|
||||||
Raid *raid = nullptr;
|
Raid *raid = nullptr;
|
||||||
|
|
||||||
if (IsRaidGrouped())
|
if (IsRaidGrouped()) {
|
||||||
raid = GetRaid();
|
raid = GetRaid();
|
||||||
else if (IsGrouped())
|
} else if (IsGrouped()) {
|
||||||
group = GetGroup();
|
group = GetGroup();
|
||||||
|
}
|
||||||
|
|
||||||
// is there an actual error message for this?
|
// is there an actual error message for this?
|
||||||
if (raid == nullptr && group == nullptr) {
|
if (raid == nullptr && group == nullptr) {
|
||||||
Message(Chat::Red, "You can not split money if you're not in a group.");
|
MessageString(Chat::Red, SPLIT_NO_GROUP);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -14883,14 +15014,15 @@ void Client::Handle_OP_Split(const EQApplicationPacket *app)
|
|||||||
10 * static_cast<uint64>(split->silver) +
|
10 * static_cast<uint64>(split->silver) +
|
||||||
100 * static_cast<uint64>(split->gold) +
|
100 * static_cast<uint64>(split->gold) +
|
||||||
1000 * static_cast<uint64>(split->platinum))) {
|
1000 * static_cast<uint64>(split->platinum))) {
|
||||||
Message(Chat::Red, "You do not have enough money to do that split.");
|
MessageString(Chat::Red, SPLIT_FAIL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (raid)
|
if (raid) {
|
||||||
raid->SplitMoney(raid->GetGroup(this), split->copper, split->silver, split->gold, split->platinum);
|
raid->SplitMoney(raid->GetGroup(this), split->copper, split->silver, split->gold, split->platinum);
|
||||||
else if (group)
|
} else if (group) {
|
||||||
group->SplitMoney(split->copper, split->silver, split->gold, split->platinum, this);
|
group->SplitMoney(split->copper, split->silver, split->gold, split->platinum, this, true);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -15518,7 +15650,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case TraderOn: {
|
case TraderOn: {
|
||||||
if (Buyer) {
|
if (IsBuyer()) {
|
||||||
TraderEndTrader();
|
TraderEndTrader();
|
||||||
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
|
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
|
||||||
return;
|
return;
|
||||||
@@ -15564,7 +15696,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
|||||||
auto trader = entity_list.GetClientByID(in->trader_id);
|
auto trader = entity_list.GetClientByID(in->trader_id);
|
||||||
|
|
||||||
switch (in->method) {
|
switch (in->method) {
|
||||||
case ByVendor: {
|
case BazaarByVendor: {
|
||||||
if (trader) {
|
if (trader) {
|
||||||
LogTrading("Buy item directly from vendor id <green>[{}] item_id <green>[{}] quantity <green>[{}] "
|
LogTrading("Buy item directly from vendor id <green>[{}] item_id <green>[{}] quantity <green>[{}] "
|
||||||
"serial_number <green>[{}]",
|
"serial_number <green>[{}]",
|
||||||
@@ -15577,7 +15709,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ByParcel: {
|
case BazaarByParcel: {
|
||||||
if (!RuleB(Parcel, EnableParcelMerchants) || !RuleB(Bazaar, EnableParcelDelivery)) {
|
if (!RuleB(Parcel, EnableParcelMerchants) || !RuleB(Bazaar, EnableParcelDelivery)) {
|
||||||
LogTrading(
|
LogTrading(
|
||||||
"Bazaar purchase attempt by parcel delivery though 'Parcel:EnableParcelMerchants' or "
|
"Bazaar purchase attempt by parcel delivery though 'Parcel:EnableParcelMerchants' or "
|
||||||
@@ -15587,7 +15719,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
|||||||
Chat::Yellow,
|
Chat::Yellow,
|
||||||
"The bazaar parcel delivey system is not enabled on this server. Please visit the vendor directly in the Bazaar."
|
"The bazaar parcel delivey system is not enabled on this server. Please visit the vendor directly in the Bazaar."
|
||||||
);
|
);
|
||||||
in->method = ByParcel;
|
in->method = BazaarByParcel;
|
||||||
in->sub_action = Failed;
|
in->sub_action = Failed;
|
||||||
TradeRequestFailed(app);
|
TradeRequestFailed(app);
|
||||||
return;
|
return;
|
||||||
@@ -15602,7 +15734,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
|||||||
BuyTraderItemOutsideBazaar(in, app);
|
BuyTraderItemOutsideBazaar(in, app);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ByDirectToInventory: {
|
case BazaarByDirectToInventory: {
|
||||||
if (!RuleB(Parcel, EnableDirectToInventoryDelivery)) {
|
if (!RuleB(Parcel, EnableDirectToInventoryDelivery)) {
|
||||||
LogTrading("Bazaar purchase attempt by direct inventory delivery though "
|
LogTrading("Bazaar purchase attempt by direct inventory delivery though "
|
||||||
"'Parcel:EnableDirectToInventoryDelivery' not enabled."
|
"'Parcel:EnableDirectToInventoryDelivery' not enabled."
|
||||||
@@ -15611,7 +15743,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
|||||||
Chat::Yellow,
|
Chat::Yellow,
|
||||||
"Direct inventory delivey is not enabled on this server. Please visit the vendor directly."
|
"Direct inventory delivey is not enabled on this server. Please visit the vendor directly."
|
||||||
);
|
);
|
||||||
in->method = ByDirectToInventory;
|
in->method = BazaarByDirectToInventory;
|
||||||
in->sub_action = Failed;
|
in->sub_action = Failed;
|
||||||
TradeRequestFailed(app);
|
TradeRequestFailed(app);
|
||||||
return;
|
return;
|
||||||
@@ -15628,7 +15760,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
|||||||
Chat::Yellow,
|
Chat::Yellow,
|
||||||
"Direct inventory delivey is not yet implemented. Please visit the vendor directly or purchase via parcel delivery."
|
"Direct inventory delivey is not yet implemented. Please visit the vendor directly or purchase via parcel delivery."
|
||||||
);
|
);
|
||||||
in->method = ByDirectToInventory;
|
in->method = BazaarByDirectToInventory;
|
||||||
in->sub_action = Failed;
|
in->sub_action = Failed;
|
||||||
TradeRequestFailed(app);
|
TradeRequestFailed(app);
|
||||||
break;
|
break;
|
||||||
@@ -16027,7 +16159,8 @@ void Client::Handle_OP_WearChange(const EQApplicationPacket *app)
|
|||||||
wc->hero_forge_model = GetHerosForgeModel(wc->wear_slot_id);
|
wc->hero_forge_model = GetHerosForgeModel(wc->wear_slot_id);
|
||||||
|
|
||||||
// we could maybe ignore this and just send our own from moveitem
|
// we could maybe ignore this and just send our own from moveitem
|
||||||
entity_list.QueueClients(this, app, false);
|
// We probably need to skip this entirely when it is send as an ack, but not sure how to ID that.
|
||||||
|
entity_list.QueueClients(this, app, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app)
|
void Client::Handle_OP_WhoAllRequest(const EQApplicationPacket *app)
|
||||||
@@ -16774,9 +16907,14 @@ void Client::Handle_OP_RaidClearNPCMarks(const EQApplicationPacket* app)
|
|||||||
|
|
||||||
void Client::RecordStats()
|
void Client::RecordStats()
|
||||||
{
|
{
|
||||||
|
const uint32 character_id = CharacterID();
|
||||||
|
if (!character_id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
auto r = CharacterStatsRecordRepository::FindOne(
|
auto r = CharacterStatsRecordRepository::FindOne(
|
||||||
database,
|
database,
|
||||||
CharacterID()
|
character_id
|
||||||
);
|
);
|
||||||
|
|
||||||
r.status = Admin();
|
r.status = Admin();
|
||||||
@@ -16854,7 +16992,7 @@ void Client::RecordStats()
|
|||||||
if (r.character_id > 0) {
|
if (r.character_id > 0) {
|
||||||
CharacterStatsRecordRepository::UpdateOne(database, r);
|
CharacterStatsRecordRepository::UpdateOne(database, r);
|
||||||
} else {
|
} else {
|
||||||
r.character_id = CharacterID();
|
r.character_id = character_id;
|
||||||
r.created_at = std::time(nullptr);
|
r.created_at = std::time(nullptr);
|
||||||
CharacterStatsRecordRepository::InsertOne(database, r);
|
CharacterStatsRecordRepository::InsertOne(database, r);
|
||||||
}
|
}
|
||||||
|
|||||||
+87
-26
@@ -289,6 +289,37 @@ bool Client::Process() {
|
|||||||
entity_list.ScanCloseMobs(close_mobs, this, IsMoving());
|
entity_list.ScanCloseMobs(close_mobs, this, IsMoving());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RuleB(Inventory, LazyLoadBank)) {
|
||||||
|
// poll once a second to see if we are close to a banker and we haven't loaded the bank yet
|
||||||
|
if (!m_lazy_load_bank && lazy_load_bank_check_timer.Check()) {
|
||||||
|
if (m_lazy_load_sent_bank_slots <= EQ::invslot::SHARED_BANK_END && IsCloseToBanker()) {
|
||||||
|
m_lazy_load_bank = true;
|
||||||
|
lazy_load_bank_check_timer.Disable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_lazy_load_bank && m_lazy_load_sent_bank_slots <= EQ::invslot::SHARED_BANK_END) {
|
||||||
|
const EQ::ItemInstance *inst = nullptr;
|
||||||
|
|
||||||
|
// Jump the gaps
|
||||||
|
if (m_lazy_load_sent_bank_slots < EQ::invslot::BANK_BEGIN) {
|
||||||
|
m_lazy_load_sent_bank_slots = EQ::invslot::BANK_BEGIN;
|
||||||
|
}
|
||||||
|
else if (m_lazy_load_sent_bank_slots > EQ::invslot::BANK_END &&
|
||||||
|
m_lazy_load_sent_bank_slots < EQ::invslot::SHARED_BANK_BEGIN) {
|
||||||
|
m_lazy_load_sent_bank_slots = EQ::invslot::SHARED_BANK_BEGIN;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
m_lazy_load_sent_bank_slots++;
|
||||||
|
}
|
||||||
|
|
||||||
|
inst = m_inv[m_lazy_load_sent_bank_slots];
|
||||||
|
if (inst) {
|
||||||
|
SendItemPacket(m_lazy_load_sent_bank_slots, inst, ItemPacketType::ItemPacketTrade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool may_use_attacks = false;
|
bool may_use_attacks = false;
|
||||||
/*
|
/*
|
||||||
Things which prevent us from attacking:
|
Things which prevent us from attacking:
|
||||||
@@ -321,48 +352,46 @@ bool Client::Process() {
|
|||||||
auto_fire = false;
|
auto_fire = false;
|
||||||
}
|
}
|
||||||
EQ::ItemInstance *ranged = GetInv().GetItem(EQ::invslot::slotRange);
|
EQ::ItemInstance *ranged = GetInv().GetItem(EQ::invslot::slotRange);
|
||||||
if (ranged)
|
if (ranged) {
|
||||||
{
|
|
||||||
if (ranged->GetItem() && ranged->GetItem()->ItemType == EQ::item::ItemTypeBow) {
|
if (ranged->GetItem() && ranged->GetItem()->ItemType == EQ::item::ItemTypeBow) {
|
||||||
if (ranged_timer.Check(false)) {
|
if (ranged_timer.Check(false)) {
|
||||||
if (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
if (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
||||||
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
||||||
if (CheckLosFN(GetTarget()) && CheckWaterAutoFireLoS(GetTarget())) {
|
if (CheckLosFN(GetTarget()) && CheckWaterAutoFireLoS(GetTarget())) {
|
||||||
//client has built in los check, but auto fire does not.. done last.
|
//client has built in los check, but auto fire does not.. done last.
|
||||||
RangedAttack(GetTarget());
|
if (RangedAttack(GetTarget()) && CheckDoubleRangedAttack()) {
|
||||||
if (CheckDoubleRangedAttack())
|
|
||||||
RangedAttack(GetTarget(), true);
|
RangedAttack(GetTarget(), true);
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
ranged_timer.Start();
|
ranged_timer.Start();
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
ranged_timer.Start();
|
ranged_timer.Start();
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
ranged_timer.Start();
|
ranged_timer.Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ranged->GetItem() && (ranged->GetItem()->ItemType == EQ::item::ItemTypeLargeThrowing || ranged->GetItem()->ItemType == EQ::item::ItemTypeSmallThrowing)) {
|
} else if (ranged->GetItem() && (ranged->GetItem()->ItemType == EQ::item::ItemTypeLargeThrowing || ranged->GetItem()->ItemType == EQ::item::ItemTypeSmallThrowing)) {
|
||||||
if (ranged_timer.Check(false)) {
|
if (ranged_timer.Check(false)) {
|
||||||
if (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
if (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
||||||
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
||||||
if (CheckLosFN(GetTarget()) && CheckWaterAutoFireLoS(GetTarget())) {
|
if (CheckLosFN(GetTarget()) && CheckWaterAutoFireLoS(GetTarget())) {
|
||||||
//client has built in los check, but auto fire does not.. done last.
|
//client has built in los check, but auto fire does not.. done last.
|
||||||
ThrowingAttack(GetTarget());
|
ThrowingAttack(GetTarget());
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
ranged_timer.Start();
|
ranged_timer.Start();
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
ranged_timer.Start();
|
ranged_timer.Start();
|
||||||
}
|
}
|
||||||
else
|
} else {
|
||||||
ranged_timer.Start();
|
ranged_timer.Start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Mob *auto_attack_target = GetTarget();
|
Mob *auto_attack_target = GetTarget();
|
||||||
if (auto_attack && auto_attack_target != nullptr && may_use_attacks && attack_timer.Check()) {
|
if (auto_attack && auto_attack_target != nullptr && may_use_attacks && attack_timer.Check()) {
|
||||||
@@ -782,6 +811,7 @@ void Client::BulkSendInventoryItems()
|
|||||||
last_pos = ob.tellp();
|
last_pos = ob.tellp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!RuleB(Inventory, LazyLoadBank)) {
|
||||||
// Bank items
|
// Bank items
|
||||||
for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) {
|
for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) {
|
||||||
const EQ::ItemInstance* inst = m_inv[slot_id];
|
const EQ::ItemInstance* inst = m_inv[slot_id];
|
||||||
@@ -809,6 +839,7 @@ void Client::BulkSendInventoryItems()
|
|||||||
|
|
||||||
last_pos = ob.tellp();
|
last_pos = ob.tellp();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto outapp = new EQApplicationPacket(OP_CharInventory);
|
auto outapp = new EQApplicationPacket(OP_CharInventory);
|
||||||
outapp->size = ob.size();
|
outapp->size = ob.size();
|
||||||
@@ -1034,32 +1065,62 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I
|
|||||||
name, (uint16)spells[SpellID].base_value[0],
|
name, (uint16)spells[SpellID].base_value[0],
|
||||||
SpellID, ZoneID, InstanceID);
|
SpellID, ZoneID, InstanceID);
|
||||||
|
|
||||||
if (RuleB(Spells, BuffsFadeOnDeath)) {
|
const bool use_old_resurrection = (
|
||||||
BuffFadeNonPersistDeath();
|
RuleB(Character, UseOldRaceRezEffects) &&
|
||||||
}
|
(
|
||||||
|
GetRace() == Race::Barbarian ||
|
||||||
|
GetRace() == Race::Dwarf ||
|
||||||
|
GetRace() == Race::Troll ||
|
||||||
|
GetRace() == Race::Ogre
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const uint16 resurrection_sickness_spell_id = (
|
||||||
|
use_old_resurrection ?
|
||||||
|
RuleI(Character, OldResurrectionSicknessSpellID) :
|
||||||
|
RuleI(Character, ResurrectionSicknessSpellID)
|
||||||
|
);
|
||||||
|
|
||||||
int SpellEffectDescNum = GetSpellEffectDescriptionNumber(SpellID);
|
int SpellEffectDescNum = GetSpellEffectDescriptionNumber(SpellID);
|
||||||
// Rez spells with Rez effects have this DescNum (first is Titanium, second is 6.2 Client)
|
// Rez spells with Rez effects have this DescNum (first is Titanium, second is 6.2 Client)
|
||||||
if(RuleB(Character, UseResurrectionSickness) && SpellEffectDescNum == 82 || SpellEffectDescNum == 39067) {
|
if(RuleB(Character, UseResurrectionSickness) && SpellEffectDescNum == 82 || SpellEffectDescNum == 39067) {
|
||||||
SetHP(GetMaxHP() / 5);
|
SetHP(GetMaxHP() / 5);
|
||||||
SetMana(0);
|
SetMana(0);
|
||||||
int resurrection_sickness_spell_id = (
|
|
||||||
RuleB(Character, UseOldRaceRezEffects) &&
|
if (RuleB(Spells, BuffsFadeOnDeath)) {
|
||||||
(
|
BuffFadeNonPersistDeath();
|
||||||
GetRace() == BARBARIAN ||
|
}
|
||||||
GetRace() == DWARF ||
|
|
||||||
GetRace() == TROLL ||
|
|
||||||
GetRace() == OGRE
|
|
||||||
) ?
|
|
||||||
RuleI(Character, OldResurrectionSicknessSpellID) :
|
|
||||||
RuleI(Character, ResurrectionSicknessSpellID)
|
|
||||||
);
|
|
||||||
SpellOnTarget(resurrection_sickness_spell_id, this);
|
SpellOnTarget(resurrection_sickness_spell_id, this);
|
||||||
} else if (SpellID == SPELL_DIVINE_REZ) {
|
} else if (SpellID == SPELL_DIVINE_REZ) {
|
||||||
|
if (RuleB(Spells, BuffsFadeOnDeath)) {
|
||||||
|
BuffFadeNonPersistDeath();
|
||||||
|
}
|
||||||
|
|
||||||
RestoreHealth();
|
RestoreHealth();
|
||||||
RestoreMana();
|
RestoreMana();
|
||||||
RestoreEndurance();
|
RestoreEndurance();
|
||||||
} else {
|
} else {
|
||||||
|
if (RuleB(Character, UseResurrectionSickness)) {
|
||||||
|
bool has_resurrection_sickness = false;
|
||||||
|
|
||||||
|
for (int slot = 0; slot < GetMaxTotalSlots(); slot++) {
|
||||||
|
if (IsValidSpell(buffs[slot].spellid) && IsResurrectionSicknessSpell(buffs[slot].spellid)){
|
||||||
|
has_resurrection_sickness = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to wipe buffs after checking if client had rez effects.
|
||||||
|
if (RuleB(Spells, BuffsFadeOnDeath)) {
|
||||||
|
BuffFadeNonPersistDeath();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (has_resurrection_sickness) {
|
||||||
|
SpellOnTarget(resurrection_sickness_spell_id, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
SetHP(GetMaxHP() / 20);
|
SetHP(GetMaxHP() / 20);
|
||||||
SetMana(GetMaxMana() / 20);
|
SetMana(GetMaxMana() / 20);
|
||||||
SetEndurance(GetMaxEndurance() / 20);
|
SetEndurance(GetMaxEndurance() / 20);
|
||||||
|
|||||||
+3
-1
@@ -112,7 +112,7 @@ int command_init(void)
|
|||||||
command_add("delpetition", "[petition number] - Delete a petition", AccountStatus::ApprenticeGuide, command_delpetition) ||
|
command_add("delpetition", "[petition number] - Delete a petition", AccountStatus::ApprenticeGuide, command_delpetition) ||
|
||||||
command_add("depop", "[Start Spawn Timer] - Depop your NPC target and optionally start their spawn timer (false by default)", AccountStatus::Guide, command_depop) ||
|
command_add("depop", "[Start Spawn Timer] - Depop your NPC target and optionally start their spawn timer (false by default)", AccountStatus::Guide, command_depop) ||
|
||||||
command_add("depopzone", "[Start Spawn Timers] - Depop the zone and optionally start spawn timers (false by default)", AccountStatus::GMAdmin, command_depopzone) ||
|
command_add("depopzone", "[Start Spawn Timers] - Depop the zone and optionally start spawn timers (false by default)", AccountStatus::GMAdmin, command_depopzone) ||
|
||||||
command_add("devtools", "[Enable|Disable] - Manages Developer Tools (send no parameter for menu)", AccountStatus::GMMgmt, command_devtools) ||
|
command_add("devtools", "[menu|window] [enable|disable] - Manages Developer Tools (send no parameter for menu)", AccountStatus::GMMgmt, command_devtools) ||
|
||||||
command_add("disablerecipe", "[Recipe ID] - Disables a Recipe", AccountStatus::QuestTroupe, command_disablerecipe) ||
|
command_add("disablerecipe", "[Recipe ID] - Disables a Recipe", AccountStatus::QuestTroupe, command_disablerecipe) ||
|
||||||
command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", AccountStatus::QuestTroupe, command_disarmtrap) ||
|
command_add("disarmtrap", "Analog for ldon disarm trap for the newer clients since we still don't have it working.", AccountStatus::QuestTroupe, command_disarmtrap) ||
|
||||||
command_add("door", "Door editing command", AccountStatus::QuestTroupe, command_door) ||
|
command_add("door", "Door editing command", AccountStatus::QuestTroupe, command_door) ||
|
||||||
@@ -134,6 +134,7 @@ int command_init(void)
|
|||||||
command_add("fish", "Fish for an item", AccountStatus::QuestTroupe, command_fish) ||
|
command_add("fish", "Fish for an item", AccountStatus::QuestTroupe, command_fish) ||
|
||||||
command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) ||
|
command_add("fixmob", "[race|gender|texture|helm|face|hair|haircolor|beard|beardcolor|heritage|tattoo|detail] [next|prev] - Manipulate appearance of your target", AccountStatus::QuestTroupe, command_fixmob) ||
|
||||||
command_add("flagedit", "Edit zone flags on your target. Use #flagedit help for more info.", AccountStatus::GMAdmin, command_flagedit) ||
|
command_add("flagedit", "Edit zone flags on your target. Use #flagedit help for more info.", AccountStatus::GMAdmin, command_flagedit) ||
|
||||||
|
command_add("fleeinfo", "- Gives info about whether a NPC will flee or not, using the command issuer as top hate.", AccountStatus::QuestTroupe, command_fleeinfo) ||
|
||||||
command_add("forage", "Forage an item", AccountStatus::QuestTroupe, command_forage) ||
|
command_add("forage", "Forage an item", AccountStatus::QuestTroupe, command_forage) ||
|
||||||
command_add("gearup", "Developer tool to quickly equip yourself or your target", AccountStatus::GMMgmt, command_gearup) ||
|
command_add("gearup", "Developer tool to quickly equip yourself or your target", AccountStatus::GMMgmt, command_gearup) ||
|
||||||
command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) ||
|
command_add("giveitem", "[itemid] [charges] - Summon an item onto your target's cursor. Charges are optional.", AccountStatus::GMMgmt, command_giveitem) ||
|
||||||
@@ -829,6 +830,7 @@ void command_bot(Client *c, const Seperator *sep)
|
|||||||
#include "gm_commands/fish.cpp"
|
#include "gm_commands/fish.cpp"
|
||||||
#include "gm_commands/fixmob.cpp"
|
#include "gm_commands/fixmob.cpp"
|
||||||
#include "gm_commands/flagedit.cpp"
|
#include "gm_commands/flagedit.cpp"
|
||||||
|
#include "gm_commands/fleeinfo.cpp"
|
||||||
#include "gm_commands/forage.cpp"
|
#include "gm_commands/forage.cpp"
|
||||||
#include "gm_commands/gearup.cpp"
|
#include "gm_commands/gearup.cpp"
|
||||||
#include "gm_commands/giveitem.cpp"
|
#include "gm_commands/giveitem.cpp"
|
||||||
|
|||||||
@@ -87,6 +87,7 @@ void command_find(Client *c, const Seperator *sep);
|
|||||||
void command_fish(Client* c, const Seperator* sep);
|
void command_fish(Client* c, const Seperator* sep);
|
||||||
void command_fixmob(Client *c, const Seperator *sep);
|
void command_fixmob(Client *c, const Seperator *sep);
|
||||||
void command_flagedit(Client *c, const Seperator *sep);
|
void command_flagedit(Client *c, const Seperator *sep);
|
||||||
|
void command_fleeinfo(Client *c, const Seperator *sep);
|
||||||
void command_forage(Client* c, const Seperator* sep);
|
void command_forage(Client* c, const Seperator* sep);
|
||||||
void command_gearup(Client *c, const Seperator *sep);
|
void command_gearup(Client *c, const Seperator *sep);
|
||||||
void command_giveitem(Client *c, const Seperator *sep);
|
void command_giveitem(Client *c, const Seperator *sep);
|
||||||
|
|||||||
+2
-2
@@ -633,8 +633,8 @@ namespace SBIndex {
|
|||||||
constexpr uint16 SKILLATK_PROC_SPELL_ID = 0; // SPA 288
|
constexpr uint16 SKILLATK_PROC_SPELL_ID = 0; // SPA 288
|
||||||
constexpr uint16 SKILLATK_PROC_CHANCE = 1; // SPA 288
|
constexpr uint16 SKILLATK_PROC_CHANCE = 1; // SPA 288
|
||||||
constexpr uint16 SKILLATK_PROC_SKILL = 2; // SPA 288
|
constexpr uint16 SKILLATK_PROC_SKILL = 2; // SPA 288
|
||||||
constexpr uint16 SLAYUNDEAD_RATE_MOD = 0; // SPA 219
|
constexpr uint16 SLAYUNDEAD_DMG_MOD = 0; // SPA 219
|
||||||
constexpr uint16 SLAYUNDEAD_DMG_MOD = 1; // SPA 219
|
constexpr uint16 SLAYUNDEAD_RATE_MOD = 1; // SPA 219
|
||||||
constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223
|
constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223
|
||||||
constexpr uint16 DOUBLE_RIPOSTE_SKILL_ATK_CHANCE = 1; // SPA 223
|
constexpr uint16 DOUBLE_RIPOSTE_SKILL_ATK_CHANCE = 1; // SPA 223
|
||||||
constexpr uint16 DOUBLE_RIPOSTE_SKILL = 2; // SPA 223
|
constexpr uint16 DOUBLE_RIPOSTE_SKILL = 2; // SPA 223
|
||||||
|
|||||||
@@ -428,8 +428,6 @@ void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size_before = g_data_bucket_cache.size();
|
|
||||||
|
|
||||||
LogDataBucketsDetail("cache size before [{}] l size [{}]", g_data_bucket_cache.size(), l.size());
|
LogDataBucketsDetail("cache size before [{}] l size [{}]", g_data_bucket_cache.size(), l.size());
|
||||||
|
|
||||||
uint32 added_count = 0;
|
uint32 added_count = 0;
|
||||||
@@ -440,8 +438,6 @@ void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
g_data_bucket_cache.reserve(g_data_bucket_cache.size() + added_count);
|
|
||||||
|
|
||||||
for (const auto &e: l) {
|
for (const auto &e: l) {
|
||||||
if (!ExistsInCache(e)) {
|
if (!ExistsInCache(e)) {
|
||||||
LogDataBucketsDetail("bucket id [{}] bucket key [{}] bucket value [{}]", e.id, e.key_, e.value);
|
LogDataBucketsDetail("bucket id [{}] bucket key [{}] bucket value [{}]", e.id, e.key_, e.value);
|
||||||
|
|||||||
+2
-2
@@ -542,8 +542,8 @@ void Doors::HandleClick(Client *sender, uint8 trigger)
|
|||||||
if (EQ::ValueWithin(m_open_type, 57, 58) && HasDestinationZone()) {
|
if (EQ::ValueWithin(m_open_type, 57, 58) && HasDestinationZone()) {
|
||||||
bool has_key_required = (required_key_item && required_key_item == player_key);
|
bool has_key_required = (required_key_item && required_key_item == player_key);
|
||||||
|
|
||||||
if (sender->GetGM() && has_key_required) {
|
if (sender->GetGM() && !has_key_required) {
|
||||||
has_key_required = false;
|
has_key_required = true;
|
||||||
sender->Message(Chat::White, "Your GM flag allows you to open this door without a key.");
|
sender->Message(Chat::White, "Your GM flag allows you to open this door without a key.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -60,6 +60,12 @@ public:
|
|||||||
void SetPosition(const glm::vec4 &position);
|
void SetPosition(const glm::vec4 &position);
|
||||||
void SetSize(uint16 size);
|
void SetSize(uint16 size);
|
||||||
void ToggleState(Mob *sender);
|
void ToggleState(Mob *sender);
|
||||||
|
inline std::string GetDestinationZoneName() { return m_destination_zone_name; }
|
||||||
|
inline int GetDestinationInstanceID() { return m_destination_instance_id; }
|
||||||
|
inline float GetDestinationX() { return m_destination.x; }
|
||||||
|
inline float GetDestinationY() { return m_destination.y; }
|
||||||
|
inline float GetDestinationZ() { return m_destination.z; }
|
||||||
|
inline float GetDestinationHeading() { return m_destination.w; }
|
||||||
|
|
||||||
float GetX();
|
float GetX();
|
||||||
float GetY();
|
float GetY();
|
||||||
|
|||||||
+63
-105
@@ -1028,17 +1028,8 @@ void Client::SendDisciplineTimer(uint32 timer_id, uint32 duration)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
void EntityList::AETaunt(Client* taunter, float range, int bonus_hate)
|
||||||
* @param taunter
|
|
||||||
* @param range
|
|
||||||
* @param bonus_hate
|
|
||||||
*/
|
|
||||||
void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
|
||||||
* Live AE taunt range - Hardcoded.
|
|
||||||
*/
|
|
||||||
if (range == 0) {
|
if (range == 0) {
|
||||||
range = 40;
|
range = 40;
|
||||||
}
|
}
|
||||||
@@ -1060,9 +1051,11 @@ void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate)
|
|||||||
z_difference *= -1;
|
z_difference *= -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (z_difference < 10
|
if (
|
||||||
&& taunter->IsAttackAllowed(them)
|
z_difference < 10 &&
|
||||||
&& DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared) {
|
taunter->IsAttackAllowed(them) &&
|
||||||
|
DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared
|
||||||
|
) {
|
||||||
if (taunter->CheckLosFN(them)) {
|
if (taunter->CheckLosFN(them)) {
|
||||||
taunter->Taunt(them->CastToNPC(), true, 0, true, bonus_hate);
|
taunter->Taunt(them->CastToNPC(), true, 0, true, bonus_hate);
|
||||||
}
|
}
|
||||||
@@ -1070,36 +1063,29 @@ void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Causes caster to hit every mob within dist range of center with spell_id
|
|
||||||
*
|
|
||||||
* @param caster_mob
|
|
||||||
* @param center_mob
|
|
||||||
* @param spell_id
|
|
||||||
* @param affect_caster
|
|
||||||
* @param resist_adjust
|
|
||||||
* @param max_targets
|
|
||||||
*/
|
|
||||||
void EntityList::AESpell(
|
void EntityList::AESpell(
|
||||||
Mob* caster_mob,
|
Mob* caster_mob,
|
||||||
Mob* center_mob,
|
Mob* center_mob,
|
||||||
uint16 spell_id,
|
uint16 spell_id,
|
||||||
bool affect_caster,
|
bool affect_caster,
|
||||||
int16 resist_adjust,
|
int16 resist_adjust,
|
||||||
int *max_targets
|
int* max_targets,
|
||||||
|
bool is_scripted
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
const auto &cast_target_position =
|
const auto& cast_target_position = (
|
||||||
spells[spell_id].target_type == ST_Ring ?
|
(!is_scripted && spells[spell_id].target_type == ST_Ring) ?
|
||||||
caster_mob->GetTargetRingLocation() :
|
caster_mob->GetTargetRingLocation() :
|
||||||
static_cast<glm::vec3>(center_mob->GetPosition());
|
static_cast<glm::vec3>(center_mob->GetPosition())
|
||||||
|
);
|
||||||
|
|
||||||
Mob* current_mob = nullptr;
|
Mob* current_mob = nullptr;
|
||||||
|
|
||||||
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
|
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
|
||||||
bool is_npc = caster_mob->IsNPC();
|
bool is_npc = caster_mob->IsNPC();
|
||||||
float distance = caster_mob->GetAOERange(spell_id);
|
float distance = caster_mob->GetAOERange(spell_id);
|
||||||
float distance_squared = distance * distance;
|
float distance_squared = distance * distance;
|
||||||
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range;
|
float min_range_squared = spells[spell_id].min_range * spells[spell_id].min_range;
|
||||||
glm::vec2 min = { cast_target_position.x - distance, cast_target_position.y - distance };
|
glm::vec2 min = { cast_target_position.x - distance, cast_target_position.y - distance };
|
||||||
glm::vec2 max = { cast_target_position.x + distance, cast_target_position.y + distance };
|
glm::vec2 max = { cast_target_position.x + distance, cast_target_position.y + distance };
|
||||||
|
|
||||||
@@ -1110,17 +1096,18 @@ void EntityList::AESpell(
|
|||||||
max_targets = nullptr;
|
max_targets = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Max AOE targets
|
|
||||||
*/
|
|
||||||
int max_targets_allowed = RuleI(Range, AOEMaxTargets); // unlimited
|
int max_targets_allowed = RuleI(Range, AOEMaxTargets); // unlimited
|
||||||
if (max_targets) { // rains pass this in since they need to preserve the count through waves
|
if (max_targets) { // rains pass this in since they need to preserve the count through waves
|
||||||
max_targets_allowed = *max_targets;
|
max_targets_allowed = *max_targets;
|
||||||
}
|
} else if (spells[spell_id].aoe_max_targets) {
|
||||||
else if (spells[spell_id].aoe_max_targets) {
|
|
||||||
max_targets_allowed = spells[spell_id].aoe_max_targets;
|
max_targets_allowed = spells[spell_id].aoe_max_targets;
|
||||||
}
|
} else if (
|
||||||
else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc && !IsEffectInSpell(spell_id, SE_Lull) && !IsEffectInSpell(spell_id, SE_Mez)) {
|
IsTargetableAESpell(spell_id) &&
|
||||||
|
is_detrimental_spell &&
|
||||||
|
!is_npc &&
|
||||||
|
!IsEffectInSpell(spell_id, SE_Lull) &&
|
||||||
|
!IsEffectInSpell(spell_id, SE_Mez)
|
||||||
|
) {
|
||||||
max_targets_allowed = 4;
|
max_targets_allowed = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1161,16 +1148,11 @@ void EntityList::AESpell(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (spells[spell_id].pcnpc_only_flag == PCNPCOnlyFlagType::PC && !current_mob->IsOfClientBotMerc()) {
|
||||||
* Check PC / NPC
|
|
||||||
* 1 = PC
|
|
||||||
* 2 = NPC
|
|
||||||
*/
|
|
||||||
if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsOfClientBotMerc()) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spells[spell_id].pcnpc_only_flag == 2 && current_mob->IsOfClientBotMerc()) {
|
if (spells[spell_id].pcnpc_only_flag == PCNPCOnlyFlagType::NPC && current_mob->IsOfClientBotMerc()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1184,41 +1166,40 @@ void EntityList::AESpell(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (distance_to_target < min_range2) {
|
if (distance_to_target < min_range_squared) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_npc && current_mob->IsNPC() &&
|
if (
|
||||||
spells[spell_id].target_type != ST_AreaNPCOnly) { //check npc->npc casting
|
is_npc &&
|
||||||
FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob);
|
current_mob->IsNPC() &&
|
||||||
|
spells[spell_id].target_type != ST_AreaNPCOnly
|
||||||
|
) {
|
||||||
|
const auto faction_value = current_mob->GetReverseFactionCon(caster_mob);
|
||||||
if (is_detrimental_spell) {
|
if (is_detrimental_spell) {
|
||||||
//affect mobs that are on our hate list, or
|
|
||||||
//which have bad faction with us
|
|
||||||
if (
|
if (
|
||||||
!(caster_mob->CheckAggro(current_mob) ||
|
!(caster_mob->CheckAggro(current_mob) ||
|
||||||
faction_value == FACTION_THREATENINGLY ||
|
faction_value == FACTION_THREATENINGLY ||
|
||||||
faction_value == FACTION_SCOWLS)) {
|
faction_value == FACTION_SCOWLS)
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
//only affect mobs we would assist.
|
|
||||||
if (!(faction_value <= FACTION_AMIABLY)) {
|
if (!(faction_value <= FACTION_AMIABLY)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Finally, make sure they are within range
|
|
||||||
*/
|
|
||||||
if (is_detrimental_spell) {
|
if (is_detrimental_spell) {
|
||||||
if (!caster_mob->IsAttackAllowed(current_mob, true)) {
|
if (!caster_mob->IsAttackAllowed(current_mob, true)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) {
|
if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN(
|
if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN(
|
||||||
caster_mob->GetTargetRingX(),
|
caster_mob->GetTargetRingX(),
|
||||||
caster_mob->GetTargetRingY(),
|
caster_mob->GetTargetRingY(),
|
||||||
@@ -1226,9 +1207,7 @@ void EntityList::AESpell(
|
|||||||
current_mob->GetSize())) {
|
current_mob->GetSize())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
|
* Check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
|
||||||
* This does not check faction for beneficial AE buffs... only agro and attackable.
|
* This does not check faction for beneficial AE buffs... only agro and attackable.
|
||||||
@@ -1238,6 +1217,7 @@ void EntityList::AESpell(
|
|||||||
if (caster_mob->IsAttackAllowed(current_mob, true)) {
|
if (caster_mob->IsAttackAllowed(current_mob, true)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (caster_mob->CheckAggro(current_mob)) {
|
if (caster_mob->CheckAggro(current_mob)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1264,17 +1244,12 @@ void EntityList::AESpell(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param caster
|
|
||||||
* @param center
|
|
||||||
* @param spell_id
|
|
||||||
* @param affect_caster
|
|
||||||
*/
|
|
||||||
void EntityList::MassGroupBuff(
|
void EntityList::MassGroupBuff(
|
||||||
Mob* caster,
|
Mob* caster,
|
||||||
Mob* center,
|
Mob* center,
|
||||||
uint16 spell_id,
|
uint16 spell_id,
|
||||||
bool affect_caster)
|
bool affect_caster
|
||||||
|
)
|
||||||
{
|
{
|
||||||
Mob* current_mob = nullptr;
|
Mob* current_mob = nullptr;
|
||||||
float distance = caster->GetAOERange(spell_id);
|
float distance = caster->GetAOERange(spell_id);
|
||||||
@@ -1287,17 +1262,11 @@ void EntityList::MassGroupBuff(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (current_mob == center) { // Skip Center
|
||||||
* Skip center
|
|
||||||
*/
|
|
||||||
if (current_mob == center) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
if (current_mob == caster && !affect_caster) { // Skip Caster
|
||||||
* Skip self
|
|
||||||
*/
|
|
||||||
if (current_mob == caster && !affect_caster) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1305,17 +1274,13 @@ void EntityList::MassGroupBuff(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Pets
|
|
||||||
*/
|
|
||||||
if (current_mob->IsNPC()) {
|
if (current_mob->IsNPC()) {
|
||||||
Mob* owner = current_mob->GetOwner();
|
Mob* owner = current_mob->GetOwner();
|
||||||
if (owner) {
|
if (owner) {
|
||||||
if (!owner->IsOfClientBot()) {
|
if (!owner->IsOfClientBot()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1328,27 +1293,18 @@ void EntityList::MassGroupBuff(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Rampage - Normal and Duration rampages
|
|
||||||
* NPCs handle it differently in Mob::Rampage
|
|
||||||
*
|
|
||||||
* @param attacker
|
|
||||||
* @param distance
|
|
||||||
* @param Hand
|
|
||||||
* @param count
|
|
||||||
* @param is_from_spell
|
|
||||||
*/
|
|
||||||
void EntityList::AEAttack(
|
void EntityList::AEAttack(
|
||||||
Mob* attacker,
|
Mob* attacker,
|
||||||
float distance,
|
float distance,
|
||||||
int Hand,
|
int16 slot_id,
|
||||||
int count,
|
int hit_count,
|
||||||
bool is_from_spell,
|
bool is_from_spell,
|
||||||
int attack_rounds)
|
int attack_rounds
|
||||||
|
)
|
||||||
{
|
{
|
||||||
Mob* current_mob = nullptr;
|
Mob* current_mob = nullptr;
|
||||||
float distance_squared = distance * distance;
|
float distance_squared = distance * distance;
|
||||||
int hit_count = 0;
|
int current_hits = 0;
|
||||||
|
|
||||||
for (auto& it: entity_list.GetCloseMobList(attacker, distance)) {
|
for (auto& it: entity_list.GetCloseMobList(attacker, distance)) {
|
||||||
current_mob = it.second;
|
current_mob = it.second;
|
||||||
@@ -1356,27 +1312,29 @@ void EntityList::AEAttack(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (current_mob->IsNPC()
|
if (
|
||||||
&& current_mob != attacker //this is not needed unless NPCs can use this
|
current_mob->IsNPC() &&
|
||||||
&& (attacker->IsAttackAllowed(current_mob))
|
current_mob != attacker &&
|
||||||
&& !current_mob->IsHorse() /* dont attack mounts */
|
attacker->IsAttackAllowed(current_mob) &&
|
||||||
&& (DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared)
|
!current_mob->IsHorse() &&
|
||||||
|
DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared
|
||||||
) {
|
) {
|
||||||
|
|
||||||
for (int i = 0; i < attack_rounds; i++) {
|
for (int i = 0; i < attack_rounds; i++) {
|
||||||
if (!attacker->IsClient() || attacker->GetClass() == Class::Monk || attacker->GetClass() == Class::Ranger) {
|
if (
|
||||||
attacker->Attack(current_mob, Hand, false, false, is_from_spell);
|
!attacker->IsClient() ||
|
||||||
|
attacker->GetClass() == Class::Monk ||
|
||||||
|
attacker->GetClass() == Class::Ranger
|
||||||
|
) {
|
||||||
|
attacker->Attack(current_mob, slot_id, false, false, is_from_spell);
|
||||||
} else {
|
} else {
|
||||||
attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell);
|
attacker->CastToClient()->DoAttackRounds(current_mob, slot_id, is_from_spell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hit_count++;
|
current_hits++;
|
||||||
if (count != 0 && hit_count >= count) {
|
if (hit_count != 0 && current_hits >= hit_count) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+5
-4
@@ -2064,9 +2064,14 @@ void PerlembParser::ExportEventVariables(
|
|||||||
Corpse* corpse = std::any_cast<Corpse*>(extra_pointers->at(0));
|
Corpse* corpse = std::any_cast<Corpse*>(extra_pointers->at(0));
|
||||||
if (corpse) {
|
if (corpse) {
|
||||||
ExportVar(package_name.c_str(), "killed_corpse_id", corpse->GetID());
|
ExportVar(package_name.c_str(), "killed_corpse_id", corpse->GetID());
|
||||||
|
ExportVar(package_name.c_str(), "killed_x", corpse->GetX());
|
||||||
|
ExportVar(package_name.c_str(), "killed_y", corpse->GetY());
|
||||||
|
ExportVar(package_name.c_str(), "killed_z", corpse->GetZ());
|
||||||
|
ExportVar(package_name.c_str(), "killed_h", corpse->GetHeading());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EVENT_DEATH_ZONE only
|
||||||
if (extra_pointers && extra_pointers->size() >= 2) {
|
if (extra_pointers && extra_pointers->size() >= 2) {
|
||||||
NPC* killed = std::any_cast<NPC*>(extra_pointers->at(1));
|
NPC* killed = std::any_cast<NPC*>(extra_pointers->at(1));
|
||||||
if (killed) {
|
if (killed) {
|
||||||
@@ -2076,10 +2081,6 @@ void PerlembParser::ExportEventVariables(
|
|||||||
killed->IsBot() ? killed->CastToBot()->GetBotID() : 0
|
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_npc_id", killed->IsNPC() ? killed->GetNPCTypeID() : 0);
|
||||||
ExportVar(package_name.c_str(), "killed_x", killed->GetX());
|
|
||||||
ExportVar(package_name.c_str(), "killed_y", killed->GetY());
|
|
||||||
ExportVar(package_name.c_str(), "killed_z", killed->GetZ());
|
|
||||||
ExportVar(package_name.c_str(), "killed_h", killed->GetHeading());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
+32
-2
@@ -34,11 +34,14 @@
|
|||||||
#include "questmgr.h"
|
#include "questmgr.h"
|
||||||
#include "zone.h"
|
#include "zone.h"
|
||||||
#include "data_bucket.h"
|
#include "data_bucket.h"
|
||||||
|
#include "../common/events/player_event_logs.h"
|
||||||
|
#include "worldserver.h"
|
||||||
|
|
||||||
#include <cctype>
|
#include <cctype>
|
||||||
|
|
||||||
extern Zone *zone;
|
extern Zone *zone;
|
||||||
extern QueryServ *QServ;
|
extern QueryServ *QServ;
|
||||||
|
extern WorldServer worldserver;
|
||||||
|
|
||||||
#ifdef EMBPERL_XS_CLASSES
|
#ifdef EMBPERL_XS_CLASSES
|
||||||
|
|
||||||
@@ -1273,7 +1276,7 @@ int Perl__tasktimeleft(int task_id)
|
|||||||
return quest_manager.tasktimeleft(task_id);
|
return quest_manager.tasktimeleft(task_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Perl__istaskcompleted(int task_id)
|
bool Perl__istaskcompleted(int task_id)
|
||||||
{
|
{
|
||||||
return quest_manager.istaskcompleted(task_id);
|
return quest_manager.istaskcompleted(task_id);
|
||||||
}
|
}
|
||||||
@@ -5946,7 +5949,33 @@ bool Perl__send_parcel(perl::reference table_ref)
|
|||||||
e.note = note;
|
e.note = note;
|
||||||
e.sent_date = std::time(nullptr);
|
e.sent_date = std::time(nullptr);
|
||||||
|
|
||||||
return CharacterParcelsRepository::InsertOne(database, e).id;
|
auto out = CharacterParcelsRepository::InsertOne(database, e).id;
|
||||||
|
if (out) {
|
||||||
|
Parcel_Struct ps{};
|
||||||
|
ps.item_slot = e.slot_id;
|
||||||
|
strn0cpy(ps.send_to, name.c_str(), sizeof(ps.send_to));
|
||||||
|
|
||||||
|
std::unique_ptr<ServerPacket> server_packet(new ServerPacket(ServerOP_ParcelDelivery, sizeof(Parcel_Struct)));
|
||||||
|
auto data = (Parcel_Struct *) server_packet->pBuffer;
|
||||||
|
|
||||||
|
data->item_slot = ps.item_slot;
|
||||||
|
strn0cpy(data->send_to, ps.send_to, sizeof(data->send_to));
|
||||||
|
|
||||||
|
worldserver.SendPacket(server_packet.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Perl__aretaskscompleted(perl::array task_ids)
|
||||||
|
{
|
||||||
|
std::vector<int> v;
|
||||||
|
|
||||||
|
for (const auto& e : task_ids) {
|
||||||
|
v.emplace_back(static_cast<int>(e));
|
||||||
|
}
|
||||||
|
|
||||||
|
return quest_manager.aretaskscompleted(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
void perl_register_quest()
|
void perl_register_quest()
|
||||||
@@ -6272,6 +6301,7 @@ void perl_register_quest()
|
|||||||
package.add("addloot", (void(*)(int, int))&Perl__addloot);
|
package.add("addloot", (void(*)(int, int))&Perl__addloot);
|
||||||
package.add("addloot", (void(*)(int, int, bool))&Perl__addloot);
|
package.add("addloot", (void(*)(int, int, bool))&Perl__addloot);
|
||||||
package.add("addskill", &Perl__addskill);
|
package.add("addskill", &Perl__addskill);
|
||||||
|
package.add("aretaskscompleted", &Perl__aretaskscompleted);
|
||||||
package.add("assigntask", (void(*)(int))&Perl__assigntask);
|
package.add("assigntask", (void(*)(int))&Perl__assigntask);
|
||||||
package.add("assigntask", (void(*)(int, bool))&Perl__assigntask);
|
package.add("assigntask", (void(*)(int, bool))&Perl__assigntask);
|
||||||
package.add("attack", &Perl__attack);
|
package.add("attack", &Perl__attack);
|
||||||
|
|||||||
@@ -1655,7 +1655,10 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap
|
|||||||
Send = clear_target_window;
|
Send = clear_target_window;
|
||||||
if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) {
|
if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) {
|
||||||
if (c->GetGM()) {
|
if (c->GetGM()) {
|
||||||
|
if (!c->EntityVariableExists(SEE_BUFFS_FLAG)) {
|
||||||
c->Message(Chat::White, "Your GM flag allows you to always see your targets' buffs.");
|
c->Message(Chat::White, "Your GM flag allows you to always see your targets' buffs.");
|
||||||
|
c->SetEntityVariable(SEE_BUFFS_FLAG, "1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Send = !clear_target_window;
|
Send = !clear_target_window;
|
||||||
@@ -4480,6 +4483,36 @@ void EntityList::QuestJournalledSayClose(
|
|||||||
delete outapp;
|
delete outapp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z,
|
||||||
|
float trg_x, float trg_y, float trg_z, float perwalk)
|
||||||
|
{
|
||||||
|
if (zone->zonemap == nullptr) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::vec3 myloc;
|
||||||
|
glm::vec3 oloc;
|
||||||
|
glm::vec3 hit;
|
||||||
|
|
||||||
|
myloc.x = cur_x;
|
||||||
|
myloc.y = cur_y;
|
||||||
|
myloc.z = cur_z+5;
|
||||||
|
|
||||||
|
oloc.x = trg_x;
|
||||||
|
oloc.y = trg_y;
|
||||||
|
oloc.z = trg_z+5;
|
||||||
|
|
||||||
|
if (myloc.x == oloc.x && myloc.y == oloc.y && myloc.z == oloc.z) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!zone->zonemap->LineIntersectsZoneNoZLeaps(myloc,oloc,perwalk,&hit)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
Corpse *EntityList::GetClosestCorpse(Mob *sender, const char *Name)
|
Corpse *EntityList::GetClosestCorpse(Mob *sender, const char *Name)
|
||||||
{
|
{
|
||||||
if (!sender)
|
if (!sender)
|
||||||
|
|||||||
+9
-4
@@ -60,6 +60,8 @@ class Bot;
|
|||||||
|
|
||||||
extern EntityList entity_list;
|
extern EntityList entity_list;
|
||||||
|
|
||||||
|
constexpr const char* SEE_BUFFS_FLAG = "see_buffs_flag";
|
||||||
|
|
||||||
class Entity
|
class Entity
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -114,6 +116,7 @@ public:
|
|||||||
inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; }
|
inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; }
|
||||||
|
|
||||||
virtual const char* GetName() { return ""; }
|
virtual const char* GetName() { return ""; }
|
||||||
|
bool CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z, float trg_x, float trg_y, float trg_z, float perwalk=1);
|
||||||
|
|
||||||
Bot* CastToBot();
|
Bot* CastToBot();
|
||||||
const Bot* CastToBot() const;
|
const Bot* CastToBot() const;
|
||||||
@@ -435,19 +438,20 @@ public:
|
|||||||
void AEAttack(
|
void AEAttack(
|
||||||
Mob* attacker,
|
Mob* attacker,
|
||||||
float distance,
|
float distance,
|
||||||
int Hand = EQ::invslot::slotPrimary,
|
int16 slot_id = EQ::invslot::slotPrimary,
|
||||||
int count = 0,
|
int hit_count = 0,
|
||||||
bool is_from_spell = false,
|
bool is_from_spell = false,
|
||||||
int attack_rounds = 1
|
int attack_rounds = 1
|
||||||
);
|
);
|
||||||
void AETaunt(Client *caster, float range = 0, int32 bonus_hate = 0);
|
void AETaunt(Client* caster, float range = 0, int bonus_hate = 0);
|
||||||
void AESpell(
|
void AESpell(
|
||||||
Mob* caster,
|
Mob* caster,
|
||||||
Mob* center,
|
Mob* center,
|
||||||
uint16 spell_id,
|
uint16 spell_id,
|
||||||
bool affect_caster = true,
|
bool affect_caster = true,
|
||||||
int16 resist_adjust = 0,
|
int16 resist_adjust = 0,
|
||||||
int *max_targets = nullptr
|
int* max_targets = nullptr,
|
||||||
|
bool is_scripted = false
|
||||||
);
|
);
|
||||||
void MassGroupBuff(Mob* caster, Mob* center, uint16 spell_id, bool affect_caster = true);
|
void MassGroupBuff(Mob* caster, Mob* center, uint16 spell_id, bool affect_caster = true);
|
||||||
|
|
||||||
@@ -501,6 +505,7 @@ public:
|
|||||||
Mob* GetTargetForMez(Mob* caster);
|
Mob* GetTargetForMez(Mob* caster);
|
||||||
uint32 CheckNPCsClose(Mob *center);
|
uint32 CheckNPCsClose(Mob *center);
|
||||||
|
|
||||||
|
int FleeAllyCount(Mob* attacker, Mob* skipped);
|
||||||
Corpse* GetClosestCorpse(Mob* sender, const char *Name);
|
Corpse* GetClosestCorpse(Mob* sender, const char *Name);
|
||||||
void TryWakeTheDead(Mob* sender, Mob* target, int32 spell_id, uint32 max_distance, uint32 duration, uint32 amount_pets);
|
void TryWakeTheDead(Mob* sender, Mob* target, int32 spell_id, uint32 max_distance, uint32 duration, uint32 amount_pets);
|
||||||
NPC* GetClosestBanker(Mob* sender, uint32 &distance);
|
NPC* GetClosestBanker(Mob* sender, uint32 &distance);
|
||||||
|
|||||||
+56
-48
@@ -527,7 +527,7 @@ void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, boo
|
|||||||
// Are we also doing linear AA acceleration?
|
// Are we also doing linear AA acceleration?
|
||||||
if (RuleB(AA, ModernAAScalingEnabled) && aaexp > 0)
|
if (RuleB(AA, ModernAAScalingEnabled) && aaexp > 0)
|
||||||
{
|
{
|
||||||
aaexp = ScaleAAXPBasedOnCurrentAATotal(GetAAPoints(), aaexp);
|
aaexp = ScaleAAXPBasedOnCurrentAATotal(GetSpentAA() + GetAAPoints(), aaexp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for AA XP Cap
|
// Check for AA XP Cap
|
||||||
@@ -607,10 +607,10 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
|||||||
Message(Chat::Red, "Error in Client::SetEXP. EXP not set.");
|
Message(Chat::Red, "Error in Client::SetEXP. EXP not set.");
|
||||||
return; // Must be invalid class/race
|
return; // Must be invalid class/race
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32 i = 0;
|
uint32 i = 0;
|
||||||
uint32 membercount = 0;
|
uint32 membercount = 0;
|
||||||
if(GetGroup())
|
if(GetGroup()) {
|
||||||
{
|
|
||||||
for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
|
for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
|
||||||
if (GetGroup()->members[i] != nullptr) {
|
if (GetGroup()->members[i] != nullptr) {
|
||||||
membercount++;
|
membercount++;
|
||||||
@@ -627,25 +627,29 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
|||||||
if (RuleI(Character, ShowExpValues) >= 1) {
|
if (RuleI(Character, ShowExpValues) >= 1) {
|
||||||
if (exp_gained > 0 && aa_exp_gained > 0) {
|
if (exp_gained > 0 && aa_exp_gained > 0) {
|
||||||
exp_amount_message = fmt::format("({}) ({})", exp_gained, aa_exp_gained);
|
exp_amount_message = fmt::format("({}) ({})", exp_gained, aa_exp_gained);
|
||||||
}
|
} else if (exp_gained > 0) {
|
||||||
else if (exp_gained > 0) {
|
|
||||||
exp_amount_message = fmt::format("({})", exp_gained);
|
exp_amount_message = fmt::format("({})", exp_gained);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
exp_amount_message = fmt::format("({}) AA", aa_exp_gained);
|
exp_amount_message = fmt::format("({}) AA", aa_exp_gained);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string exp_percent_message = "";
|
std::string exp_percent_message = "";
|
||||||
if (RuleI(Character, ShowExpValues) >= 2) {
|
if (RuleI(Character, ShowExpValues) >= 2) {
|
||||||
if (exp_gained > 0 && aa_exp_gained > 0) exp_percent_message = StringFormat("(%.3f%%, %.3f%%AA)", exp_percent, aa_exp_percent);
|
if (exp_gained > 0 && aa_exp_gained > 0) {
|
||||||
else if (exp_gained > 0) exp_percent_message = StringFormat("(%.3f%%)", exp_percent);
|
exp_percent_message = StringFormat("(%.3f%%, %.3f%%AA)", exp_percent, aa_exp_percent);
|
||||||
else exp_percent_message = StringFormat("(%.3f%%AA)", aa_exp_percent);
|
} else if (exp_gained > 0) {
|
||||||
|
exp_percent_message = StringFormat("(%.3f%%)", exp_percent);
|
||||||
|
} else {
|
||||||
|
exp_percent_message = StringFormat("(%.3f%%AA)", aa_exp_percent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (isrezzexp) {
|
if (isrezzexp) {
|
||||||
if (RuleI(Character, ShowExpValues) > 0)
|
if (RuleI(Character, ShowExpValues) > 0) {
|
||||||
Message(Chat::Experience, "You regain %s experience from resurrection. %s", exp_amount_message.c_str(), exp_percent_message.c_str());
|
Message(Chat::Experience, "You regain %s experience from resurrection. %s", exp_amount_message.c_str(), exp_percent_message.c_str());
|
||||||
else MessageString(Chat::Experience, REZ_REGAIN);
|
} else {
|
||||||
|
MessageString(Chat::Experience, REZ_REGAIN);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (membercount > 1) {
|
if (membercount > 1) {
|
||||||
if (RuleI(Character, ShowExpValues) > 0) {
|
if (RuleI(Character, ShowExpValues) > 0) {
|
||||||
@@ -673,14 +677,17 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else if(total_add_exp < total_current_exp) { //only loss message if you lose exp, no message if you gained/lost nothing.
|
||||||
else if(total_add_exp < total_current_exp){ //only loss message if you lose exp, no message if you gained/lost nothing.
|
|
||||||
uint64 exp_lost = current_exp - set_exp;
|
uint64 exp_lost = current_exp - set_exp;
|
||||||
float exp_percent = (float)((float)exp_lost / (float)(GetEXPForLevel(GetLevel() + 1) - GetEXPForLevel(GetLevel())))*(float)100;
|
float exp_percent = (float)((float)exp_lost / (float)(GetEXPForLevel(GetLevel() + 1) - GetEXPForLevel(GetLevel())))*(float)100;
|
||||||
|
|
||||||
if (RuleI(Character, ShowExpValues) == 1 && exp_lost > 0) Message(Chat::Yellow, "You have lost %i experience.", exp_lost);
|
if (RuleI(Character, ShowExpValues) == 1 && exp_lost > 0) {
|
||||||
else if (RuleI(Character, ShowExpValues) == 2 && exp_lost > 0) Message(Chat::Yellow, "You have lost %i experience. (%.3f%%)", exp_lost, exp_percent);
|
Message(Chat::Yellow, "You have lost %i experience.", exp_lost);
|
||||||
else Message(Chat::Yellow, "You have lost experience.");
|
} else if (RuleI(Character, ShowExpValues) == 2 && exp_lost > 0) {
|
||||||
|
Message(Chat::Yellow, "You have lost %i experience. (%.3f%%)", exp_lost, exp_percent);
|
||||||
|
} else {
|
||||||
|
Message(Chat::Yellow, "You have lost experience.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//check_level represents the level we should be when we have
|
//check_level represents the level we should be when we have
|
||||||
@@ -698,9 +705,10 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
|||||||
}
|
}
|
||||||
level_count++;
|
level_count++;
|
||||||
|
|
||||||
if(GetMercenaryID())
|
if (GetMercenaryID()) {
|
||||||
UpdateMercLevel();
|
UpdateMercLevel();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
//see if we lost any levels
|
//see if we lost any levels
|
||||||
while (set_exp < GetEXPForLevel(check_level-1)) {
|
while (set_exp < GetEXPForLevel(check_level-1)) {
|
||||||
check_level--;
|
check_level--;
|
||||||
@@ -709,9 +717,10 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
level_increase = false;
|
level_increase = false;
|
||||||
if(GetMercenaryID())
|
if (GetMercenaryID()) {
|
||||||
UpdateMercLevel();
|
UpdateMercLevel();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
check_level--;
|
check_level--;
|
||||||
|
|
||||||
|
|
||||||
@@ -747,12 +756,14 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
|||||||
//Message(Chat::Yellow, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA);
|
//Message(Chat::Yellow, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA);
|
||||||
char val1[20] = { 0 };
|
char val1[20] = { 0 };
|
||||||
char val2[20] = { 0 };
|
char val2[20] = { 0 };
|
||||||
if (gained == 1 && m_pp.aapoints == 1)
|
|
||||||
|
if (gained == 1 && m_pp.aapoints == 1) {
|
||||||
MessageString(Chat::Experience, GAIN_SINGLE_AA_SINGLE_AA, ConvertArray(m_pp.aapoints, val1)); //You have gained an ability point! You now have %1 ability point.
|
MessageString(Chat::Experience, GAIN_SINGLE_AA_SINGLE_AA, ConvertArray(m_pp.aapoints, val1)); //You have gained an ability point! You now have %1 ability point.
|
||||||
else if (gained == 1 && m_pp.aapoints > 1)
|
} else if (gained == 1 && m_pp.aapoints > 1) {
|
||||||
MessageString(Chat::Experience, GAIN_SINGLE_AA_MULTI_AA, ConvertArray(m_pp.aapoints, val1)); //You have gained an ability point! You now have %1 ability points.
|
MessageString(Chat::Experience, GAIN_SINGLE_AA_MULTI_AA, ConvertArray(m_pp.aapoints, val1)); //You have gained an ability point! You now have %1 ability points.
|
||||||
else
|
} else {
|
||||||
MessageString(Chat::Experience, GAIN_MULTI_AA_MULTI_AA, ConvertArray(gained, val1), ConvertArray(m_pp.aapoints, val2)); //You have gained %1 ability point(s)! You now have %2 ability point(s).
|
MessageString(Chat::Experience, GAIN_MULTI_AA_MULTI_AA, ConvertArray(gained, val1), ConvertArray(m_pp.aapoints, val2)); //You have gained %1 ability point(s)! You now have %2 ability point(s).
|
||||||
|
}
|
||||||
|
|
||||||
if (RuleB(AA, SoundForAAEarned)) {
|
if (RuleB(AA, SoundForAAEarned)) {
|
||||||
SendSound();
|
SendSound();
|
||||||
@@ -774,48 +785,45 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
|||||||
//Message(Chat::Yellow, "You now have %d skill points available to spend.", m_pp.aapoints);
|
//Message(Chat::Yellow, "You now have %d skill points available to spend.", m_pp.aapoints);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 maxlevel = RuleI(Character, MaxExpLevel) + 1;
|
uint8 max_level = RuleI(Character, MaxExpLevel) + 1;
|
||||||
|
|
||||||
if(maxlevel <= 1)
|
if (max_level <= 1) {
|
||||||
maxlevel = RuleI(Character, MaxLevel) + 1;
|
max_level = RuleI(Character, MaxLevel) + 1;
|
||||||
|
|
||||||
if(check_level > maxlevel) {
|
|
||||||
check_level = maxlevel;
|
|
||||||
|
|
||||||
if(RuleB(Character, KeepLevelOverMax)) {
|
|
||||||
set_exp = GetEXPForLevel(GetLevel()+1);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
set_exp = GetEXPForLevel(maxlevel);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto client_max_level = GetClientMaxLevel();
|
auto client_max_level = GetClientMaxLevel();
|
||||||
if (client_max_level) {
|
if (client_max_level) {
|
||||||
if (GetLevel() >= client_max_level) {
|
max_level = client_max_level + 1;
|
||||||
auto exp_needed = GetEXPForLevel(client_max_level);
|
|
||||||
if (set_exp > exp_needed) {
|
|
||||||
set_exp = exp_needed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (check_level > max_level) {
|
||||||
|
check_level = max_level;
|
||||||
|
|
||||||
|
if (RuleB(Character, KeepLevelOverMax)) {
|
||||||
|
set_exp = GetEXPForLevel(GetLevel()+1);
|
||||||
|
} else {
|
||||||
|
set_exp = GetEXPForLevel(max_level);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((GetLevel() != check_level) && !(check_level >= maxlevel)) {
|
if ((GetLevel() != check_level) && !(check_level >= max_level)) {
|
||||||
char val1[20]={0};
|
char val1[20]={0};
|
||||||
if (level_increase)
|
if (level_increase) {
|
||||||
{
|
if (level_count == 1) {
|
||||||
if (level_count == 1)
|
|
||||||
MessageString(Chat::Experience, GAIN_LEVEL, ConvertArray(check_level, val1));
|
MessageString(Chat::Experience, GAIN_LEVEL, ConvertArray(check_level, val1));
|
||||||
else
|
} else {
|
||||||
Message(Chat::Yellow, "Welcome to level %i!", check_level);
|
Message(Chat::Yellow, "Welcome to level %i!", check_level);
|
||||||
|
}
|
||||||
|
|
||||||
if (check_level == RuleI(Character, DeathItemLossLevel) &&
|
if (check_level == RuleI(Character, DeathItemLossLevel) &&
|
||||||
m_ClientVersionBit & EQ::versions::maskUFAndEarlier)
|
m_ClientVersionBit & EQ::versions::maskUFAndEarlier) {
|
||||||
MessageString(Chat::Yellow, CORPSE_ITEM_LOST);
|
MessageString(Chat::Yellow, CORPSE_ITEM_LOST);
|
||||||
|
}
|
||||||
|
|
||||||
if (check_level == RuleI(Character, DeathExpLossLevel))
|
if (check_level == RuleI(Character, DeathExpLossLevel)) {
|
||||||
MessageString(Chat::Yellow, CORPSE_EXP_LOST);
|
MessageString(Chat::Yellow, CORPSE_EXP_LOST);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
uint8 myoldlevel = GetLevel();
|
uint8 myoldlevel = GetLevel();
|
||||||
|
|
||||||
@@ -828,8 +836,8 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
|||||||
}
|
}
|
||||||
|
|
||||||
//If were at max level then stop gaining experience if we make it to the cap
|
//If were at max level then stop gaining experience if we make it to the cap
|
||||||
if(GetLevel() == maxlevel - 1){
|
if (GetLevel() == max_level - 1){
|
||||||
uint32 expneeded = GetEXPForLevel(maxlevel);
|
uint32 expneeded = GetEXPForLevel(max_level);
|
||||||
if (set_exp > expneeded) {
|
if (set_exp > expneeded) {
|
||||||
set_exp = expneeded;
|
set_exp = expneeded;
|
||||||
}
|
}
|
||||||
|
|||||||
+281
-57
@@ -19,6 +19,7 @@
|
|||||||
#include "../common/rulesys.h"
|
#include "../common/rulesys.h"
|
||||||
|
|
||||||
#include "map.h"
|
#include "map.h"
|
||||||
|
#include "water_map.h"
|
||||||
#include "zone.h"
|
#include "zone.h"
|
||||||
|
|
||||||
#ifdef _WINDOWS
|
#ifdef _WINDOWS
|
||||||
@@ -29,17 +30,52 @@ extern Zone* zone;
|
|||||||
|
|
||||||
#define FEAR_PATHING_DEBUG
|
#define FEAR_PATHING_DEBUG
|
||||||
|
|
||||||
|
int Mob::GetFleeRatio(Mob* other)
|
||||||
|
{
|
||||||
|
int flee_ratio = GetSpecialAbility(SpecialAbility::FleePercent); // if a special SpecialAbility::FleePercent exists
|
||||||
|
Mob *hate_top = GetHateTop();
|
||||||
|
|
||||||
|
if (other != nullptr) {
|
||||||
|
hate_top = other;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hate_top) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If no special flee_percent check for Gray or Other con rates
|
||||||
|
if (flee_ratio == 0) {
|
||||||
|
flee_ratio = RuleI(Combat, FleeHPRatio);
|
||||||
|
if (GetLevelCon(hate_top->GetLevel(), GetLevel()) == ConsiderColor::Gray && RuleB(Combat, FleeGray) &&
|
||||||
|
GetLevel() <= RuleI(Combat, FleeGrayMaxLevel)) {
|
||||||
|
flee_ratio = RuleI(Combat, FleeGrayHPRatio);
|
||||||
|
LogFlee("Mob [{}] using combat flee gray flee_ratio [{}]", GetCleanName(), flee_ratio);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return flee_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
//this is called whenever we are damaged to process possible fleeing
|
//this is called whenever we are damaged to process possible fleeing
|
||||||
void Mob::CheckFlee()
|
void Mob::CheckFlee()
|
||||||
{
|
{
|
||||||
|
if (IsPet() || IsCasting() || GetHP() == 0 || GetBodyType() == BodyType::Undead || (IsNPC() && CastToNPC()->IsUnderwaterOnly())) {
|
||||||
// if mob is dead why would you run?
|
|
||||||
if (GetHP() == 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if were already fleeing, don't need to check more...
|
//if were already fleeing, we only need to check speed. Speed changes will trigger pathing updates.
|
||||||
if (flee_mode && currently_fleeing) {
|
if (flee_mode && currently_fleeing) {
|
||||||
|
int flee_speed = GetFearSpeed();
|
||||||
|
if (flee_speed < 1) {
|
||||||
|
flee_speed = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetRunAnimSpeed(flee_speed);
|
||||||
|
|
||||||
|
if (IsMoving() && flee_speed < 1) {
|
||||||
|
StopNavigation();
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,38 +85,17 @@ void Mob::CheckFlee()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Undead do not flee
|
|
||||||
if (GetBodyType() == BodyType::Undead) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if Flee Timer is cleared
|
|
||||||
if (!flee_timer.Check()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hp_ratio = GetIntHPRatio();
|
int hp_ratio = GetIntHPRatio();
|
||||||
int flee_ratio = GetSpecialAbility(SpecialAbility::FleePercent); // if a special SpecialAbility::FleePercent exists
|
int flee_ratio = GetFleeRatio();
|
||||||
Mob *hate_top = GetHateTop();
|
Mob *hate_top = GetHateTop();
|
||||||
|
|
||||||
LogFlee("Mob [{}] hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio);
|
|
||||||
|
|
||||||
// Sanity Check for race conditions
|
// Sanity Check for race conditions
|
||||||
if (hate_top == nullptr) {
|
if(!hate_top) {
|
||||||
|
//this should never happen...
|
||||||
|
StartFleeing();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If no special flee_percent check for Gray or Other con rates
|
|
||||||
if (GetLevelCon(hate_top->GetLevel(), GetLevel()) == ConsiderColor::Gray && flee_ratio == 0 && RuleB(Combat, FleeGray) &&
|
|
||||||
GetLevel() <= RuleI(Combat, FleeGrayMaxLevel)) {
|
|
||||||
flee_ratio = RuleI(Combat, FleeGrayHPRatio);
|
|
||||||
LogFlee("Mob [{}] using combat flee gray hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio);
|
|
||||||
}
|
|
||||||
else if (flee_ratio == 0) {
|
|
||||||
flee_ratio = RuleI(Combat, FleeHPRatio);
|
|
||||||
LogFlee("Mob [{}] using combat flee hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool mob_has_low_enough_health_to_flee = hp_ratio >= flee_ratio;
|
bool mob_has_low_enough_health_to_flee = hp_ratio >= flee_ratio;
|
||||||
if (mob_has_low_enough_health_to_flee) {
|
if (mob_has_low_enough_health_to_flee) {
|
||||||
LogFlee(
|
LogFlee(
|
||||||
@@ -141,7 +156,7 @@ void Mob::CheckFlee()
|
|||||||
// if FleeIfNotAlone is true, we skip alone check
|
// if FleeIfNotAlone is true, we skip alone check
|
||||||
// roll chance
|
// roll chance
|
||||||
if (GetSpecialAbility(SpecialAbility::AlwaysFlee) ||
|
if (GetSpecialAbility(SpecialAbility::AlwaysFlee) ||
|
||||||
((RuleB(Combat, FleeIfNotAlone) || entity_list.GetHatedCount(hate_top, this, true) == 0) &&
|
((RuleB(Combat, FleeIfNotAlone) || entity_list.FleeAllyCount(hate_top, this) == 0) &&
|
||||||
zone->random.Roll(flee_chance))) {
|
zone->random.Roll(flee_chance))) {
|
||||||
|
|
||||||
LogFlee(
|
LogFlee(
|
||||||
@@ -158,9 +173,63 @@ void Mob::CheckFlee()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mob::StopFleeing()
|
||||||
|
{
|
||||||
|
if (!flee_mode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
flee_mode = false;
|
||||||
|
|
||||||
|
//see if we are legitimately feared or blind now
|
||||||
|
if (!spellbonuses.IsFeared && !IsBlind()) {
|
||||||
|
currently_fleeing = false;
|
||||||
|
StopNavigation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mob::FleeInfo(Mob* client)
|
||||||
|
{
|
||||||
|
float other_ratio = client->GetHPRatio();
|
||||||
|
bool wontflee = false;
|
||||||
|
std::string reason;
|
||||||
|
std::string flee;
|
||||||
|
|
||||||
|
int allycount = entity_list.FleeAllyCount(client, this);
|
||||||
|
|
||||||
|
if (flee_mode && currently_fleeing) {
|
||||||
|
wontflee = true;
|
||||||
|
reason = "NPC is already fleeing!";
|
||||||
|
} else if (GetSpecialAbility(SpecialAbility::FleeingImmunity)) {
|
||||||
|
wontflee = true;
|
||||||
|
reason = "NPC is immune to fleeing.";
|
||||||
|
} else if (other_ratio < 20) {
|
||||||
|
wontflee = true;
|
||||||
|
reason = "Player has low health.";
|
||||||
|
} else if (GetSpecialAbility(SpecialAbility::AlwaysFlee)) {
|
||||||
|
flee = "NPC has ALWAYS_FLEE set.";
|
||||||
|
} else if (RuleB(Combat, FleeIfNotAlone) || (!RuleB(Combat, FleeIfNotAlone) && allycount == 0)) {
|
||||||
|
flee = "NPC has no allies nearby or the rule to flee when not alone is enabled.";
|
||||||
|
} else {
|
||||||
|
wontflee = true;
|
||||||
|
reason = "NPC likely has allies nearby.";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!wontflee) {
|
||||||
|
client->Message(Chat::Green, "%s will flee at %d percent because %s", GetName(), GetFleeRatio(client), flee.c_str());
|
||||||
|
} else {
|
||||||
|
client->Message(Chat::Red, "%s will not flee because %s", GetName(), reason.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
client->Message(Chat::Default, "NPC ally count %d", allycount);
|
||||||
|
}
|
||||||
|
|
||||||
void Mob::ProcessFlee()
|
void Mob::ProcessFlee()
|
||||||
{
|
{
|
||||||
|
if (!flee_mode) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
//Stop fleeing if effect is applied after they start to run.
|
//Stop fleeing if effect is applied after they start to run.
|
||||||
//When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee.
|
//When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee.
|
||||||
@@ -170,46 +239,201 @@ void Mob::ProcessFlee()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int hpratio = GetIntHPRatio();
|
|
||||||
int fleeratio = GetSpecialAbility(SpecialAbility::FleePercent); // if a special SpecialAbility::FleePercent exists
|
|
||||||
Mob *hate_top = GetHateTop();
|
Mob *hate_top = GetHateTop();
|
||||||
|
bool dying = GetIntHPRatio() < GetFleeRatio();
|
||||||
|
|
||||||
// If no special flee_percent check for Gray or Other con rates
|
// We have stopped fleeing for an unknown reason (couldn't find a node is possible) restart.
|
||||||
if(hate_top != nullptr && GetLevelCon(hate_top->GetLevel(), GetLevel()) == ConsiderColor::Gray && fleeratio == 0 && RuleB(Combat, FleeGray)) {
|
if (flee_mode && !currently_fleeing) {
|
||||||
fleeratio = RuleI(Combat, FleeGrayHPRatio);
|
if(dying) {
|
||||||
} else if(fleeratio == 0) {
|
StartFleeing();
|
||||||
fleeratio = RuleI(Combat, FleeHPRatio );
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mob is still too low. Keep Running
|
//see if we are still dying, if so, do nothing
|
||||||
if(hpratio < fleeratio) {
|
if (dying) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
//we are not dying anymore... see what we do next
|
//we are not dying anymore, check to make sure we're not blind or feared and cancel flee.
|
||||||
|
StopFleeing();
|
||||||
flee_mode = false;
|
|
||||||
|
|
||||||
//see if we are legitimately feared or blind now
|
|
||||||
if (!spellbonuses.IsFeared && !spellbonuses.IsBlind) {
|
|
||||||
//not feared or blind... were done...
|
|
||||||
currently_fleeing = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mob::CalculateNewFearpoint()
|
void Mob::CalculateNewFearpoint()
|
||||||
{
|
{
|
||||||
if (RuleB(Pathing, Fear) && zone->pathing) {
|
// blind waypoint logic isn't the same as fear's. Has a chance to run toward the player
|
||||||
auto Node = zone->pathing->GetRandomLocation(glm::vec3(GetX(), GetY(), GetZ()));
|
// chance is very high if the player is moving, otherwise it's low
|
||||||
if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) {
|
if (IsBlind() && !IsFeared() && GetTarget()) {
|
||||||
m_FearWalkTarget = Node;
|
int roll = 20;
|
||||||
|
if (GetTarget()->GetCurrentSpeed() > 0.1f || (GetTarget()->IsClient() && GetTarget()->animation != 0)) {
|
||||||
|
roll = 80;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zone->random.Roll(roll)) {
|
||||||
|
m_FearWalkTarget = glm::vec3(GetTarget()->GetPosition());
|
||||||
currently_fleeing = true;
|
currently_fleeing = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (RuleB(Pathing, Fear) && zone->pathing) {
|
||||||
|
glm::vec3 Node;
|
||||||
|
int flags = PathingNotDisabled ^ PathingZoneLine;
|
||||||
|
|
||||||
|
if (IsNPC() && CastToNPC()->IsUnderwaterOnly() && !zone->IsWaterZone(GetZOffset())) {
|
||||||
|
Node = glm::vec3(0.0f);
|
||||||
|
} else {
|
||||||
|
Node = zone->pathing->GetRandomLocation(glm::vec3(GetX(), GetY(), GetZOffset()), flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) {
|
||||||
|
Node.z = GetFixedZ(Node);
|
||||||
|
PathfinderOptions opts;
|
||||||
|
opts.smooth_path = true;
|
||||||
|
opts.step_size = RuleR(Pathing, NavmeshStepSize);
|
||||||
|
opts.offset = GetZOffset();
|
||||||
|
opts.flags = flags;
|
||||||
|
auto partial = false;
|
||||||
|
auto stuck = false;
|
||||||
|
auto route = zone->pathing->FindPath(
|
||||||
|
glm::vec3(GetX(), GetY(), GetZOffset()),
|
||||||
|
glm::vec3(Node.x, Node.y, Node.z),
|
||||||
|
partial,
|
||||||
|
stuck,
|
||||||
|
opts
|
||||||
|
);
|
||||||
|
glm::vec3 last_good_loc = Node;
|
||||||
|
int route_size = route.size();
|
||||||
|
int route_count = 0;
|
||||||
|
bool have_los = true;
|
||||||
|
|
||||||
|
if (route_size == 2) {
|
||||||
|
// FindPath() often fails to compute a route in some places, so to prevent running through walls we need to check LOS on all 2 node routes
|
||||||
|
// size 2 route usually means FindPath() bugged out. sometimes it returns locs outside the geometry
|
||||||
|
if (CheckLosFN(Node.x, Node.y, Node.z, 6.0)) {
|
||||||
|
LogPathingDetail("Direct route to fearpoint [{}], [{}], [{}] calculated for [{}]", last_good_loc.x, last_good_loc.y, last_good_loc.z, GetName());
|
||||||
|
m_FearWalkTarget = last_good_loc;
|
||||||
|
currently_fleeing = true;
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
LogPathingDetail("FindRoute() returned single hop route to destination without LOS: [{}], [{}], [{}] for [{}]", last_good_loc.x, last_good_loc.y, last_good_loc.z, GetName());
|
||||||
|
}
|
||||||
|
// use fallback logic if LOS fails
|
||||||
|
} else if (!stuck) {
|
||||||
|
// check route for LOS failures to prevent mobs ending up outside of playable area
|
||||||
|
// only checking the last few hops because LOS will often fail in a valid route which can result in mobs getting undesirably trapped
|
||||||
|
auto iter = route.begin();
|
||||||
|
glm::vec3 previous_pos(GetX(), GetY(), GetZOffset());
|
||||||
|
while (iter != route.end() && have_los == true) {
|
||||||
|
auto ¤t_node = (*iter);
|
||||||
|
iter++;
|
||||||
|
route_count++;
|
||||||
|
|
||||||
|
if (iter == route.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
previous_pos = current_node.pos;
|
||||||
|
auto &next_node = (*iter);
|
||||||
|
|
||||||
|
if (next_node.teleport) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((route_size - route_count) < 5 && !zone->zonemap->CheckLoS(previous_pos, next_node.pos)) {
|
||||||
|
have_los = false;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
last_good_loc = next_node.pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (have_los || route_count > 2) {
|
||||||
|
if (have_los) {
|
||||||
|
LogPathingDetail("Route to fearpoint [{}], [{}], [{}] calculated for [{}]; route size: [{}]", last_good_loc.x, last_good_loc.y, last_good_loc.z, GetName(), route_size);
|
||||||
|
} else {
|
||||||
|
LogPathingDetail("Using truncated route to fearpoint [{}], [{}], [{}] for [{}]; node count: [{}]; route size [{}]", last_good_loc.x, last_good_loc.y, last_good_loc.z, GetName(), route_count, route_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_FearWalkTarget = last_good_loc;
|
||||||
|
currently_fleeing = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fallback logic if pathing system can't be used
|
||||||
|
bool inliquid = zone->HasWaterMap() && zone->watermap->InLiquid(glm::vec3(GetPosition())) || zone->IsWaterZone(GetZ());
|
||||||
|
bool stay_inliquid = (inliquid && IsNPC() && CastToNPC()->IsUnderwaterOnly());
|
||||||
|
bool levitating = IsClient() && (FindType(SE_Levitate) || flymode != GravityBehavior::Ground);
|
||||||
|
bool open_outdoor_zone = !zone->CanCastOutdoor() && !zone->IsCity();
|
||||||
|
|
||||||
|
int loop = 0;
|
||||||
|
float ranx, rany, ranz;
|
||||||
|
currently_fleeing = false;
|
||||||
|
glm::vec3 myloc(GetX(), GetY(), GetZ());
|
||||||
|
glm::vec3 myceil = myloc;
|
||||||
|
float ceil = zone->zonemap->FindCeiling(myloc, &myceil);
|
||||||
|
|
||||||
|
if (ceil != BEST_Z_INVALID) {
|
||||||
|
ceil -= 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (loop < 100) { //Max 100 tries
|
||||||
|
int ran = 250 - (loop * 2);
|
||||||
|
loop++;
|
||||||
|
|
||||||
|
if (open_outdoor_zone && loop < 20) { // try a distant loc first; other way will likely pick a close loc
|
||||||
|
ranx = zone->random.Int(0, ran);
|
||||||
|
rany = zone->random.Int(0, ran);
|
||||||
|
if (ranx + rany < 200) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ranx = GetX() + (zone->random.Int(0, 1) == 1 ? ranx : -ranx);
|
||||||
|
rany = GetY() + (zone->random.Int(0, 1) == 1 ? rany : -rany);
|
||||||
|
} else {
|
||||||
|
ranx = GetX() + zone->random.Int(0, ran - 1) - zone->random.Int(0, ran - 1);
|
||||||
|
rany = GetY() + zone->random.Int(0, ran - 1) - zone->random.Int(0, ran - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
ranz = BEST_Z_INVALID;
|
||||||
|
glm::vec3 newloc(ranx, rany, ceil != BEST_Z_INVALID ? ceil : GetZ());
|
||||||
|
|
||||||
|
if (stay_inliquid || levitating || (loop > 50 && inliquid)) {
|
||||||
|
if (zone->zonemap->CheckLoS(myloc, newloc)) {
|
||||||
|
ranz = GetZ();
|
||||||
|
currently_fleeing = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ceil != BEST_Z_INVALID) {
|
||||||
|
ranz = zone->zonemap->FindGround(newloc, &myceil);
|
||||||
|
} else {
|
||||||
|
ranz = zone->zonemap->FindBestZ(newloc, &myceil);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ranz != BEST_Z_INVALID) {
|
||||||
|
ranz = SetBestZ(ranz);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ranz == BEST_Z_INVALID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
float fdist = ranz - GetZ();
|
||||||
|
if (fdist >= -50 && fdist <= 50 && CheckCoordLosNoZLeaps(GetX(), GetY(), GetZ(), ranx, rany, ranz)) {
|
||||||
|
currently_fleeing = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currently_fleeing) {
|
||||||
|
m_FearWalkTarget = glm::vec3(ranx, rany, ranz);
|
||||||
|
LogPathingDetail("Non-pathed fearpoint [{}], [{}], [{}] selected for [{}]", ranx, rany, ranz, GetName());
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LogPathing("No path found to selected node during CalculateNewFearpoint.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,19 @@
|
|||||||
|
|
||||||
void command_devtools(Client *c, const Seperator *sep)
|
void command_devtools(Client *c, const Seperator *sep)
|
||||||
{
|
{
|
||||||
bool is_disable = !strcasecmp(sep->arg[1], "disable");
|
const uint16 arguments = sep->argnum;
|
||||||
bool is_enable = !strcasecmp(sep->arg[1], "enable");
|
if (arguments != 2) {
|
||||||
|
c->ShowDevToolsMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_disable || is_enable) {
|
const std::string& type = sep->arg[1];
|
||||||
c->SetDevToolsEnabled(is_enable);
|
const bool toggle = Strings::ToBool(sep->arg[2]);
|
||||||
|
|
||||||
|
if (Strings::EqualFold(type, "menu")) {
|
||||||
|
c->SetDevToolsEnabled(toggle);
|
||||||
|
} else if (Strings::EqualFold(type, "window")) {
|
||||||
|
c->SetDisplayMobInfoWindow(toggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
c->ShowDevToolsMenu();
|
c->ShowDevToolsMenu();
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "find/race.cpp"
|
#include "find/race.cpp"
|
||||||
#include "find/recipe.cpp"
|
#include "find/recipe.cpp"
|
||||||
#include "find/skill.cpp"
|
#include "find/skill.cpp"
|
||||||
|
#include "find/stance.cpp"
|
||||||
#include "find/spell.cpp"
|
#include "find/spell.cpp"
|
||||||
#include "find/special_ability.cpp"
|
#include "find/special_ability.cpp"
|
||||||
#include "find/task.cpp"
|
#include "find/task.cpp"
|
||||||
@@ -58,6 +59,7 @@ void command_find(Client *c, const Seperator *sep)
|
|||||||
Cmd{.cmd = "recipe", .u = "recipe [Search Criteria]", .fn = FindRecipe, .a = {"#findrecipe"}},
|
Cmd{.cmd = "recipe", .u = "recipe [Search Criteria]", .fn = FindRecipe, .a = {"#findrecipe"}},
|
||||||
Cmd{.cmd = "skill", .u = "skill [Search Criteria]", .fn = FindSkill, .a = {"#findskill"}},
|
Cmd{.cmd = "skill", .u = "skill [Search Criteria]", .fn = FindSkill, .a = {"#findskill"}},
|
||||||
Cmd{.cmd = "special_ability", .u = "special_ability [Search Criteria]", .fn = FindSpecialAbility, .a = {"#fsa", "#findspecialability"}},
|
Cmd{.cmd = "special_ability", .u = "special_ability [Search Criteria]", .fn = FindSpecialAbility, .a = {"#fsa", "#findspecialability"}},
|
||||||
|
Cmd{.cmd = "stance", .u = "stance [Search Criteria]", .fn = FindStance, .a = {"#findstance"}},
|
||||||
Cmd{.cmd = "spell", .u = "spell [Search Criteria]", .fn = FindSpell, .a = {"#fs", "#findspell"}},
|
Cmd{.cmd = "spell", .u = "spell [Search Criteria]", .fn = FindSpell, .a = {"#fs", "#findspell"}},
|
||||||
Cmd{.cmd = "task", .u = "task [Search Criteria]", .fn = FindTask, .a = {"#findtask"}},
|
Cmd{.cmd = "task", .u = "task [Search Criteria]", .fn = FindTask, .a = {"#findtask"}},
|
||||||
Cmd{.cmd = "zone", .u = "zone [Search Criteria]", .fn = FindZone, .a = {"#fz", "#findzone"}},
|
Cmd{.cmd = "zone", .u = "zone [Search Criteria]", .fn = FindZone, .a = {"#fz", "#findzone"}},
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
#include "../../client.h"
|
||||||
|
|
||||||
|
void FindStance(Client *c, const Seperator *sep)
|
||||||
|
{
|
||||||
|
if (sep->IsNumber(2)) {
|
||||||
|
const uint8 stance_id = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
|
||||||
|
const std::string& stance_name = Stance::GetName(stance_id);
|
||||||
|
if (Strings::EqualFold(stance_name, "UNKNOWN STANCE")) {
|
||||||
|
c->Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"Stance ID {} does not exist.",
|
||||||
|
stance_id
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"Stance {} | {}",
|
||||||
|
stance_id,
|
||||||
|
stance_name
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& search_criteria = Strings::ToLower(sep->argplus[2]);
|
||||||
|
|
||||||
|
uint32 found_count = 0;
|
||||||
|
|
||||||
|
for (const auto& e : stance_names) {
|
||||||
|
const std::string& stance_name_lower = Strings::ToLower(e.second);
|
||||||
|
if (!Strings::Contains(stance_name_lower, search_criteria)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"Stance {} | {}",
|
||||||
|
e.first,
|
||||||
|
e.second
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
|
||||||
|
found_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
c->Message(
|
||||||
|
Chat::White,
|
||||||
|
fmt::format(
|
||||||
|
"{} Stance{} found matching '{}'.",
|
||||||
|
found_count,
|
||||||
|
found_count != 1 ? "s" : "",
|
||||||
|
sep->argplus[2]
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#include "../client.h"
|
||||||
|
|
||||||
|
void command_fleeinfo(Client *c, const Seperator *sep)
|
||||||
|
{
|
||||||
|
if (c->GetTarget() && c->GetTarget()->IsNPC()) {
|
||||||
|
Mob* client = entity_list.GetMob(c->GetID());
|
||||||
|
if (client) {
|
||||||
|
c->GetTarget()->FleeInfo(client);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c->Message(Chat::Red, "Please target a NPC to use this command on.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -8,14 +8,15 @@ void command_grantaa(Client *c, const Seperator *sep)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const uint8 unlock_level = sep->IsNumber(1) ? static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[1])) : 0;
|
const uint8 unlock_level = sep->IsNumber(1) ? static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[1])) : 0;
|
||||||
|
const bool skip_grant_only = sep->IsNumber(2) ? Strings::ToBool(sep->arg[2]) : false;
|
||||||
|
|
||||||
auto t = c->GetTarget()->CastToClient();
|
auto t = c->GetTarget()->CastToClient();
|
||||||
t->GrantAllAAPoints(unlock_level);
|
t->GrantAllAAPoints(unlock_level, skip_grant_only);
|
||||||
|
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"Successfully granted all Alternate Advancements for {}{}.",
|
"Successfully granted all Alternate Advancements for {}{}{}.",
|
||||||
c->GetTargetDescription(t),
|
c->GetTargetDescription(t),
|
||||||
(
|
(
|
||||||
unlock_level ?
|
unlock_level ?
|
||||||
@@ -24,7 +25,8 @@ void command_grantaa(Client *c, const Seperator *sep)
|
|||||||
unlock_level
|
unlock_level
|
||||||
) :
|
) :
|
||||||
""
|
""
|
||||||
)
|
),
|
||||||
|
skip_grant_only ? "except for grant only AAs" : ""
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ void command_parcels(Client *c, const Seperator *sep)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterParcelsRepository::CharacterParcels parcel_out;
|
auto parcel_out = CharacterParcelsRepository::NewEntity();
|
||||||
parcel_out.from_name = c->GetName();
|
parcel_out.from_name = c->GetName();
|
||||||
parcel_out.note = note;
|
parcel_out.note = note;
|
||||||
parcel_out.sent_date = time(nullptr);
|
parcel_out.sent_date = time(nullptr);
|
||||||
@@ -241,7 +241,7 @@ void command_parcels(Client *c, const Seperator *sep)
|
|||||||
? inst->GetItem()->MaxCharges : (int16) quantity;
|
? inst->GetItem()->MaxCharges : (int16) quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterParcelsRepository::CharacterParcels parcel_out;
|
auto parcel_out = CharacterParcelsRepository::NewEntity();
|
||||||
parcel_out.from_name = c->GetName();
|
parcel_out.from_name = c->GetName();
|
||||||
parcel_out.note = note.empty() ? "" : note;
|
parcel_out.note = note.empty() ? "" : note;
|
||||||
parcel_out.sent_date = time(nullptr);
|
parcel_out.sent_date = time(nullptr);
|
||||||
|
|||||||
@@ -25,6 +25,13 @@ void SetLevel(Client *c, const Seperator *sep)
|
|||||||
t->SetLevel(level, true);
|
t->SetLevel(level, true);
|
||||||
|
|
||||||
if (t->IsClient()) {
|
if (t->IsClient()) {
|
||||||
|
for (const auto& s : EQ::skills::GetSkillTypeMap()) {
|
||||||
|
const uint16 max_skill_value = t->CastToClient()->MaxSkill(s.first);
|
||||||
|
if (t->GetSkill(s.first) > max_skill_value) {
|
||||||
|
t->CastToClient()->SetSkill(s.first, max_skill_value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
t->CastToClient()->SendLevelAppearance();
|
t->CastToClient()->SendLevelAppearance();
|
||||||
|
|
||||||
if (RuleB(Bots, Enabled) && RuleB(Bots, BotLevelsWithOwner)) {
|
if (RuleB(Bots, Enabled) && RuleB(Bots, BotLevelsWithOwner)) {
|
||||||
|
|||||||
@@ -160,23 +160,22 @@ void ShowInventory(Client *c, const Seperator *sep)
|
|||||||
|
|
||||||
linker.SetItemInst(inst_main);
|
linker.SetItemInst(inst_main);
|
||||||
|
|
||||||
if (item_data) {
|
if (item_data && inst_main) {
|
||||||
|
//auto inst = c->GetInv().GetItem(scope_bit & peekWorld ? EQ::invslot::WORLD_BEGIN + index_main : index_main);
|
||||||
c->Message(
|
c->Message(
|
||||||
Chat::White,
|
Chat::White,
|
||||||
fmt::format(
|
fmt::format(
|
||||||
"Slot {} | {} ({}/{}){}",
|
"Slot {} | {} ({}/{}){}",
|
||||||
((scope_bit & peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main),
|
scope_bit & peekWorld ? EQ::invslot::WORLD_BEGIN + index_main : index_main,
|
||||||
linker.GenerateLink(),
|
linker.GenerateLink(),
|
||||||
item_data->ID,
|
item_data->ID,
|
||||||
c->GetInv().GetItem(((scope_bit &peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main))->GetSerialNumber(),
|
inst_main->GetSerialNumber(),
|
||||||
(
|
|
||||||
inst_main->IsStackable() && inst_main->GetCharges() > 0 ?
|
inst_main->IsStackable() && inst_main->GetCharges() > 0 ?
|
||||||
fmt::format(
|
fmt::format(
|
||||||
" (Stack of {})",
|
" (Stack of {})",
|
||||||
inst_main->GetCharges()
|
inst_main->GetCharges()
|
||||||
) :
|
) :
|
||||||
""
|
""
|
||||||
)
|
|
||||||
).c_str()
|
).c_str()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -239,7 +238,7 @@ void ShowInventory(Client *c, const Seperator *sep)
|
|||||||
sub_index,
|
sub_index,
|
||||||
linker.GenerateLink(),
|
linker.GenerateLink(),
|
||||||
item_data->ID,
|
item_data->ID,
|
||||||
c->GetInv().GetItem(EQ::InventoryProfile::CalcSlotId(index_main, sub_index))->GetSerialNumber(),
|
inst_sub->GetSerialNumber(),
|
||||||
(
|
(
|
||||||
inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ?
|
inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ?
|
||||||
fmt::format(
|
fmt::format(
|
||||||
|
|||||||
+23
-11
@@ -115,7 +115,7 @@ Group::~Group()
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Split money used in OP_Split (/split and /autosplit).
|
//Split money used in OP_Split (/split and /autosplit).
|
||||||
void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter)
|
void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter, bool share)
|
||||||
{
|
{
|
||||||
// Return early if no money to split.
|
// Return early if no money to split.
|
||||||
if (!copper && !silver && !gold && !platinum) {
|
if (!copper && !silver && !gold && !platinum) {
|
||||||
@@ -152,6 +152,8 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8 random_member = zone->random.Int(0, member_count - 1);
|
||||||
|
|
||||||
// Calculate split and remainder for each coin type
|
// Calculate split and remainder for each coin type
|
||||||
uint32 copper_split = copper / member_count;
|
uint32 copper_split = copper / member_count;
|
||||||
uint32 copper_remainder = copper % member_count;
|
uint32 copper_remainder = copper % member_count;
|
||||||
@@ -172,16 +174,33 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu
|
|||||||
uint32 receive_gold = gold_split;
|
uint32 receive_gold = gold_split;
|
||||||
uint32 receive_platinum = platinum_split;
|
uint32 receive_platinum = platinum_split;
|
||||||
|
|
||||||
// splitter gets the remainders of coin
|
// if /split is used then splitter gets the remainder + split.
|
||||||
if (member_client == splitter) {
|
// if /autosplit is used then random players in the group will get the remainder + split.
|
||||||
|
if(share ? member_client == splitter : member_client == members[random_member]) {
|
||||||
receive_copper += copper_remainder;
|
receive_copper += copper_remainder;
|
||||||
receive_silver += silver_remainder;
|
receive_silver += silver_remainder;
|
||||||
receive_gold += gold_remainder;
|
receive_gold += gold_remainder;
|
||||||
receive_platinum += platinum_remainder;
|
receive_platinum += platinum_remainder;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the coins to the player's purse.
|
// the group member other than the character doing the /split only gets this message "(splitter) shares the money with the group"
|
||||||
|
if (share && member_client != splitter) {
|
||||||
|
member_client->MessageString(
|
||||||
|
YOU_RECEIVE_AS_SPLIT,
|
||||||
|
SHARE_MONEY,
|
||||||
|
splitter->GetCleanName()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if there are any coins to add to the player's purse.
|
||||||
|
if (receive_copper || receive_silver || receive_gold || receive_platinum) {
|
||||||
member_client->AddMoneyToPP(receive_copper, receive_silver, receive_gold, receive_platinum, true);
|
member_client->AddMoneyToPP(receive_copper, receive_silver, receive_gold, receive_platinum, true);
|
||||||
|
member_client->MessageString(
|
||||||
|
Chat::MoneySplit,
|
||||||
|
YOU_RECEIVE_AS_SPLIT,
|
||||||
|
Strings::Money(receive_platinum, receive_gold, receive_silver, receive_copper).c_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// If logging of player money transactions is enabled, record the transaction.
|
// If logging of player money transactions is enabled, record the transaction.
|
||||||
if (player_event_logs.IsEventEnabled(PlayerEvent::SPLIT_MONEY)) {
|
if (player_event_logs.IsEventEnabled(PlayerEvent::SPLIT_MONEY)) {
|
||||||
@@ -194,13 +213,6 @@ void Group::SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinu
|
|||||||
};
|
};
|
||||||
RecordPlayerEventLogWithClient(member_client, PlayerEvent::SPLIT_MONEY, e);
|
RecordPlayerEventLogWithClient(member_client, PlayerEvent::SPLIT_MONEY, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify the player of their received coins.
|
|
||||||
member_client->MessageString(
|
|
||||||
Chat::MoneySplit,
|
|
||||||
YOU_RECEIVE_AS_SPLIT,
|
|
||||||
Strings::Money(receive_platinum, receive_gold, receive_silver, receive_copper).c_str()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -80,7 +80,7 @@ public:
|
|||||||
void GroupMessage(Mob* sender,uint8 language,uint8 lang_skill,const char* message);
|
void GroupMessage(Mob* sender,uint8 language,uint8 lang_skill,const char* message);
|
||||||
void GroupMessageString(Mob* sender, uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0);
|
void GroupMessageString(Mob* sender, uint32 type, uint32 string_id, const char* message,const char* message2=0,const char* message3=0,const char* message4=0,const char* message5=0,const char* message6=0,const char* message7=0,const char* message8=0,const char* message9=0, uint32 distance = 0);
|
||||||
uint32 GetTotalGroupDamage(Mob* other);
|
uint32 GetTotalGroupDamage(Mob* other);
|
||||||
void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr);
|
void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr, bool share = false);
|
||||||
inline void SetLeader(Mob* c){ leader = c; };
|
inline void SetLeader(Mob* c){ leader = c; };
|
||||||
inline Mob* GetLeader() { return leader; };
|
inline Mob* GetLeader() { return leader; };
|
||||||
std::string GetLeaderName();
|
std::string GetLeaderName();
|
||||||
|
|||||||
@@ -1864,6 +1864,10 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
|||||||
LogInventory("Dest slot [{}] has item [{}] ([{}]) with [{}] charges in it", dst_slot_id, dst_inst->GetItem()->Name, dst_inst->GetItem()->ID, dst_inst->GetCharges());
|
LogInventory("Dest slot [{}] has item [{}] ([{}]) with [{}] charges in it", dst_slot_id, dst_inst->GetItem()->Name, dst_inst->GetItem()->ID, dst_inst->GetCharges());
|
||||||
dstitemid = dst_inst->GetItem()->ID;
|
dstitemid = dst_inst->GetItem()->ID;
|
||||||
}
|
}
|
||||||
|
if (IsBuyer() && srcitemid > 0) {
|
||||||
|
CheckIfMovedItemIsPartOfBuyLines(srcitemid);
|
||||||
|
}
|
||||||
|
|
||||||
if (IsTrader() && srcitemid>0){
|
if (IsTrader() && srcitemid>0){
|
||||||
EQ::ItemInstance* srcbag;
|
EQ::ItemInstance* srcbag;
|
||||||
EQ::ItemInstance* dstbag;
|
EQ::ItemInstance* dstbag;
|
||||||
@@ -3427,6 +3431,11 @@ void Client::SetBandolier(const EQApplicationPacket *app)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RuleI(Character, BandolierSwapDelay) > 0) {
|
||||||
|
bandolier_throttle_timer.Start(RuleI(Character, BandolierSwapDelay));
|
||||||
|
}
|
||||||
|
|
||||||
// finally, recalculate any stat bonuses from the item change
|
// finally, recalculate any stat bonuses from the item change
|
||||||
CalcBonuses();
|
CalcBonuses();
|
||||||
}
|
}
|
||||||
@@ -4850,3 +4859,63 @@ bool Client::HasItemOnCorpse(uint32 item_id)
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Client::PutItemInInventoryWithStacking(EQ::ItemInstance *inst)
|
||||||
|
{
|
||||||
|
auto free_id = GetInv().FindFirstFreeSlotThatFitsItem(inst->GetItem());
|
||||||
|
if (inst->IsStackable()) {
|
||||||
|
if (TryStacking(inst, ItemPacketTrade, true, false)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (free_id != INVALID_INDEX) {
|
||||||
|
if (PutItemInInventory(free_id, *inst, true)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Client::FindNumberOfFreeInventorySlotsWithSizeCheck(std::vector<BuyerLineTradeItems_Struct> items)
|
||||||
|
{
|
||||||
|
uint32 count = 0;
|
||||||
|
for (int16 i = EQ::invslot::GENERAL_BEGIN; i <= EQ::invslot::GENERAL_END; i++) {
|
||||||
|
if ((((uint64) 1 << i) & GetInv().GetLookup()->PossessionsBitmask) == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
EQ::ItemInstance *inv_item = GetInv().GetItem(i);
|
||||||
|
|
||||||
|
if (!inv_item) {
|
||||||
|
// Found available slot in personal inventory. Fits all sizes
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count >= items.size()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inv_item->IsClassBag()) {
|
||||||
|
for (auto const& item:items) {
|
||||||
|
auto item_tmp = database.GetItem(item.item_id);
|
||||||
|
if (EQ::InventoryProfile::CanItemFitInContainer(item_tmp, inv_item->GetItem())) {
|
||||||
|
int16 base_slot_id = EQ::InventoryProfile::CalcSlotId(i, EQ::invbag::SLOT_BEGIN);
|
||||||
|
uint8 bag_size = inv_item->GetItem()->BagSlots;
|
||||||
|
|
||||||
|
for (uint8 bag_slot = EQ::invbag::SLOT_BEGIN; bag_slot < bag_size; bag_slot++) {
|
||||||
|
auto bag_item = GetInv().GetItem(base_slot_id + bag_slot);
|
||||||
|
if (!bag_item) {
|
||||||
|
// Found a bag slot that fits the item
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count >= items.size()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|||||||
+77
-17
@@ -1214,25 +1214,23 @@ void Lua_Client::AddPVPPoints(uint32 points) {
|
|||||||
self->AddPVPPoints(points);
|
self->AddPVPPoints(points);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lua_Client::AddCrystals(uint32 radiant, uint32 ebon) {
|
void Lua_Client::AddCrystals(uint32 radiant_count, uint32 ebon_count) {
|
||||||
Lua_Safe_Call_Void();
|
Lua_Safe_Call_Void();
|
||||||
|
|
||||||
if (ebon != 0) {
|
if (ebon_count != 0) {
|
||||||
if (ebon > 0) {
|
if (ebon_count > 0) {
|
||||||
self->AddEbonCrystals(ebon);
|
self->AddEbonCrystals(ebon_count);
|
||||||
return;
|
} else {
|
||||||
|
self->RemoveEbonCrystals(ebon_count);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self->RemoveEbonCrystals(ebon);
|
if (radiant_count != 0) {
|
||||||
|
if (radiant_count > 0) {
|
||||||
|
self->AddRadiantCrystals(radiant_count);
|
||||||
|
} else {
|
||||||
|
self->RemoveRadiantCrystals(radiant_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (radiant != 0) {
|
|
||||||
if (radiant > 0) {
|
|
||||||
self->AddRadiantCrystals(radiant);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
self->RemoveRadiantCrystals(radiant);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1411,9 +1409,9 @@ void Lua_Client::FailTask(int task) {
|
|||||||
self->FailTask(task);
|
self->FailTask(task);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Lua_Client::IsTaskCompleted(int task) {
|
bool Lua_Client::IsTaskCompleted(int task_id) {
|
||||||
Lua_Safe_Call_Bool();
|
Lua_Safe_Call_Bool();
|
||||||
return self->IsTaskCompleted(task) != 0;
|
return self->IsTaskCompleted(task_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Lua_Client::IsTaskActive(int task) {
|
bool Lua_Client::IsTaskActive(int task) {
|
||||||
@@ -1771,7 +1769,7 @@ int Lua_Client::CalcATK() {
|
|||||||
return self->CalcATK();
|
return self->CalcATK();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Lua_Client::FilteredMessage(Mob *sender, uint32 type, int filter, const char *message)
|
void Lua_Client::FilteredMessage(Lua_Mob sender, uint32 type, int filter, const char *message)
|
||||||
{
|
{
|
||||||
Lua_Safe_Call_Void();
|
Lua_Safe_Call_Void();
|
||||||
self->FilteredMessage(sender, type, (eqFilterType)filter, message);
|
self->FilteredMessage(sender, type, (eqFilterType)filter, message);
|
||||||
@@ -3218,6 +3216,12 @@ void Lua_Client::GrantAllAAPoints(uint8 unlock_level)
|
|||||||
self->GrantAllAAPoints(unlock_level);
|
self->GrantAllAAPoints(unlock_level);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Lua_Client::GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->GrantAllAAPoints(unlock_level, skip_grant_only);
|
||||||
|
}
|
||||||
|
|
||||||
void Lua_Client::AddEbonCrystals(uint32 amount)
|
void Lua_Client::AddEbonCrystals(uint32 amount)
|
||||||
{
|
{
|
||||||
Lua_Safe_Call_Void();
|
Lua_Safe_Call_Void();
|
||||||
@@ -3381,6 +3385,57 @@ uint8 Lua_Client::GetSkillTrainLevel(int skill_id)
|
|||||||
return self->GetSkillTrainLevel(static_cast<EQ::skills::SkillType>(skill_id), self->GetClass());
|
return self->GetSkillTrainLevel(static_cast<EQ::skills::SkillType>(skill_id), self->GetClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Lua_Client::AreTasksCompleted(luabind::object task_ids)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Int();
|
||||||
|
|
||||||
|
if (luabind::type(task_ids) != LUA_TTABLE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> v;
|
||||||
|
int index = 1;
|
||||||
|
while (luabind::type(task_ids[index]) != LUA_TNIL) {
|
||||||
|
auto current_id = task_ids[index];
|
||||||
|
int task_id = 0;
|
||||||
|
if (luabind::type(current_id) != LUA_TNIL) {
|
||||||
|
try {
|
||||||
|
task_id = luabind::object_cast<int>(current_id);
|
||||||
|
} catch(luabind::cast_failed &) {
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
v.push_back(task_id);
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (v.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return self->AreTasksCompleted(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_Client::AreaTaunt()
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
entity_list.AETaunt(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_Client::AreaTaunt(float range)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
entity_list.AETaunt(self, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_Client::AreaTaunt(float range, int bonus_hate)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
entity_list.AETaunt(self, range, bonus_hate);
|
||||||
|
}
|
||||||
|
|
||||||
luabind::scope lua_register_client() {
|
luabind::scope lua_register_client() {
|
||||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||||
.def(luabind::constructor<>())
|
.def(luabind::constructor<>())
|
||||||
@@ -3427,6 +3482,10 @@ luabind::scope lua_register_client() {
|
|||||||
.def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool))&Lua_Client::ApplySpellRaid)
|
.def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool))&Lua_Client::ApplySpellRaid)
|
||||||
.def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool,bool))&Lua_Client::ApplySpellRaid)
|
.def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool,bool))&Lua_Client::ApplySpellRaid)
|
||||||
.def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool,bool,bool))&Lua_Client::ApplySpellRaid)
|
.def("ApplySpellRaid", (void(Lua_Client::*)(int,int,int,bool,bool,bool))&Lua_Client::ApplySpellRaid)
|
||||||
|
.def("AreTasksCompleted", (bool(Lua_Client::*)(luabind::object))&Lua_Client::AreTasksCompleted)
|
||||||
|
.def("AreaTaunt", (void(Lua_Client::*)(void))&Lua_Client::AreaTaunt)
|
||||||
|
.def("AreaTaunt", (void(Lua_Client::*)(float))&Lua_Client::AreaTaunt)
|
||||||
|
.def("AreaTaunt", (void(Lua_Client::*)(float, int))&Lua_Client::AreaTaunt)
|
||||||
.def("AssignTask", (void(Lua_Client::*)(int))&Lua_Client::AssignTask)
|
.def("AssignTask", (void(Lua_Client::*)(int))&Lua_Client::AssignTask)
|
||||||
.def("AssignTask", (void(Lua_Client::*)(int,int))&Lua_Client::AssignTask)
|
.def("AssignTask", (void(Lua_Client::*)(int,int))&Lua_Client::AssignTask)
|
||||||
.def("AssignTask", (void(Lua_Client::*)(int,int,bool))&Lua_Client::AssignTask)
|
.def("AssignTask", (void(Lua_Client::*)(int,int,bool))&Lua_Client::AssignTask)
|
||||||
@@ -3646,6 +3705,7 @@ luabind::scope lua_register_client() {
|
|||||||
.def("GoFish", (void(Lua_Client::*)(void))&Lua_Client::GoFish)
|
.def("GoFish", (void(Lua_Client::*)(void))&Lua_Client::GoFish)
|
||||||
.def("GrantAllAAPoints", (void(Lua_Client::*)(void))&Lua_Client::GrantAllAAPoints)
|
.def("GrantAllAAPoints", (void(Lua_Client::*)(void))&Lua_Client::GrantAllAAPoints)
|
||||||
.def("GrantAllAAPoints", (void(Lua_Client::*)(uint8))&Lua_Client::GrantAllAAPoints)
|
.def("GrantAllAAPoints", (void(Lua_Client::*)(uint8))&Lua_Client::GrantAllAAPoints)
|
||||||
|
.def("GrantAllAAPoints", (void(Lua_Client::*)(uint8,bool))&Lua_Client::GrantAllAAPoints)
|
||||||
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility)
|
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int))&Lua_Client::GrantAlternateAdvancementAbility)
|
||||||
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility)
|
.def("GrantAlternateAdvancementAbility", (bool(Lua_Client::*)(int, int, bool))&Lua_Client::GrantAlternateAdvancementAbility)
|
||||||
.def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID)
|
.def("GuildID", (uint32(Lua_Client::*)(void))&Lua_Client::GuildID)
|
||||||
|
|||||||
+8
-3
@@ -316,7 +316,7 @@ public:
|
|||||||
void KeyRingAdd(uint32 item);
|
void KeyRingAdd(uint32 item);
|
||||||
bool KeyRingCheck(uint32 item);
|
bool KeyRingCheck(uint32 item);
|
||||||
void AddPVPPoints(uint32 points);
|
void AddPVPPoints(uint32 points);
|
||||||
void AddCrystals(uint32 radiant, uint32 ebon);
|
void AddCrystals(uint32 radiant_count, uint32 ebon_count);
|
||||||
void SetEbonCrystals(uint32 value);
|
void SetEbonCrystals(uint32 value);
|
||||||
void SetRadiantCrystals(uint32 value);
|
void SetRadiantCrystals(uint32 value);
|
||||||
uint32 GetPVPPoints();
|
uint32 GetPVPPoints();
|
||||||
@@ -360,7 +360,7 @@ public:
|
|||||||
void AssignTask(int task_id, int npc_id);
|
void AssignTask(int task_id, int npc_id);
|
||||||
void AssignTask(int task_id, int npc_id, bool enforce_level_requirement);
|
void AssignTask(int task_id, int npc_id, bool enforce_level_requirement);
|
||||||
void FailTask(int task);
|
void FailTask(int task);
|
||||||
bool IsTaskCompleted(int task);
|
bool IsTaskCompleted(int task_id);
|
||||||
bool IsTaskActive(int task);
|
bool IsTaskActive(int task);
|
||||||
bool IsTaskActivityActive(int task, int activity);
|
bool IsTaskActivityActive(int task, int activity);
|
||||||
void LockSharedTask(bool lock);
|
void LockSharedTask(bool lock);
|
||||||
@@ -424,7 +424,7 @@ public:
|
|||||||
bool IsDead();
|
bool IsDead();
|
||||||
int CalcCurrentWeight();
|
int CalcCurrentWeight();
|
||||||
int CalcATK();
|
int CalcATK();
|
||||||
void FilteredMessage(Mob *sender, uint32 type, int filter, const char* message);
|
void FilteredMessage(Lua_Mob sender, uint32 type, int filter, const char* message);
|
||||||
void EnableAreaHPRegen(int value);
|
void EnableAreaHPRegen(int value);
|
||||||
void DisableAreaHPRegen();
|
void DisableAreaHPRegen();
|
||||||
void EnableAreaManaRegen(int value);
|
void EnableAreaManaRegen(int value);
|
||||||
@@ -487,6 +487,7 @@ public:
|
|||||||
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration);
|
void SetBucket(std::string bucket_name, std::string bucket_value, std::string expiration);
|
||||||
void GrantAllAAPoints();
|
void GrantAllAAPoints();
|
||||||
void GrantAllAAPoints(uint8 unlock_level);
|
void GrantAllAAPoints(uint8 unlock_level);
|
||||||
|
void GrantAllAAPoints(uint8 unlock_level, bool skip_grant_only);
|
||||||
void AddEbonCrystals(uint32 amount);
|
void AddEbonCrystals(uint32 amount);
|
||||||
void AddRadiantCrystals(uint32 amount);
|
void AddRadiantCrystals(uint32 amount);
|
||||||
void RemoveEbonCrystals(uint32 amount);
|
void RemoveEbonCrystals(uint32 amount);
|
||||||
@@ -505,6 +506,9 @@ public:
|
|||||||
void DescribeSpecialAbilities(Lua_NPC n);
|
void DescribeSpecialAbilities(Lua_NPC n);
|
||||||
void ResetLeadershipAA();
|
void ResetLeadershipAA();
|
||||||
uint8 GetSkillTrainLevel(int skill_id);
|
uint8 GetSkillTrainLevel(int skill_id);
|
||||||
|
void AreaTaunt();
|
||||||
|
void AreaTaunt(float range);
|
||||||
|
void AreaTaunt(float range, int bonus_hate);
|
||||||
|
|
||||||
void ApplySpell(int spell_id);
|
void ApplySpell(int spell_id);
|
||||||
void ApplySpell(int spell_id, int duration);
|
void ApplySpell(int spell_id, int duration);
|
||||||
@@ -577,6 +581,7 @@ public:
|
|||||||
void CampAllBots(uint8 class_id);
|
void CampAllBots(uint8 class_id);
|
||||||
bool RemoveAAPoints(uint32 points);
|
bool RemoveAAPoints(uint32 points);
|
||||||
bool RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount);
|
bool RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount);
|
||||||
|
bool AreTasksCompleted(luabind::object task_ids);
|
||||||
|
|
||||||
void DialogueWindow(std::string markdown);
|
void DialogueWindow(std::string markdown);
|
||||||
|
|
||||||
|
|||||||
@@ -180,6 +180,96 @@ uint32 Lua_Door::GetID() {
|
|||||||
return self->GetID();
|
return self->GetID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8 Lua_Door::GetTriggerDoorID() {
|
||||||
|
Lua_Safe_Call_Int();
|
||||||
|
return self->GetTriggerDoorID();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8 Lua_Door::GetTriggerType() {
|
||||||
|
Lua_Safe_Call_Int();
|
||||||
|
return self->GetTriggerType();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lua_Door::IsLDoNDoor() {
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->IsLDoNDoor();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 Lua_Door::GetClientVersionMask() {
|
||||||
|
Lua_Safe_Call_Int();
|
||||||
|
return self->GetClientVersionMask();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Lua_Door::GetDoorParam() {
|
||||||
|
Lua_Safe_Call_Int();
|
||||||
|
return self->GetDoorParam();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lua_Door::HasDestinationZone() {
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->HasDestinationZone();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lua_Door::IsDestinationZoneSame() {
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->IsDestinationZoneSame();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Lua_Door::IsDoorBlacklisted() {
|
||||||
|
Lua_Safe_Call_Bool();
|
||||||
|
return self->IsDoorBlacklisted();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Lua_Door::GetDestinationZoneName() {
|
||||||
|
Lua_Safe_Call_String();
|
||||||
|
return self->GetDestinationZoneName();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Lua_Door::GetDestinationInstanceID() {
|
||||||
|
Lua_Safe_Call_Int();
|
||||||
|
return self->GetDestinationInstanceID();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Lua_Door::GetDestinationX() {
|
||||||
|
Lua_Safe_Call_Real();
|
||||||
|
return self->GetDestinationX();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Lua_Door::GetDestinationY() {
|
||||||
|
Lua_Safe_Call_Real();
|
||||||
|
return self->GetDestinationY();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Lua_Door::GetDestinationZ() {
|
||||||
|
Lua_Safe_Call_Real();
|
||||||
|
return self->GetDestinationZ();
|
||||||
|
}
|
||||||
|
|
||||||
|
float Lua_Door::GetDestinationHeading() {
|
||||||
|
Lua_Safe_Call_Real();
|
||||||
|
return self->GetDestinationHeading();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Lua_Door::GetDzSwitchID() {
|
||||||
|
Lua_Safe_Call_Int();
|
||||||
|
return self->GetDzSwitchID();
|
||||||
|
}
|
||||||
|
|
||||||
|
int Lua_Door::GetInvertState() {
|
||||||
|
Lua_Safe_Call_Int();
|
||||||
|
return self->GetInvertState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_Door::SetInvertState(int invert_state) {
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->SetInvertState(invert_state);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 Lua_Door::GetGuildID() {
|
||||||
|
Lua_Safe_Call_Int();
|
||||||
|
return self->GetGuildID();
|
||||||
|
}
|
||||||
|
|
||||||
luabind::scope lua_register_door() {
|
luabind::scope lua_register_door() {
|
||||||
return luabind::class_<Lua_Door, Lua_Entity>("Door")
|
return luabind::class_<Lua_Door, Lua_Entity>("Door")
|
||||||
.def(luabind::constructor<>())
|
.def(luabind::constructor<>())
|
||||||
@@ -191,24 +281,42 @@ luabind::scope lua_register_door() {
|
|||||||
.def("ForceOpen", (void(Lua_Door::*)(Lua_Mob))&Lua_Door::ForceOpen)
|
.def("ForceOpen", (void(Lua_Door::*)(Lua_Mob))&Lua_Door::ForceOpen)
|
||||||
.def("ForceOpen", (void(Lua_Door::*)(Lua_Mob,bool))&Lua_Door::ForceOpen)
|
.def("ForceOpen", (void(Lua_Door::*)(Lua_Mob,bool))&Lua_Door::ForceOpen)
|
||||||
.def("GetDisableTimer", (bool(Lua_Door::*)(void))&Lua_Door::GetDisableTimer)
|
.def("GetDisableTimer", (bool(Lua_Door::*)(void))&Lua_Door::GetDisableTimer)
|
||||||
|
.def("GetClientVersionMask", (uint32(Lua_Door::*)(void))&Lua_Door::GetClientVersionMask)
|
||||||
|
.def("GetDestinationHeading", (float(Lua_Door::*)(void))&Lua_Door::GetDestinationHeading)
|
||||||
|
.def("GetDestinationInstanceID", (int(Lua_Door::*)(void))&Lua_Door::GetDestinationInstanceID)
|
||||||
|
.def("GetDestinationX", (float(Lua_Door::*)(void))&Lua_Door::GetDestinationX)
|
||||||
|
.def("GetDestinationY", (float(Lua_Door::*)(void))&Lua_Door::GetDestinationY)
|
||||||
|
.def("GetDestinationZ", (float(Lua_Door::*)(void))&Lua_Door::GetDestinationZ)
|
||||||
|
.def("GetDestinationZoneName", (std::string(Lua_Door::*)(void))&Lua_Door::GetDestinationZoneName)
|
||||||
.def("GetDoorDBID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorDBID)
|
.def("GetDoorDBID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorDBID)
|
||||||
.def("GetDoorID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorID)
|
.def("GetDoorID", (uint32(Lua_Door::*)(void))&Lua_Door::GetDoorID)
|
||||||
.def("GetDoorName", (const char*(Lua_Door::*)(void))&Lua_Door::GetDoorName)
|
.def("GetDoorName", (const char*(Lua_Door::*)(void))&Lua_Door::GetDoorName)
|
||||||
|
.def("GetDoorParam", (int(Lua_Door::*)(void))&Lua_Door::GetDoorParam)
|
||||||
|
.def("GetDzSwitchID", (int(Lua_Door::*)(void))&Lua_Door::GetDzSwitchID)
|
||||||
|
.def("GetGuildID", (uint32(Lua_Door::*)(void))&Lua_Door::GetGuildID)
|
||||||
.def("GetHeading", (float(Lua_Door::*)(void))&Lua_Door::GetHeading)
|
.def("GetHeading", (float(Lua_Door::*)(void))&Lua_Door::GetHeading)
|
||||||
.def("GetID", (uint32(Lua_Door::*)(void))&Lua_Door::GetID)
|
.def("GetID", (uint32(Lua_Door::*)(void))&Lua_Door::GetID)
|
||||||
.def("GetIncline", (uint32(Lua_Door::*)(void))&Lua_Door::GetIncline)
|
.def("GetIncline", (uint32(Lua_Door::*)(void))&Lua_Door::GetIncline)
|
||||||
|
.def("GetInvertState", (int(Lua_Door::*)(void))&Lua_Door::GetInvertState)
|
||||||
.def("GetKeyItem", (uint32(Lua_Door::*)(void))&Lua_Door::GetKeyItem)
|
.def("GetKeyItem", (uint32(Lua_Door::*)(void))&Lua_Door::GetKeyItem)
|
||||||
.def("GetLockPick", (uint32(Lua_Door::*)(void))&Lua_Door::GetLockPick)
|
.def("GetLockPick", (uint32(Lua_Door::*)(void))&Lua_Door::GetLockPick)
|
||||||
.def("GetNoKeyring", (int(Lua_Door::*)(void))&Lua_Door::GetNoKeyring)
|
.def("GetNoKeyring", (int(Lua_Door::*)(void))&Lua_Door::GetNoKeyring)
|
||||||
.def("GetOpenType", (uint32(Lua_Door::*)(void))&Lua_Door::GetOpenType)
|
.def("GetOpenType", (uint32(Lua_Door::*)(void))&Lua_Door::GetOpenType)
|
||||||
.def("GetSize", (uint32(Lua_Door::*)(void))&Lua_Door::GetSize)
|
.def("GetSize", (uint32(Lua_Door::*)(void))&Lua_Door::GetSize)
|
||||||
|
.def("GetTriggerDoorID", (uint8(Lua_Door::*)(void))&Lua_Door::GetTriggerDoorID)
|
||||||
|
.def("GetTriggerType", (uint8(Lua_Door::*)(void))&Lua_Door::GetTriggerType)
|
||||||
.def("GetX", (float(Lua_Door::*)(void))&Lua_Door::GetX)
|
.def("GetX", (float(Lua_Door::*)(void))&Lua_Door::GetX)
|
||||||
.def("GetY", (float(Lua_Door::*)(void))&Lua_Door::GetY)
|
.def("GetY", (float(Lua_Door::*)(void))&Lua_Door::GetY)
|
||||||
.def("GetZ", (float(Lua_Door::*)(void))&Lua_Door::GetZ)
|
.def("GetZ", (float(Lua_Door::*)(void))&Lua_Door::GetZ)
|
||||||
|
.def("HasDestinationZone", (bool(Lua_Door::*)(void))&Lua_Door::HasDestinationZone)
|
||||||
|
.def("IsDestinationZoneSame", (bool(Lua_Door::*)(void))&Lua_Door::IsDestinationZoneSame)
|
||||||
|
.def("IsDoorBlacklisted", (bool(Lua_Door::*)(void))&Lua_Door::IsDoorBlacklisted)
|
||||||
|
.def("IsLDoNDoor", (bool(Lua_Door::*)(void))&Lua_Door::IsLDoNDoor)
|
||||||
.def("SetDisableTimer", (void(Lua_Door::*)(bool))&Lua_Door::SetDisableTimer)
|
.def("SetDisableTimer", (void(Lua_Door::*)(bool))&Lua_Door::SetDisableTimer)
|
||||||
.def("SetDoorName", (void(Lua_Door::*)(const char*))&Lua_Door::SetDoorName)
|
.def("SetDoorName", (void(Lua_Door::*)(const char*))&Lua_Door::SetDoorName)
|
||||||
.def("SetHeading", (void(Lua_Door::*)(float))&Lua_Door::SetHeading)
|
.def("SetHeading", (void(Lua_Door::*)(float))&Lua_Door::SetHeading)
|
||||||
.def("SetIncline", (void(Lua_Door::*)(uint32))&Lua_Door::SetIncline)
|
.def("SetIncline", (void(Lua_Door::*)(uint32))&Lua_Door::SetIncline)
|
||||||
|
.def("SetInvertState", (void(Lua_Door::*)(int))&Lua_Door::SetInvertState)
|
||||||
.def("SetKeyItem", (void(Lua_Door::*)(uint32))&Lua_Door::SetKeyItem)
|
.def("SetKeyItem", (void(Lua_Door::*)(uint32))&Lua_Door::SetKeyItem)
|
||||||
.def("SetLocation", (void(Lua_Door::*)(float,float,float))&Lua_Door::SetLocation)
|
.def("SetLocation", (void(Lua_Door::*)(float,float,float))&Lua_Door::SetLocation)
|
||||||
.def("SetLockPick", (void(Lua_Door::*)(uint32))&Lua_Door::SetLockPick)
|
.def("SetLockPick", (void(Lua_Door::*)(uint32))&Lua_Door::SetLockPick)
|
||||||
|
|||||||
@@ -63,6 +63,24 @@ public:
|
|||||||
void ForceClose(Lua_Mob sender);
|
void ForceClose(Lua_Mob sender);
|
||||||
void ForceClose(Lua_Mob sender, bool alt_mode);
|
void ForceClose(Lua_Mob sender, bool alt_mode);
|
||||||
uint32 GetID();
|
uint32 GetID();
|
||||||
|
uint8 GetTriggerDoorID();
|
||||||
|
uint8 GetTriggerType();
|
||||||
|
bool IsLDoNDoor();
|
||||||
|
uint32 GetClientVersionMask();
|
||||||
|
int GetDoorParam();
|
||||||
|
bool HasDestinationZone();
|
||||||
|
bool IsDestinationZoneSame();
|
||||||
|
bool IsDoorBlacklisted();
|
||||||
|
std::string GetDestinationZoneName();
|
||||||
|
int GetDestinationInstanceID();
|
||||||
|
float GetDestinationX();
|
||||||
|
float GetDestinationY();
|
||||||
|
float GetDestinationZ();
|
||||||
|
float GetDestinationHeading();
|
||||||
|
int GetDzSwitchID();
|
||||||
|
int GetInvertState();
|
||||||
|
void SetInvertState(int invert_state);
|
||||||
|
uint32 GetGuildID();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -680,11 +680,107 @@ Lua_Mob_List Lua_EntityList::GetCloseMobList(Lua_Mob mob, float distance, bool i
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AEAttack(attacker, distance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance, int16 slot_id)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AEAttack(attacker, distance, slot_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AEAttack(attacker, distance, slot_id, count);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count, bool is_from_spell)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AEAttack(attacker, distance, slot_id, count, is_from_spell);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count, bool is_from_spell, int attack_rounds)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AEAttack(attacker, distance, slot_id, count, is_from_spell, attack_rounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AESpell(caster, center, spell_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AESpell(caster, center, spell_id, affect_caster);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AESpell(caster, center, spell_id, affect_caster, resist_adjust);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int max_targets)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AESpell(caster, center, spell_id, affect_caster, resist_adjust, &max_targets);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaTaunt(Lua_Client caster)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AETaunt(caster);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaTaunt(Lua_Client caster, float range)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AETaunt(caster, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::AreaTaunt(Lua_Client caster, float range, int bonus_hate)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->AETaunt(caster, range, bonus_hate);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->MassGroupBuff(caster, center, spell_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Lua_EntityList::MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster)
|
||||||
|
{
|
||||||
|
Lua_Safe_Call_Void();
|
||||||
|
self->MassGroupBuff(caster, center, spell_id, affect_caster);
|
||||||
|
}
|
||||||
|
|
||||||
luabind::scope lua_register_entity_list() {
|
luabind::scope lua_register_entity_list() {
|
||||||
return luabind::class_<Lua_EntityList>("EntityList")
|
return luabind::class_<Lua_EntityList>("EntityList")
|
||||||
.def(luabind::constructor<>())
|
.def(luabind::constructor<>())
|
||||||
.property("null", &Lua_EntityList::Null)
|
.property("null", &Lua_EntityList::Null)
|
||||||
.property("valid", &Lua_EntityList::Valid)
|
.property("valid", &Lua_EntityList::Valid)
|
||||||
|
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float))&Lua_EntityList::AreaAttack)
|
||||||
|
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float, int16))&Lua_EntityList::AreaAttack)
|
||||||
|
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float, int16, int))&Lua_EntityList::AreaAttack)
|
||||||
|
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float, int16, int, bool))&Lua_EntityList::AreaAttack)
|
||||||
|
.def("AreaAttack", (void(Lua_EntityList::*)(Lua_Mob, float, int16, int, bool, int))&Lua_EntityList::AreaAttack)
|
||||||
|
.def("AreaSpell", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16))&Lua_EntityList::AreaSpell)
|
||||||
|
.def("AreaSpell", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16, bool))&Lua_EntityList::AreaSpell)
|
||||||
|
.def("AreaSpell", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16, bool, int16))&Lua_EntityList::AreaSpell)
|
||||||
|
.def("AreaSpell", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16, bool, int16, int))&Lua_EntityList::AreaSpell)
|
||||||
|
.def("AreaTaunt", (void(Lua_EntityList::*)(Lua_Client))&Lua_EntityList::AreaTaunt)
|
||||||
|
.def("AreaTaunt", (void(Lua_EntityList::*)(Lua_Client, float))&Lua_EntityList::AreaTaunt)
|
||||||
|
.def("AreaTaunt", (void(Lua_EntityList::*)(Lua_Client, float, int))&Lua_EntityList::AreaTaunt)
|
||||||
.def("CanAddHateForMob", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::CanAddHateForMob)
|
.def("CanAddHateForMob", (bool(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::CanAddHateForMob)
|
||||||
.def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob, int, uint8, const char*))&Lua_EntityList::ChannelMessage)
|
.def("ChannelMessage", (void(Lua_EntityList::*)(Lua_Mob, int, uint8, const char*))&Lua_EntityList::ChannelMessage)
|
||||||
.def("ClearClientPetitionQueue", (void(Lua_EntityList::*)(void))&Lua_EntityList::ClearClientPetitionQueue)
|
.def("ClearClientPetitionQueue", (void(Lua_EntityList::*)(void))&Lua_EntityList::ClearClientPetitionQueue)
|
||||||
@@ -759,6 +855,8 @@ luabind::scope lua_register_entity_list() {
|
|||||||
.def("Marquee", (void(Lua_EntityList::*)(uint32, std::string))&Lua_EntityList::Marquee)
|
.def("Marquee", (void(Lua_EntityList::*)(uint32, std::string))&Lua_EntityList::Marquee)
|
||||||
.def("Marquee", (void(Lua_EntityList::*)(uint32, std::string, uint32))&Lua_EntityList::Marquee)
|
.def("Marquee", (void(Lua_EntityList::*)(uint32, std::string, uint32))&Lua_EntityList::Marquee)
|
||||||
.def("Marquee", (void(Lua_EntityList::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_EntityList::Marquee)
|
.def("Marquee", (void(Lua_EntityList::*)(uint32, uint32, uint32, uint32, uint32, std::string))&Lua_EntityList::Marquee)
|
||||||
|
.def("MassGroupBuff", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16))&Lua_EntityList::MassGroupBuff)
|
||||||
|
.def("MassGroupBuff", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob, uint16, bool))&Lua_EntityList::MassGroupBuff)
|
||||||
.def("Message", (void(Lua_EntityList::*)(uint32, uint32, const char*))&Lua_EntityList::Message)
|
.def("Message", (void(Lua_EntityList::*)(uint32, uint32, const char*))&Lua_EntityList::Message)
|
||||||
.def("MessageClose", (void(Lua_EntityList::*)(Lua_Mob, bool, float, uint32, const char*))&Lua_EntityList::MessageClose)
|
.def("MessageClose", (void(Lua_EntityList::*)(Lua_Mob, bool, float, uint32, const char*))&Lua_EntityList::MessageClose)
|
||||||
.def("MessageGroup", (void(Lua_EntityList::*)(Lua_Mob, bool, uint32, const char*))&Lua_EntityList::MessageGroup)
|
.def("MessageGroup", (void(Lua_EntityList::*)(Lua_Mob, bool, uint32, const char*))&Lua_EntityList::MessageGroup)
|
||||||
|
|||||||
@@ -142,6 +142,21 @@ public:
|
|||||||
Lua_Mob_List GetCloseMobList(Lua_Mob mob);
|
Lua_Mob_List GetCloseMobList(Lua_Mob mob);
|
||||||
Lua_Mob_List GetCloseMobList(Lua_Mob mob, float distance);
|
Lua_Mob_List GetCloseMobList(Lua_Mob mob, float distance);
|
||||||
Lua_Mob_List GetCloseMobList(Lua_Mob mob, float distance, bool ignore_self);
|
Lua_Mob_List GetCloseMobList(Lua_Mob mob, float distance, bool ignore_self);
|
||||||
|
void AreaAttack(Lua_Mob attacker, float distance);
|
||||||
|
void AreaAttack(Lua_Mob attacker, float distance, int16 slot_id);
|
||||||
|
void AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count);
|
||||||
|
void AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count, bool is_from_spell);
|
||||||
|
void AreaAttack(Lua_Mob attacker, float distance, int16 slot_id, int count, bool is_from_spell, int attack_rounds);
|
||||||
|
void AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id);
|
||||||
|
void AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster);
|
||||||
|
void AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust);
|
||||||
|
void AreaSpell(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int max_targets);
|
||||||
|
void AreaTaunt(Lua_Client caster);
|
||||||
|
void AreaTaunt(Lua_Client caster, float range);
|
||||||
|
void AreaTaunt(Lua_Client caster, float range, int bonus_hate);
|
||||||
|
void MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id);
|
||||||
|
void MassGroupBuff(Lua_Mob caster, Lua_Mob center, uint16 spell_id, bool affect_caster);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user