mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-27 17:12:28 +00:00
Compare commits
87 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 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 | |||
| 7918fed81c | |||
| ac24c9bf5a | |||
| 7b914c731b | |||
| 7362c0ebb5 | |||
| ae213a4e4b | |||
| 187288f3aa | |||
| abc8c3d886 | |||
| 0b2493beb8 | |||
| 9cebba5911 | |||
| 4478328b2a | |||
| 55a7e1646d | |||
| b6b8491060 | |||
| 850053a136 | |||
| 1aa8758b0a | |||
| b1aa087b9f | |||
| 1e57a0372f | |||
| 9614ea59ec | |||
| 7a648cce16 | |||
| 8640776a21 |
+161
@@ -1,3 +1,164 @@
|
||||
## [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
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix trader mode ([#4397](https://github.com/EQEmu/Server/pull/4397)) @joligario 2024-06-17
|
||||
|
||||
## [22.53.0] 6/14/2024
|
||||
|
||||
### Bug
|
||||
|
||||
* Anon players should not show in /who all ([#4392](https://github.com/EQEmu/Server/pull/4392)) @fryguy503 2024-06-14
|
||||
* Escape should put player into SOS if owned. ([#4388](https://github.com/EQEmu/Server/pull/4388)) @fryguy503 2024-06-07
|
||||
* Prevent Resurrection Spells from being resisted ([#4393](https://github.com/EQEmu/Server/pull/4393)) @fryguy503 2024-06-14
|
||||
|
||||
### Code
|
||||
|
||||
* Cleanup Account Status Code ([#4376](https://github.com/EQEmu/Server/pull/4376)) @Kinglykrab 2024-06-02
|
||||
* Cleanup Body Type Code ([#4366](https://github.com/EQEmu/Server/pull/4366)) @Kinglykrab 2024-06-02
|
||||
* Cleanup Object Type Code ([#4375](https://github.com/EQEmu/Server/pull/4375)) @Kinglykrab 2024-06-14
|
||||
* Remove unused code in emu_constants.h ([#4384](https://github.com/EQEmu/Server/pull/4384)) @Kinglykrab 2024-06-14
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix #goto Target ([#4382](https://github.com/EQEmu/Server/pull/4382)) @Kinglykrab 2024-06-03
|
||||
* Fix Swarm Pet Damage Messages ([#4383](https://github.com/EQEmu/Server/pull/4383)) @Kinglykrab 2024-06-04
|
||||
* Fix for players having empty bazaar window dropdown list, even though trader is tagged as a trader. ([#4391](https://github.com/EQEmu/Server/pull/4391)) @neckkola 2024-06-14
|
||||
* Fix potential trader crash when serialized item not found ([#4386](https://github.com/EQEmu/Server/pull/4386)) @joligario 2024-06-14
|
||||
|
||||
### Rules
|
||||
|
||||
* Add Invisible Augment Rules ([#4385](https://github.com/EQEmu/Server/pull/4385)) @Kinglykrab 2024-06-14
|
||||
* Classic Harm Touch Formula ([#4394](https://github.com/EQEmu/Server/pull/4394)) @fryguy503 2024-06-14
|
||||
* Mend/Sneak allow success tuning ([#4390](https://github.com/EQEmu/Server/pull/4390)) @fryguy503 2024-06-14
|
||||
* Snare Override Movement Bonus ([#4381](https://github.com/EQEmu/Server/pull/4381)) @fryguy503 2024-06-02
|
||||
|
||||
## [22.52.0] 6/1/2024
|
||||
|
||||
### Code
|
||||
|
||||
+38
-139
@@ -29,7 +29,10 @@
|
||||
#include "../../common/content/world_content_service.h"
|
||||
#include "../../common/zone_store.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/spells_new_repository.h"
|
||||
#include "../../common/file.h"
|
||||
#include "../../common/events/player_event_logs.h"
|
||||
#include "../../common/skill_caps.h"
|
||||
@@ -99,25 +102,22 @@ int main(int argc, char **argv)
|
||||
->LoadLogDatabaseSettings()
|
||||
->StartFileLogs();
|
||||
|
||||
std::string arg_1;
|
||||
std::string export_type;
|
||||
|
||||
if (argv[1]) {
|
||||
arg_1 = argv[1];
|
||||
export_type = argv[1];
|
||||
}
|
||||
|
||||
if (arg_1 == "spells") {
|
||||
if (Strings::EqualFold(export_type, "spells")) {
|
||||
ExportSpells(&content_db);
|
||||
return 0;
|
||||
}
|
||||
if (arg_1 == "skills") {
|
||||
} else if (Strings::EqualFold(export_type, "skills")) {
|
||||
ExportSkillCaps(&content_db);
|
||||
return 0;
|
||||
}
|
||||
if (arg_1 == "basedata") {
|
||||
} else if (Strings::EqualFold(export_type, "basedata") || Strings::EqualFold(export_type, "base_data")) {
|
||||
ExportBaseData(&content_db);
|
||||
return 0;
|
||||
}
|
||||
if (arg_1 == "dbstring") {
|
||||
} else if (Strings::EqualFold(export_type, "dbstr") || Strings::EqualFold(export_type, "dbstring")) {
|
||||
ExportDBStrings(&database);
|
||||
return 0;
|
||||
}
|
||||
@@ -132,180 +132,79 @@ int main(int argc, char **argv)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExportSpells(SharedDatabase *db)
|
||||
void ExportSpells(SharedDatabase* db)
|
||||
{
|
||||
LogInfo("Exporting Spells");
|
||||
|
||||
std::string file = fmt::format("{}/export/spells_us.txt", path.GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "w");
|
||||
if (!f) {
|
||||
std::ofstream file(fmt::format("{}/export/spells_us.txt", path.GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/spells_us.txt to write, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string query = "SELECT * FROM spells_new ORDER BY id";
|
||||
auto results = db->QueryDatabase(query);
|
||||
const auto& lines = SpellsNewRepository::GetSpellFileLines(*db);
|
||||
|
||||
if (results.Success()) {
|
||||
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('^');
|
||||
}
|
||||
const std::string& file_string = Strings::Implode("\n", lines);
|
||||
|
||||
if (row[i] != nullptr) {
|
||||
line += row[i];
|
||||
}
|
||||
}
|
||||
file << file_string;
|
||||
|
||||
fprintf(f, "%s\n", line.c_str());
|
||||
}
|
||||
}
|
||||
else {
|
||||
}
|
||||
file.close();
|
||||
|
||||
fclose(f);
|
||||
}
|
||||
|
||||
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;
|
||||
LogInfo("Exported [{}] Spell{}", lines.size(), lines.size() != 1 ? "s" : "");
|
||||
}
|
||||
|
||||
void ExportSkillCaps(SharedDatabase* db)
|
||||
{
|
||||
LogInfo("Exporting Skill Caps");
|
||||
|
||||
std::ofstream file(fmt::format("{}/export/SkillCaps.txt", path.GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/SkillCaps.txt to write, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8 class_id = Class::Warrior; class_id <= Class::Berserker; class_id++) {
|
||||
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;
|
||||
const auto& lines = SkillCapsRepository::GetSkillCapFileLines(*db);
|
||||
|
||||
for (
|
||||
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;
|
||||
}
|
||||
const std::string& file_string = Strings::Implode("\n", lines);
|
||||
|
||||
file << fmt::format("{}^{}^{}^{}^0", class_id, skill_id, level, cap) << std::endl;
|
||||
|
||||
previous_cap = cap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
file << file_string;
|
||||
|
||||
file.close();
|
||||
|
||||
LogInfo("Exported [{}] Skill Cap{}", lines.size(), lines.size() != 1 ? "s" : "");
|
||||
}
|
||||
|
||||
void ExportBaseData(SharedDatabase *db)
|
||||
{
|
||||
LogInfo("Exporting Base Data");
|
||||
|
||||
std::string file = fmt::format("{}/export/BaseData.txt", path.GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "w");
|
||||
if (!f) {
|
||||
std::ofstream file(fmt::format("{}/export/BaseData.txt", path.GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/BaseData.txt to write, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string query = "SELECT * FROM base_data ORDER BY level, class";
|
||||
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('^');
|
||||
}
|
||||
const auto& lines = BaseDataRepository::GetBaseDataFileLines(*db);
|
||||
|
||||
if (row[rowIndex] != nullptr) {
|
||||
line += row[rowIndex];
|
||||
}
|
||||
}
|
||||
const std::string& file_string = Strings::Implode("\n", lines);
|
||||
|
||||
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)
|
||||
{
|
||||
LogInfo("Exporting DB Strings");
|
||||
|
||||
std::string file = fmt::format("{}/export/dbstr_us.txt", path.GetServerPath());
|
||||
FILE *f = fopen(file.c_str(), "w");
|
||||
if (!f) {
|
||||
std::ofstream file(fmt::format("{}/export/dbstr_us.txt", path.GetServerPath()));
|
||||
if (!file || !file.is_open()) {
|
||||
LogError("Unable to open export/dbstr_us.txt to write, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(f, "Major^Minor^String(New)\n");
|
||||
const std::string query = "SELECT * FROM db_str ORDER BY id, type";
|
||||
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('^');
|
||||
}
|
||||
const auto& lines = DbStrRepository::GetDBStrFileLines(*db);
|
||||
|
||||
if (row[rowIndex] != nullptr) {
|
||||
line += row[rowIndex];
|
||||
}
|
||||
}
|
||||
const std::string& file_string = Strings::Implode("\n", lines);
|
||||
|
||||
fprintf(f, "%s\n", line.c_str());
|
||||
}
|
||||
}
|
||||
file << file_string;
|
||||
|
||||
fclose(f);
|
||||
file.close();
|
||||
|
||||
LogInfo("Exported [{}] Database String{}", lines.size(), lines.size() != 1 ? "s" : "");
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
|
||||
SET(common_sources
|
||||
base_packet.cpp
|
||||
bazaar.cpp
|
||||
bodytypes.cpp
|
||||
classes.cpp
|
||||
cli/eqemu_command_handler.cpp
|
||||
compression.cpp
|
||||
@@ -157,6 +158,7 @@ SET(repositories
|
||||
repositories/base/base_bugs_repository.h
|
||||
repositories/base/base_bug_reports_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_alternate_abilities_repository.h
|
||||
repositories/base/base_character_alt_currency_repository.h
|
||||
@@ -338,7 +340,8 @@ SET(repositories
|
||||
repositories/books_repository.h
|
||||
repositories/bugs_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_alternate_abilities_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));
|
||||
}
|
||||
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) {
|
||||
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
#include "../common/global_define.h"
|
||||
#include "../common/bodytypes.h"
|
||||
|
||||
std::string BodyType::GetName(uint8 body_type_id)
|
||||
{
|
||||
return IsValid(body_type_id) ? body_type_names[body_type_id] : "UNKNOWN BODY TYPE";
|
||||
}
|
||||
|
||||
bool BodyType::IsValid(uint8 body_type_id)
|
||||
{
|
||||
return body_type_names.find(body_type_id) != body_type_names.end();
|
||||
}
|
||||
+90
-46
@@ -18,52 +18,96 @@
|
||||
#ifndef BODYTYPES_H
|
||||
#define BODYTYPES_H
|
||||
|
||||
typedef enum {
|
||||
BT_Humanoid = 1,
|
||||
BT_Lycanthrope = 2,
|
||||
BT_Undead = 3,
|
||||
BT_Giant = 4,
|
||||
BT_Construct = 5,
|
||||
BT_Extraplanar = 6,
|
||||
BT_Magical = 7, //this name might be a bit off,
|
||||
BT_SummonedUndead = 8,
|
||||
BT_RaidGiant = 9, //Velious era Raid Giant
|
||||
BT_RaidColdain = 10, //Velious era Raid Coldain
|
||||
BT_NoTarget = 11, //no name, can't target this bodytype
|
||||
BT_Vampire = 12,
|
||||
BT_Atenha_Ra = 13,
|
||||
BT_Greater_Akheva = 14,
|
||||
BT_Khati_Sha = 15,
|
||||
BT_Seru = 16,
|
||||
BT_Grieg_Veneficus = 17,
|
||||
BT_Draz_Nurakk = 18,
|
||||
BT_Zek = 19, //"creatures from the Plane of War."
|
||||
BT_Luggald = 20,
|
||||
BT_Animal = 21,
|
||||
BT_Insect = 22,
|
||||
BT_Monster = 23,
|
||||
BT_Summoned = 24, //Elemental?
|
||||
BT_Plant = 25,
|
||||
BT_Dragon = 26,
|
||||
BT_Summoned2 = 27,
|
||||
BT_Summoned3 = 28,
|
||||
BT_Dragon2 = 29, //database data indicates this is a dragon type (kunark and DoN?)
|
||||
BT_VeliousDragon = 30, //might not be a tight set
|
||||
BT_Familiar = 31,
|
||||
BT_Dragon3 = 32,
|
||||
BT_Boxes = 33,
|
||||
BT_Muramite = 34, //tribal dudes
|
||||
// ...
|
||||
BT_NoTarget2 = 60,
|
||||
// ...
|
||||
BT_SwarmPet = 63, //Looks like weapon proc related temp pets and few misc pets, should not be used for checking swarm pets in general.
|
||||
BT_MonsterSummon = 64,
|
||||
// 65, trap or effect related?
|
||||
BT_InvisMan = 66, //no name, seen on 'InvisMan', can be /targeted
|
||||
BT_Special = 67
|
||||
} bodyType;
|
||||
/* bodytypes above 64 make the mob not show up */
|
||||
#include "types.h"
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
constexpr int format_as(bodyType type) { return static_cast<int>(type); }
|
||||
// body types above 64 make the mob invisible
|
||||
namespace BodyType {
|
||||
constexpr uint8 Humanoid = 1;
|
||||
constexpr uint8 Lycanthrope = 2;
|
||||
constexpr uint8 Undead = 3;
|
||||
constexpr uint8 Giant = 4;
|
||||
constexpr uint8 Construct = 5;
|
||||
constexpr uint8 Extraplanar = 6;
|
||||
constexpr uint8 Magical = 7; // this name might be a bit off,
|
||||
constexpr uint8 SummonedUndead = 8;
|
||||
constexpr uint8 RaidGiant = 9; // Velious era Raid Giant
|
||||
constexpr uint8 RaidColdain = 10; // Velious era Raid Coldain
|
||||
constexpr uint8 NoTarget = 11; // no name, can't target this bodytype
|
||||
constexpr uint8 Vampire = 12;
|
||||
constexpr uint8 AtenHaRa = 13;
|
||||
constexpr uint8 GreaterAkheva = 14;
|
||||
constexpr uint8 KhatiSha = 15;
|
||||
constexpr uint8 Seru = 16;
|
||||
constexpr uint8 GriegVeneficus = 17;
|
||||
constexpr uint8 DrazNurakk = 18;
|
||||
constexpr uint8 Zek = 19; //"creatures from the Plane of War."
|
||||
constexpr uint8 Luggald = 20;
|
||||
constexpr uint8 Animal = 21;
|
||||
constexpr uint8 Insect = 22;
|
||||
constexpr uint8 Monster = 23;
|
||||
constexpr uint8 Summoned = 24; // Elemental?
|
||||
constexpr uint8 Plant = 25;
|
||||
constexpr uint8 Dragon = 26;
|
||||
constexpr uint8 Summoned2 = 27;
|
||||
constexpr uint8 Summoned3 = 28;
|
||||
constexpr uint8 Dragon2 = 29; // database data indicates this is a dragon type (Kunark and DoN?)
|
||||
constexpr uint8 VeliousDragon = 30; // might not be a tight set
|
||||
constexpr uint8 Familiar = 31;
|
||||
constexpr uint8 Dragon3 = 32;
|
||||
constexpr uint8 Boxes = 33;
|
||||
constexpr uint8 Muramite = 34; // tribal dudes
|
||||
constexpr uint8 NoTarget2 = 60;
|
||||
constexpr uint8 SwarmPet = 63; // Looks like weapon proc related temp pets and few misc pets, should not be used for checking swarm pets in general.
|
||||
constexpr uint8 MonsterSummon = 64;
|
||||
constexpr uint8 InvisibleMan = 66; // no name, seen on 'InvisMan', can be /targeted
|
||||
constexpr uint8 Special = 67;
|
||||
|
||||
std::string GetName(uint8 body_type_id);
|
||||
bool IsValid(uint8 body_type_id);
|
||||
}
|
||||
|
||||
static std::map<uint8, std::string> body_type_names = {
|
||||
{ BodyType::Humanoid, "Humanoid" },
|
||||
{ BodyType::Lycanthrope, "Lycanthrope" },
|
||||
{ BodyType::Undead, "Undead" },
|
||||
{ BodyType::Giant, "Giant" },
|
||||
{ BodyType::Construct, "Construct" },
|
||||
{ BodyType::Extraplanar, "Extraplanar" },
|
||||
{ BodyType::Magical, "Magical" },
|
||||
{ BodyType::SummonedUndead, "Summoned Undead" },
|
||||
{ BodyType::RaidGiant, "Raid Giant" },
|
||||
{ BodyType::RaidColdain, "Raid Coldain" },
|
||||
{ BodyType::NoTarget, "Untargetable" },
|
||||
{ BodyType::Vampire, "Vampire" },
|
||||
{ BodyType::AtenHaRa, "Aten Ha Ra" },
|
||||
{ BodyType::GreaterAkheva, "Greater Akheva" },
|
||||
{ BodyType::KhatiSha, "Khati Sha" },
|
||||
{ BodyType::Seru, "Seru" },
|
||||
{ BodyType::GriegVeneficus, "Grieg Veneficus" },
|
||||
{ BodyType::DrazNurakk, "Draz Nurakk" },
|
||||
{ BodyType::Zek, "Zek" },
|
||||
{ BodyType::Luggald, "Luggald" },
|
||||
{ BodyType::Animal, "Animal" },
|
||||
{ BodyType::Insect, "Insect" },
|
||||
{ BodyType::Monster, "Monster" },
|
||||
{ BodyType::Summoned, "Summoned" },
|
||||
{ BodyType::Plant, "Plant" },
|
||||
{ BodyType::Dragon, "Dragon" },
|
||||
{ BodyType::Summoned2, "Summoned 2" },
|
||||
{ BodyType::Summoned3, "Summoned 3" },
|
||||
{ BodyType::Dragon2, "Dragon 2" },
|
||||
{ BodyType::VeliousDragon, "Velious Dragon" },
|
||||
{ BodyType::Familiar, "Familiar" },
|
||||
{ BodyType::Dragon3, "Dragon 3" },
|
||||
{ BodyType::Boxes, "Boxes" },
|
||||
{ BodyType::Muramite, "Muramite" },
|
||||
{ BodyType::NoTarget2, "Untargetable 2" },
|
||||
{ BodyType::SwarmPet, "Swarm Pet" },
|
||||
{ BodyType::MonsterSummon, "Monster Summon" },
|
||||
{ BodyType::InvisibleMan, "Invisible Man" },
|
||||
{ BodyType::Special, "Special" },
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "../rulesys.h"
|
||||
#include "../eqemu_logsys.h"
|
||||
#include "../repositories/instance_list_repository.h"
|
||||
#include "../zone_store.h"
|
||||
|
||||
|
||||
WorldContentService::WorldContentService()
|
||||
@@ -183,8 +184,8 @@ void WorldContentService::ReloadContentFlags()
|
||||
}
|
||||
|
||||
SetContentFlags(set_content_flags);
|
||||
LoadZones();
|
||||
LoadStaticGlobalZoneInstances();
|
||||
zone_store.LoadZones(*m_content_database);
|
||||
}
|
||||
|
||||
Database *WorldContentService::GetDatabase() const
|
||||
@@ -236,18 +237,6 @@ void WorldContentService::SetContentFlag(const std::string &content_flag_name, b
|
||||
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)
|
||||
{
|
||||
auto r = FindZone(zc->zoneID, zc->instanceID);
|
||||
@@ -261,93 +250,72 @@ void WorldContentService::HandleZoneRoutingMiddleware(ZoneChange_Struct *zc)
|
||||
// LoadStaticGlobalZoneInstances loads all static global zone instances
|
||||
// these are zones that are never set to expire and are global
|
||||
// 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(),
|
||||
fmt::format("never_expires = 1 AND is_global = 1")
|
||||
);
|
||||
|
||||
LogInfo("Loaded [{}] zone_instances", m_zone_instances.size());
|
||||
LogInfo("Loaded [{}] zone_instances", m_zone_static_instances.size());
|
||||
|
||||
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
|
||||
// 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)
|
||||
{
|
||||
// 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 (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) {
|
||||
|
||||
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
|
||||
for (auto &i: m_zone_instances) {
|
||||
if (i.zone == zone_id && i.version == z.version) {
|
||||
LogInfo(
|
||||
"Routed player to instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
i.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
i.notes
|
||||
);
|
||||
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
||||
.instance = i,
|
||||
.zone = z
|
||||
};
|
||||
}
|
||||
if (instance_id > 0 && i.id != instance_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LogInfo(
|
||||
"Routed player to non-instance zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
"Routed player to public static instance [{}] of zone [{}] ({}) version [{}] long_name [{}] notes [{}]",
|
||||
i.id,
|
||||
z.short_name,
|
||||
z.zoneidnumber,
|
||||
z.version,
|
||||
z.long_name,
|
||||
z.note
|
||||
i.notes
|
||||
);
|
||||
|
||||
return WorldContentService::FindZoneResult{
|
||||
.zone_id = static_cast<uint32>(z.zoneidnumber),
|
||||
.instance = InstanceListRepository::NewEntity(),
|
||||
.instance = i,
|
||||
.zone = z
|
||||
};
|
||||
}
|
||||
@@ -359,7 +327,7 @@ WorldContentService::FindZoneResult WorldContentService::FindZone(uint32 zone_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) {
|
||||
return true;
|
||||
}
|
||||
@@ -367,3 +335,15 @@ bool WorldContentService::IsInPublicStaticInstance(uint32 instance_id)
|
||||
|
||||
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();
|
||||
|
||||
bool DoesPassContentFiltering(const ContentFlags& f);
|
||||
bool DoesZonePassContentFiltering(const ZoneRepository::Zone& z);
|
||||
|
||||
WorldContentService * SetDatabase(Database *database);
|
||||
Database *GetDatabase() const;
|
||||
@@ -189,10 +190,8 @@ private:
|
||||
Database *m_content_database;
|
||||
|
||||
// holds a record of the zone table from the database
|
||||
std::vector<ZoneRepository::Zone> m_zones = {};
|
||||
WorldContentService *LoadStaticGlobalZoneInstances();
|
||||
std::vector<InstanceListRepository::InstanceList> m_zone_instances;
|
||||
WorldContentService * LoadZones();
|
||||
std::vector<InstanceListRepository::InstanceList> m_zone_static_instances;
|
||||
};
|
||||
|
||||
extern WorldContentService content_service;
|
||||
|
||||
+33
-5
@@ -66,6 +66,7 @@
|
||||
#endif
|
||||
|
||||
#include "database.h"
|
||||
#include "data_verification.h"
|
||||
#include "eq_packet_structs.h"
|
||||
#include "extprofile.h"
|
||||
#include "strings.h"
|
||||
@@ -77,6 +78,8 @@
|
||||
#include "zone_store.h"
|
||||
#include "repositories/merchantlist_temp_repository.h"
|
||||
#include "repositories/bot_data_repository.h"
|
||||
#include "repositories/trader_repository.h"
|
||||
#include "repositories/buyer_repository.h"
|
||||
|
||||
extern Client client;
|
||||
|
||||
@@ -306,13 +309,15 @@ bool Database::ReserveName(uint32 account_id, const std::string& name)
|
||||
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 (e.id) {
|
||||
auto g = GuildMembersRepository::NewEntity();
|
||||
|
||||
g.char_id = e.id;
|
||||
g.guild_id = guild_id;
|
||||
g.rank_ = guild_rank;
|
||||
|
||||
GuildMembersRepository::InsertOne(*this, g);
|
||||
}
|
||||
@@ -1628,16 +1633,29 @@ uint32 Database::GetGuildIDByCharID(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)
|
||||
{
|
||||
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()
|
||||
@@ -2104,3 +2122,13 @@ void Database::ClearGuildOnlineStatus()
|
||||
{
|
||||
GuildMembersRepository::ClearOnlineStatus(*this);
|
||||
}
|
||||
|
||||
void Database::ClearTraderDetails()
|
||||
{
|
||||
TraderRepository::Truncate(*this);
|
||||
}
|
||||
|
||||
void Database::ClearBuyerDetails()
|
||||
{
|
||||
BuyerRepository::DeleteBuyer(*this, 0);
|
||||
}
|
||||
|
||||
@@ -244,6 +244,8 @@ public:
|
||||
|
||||
void PurgeAllDeletedDataBuckets();
|
||||
void ClearGuildOnlineStatus();
|
||||
void ClearTraderDetails();
|
||||
void ClearBuyerDetails();
|
||||
|
||||
|
||||
/* Database Variables */
|
||||
|
||||
@@ -4947,7 +4947,7 @@ UPDATE `aa_ability` SET `auto_grant_enabled` = 1 WHERE `grant_only` = 0 AND `cha
|
||||
.version = 9237,
|
||||
.description = "2023_10_15_import_13th_floor.sql",
|
||||
.check = "SHOW COLUMNS FROM `items` LIKE 'bardeffect';",
|
||||
.condition = "contains",
|
||||
.condition = "missing",
|
||||
.match = "mediumint",
|
||||
.sql = R"(
|
||||
ALTER TABLE `items`
|
||||
@@ -5660,6 +5660,92 @@ ALTER TABLE `trader`
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`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
|
||||
|
||||
@@ -150,6 +150,17 @@ ADD COLUMN `augment_six` int(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `augment_five
|
||||
.sql = R"(
|
||||
ALTER TABLE `bot_data`
|
||||
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
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace DatabaseSchema {
|
||||
{
|
||||
return {
|
||||
{"adventure_stats", "player_id"},
|
||||
{"buyer", "charid"},
|
||||
{"char_recipe_list", "char_id"},
|
||||
{"character_activities", "charid"},
|
||||
{"character_alt_currency", "char_id"},
|
||||
@@ -107,6 +106,8 @@ namespace DatabaseSchema {
|
||||
"adventure_details",
|
||||
"adventure_stats",
|
||||
"buyer",
|
||||
"buyer_buy_lines",
|
||||
"buyer_trade_items",
|
||||
"char_recipe_list",
|
||||
"character_activities",
|
||||
"character_alt_currency",
|
||||
@@ -325,6 +326,9 @@ namespace DatabaseSchema {
|
||||
"banned_ips",
|
||||
"bug_reports",
|
||||
"bugs",
|
||||
"buyer",
|
||||
"buyer_buy_lines",
|
||||
"buyer_trade_items",
|
||||
"completed_shared_task_activity_state",
|
||||
"completed_shared_task_members",
|
||||
"completed_shared_tasks",
|
||||
|
||||
+25
-204
@@ -80,39 +80,19 @@ bool Bug::IsValid(uint32 category_id)
|
||||
return bug_category_names.find(category_id) != bug_category_names.end();
|
||||
}
|
||||
|
||||
const char *EQ::constants::GetStanceName(StanceType stance_type) {
|
||||
switch (stance_type) {
|
||||
case stanceUnknown:
|
||||
return "Unknown";
|
||||
case stancePassive:
|
||||
return "Passive";
|
||||
case stanceBalanced:
|
||||
return "Balanced";
|
||||
case stanceEfficient:
|
||||
return "Efficient";
|
||||
case stanceReactive:
|
||||
return "Reactive";
|
||||
case stanceAggressive:
|
||||
return "Aggressive";
|
||||
case stanceAssist:
|
||||
return "Assist";
|
||||
case stanceBurn:
|
||||
return "Burn";
|
||||
case stanceEfficient2:
|
||||
return "Efficient2";
|
||||
case stanceBurnAE:
|
||||
return "BurnAE";
|
||||
default:
|
||||
return "Invalid";
|
||||
}
|
||||
std::string Stance::GetName(uint8 stance_id)
|
||||
{
|
||||
return IsValid(stance_id) ? stance_names[stance_id] : "UNKNOWN STANCE";
|
||||
}
|
||||
|
||||
int EQ::constants::ConvertStanceTypeToIndex(StanceType stance_type) {
|
||||
if (EQ::ValueWithin(stance_type, EQ::constants::stancePassive, EQ::constants::stanceBurnAE)) {
|
||||
return (stance_type - EQ::constants::stancePassive);
|
||||
}
|
||||
bool Stance::IsValid(uint8 stance_id)
|
||||
{
|
||||
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()
|
||||
@@ -206,102 +186,6 @@ std::string EQ::constants::GetFlyModeName(int8 flymode_id)
|
||||
return EQ::constants::GetFlyModeMap().find(flymode_id)->second;
|
||||
}
|
||||
|
||||
const std::map<bodyType, std::string>& EQ::constants::GetBodyTypeMap()
|
||||
{
|
||||
static const std::map<bodyType, std::string> bodytype_map = {
|
||||
{ BT_Humanoid, "Humanoid" },
|
||||
{ BT_Lycanthrope, "Lycanthrope" },
|
||||
{ BT_Undead, "Undead" },
|
||||
{ BT_Giant, "Giant" },
|
||||
{ BT_Construct, "Construct" },
|
||||
{ BT_Extraplanar, "Extraplanar" },
|
||||
{ BT_Magical, "Magical" },
|
||||
{ BT_SummonedUndead, "Summoned Undead" },
|
||||
{ BT_RaidGiant, "Raid Giant" },
|
||||
{ BT_RaidColdain, "Raid Coldain" },
|
||||
{ BT_NoTarget, "Untargetable" },
|
||||
{ BT_Vampire, "Vampire" },
|
||||
{ BT_Atenha_Ra, "Aten Ha Ra" },
|
||||
{ BT_Greater_Akheva, "Greater Akheva" },
|
||||
{ BT_Khati_Sha, "Khati Sha" },
|
||||
{ BT_Seru, "Seru" },
|
||||
{ BT_Grieg_Veneficus, "Grieg Veneficus" },
|
||||
{ BT_Draz_Nurakk, "Draz Nurakk" },
|
||||
{ BT_Zek, "Zek" },
|
||||
{ BT_Luggald, "Luggald" },
|
||||
{ BT_Animal, "Animal" },
|
||||
{ BT_Insect, "Insect" },
|
||||
{ BT_Monster, "Monster" },
|
||||
{ BT_Summoned, "Summoned" },
|
||||
{ BT_Plant, "Plant" },
|
||||
{ BT_Dragon, "Dragon" },
|
||||
{ BT_Summoned2, "Summoned 2" },
|
||||
{ BT_Summoned3, "Summoned 3" },
|
||||
{ BT_Dragon2, "Dragon 2" },
|
||||
{ BT_VeliousDragon, "Velious Dragon" },
|
||||
{ BT_Familiar, "Familiar" },
|
||||
{ BT_Dragon3, "Dragon 3" },
|
||||
{ BT_Boxes, "Boxes" },
|
||||
{ BT_Muramite, "Muramite" },
|
||||
{ BT_NoTarget2, "Untargetable 2" },
|
||||
{ BT_SwarmPet, "Swarm Pet" },
|
||||
{ BT_MonsterSummon, "Monster Summon" },
|
||||
{ BT_InvisMan, "Invisible Man" },
|
||||
{ BT_Special, "Special" },
|
||||
};
|
||||
|
||||
return bodytype_map;
|
||||
}
|
||||
|
||||
std::string EQ::constants::GetBodyTypeName(bodyType bodytype_id)
|
||||
{
|
||||
if (EQ::constants::GetBodyTypeMap().find(bodytype_id) != EQ::constants::GetBodyTypeMap().end()) {
|
||||
return EQ::constants::GetBodyTypeMap().find(bodytype_id)->second;
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const std::map<uint8, std::string>& EQ::constants::GetAccountStatusMap()
|
||||
{
|
||||
static const std::map<uint8, std::string> account_status_map = {
|
||||
{ AccountStatus::Player, "Player" },
|
||||
{ AccountStatus::Steward, "Steward" },
|
||||
{ AccountStatus::ApprenticeGuide, "Apprentice Guide" },
|
||||
{ AccountStatus::Guide, "Guide" },
|
||||
{ AccountStatus::QuestTroupe, "Quest Troupe" },
|
||||
{ AccountStatus::SeniorGuide, "Senior Guide" },
|
||||
{ AccountStatus::GMTester, "GM Tester" },
|
||||
{ AccountStatus::EQSupport, "EQ Support" },
|
||||
{ AccountStatus::GMStaff, "GM Staff" },
|
||||
{ AccountStatus::GMAdmin, "GM Admin" },
|
||||
{ AccountStatus::GMLeadAdmin, "GM Lead Admin" },
|
||||
{ AccountStatus::QuestMaster, "Quest Master" },
|
||||
{ AccountStatus::GMAreas, "GM Areas" },
|
||||
{ AccountStatus::GMCoder, "GM Coder" },
|
||||
{ AccountStatus::GMMgmt, "GM Mgmt" },
|
||||
{ AccountStatus::GMImpossible, "GM Impossible" },
|
||||
{ AccountStatus::Max, "GM Max" }
|
||||
};
|
||||
|
||||
return account_status_map;
|
||||
}
|
||||
|
||||
std::string EQ::constants::GetAccountStatusName(uint8 account_status)
|
||||
{
|
||||
for (
|
||||
auto status_level = EQ::constants::GetAccountStatusMap().rbegin();
|
||||
status_level != EQ::constants::GetAccountStatusMap().rend();
|
||||
++status_level
|
||||
) {
|
||||
if (account_status >= status_level->first) {
|
||||
return status_level->second;
|
||||
}
|
||||
}
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
const std::map<uint8, std::string>& EQ::constants::GetConsiderLevelMap()
|
||||
{
|
||||
static const std::map<uint8, std::string> consider_level_map = {
|
||||
@@ -392,84 +276,6 @@ std::string EQ::constants::GetSpawnAnimationName(uint8 animation_id)
|
||||
return EQ::constants::GetSpawnAnimationMap().find(animation_id)->second;
|
||||
}
|
||||
|
||||
const std::map<int, std::string>& EQ::constants::GetObjectTypeMap()
|
||||
{
|
||||
static const std::map<int, std::string> object_type_map = {
|
||||
{ ObjectTypes::SmallBag, "Small Bag" },
|
||||
{ ObjectTypes::LargeBag, "Large Bag" },
|
||||
{ ObjectTypes::Quiver, "Quiver" },
|
||||
{ ObjectTypes::BeltPouch, "Belt Pouch" },
|
||||
{ ObjectTypes::WristPouch, "Wrist Pouch" },
|
||||
{ ObjectTypes::Backpack, "Backpack" },
|
||||
{ ObjectTypes::SmallChest, "Small Chest" },
|
||||
{ ObjectTypes::LargeChest, "Large Chest" },
|
||||
{ ObjectTypes::Bandolier, "Bandolier" },
|
||||
{ ObjectTypes::Medicine, "Medicine" },
|
||||
{ ObjectTypes::Tinkering, "Tinkering" },
|
||||
{ ObjectTypes::Lexicon, "Lexicon" },
|
||||
{ ObjectTypes::PoisonMaking, "Mortar and Pestle" },
|
||||
{ ObjectTypes::Quest, "Quest" },
|
||||
{ ObjectTypes::MixingBowl, "Mixing Bowl" },
|
||||
{ ObjectTypes::Baking, "Baking" },
|
||||
{ ObjectTypes::Tailoring, "Tailoring" },
|
||||
{ ObjectTypes::Blacksmithing, "Blacksmithing" },
|
||||
{ ObjectTypes::Fletching, "Fletching" },
|
||||
{ ObjectTypes::Brewing, "Brewing" },
|
||||
{ ObjectTypes::JewelryMaking, "Jewelry Making" },
|
||||
{ ObjectTypes::Pottery, "Pottery" },
|
||||
{ ObjectTypes::Kiln, "Kiln" },
|
||||
{ ObjectTypes::KeyMaker, "Key Maker" },
|
||||
{ ObjectTypes::ResearchWIZ, "Lexicon" },
|
||||
{ ObjectTypes::ResearchMAG, "Lexicon" },
|
||||
{ ObjectTypes::ResearchNEC, "Lexicon" },
|
||||
{ ObjectTypes::ResearchENC, "Lexicon" },
|
||||
{ ObjectTypes::Unknown, "Unknown" },
|
||||
{ ObjectTypes::ResearchPractice, "Lexicon" },
|
||||
{ ObjectTypes::Alchemy, "Alchemy" },
|
||||
{ ObjectTypes::HighElfForge, "High Elf Forge" },
|
||||
{ ObjectTypes::DarkElfForge, "Dark Elf Forge" },
|
||||
{ ObjectTypes::OgreForge, "Ogre Forge" },
|
||||
{ ObjectTypes::DwarfForge, "Dwarf Forge" },
|
||||
{ ObjectTypes::GnomeForge, "Gnome Forge" },
|
||||
{ ObjectTypes::BarbarianForge, "Barbarian Forge" },
|
||||
{ ObjectTypes::IksarForge, "Iksar Forge" },
|
||||
{ ObjectTypes::HumanForgeOne, "Human Forge" },
|
||||
{ ObjectTypes::HumanForgeTwo, "Human Forge" },
|
||||
{ ObjectTypes::HalflingTailoringOne, "Halfling Tailoring" },
|
||||
{ ObjectTypes::HalflingTailoringTwo, "Halfling Tailoring" },
|
||||
{ ObjectTypes::EruditeTailoring, "Erudite Tailoring" },
|
||||
{ ObjectTypes::WoodElfTailoring, "Wood Elf Tailoring" },
|
||||
{ ObjectTypes::WoodElfFletching, "Wood Elf Fletching" },
|
||||
{ ObjectTypes::IksarPottery, "Iksar Pottery" },
|
||||
{ ObjectTypes::Fishing, "Fishing" },
|
||||
{ ObjectTypes::TrollForge, "Troll Forge" },
|
||||
{ ObjectTypes::WoodElfForge, "Wood Elf Forge" },
|
||||
{ ObjectTypes::HalflingForge, "Halfling Forge" },
|
||||
{ ObjectTypes::EruditeForge, "Erudite Forge" },
|
||||
{ ObjectTypes::Merchant, "Merchant" },
|
||||
{ ObjectTypes::FroglokForge, "Froglok Forge" },
|
||||
{ ObjectTypes::Augmenter, "Augmenter" },
|
||||
{ ObjectTypes::Churn, "Churn" },
|
||||
{ ObjectTypes::TransformationMold, "Transformation Mold" },
|
||||
{ ObjectTypes::DetransformationMold, "Detransformation Mold" },
|
||||
{ ObjectTypes::Unattuner, "Unattuner" },
|
||||
{ ObjectTypes::TradeskillBag, "Tradeskill Bag" },
|
||||
{ ObjectTypes::CollectibleBag, "Collectible Bag" },
|
||||
{ ObjectTypes::NoDeposit, "No Deposit" }
|
||||
};
|
||||
|
||||
return object_type_map;
|
||||
}
|
||||
|
||||
std::string EQ::constants::GetObjectTypeName(int object_type)
|
||||
{
|
||||
if (!EQ::ValueWithin(object_type, ObjectTypes::SmallBag, ObjectTypes::NoDeposit)) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
return EQ::constants::GetObjectTypeMap().find(object_type)->second;
|
||||
}
|
||||
|
||||
const std::map<uint8, std::string> &EQ::constants::GetWeatherTypeMap()
|
||||
{
|
||||
static const std::map<uint8, std::string> weather_type_map = {
|
||||
@@ -629,6 +435,21 @@ std::string EQ::constants::GetConsiderColorName(uint32 consider_color)
|
||||
return c != EQ::constants::GetConsiderColorMap().end() ? c->second : std::string();
|
||||
}
|
||||
|
||||
std::string AccountStatus::GetName(uint8 account_status)
|
||||
{
|
||||
for (
|
||||
auto e = account_status_names.rbegin();
|
||||
e != account_status_names.rend();
|
||||
++e
|
||||
) {
|
||||
if (account_status >= e->first) {
|
||||
return e->second;
|
||||
}
|
||||
}
|
||||
|
||||
return "UNKNOWN ACCOUNT STATUS";
|
||||
}
|
||||
|
||||
std::string ComparisonType::GetName(uint8 type)
|
||||
{
|
||||
return IsValid(type) ? comparison_types[type] : "UNKNOWN COMPARISON TYPE";
|
||||
|
||||
+78
-115
@@ -26,6 +26,48 @@
|
||||
|
||||
#include <string.h>
|
||||
|
||||
namespace AccountStatus {
|
||||
constexpr uint8 Player = 0;
|
||||
constexpr uint8 Steward = 10;
|
||||
constexpr uint8 ApprenticeGuide = 20;
|
||||
constexpr uint8 Guide = 50;
|
||||
constexpr uint8 QuestTroupe = 80;
|
||||
constexpr uint8 SeniorGuide = 81;
|
||||
constexpr uint8 GMTester = 85;
|
||||
constexpr uint8 EQSupport = 90;
|
||||
constexpr uint8 GMStaff = 95;
|
||||
constexpr uint8 GMAdmin = 100;
|
||||
constexpr uint8 GMLeadAdmin = 150;
|
||||
constexpr uint8 QuestMaster = 160;
|
||||
constexpr uint8 GMAreas = 170;
|
||||
constexpr uint8 GMCoder = 180;
|
||||
constexpr uint8 GMMgmt = 200;
|
||||
constexpr uint8 GMImpossible = 250;
|
||||
constexpr uint8 Max = 255;
|
||||
|
||||
std::string GetName(uint8 account_status);
|
||||
}
|
||||
|
||||
static std::map<uint8, std::string> account_status_names = {
|
||||
{ AccountStatus::Player, "Player" },
|
||||
{ AccountStatus::Steward, "Steward" },
|
||||
{ AccountStatus::ApprenticeGuide, "Apprentice Guide" },
|
||||
{ AccountStatus::Guide, "Guide" },
|
||||
{ AccountStatus::QuestTroupe, "Quest Troupe" },
|
||||
{ AccountStatus::SeniorGuide, "Senior Guide" },
|
||||
{ AccountStatus::GMTester, "GM Tester" },
|
||||
{ AccountStatus::EQSupport, "EQ Support" },
|
||||
{ AccountStatus::GMStaff, "GM Staff" },
|
||||
{ AccountStatus::GMAdmin, "GM Admin" },
|
||||
{ AccountStatus::GMLeadAdmin, "GM Lead Admin" },
|
||||
{ AccountStatus::QuestMaster, "Quest Master" },
|
||||
{ AccountStatus::GMAreas, "GM Areas" },
|
||||
{ AccountStatus::GMCoder, "GM Coder" },
|
||||
{ AccountStatus::GMMgmt, "GM Mgmt" },
|
||||
{ AccountStatus::GMImpossible, "GM Impossible" },
|
||||
{ AccountStatus::Max, "GM Max" }
|
||||
};
|
||||
|
||||
namespace ComparisonType {
|
||||
constexpr uint8 Equal = 0;
|
||||
constexpr uint8 NotEqual = 1;
|
||||
@@ -55,7 +97,6 @@ static std::map<uint8, std::string> comparison_types = {
|
||||
{ ComparisonType::NotBetween, "Not Between" },
|
||||
};
|
||||
|
||||
|
||||
// local definitions are the result of using hybrid-client or server-only values and methods
|
||||
namespace EQ
|
||||
{
|
||||
@@ -233,19 +274,6 @@ namespace EQ
|
||||
const size_t SAY_LINK_CLOSER_SIZE = 1;
|
||||
const size_t SAY_LINK_MAXIMUM_SIZE = (SAY_LINK_OPENER_SIZE + SAY_LINK_BODY_SIZE + SAY_LINK_TEXT_SIZE + SAY_LINK_CLOSER_SIZE);
|
||||
|
||||
enum StanceType : int {
|
||||
stanceUnknown = 0,
|
||||
stancePassive,
|
||||
stanceBalanced,
|
||||
stanceEfficient,
|
||||
stanceReactive,
|
||||
stanceAggressive,
|
||||
stanceAssist,
|
||||
stanceBurn,
|
||||
stanceEfficient2,
|
||||
stanceBurnAE
|
||||
};
|
||||
|
||||
enum BotSpellIDs : int {
|
||||
Warrior = 3001,
|
||||
Cleric,
|
||||
@@ -296,70 +324,6 @@ namespace EQ
|
||||
Looting
|
||||
};
|
||||
|
||||
enum ObjectTypes : int {
|
||||
SmallBag,
|
||||
LargeBag,
|
||||
Quiver,
|
||||
BeltPouch,
|
||||
WristPouch,
|
||||
Backpack,
|
||||
SmallChest,
|
||||
LargeChest,
|
||||
Bandolier,
|
||||
Medicine,
|
||||
Tinkering,
|
||||
Lexicon,
|
||||
PoisonMaking,
|
||||
Quest,
|
||||
MixingBowl,
|
||||
Baking,
|
||||
Tailoring,
|
||||
Blacksmithing,
|
||||
Fletching,
|
||||
Brewing,
|
||||
JewelryMaking,
|
||||
Pottery,
|
||||
Kiln,
|
||||
KeyMaker,
|
||||
ResearchWIZ,
|
||||
ResearchMAG,
|
||||
ResearchNEC,
|
||||
ResearchENC,
|
||||
Unknown,
|
||||
ResearchPractice,
|
||||
Alchemy,
|
||||
HighElfForge,
|
||||
DarkElfForge,
|
||||
OgreForge,
|
||||
DwarfForge,
|
||||
GnomeForge,
|
||||
BarbarianForge,
|
||||
IksarForge,
|
||||
HumanForgeOne,
|
||||
HumanForgeTwo,
|
||||
HalflingTailoringOne,
|
||||
HalflingTailoringTwo,
|
||||
EruditeTailoring,
|
||||
WoodElfTailoring,
|
||||
WoodElfFletching,
|
||||
IksarPottery,
|
||||
Fishing,
|
||||
TrollForge,
|
||||
WoodElfForge,
|
||||
HalflingForge,
|
||||
EruditeForge,
|
||||
Merchant,
|
||||
FroglokForge,
|
||||
Augmenter,
|
||||
Churn,
|
||||
TransformationMold,
|
||||
DetransformationMold,
|
||||
Unattuner,
|
||||
TradeskillBag,
|
||||
CollectibleBag,
|
||||
NoDeposit
|
||||
};
|
||||
|
||||
enum WeatherTypes : uint8 {
|
||||
None,
|
||||
Raining,
|
||||
@@ -385,9 +349,6 @@ namespace EQ
|
||||
Proximity
|
||||
};
|
||||
|
||||
const char *GetStanceName(StanceType stance_type);
|
||||
int ConvertStanceTypeToIndex(StanceType stance_type);
|
||||
|
||||
extern const std::map<uint8, std::string>& GetLanguageMap();
|
||||
std::string GetLanguageName(uint8 language_id);
|
||||
|
||||
@@ -397,12 +358,6 @@ namespace EQ
|
||||
extern const std::map<int8, std::string>& GetFlyModeMap();
|
||||
std::string GetFlyModeName(int8 flymode_id);
|
||||
|
||||
extern const std::map<bodyType, std::string>& GetBodyTypeMap();
|
||||
std::string GetBodyTypeName(bodyType bodytype_id);
|
||||
|
||||
extern const std::map<uint8, std::string>& GetAccountStatusMap();
|
||||
std::string GetAccountStatusName(uint8 account_status);
|
||||
|
||||
extern const std::map<uint8, std::string>& GetConsiderLevelMap();
|
||||
std::string GetConsiderLevelName(uint8 consider_level);
|
||||
|
||||
@@ -415,9 +370,6 @@ namespace EQ
|
||||
extern const std::map<uint8, std::string>& GetSpawnAnimationMap();
|
||||
std::string GetSpawnAnimationName(uint8 animation_id);
|
||||
|
||||
extern const std::map<int, std::string>& GetObjectTypeMap();
|
||||
std::string GetObjectTypeName(int object_type);
|
||||
|
||||
extern const std::map<uint8, std::string>& GetWeatherTypeMap();
|
||||
std::string GetWeatherTypeName(uint8 weather_type);
|
||||
|
||||
@@ -433,10 +385,6 @@ namespace EQ
|
||||
extern const std::map<uint32, std::string>& GetConsiderColorMap();
|
||||
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*/
|
||||
|
||||
namespace profile {
|
||||
@@ -503,7 +451,7 @@ namespace EQ
|
||||
Raid,
|
||||
Guild
|
||||
};
|
||||
}; // namespace consent
|
||||
};
|
||||
} /*EQEmu*/
|
||||
|
||||
enum ServerLockType : int {
|
||||
@@ -512,26 +460,6 @@ enum ServerLockType : int {
|
||||
Unlock
|
||||
};
|
||||
|
||||
enum AccountStatus : uint8 {
|
||||
Player = 0,
|
||||
Steward = 10,
|
||||
ApprenticeGuide = 20,
|
||||
Guide = 50,
|
||||
QuestTroupe = 80,
|
||||
SeniorGuide = 81,
|
||||
GMTester = 85,
|
||||
EQSupport = 90,
|
||||
GMStaff = 95,
|
||||
GMAdmin = 100,
|
||||
GMLeadAdmin = 150,
|
||||
QuestMaster = 160,
|
||||
GMAreas = 170,
|
||||
GMCoder = 180,
|
||||
GMMgmt = 200,
|
||||
GMImpossible = 250,
|
||||
Max = 255
|
||||
};
|
||||
|
||||
enum Invisibility : uint8 {
|
||||
Visible,
|
||||
Invisible,
|
||||
@@ -793,4 +721,39 @@ static std::map<uint32, std::string> bug_category_names = {
|
||||
{ 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*/
|
||||
|
||||
@@ -67,6 +67,7 @@ N(OP_Buff),
|
||||
N(OP_BuffCreate),
|
||||
N(OP_BuffRemoveRequest),
|
||||
N(OP_Bug),
|
||||
N(OP_BuyerItems),
|
||||
N(OP_CameraEffect),
|
||||
N(OP_Camp),
|
||||
N(OP_CancelSneakHide),
|
||||
|
||||
@@ -758,10 +758,10 @@ typedef enum {
|
||||
FilterFocusEffects = 22, //0=show, 1=hide
|
||||
FilterPetSpells = 23, //0=show, 1=hide
|
||||
FilterHealOverTime = 24, //0=show, 1=mine only, 2=hide
|
||||
FilterUnknown25 = 25,
|
||||
FilterUnknown26 = 26,
|
||||
FilterUnknown27 = 27,
|
||||
FilterUnknown28 = 28,
|
||||
FilterItemSpeech = 25, //0=show, 1=hide // RoF2 Confirmed
|
||||
FilterStrikethrough = 26, //0=show, 1=hide // RoF2 Confirmed
|
||||
FilterStuns = 27, //0=show, 1=hide // RoF2 Confirmed
|
||||
FilterBardSongsOnPets = 28, //0=show, 1=hide // RoF2 Confirmed
|
||||
_FilterCount
|
||||
} eqFilterType;
|
||||
|
||||
@@ -1129,4 +1129,7 @@ enum ExpSource
|
||||
#define PARCEL_LIMIT 5
|
||||
#define PARCEL_BEGIN_SLOT 1
|
||||
|
||||
namespace DoorType {
|
||||
constexpr uint32 BuyerStall = 155;
|
||||
}
|
||||
#endif /*COMMON_EQ_CONSTANTS_H*/
|
||||
|
||||
+385
-66
@@ -323,6 +323,7 @@ union
|
||||
bool show_name;
|
||||
bool guild_show;
|
||||
bool trader;
|
||||
bool buyer;
|
||||
};
|
||||
|
||||
struct PlayerState_Struct {
|
||||
@@ -1620,6 +1621,32 @@ struct MoveItem_Struct
|
||||
/*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
|
||||
// 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)
|
||||
@@ -3113,60 +3140,369 @@ struct BazaarSearchResults_Struct {
|
||||
// Barter/Buyer
|
||||
//
|
||||
//
|
||||
enum {
|
||||
Barter_BuyerSearch = 0,
|
||||
Barter_SellerSearch = 1,
|
||||
Barter_BuyerModeOn = 2,
|
||||
Barter_BuyerModeOff = 3,
|
||||
Barter_BuyerItemUpdate = 5,
|
||||
Barter_BuyerItemRemove = 6,
|
||||
Barter_SellItem = 7,
|
||||
#define MAX_BUYER_COMPENSATION_ITEMS 10
|
||||
|
||||
enum BarterBuyerActions {
|
||||
Barter_BuyerSearch = 0,
|
||||
Barter_SellerSearch = 1,
|
||||
Barter_BuyerModeOn = 2,
|
||||
Barter_BuyerModeOff = 3,
|
||||
Barter_BuyerItemStart = 4,
|
||||
Barter_BuyerItemUpdate = 5,
|
||||
Barter_BuyerItemRemove = 6,
|
||||
Barter_SellItem = 7,
|
||||
Barter_SellerTransactionComplete = 8,
|
||||
Barter_BuyerTransactionComplete = 9,
|
||||
Barter_BuyerInspectBegin = 10,
|
||||
Barter_BuyerInspectEnd = 11,
|
||||
Barter_BuyerAppearance = 12,
|
||||
Barter_BuyerInspectWindow = 13,
|
||||
Barter_BarterItemInspect = 14,
|
||||
Barter_SellerBrowsing = 15,
|
||||
Barter_BuyerSearchResults = 16,
|
||||
Barter_Welcome = 17,
|
||||
Barter_WelcomeMessageUpdate = 19,
|
||||
Barter_BuyerItemInspect = 21,
|
||||
Barter_Unknown23 = 23
|
||||
Barter_BuyerTransactionComplete = 9,
|
||||
Barter_BuyerInspectBegin = 10,
|
||||
Barter_BuyerInspectEnd = 11,
|
||||
Barter_BuyerAppearance = 12,
|
||||
Barter_BuyerInspectWindow = 13,
|
||||
Barter_BarterItemInspect = 14,
|
||||
Barter_SellerBrowsing = 15,
|
||||
Barter_BuyerSearchResults = 16,
|
||||
Barter_Welcome = 17,
|
||||
Barter_WelcomeMessageUpdate = 19,
|
||||
Barter_Greeting = 20,
|
||||
Barter_BuyerItemInspect = 21,
|
||||
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 {
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ char WelcomeMessage[256];
|
||||
uint32 action;
|
||||
char welcome_message[256];
|
||||
};
|
||||
|
||||
struct BuyerItemSearch_Struct {
|
||||
/*000*/ uint32 Unknown000;
|
||||
/*004*/ char SearchString[64];
|
||||
struct BuyerLineTradeItems_Struct {
|
||||
uint32 item_id;
|
||||
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 {
|
||||
/*000*/ char ItemName[64];
|
||||
/*064*/ uint32 ItemID;
|
||||
/*068*/ uint32 Unknown068;
|
||||
/*072*/ uint32 Unknown072;
|
||||
struct BuyerLineItems_Struct {
|
||||
uint32 slot;
|
||||
uint8 enabled;
|
||||
uint32 item_id;
|
||||
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 {
|
||||
uint32 Action;
|
||||
uint32 ResultCount;
|
||||
BuyerItemSearchResultEntry_Struct Results[MAX_BUYER_ITEMSEARCH_RESULTS];
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
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 {
|
||||
uint32 Action;
|
||||
char SearchString[64];
|
||||
uint32 SearchID;
|
||||
uint32 action;
|
||||
char search_string[64];
|
||||
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 {
|
||||
/*000*/ uint32 Action; // 0x00000015
|
||||
/*004*/ uint32 ItemID;
|
||||
@@ -3174,31 +3510,6 @@ struct BuyerItemSearchLinkRequest_Struct {
|
||||
/*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 {
|
||||
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)
|
||||
@@ -6040,9 +6351,12 @@ enum BazaarTraderBarterActions {
|
||||
};
|
||||
|
||||
enum BazaarPurchaseActions {
|
||||
ByVendor = 0,
|
||||
ByParcel = 1,
|
||||
ByDirectToInventory = 2
|
||||
BazaarByVendor = 0,
|
||||
BazaarByParcel = 1,
|
||||
BazaarByDirectToInventory = 2,
|
||||
BarterByVendor = 0,
|
||||
BarterInBazaar = 1,
|
||||
BarterOutsideBazaar = 2
|
||||
};
|
||||
|
||||
enum BazaarPurchaseSubActions {
|
||||
@@ -6116,6 +6430,11 @@ struct BazaarSearchMessaging_Struct {
|
||||
}
|
||||
};
|
||||
|
||||
struct BuylineItemDetails_Struct {
|
||||
uint64 item_cost;
|
||||
uint32 item_quantity;
|
||||
};
|
||||
|
||||
// Restore structure packing to default
|
||||
#pragma pack()
|
||||
|
||||
|
||||
@@ -706,6 +706,7 @@ void PlayerEventLogs::SetSettingsDefaults()
|
||||
m_settings[PlayerEvent::PARCEL_SEND].event_enabled = 1;
|
||||
m_settings[PlayerEvent::PARCEL_RETRIEVE].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++) {
|
||||
m_settings[i].retention_days = RETENTION_DAYS_DEFAULT;
|
||||
|
||||
@@ -60,7 +60,8 @@ namespace PlayerEvent {
|
||||
GUILD_TRIBUTE_DONATE_PLAT,
|
||||
PARCEL_SEND,
|
||||
PARCEL_RETRIEVE,
|
||||
PARCEL_DELETE,
|
||||
PARCEL_DELETE,
|
||||
BARTER_TRANSACTION,
|
||||
MAX // dont remove
|
||||
};
|
||||
|
||||
@@ -122,7 +123,8 @@ namespace PlayerEvent {
|
||||
"Guild Tribute Donate Platinum",
|
||||
"Parcel Item Sent",
|
||||
"Parcel Item Retrieved",
|
||||
"Parcel Prune Routine"
|
||||
"Parcel Prune Routine",
|
||||
"Barter Transaction"
|
||||
};
|
||||
|
||||
// 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
|
||||
|
||||
@@ -1743,3 +1743,68 @@ std::vector<uint32> EQ::InventoryProfile::GetAugmentIDsBySlotID(int16 slot_id)
|
||||
|
||||
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
|
||||
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);
|
||||
std::vector<int16> FindAllFreeSlotsThatFitItem(const EQ::ItemData *inst);
|
||||
int16 FindFirstFreeSlotThatFitsItem(const EQ::ItemData *inst);
|
||||
|
||||
// Calculate slot_id for an item within a bag
|
||||
static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id
|
||||
|
||||
+55
-21
@@ -81,45 +81,79 @@ bool IpUtil::IsIpInPrivateRfc1918(const std::string &ip)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets local address - pings google to inspect what interface was used locally
|
||||
* @return
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
#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()
|
||||
{
|
||||
char my_ip_address[16];
|
||||
unsigned int my_port;
|
||||
#ifdef _WIN32
|
||||
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 my_address{};
|
||||
int sockfd;
|
||||
int sockfd;
|
||||
|
||||
// Connect to server
|
||||
if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
|
||||
// Create a UDP socket
|
||||
#ifdef _WIN32
|
||||
sockfd = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (sockfd == INVALID_SOCKET) {
|
||||
WSACleanup();
|
||||
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));
|
||||
server_address.sin_family = AF_INET;
|
||||
server_address.sin_addr.s_addr = inet_addr("172.217.160.99");
|
||||
server_address.sin_port = htons(80);
|
||||
server_address.sin_family = AF_INET;
|
||||
server_address.sin_addr.s_addr = inet_addr("8.8.8.8"); // Google DNS
|
||||
server_address.sin_port = htons(53); // DNS port
|
||||
|
||||
// Connect to server
|
||||
if (connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address)) < 0) {
|
||||
close(sockfd);
|
||||
return "";
|
||||
}
|
||||
// Perform a dummy connection to the server (UDP)
|
||||
connect(sockfd, (struct sockaddr *) &server_address, sizeof(server_address));
|
||||
|
||||
// Get my ip address and port
|
||||
// Get my IP address
|
||||
memset(&my_address, 0, sizeof(my_address));
|
||||
socklen_t len = sizeof(my_address);
|
||||
getsockname(sockfd, (struct sockaddr *) &my_address, &len);
|
||||
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
|
||||
* Uses various websites as options to return raw public IP back to the client
|
||||
|
||||
+39
-36
@@ -32,10 +32,11 @@
|
||||
|
||||
//#include <iostream>
|
||||
|
||||
int32 NextItemInstSerialNumber = 1;
|
||||
|
||||
static inline int32 GetNextItemInstSerialNumber() {
|
||||
int32 next_item_serial_number = 1;
|
||||
std::unordered_set<uint64> guids{};
|
||||
|
||||
static inline int32 GetNextItemInstSerialNumber()
|
||||
{
|
||||
// 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
|
||||
// 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.
|
||||
//
|
||||
// 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)
|
||||
NextItemInstSerialNumber = 1;
|
||||
else
|
||||
NextItemInstSerialNumber++;
|
||||
if (next_item_serial_number >= INT32_MAX) {
|
||||
next_item_serial_number = 1;
|
||||
}
|
||||
else {
|
||||
next_item_serial_number++;
|
||||
}
|
||||
|
||||
return NextItemInstSerialNumber;
|
||||
while (guids.contains(next_item_serial_number)) {
|
||||
next_item_serial_number++;
|
||||
}
|
||||
|
||||
return next_item_serial_number;
|
||||
}
|
||||
|
||||
//
|
||||
@@ -303,47 +310,34 @@ int8 EQ::ItemInstance::AvailableAugmentSlot(int32 augment_type) const
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
|
||||
auto i = invaug::SOCKET_BEGIN;
|
||||
for (; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetItem(i)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
augment_type == -1 ||
|
||||
(
|
||||
m_item->AugSlotType[i] &&
|
||||
((1 << (m_item->AugSlotType[i] - 1)) & augment_type)
|
||||
)
|
||||
) {
|
||||
break;
|
||||
for (int16 slot_id = invaug::SOCKET_BEGIN; slot_id <= invaug::SOCKET_END; ++slot_id) {
|
||||
if (IsAugmentSlotAvailable(augment_type, slot_id)) {
|
||||
return slot_id;
|
||||
}
|
||||
}
|
||||
|
||||
return (i <= invaug::SOCKET_END) ? i : INVALID_INDEX;
|
||||
return INVALID_INDEX;
|
||||
}
|
||||
|
||||
bool EQ::ItemInstance::IsAugmentSlotAvailable(int32 augment_type, uint8 slot) const
|
||||
{
|
||||
if (!m_item || !m_item->IsClassCommon()) {
|
||||
if (!m_item || !m_item->IsClassCommon() || GetItem(slot)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
return (
|
||||
(
|
||||
!GetItem(slot) &&
|
||||
m_item->AugSlotVisible[slot]
|
||||
augment_type == -1 ||
|
||||
(
|
||||
m_item->AugSlotType[slot] &&
|
||||
((1 << (m_item->AugSlotType[slot] - 1)) & augment_type)
|
||||
)
|
||||
) &&
|
||||
augment_type == -1 ||
|
||||
(
|
||||
m_item->AugSlotType[slot] &&
|
||||
((1 << (m_item->AugSlotType[slot] - 1)) & augment_type)
|
||||
RuleB(Items, AugmentItemAllowInvisibleAugments) ||
|
||||
m_item->AugSlotVisible[slot]
|
||||
)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
);
|
||||
}
|
||||
|
||||
// Retrieve item inside container
|
||||
@@ -1292,7 +1286,7 @@ int EQ::ItemInstance::GetItemBaneDamageRace(bool augments) const
|
||||
return race;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemBaneDamageBody(bodyType against, bool augments) const
|
||||
int EQ::ItemInstance::GetItemBaneDamageBody(uint8 against, bool augments) const
|
||||
{
|
||||
int64 damage = 0;
|
||||
const auto item = GetItem();
|
||||
@@ -1948,6 +1942,15 @@ int EQ::ItemInstance::GetItemSkillsStat(EQ::skills::SkillType skill, bool augmen
|
||||
return stat;
|
||||
}
|
||||
|
||||
void EQ::ItemInstance::AddGUIDToMap(uint64 existing_serial_number)
|
||||
{
|
||||
guids.emplace(existing_serial_number);
|
||||
}
|
||||
|
||||
void EQ::ItemInstance::ClearGUIDMap()
|
||||
{
|
||||
guids.clear();
|
||||
}
|
||||
//
|
||||
// class EvolveInfo
|
||||
//
|
||||
|
||||
@@ -265,7 +265,7 @@ namespace EQ
|
||||
// these two are just quick checks
|
||||
int GetItemBaneDamageBody(bool augments = false) const;
|
||||
int GetItemBaneDamageRace(bool augments = false) const;
|
||||
int GetItemBaneDamageBody(bodyType against, bool augments = false) const;
|
||||
int GetItemBaneDamageBody(uint8 against, bool augments = false) const;
|
||||
int GetItemBaneDamageRace(uint16 against, bool augments = false) const;
|
||||
int GetItemMagical(bool augments = false) const;
|
||||
int GetItemHP(bool augments = false) const;
|
||||
@@ -309,6 +309,8 @@ namespace EQ
|
||||
int GetItemSkillsStat(EQ::skills::SkillType skill, bool augments = false) const;
|
||||
uint32 GetItemGuildFavor() const;
|
||||
std::vector<uint32> GetAugmentIDs() const;
|
||||
static void AddGUIDToMap(uint64 existing_serial_number);
|
||||
static void ClearGUIDMap();
|
||||
|
||||
protected:
|
||||
//////////////////////////
|
||||
|
||||
+523
-32
@@ -356,41 +356,91 @@ namespace RoF2
|
||||
|
||||
ENCODE(OP_Barter)
|
||||
{
|
||||
EQApplicationPacket *in = *p;
|
||||
EQApplicationPacket *in = *p;
|
||||
*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)
|
||||
{
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
auto outapp = new EQApplicationPacket(OP_Barter, sizeof(structs::Buyer_SetAppearance_Struct));
|
||||
auto eq = (structs::Buyer_SetAppearance_Struct *) outapp->pBuffer;
|
||||
|
||||
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;
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_BuyerItems, sizeof(structs::BuyerRemoveItem_Struct));
|
||||
auto eq = (structs::BuyerRemoveItem_Struct *) outapp->pBuffer;
|
||||
|
||||
eq->action = structs::RoF2BuyerActions::BuyerModifyBuyLine;
|
||||
eq->slot_id = emu->buy_slot_id;
|
||||
eq->toggle = 0;
|
||||
|
||||
dest->FastQueuePacket(&outapp);
|
||||
safe_delete(in);
|
||||
|
||||
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);
|
||||
|
||||
LogTradingDetail("Sending item search results <green>[{}]", bisr.result_count);
|
||||
|
||||
uint32 packet_size = bisr.result_count * sizeof(structs::BuyerItemSearchResultEntry_Struct) + 8;
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_Barter, packet_size);
|
||||
auto eq = (char *) outapp->pBuffer;
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, eq, structs::RoF2BuyerActions::BuyerSearchResults);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
|
||||
in->size = 80;
|
||||
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
|
||||
char *OutBuffer = (char *)in->pBuffer;
|
||||
|
||||
char Name[64];
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, SubAction);
|
||||
uint32 EntityID = VARSTRUCT_DECODE_TYPE(uint32, Buffer);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, OutBuffer, EntityID);
|
||||
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;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
|
||||
ENCODE(OP_BazaarSearch)
|
||||
@@ -542,8 +592,8 @@ namespace RoF2
|
||||
LogTrading(
|
||||
"(RoF2) AddTraderToBazaarWindow action <green>[{}] trader_id <green>[{}] entity_id <green>[{}] zone_id <green>[{}]",
|
||||
eq->action,
|
||||
eq->entity_id,
|
||||
eq->trader_id,
|
||||
eq->entity_id,
|
||||
eq->zone_id
|
||||
);
|
||||
dest->FastQueuePacket(&outapp);
|
||||
@@ -682,6 +732,243 @@ namespace RoF2
|
||||
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_LENGTH_EXACT(CancelTrade_Struct);
|
||||
@@ -1690,7 +1977,7 @@ namespace RoF2
|
||||
uchar *__emu_buffer = in->pBuffer;
|
||||
ItemPacket_Struct *old_item_pkt = (ItemPacket_Struct *) __emu_buffer;
|
||||
|
||||
switch(old_item_pkt->PacketType)
|
||||
switch(old_item_pkt->PacketType)
|
||||
{
|
||||
case ItemPacketParcel: {
|
||||
ParcelMessaging_Struct pms{};
|
||||
@@ -4348,6 +4635,9 @@ namespace RoF2
|
||||
if (emu->DestructibleObject) {
|
||||
OtherData = OtherData | 0xe1; // Live has 0xe1 for OtherData
|
||||
}
|
||||
if (emu->buyer) {
|
||||
OtherData = OtherData | 0x01;
|
||||
}
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, OtherData);
|
||||
// float EmitterScalingRadius
|
||||
@@ -4459,7 +4749,7 @@ namespace RoF2
|
||||
VARSTRUCT_ENCODE_STRING(Buffer, emu->lastName);
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, 0); // aatitle
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->guild_show);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, emu->guild_show);
|
||||
VARSTRUCT_ENCODE_TYPE(uint8, Buffer, 0); // TempPet
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->petOwnerId);
|
||||
@@ -4664,6 +4954,66 @@ namespace RoF2
|
||||
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)
|
||||
{
|
||||
char *Buffer = (char *)__packet->pBuffer;
|
||||
@@ -4742,6 +5092,147 @@ namespace RoF2
|
||||
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_LENGTH_EXACT(structs::CastSpell_Struct);
|
||||
|
||||
@@ -48,6 +48,7 @@ E(OP_BeginCast)
|
||||
E(OP_BlockedBuffs)
|
||||
E(OP_Buff)
|
||||
E(OP_BuffCreate)
|
||||
E(OP_BuyerItems)
|
||||
E(OP_CancelTrade)
|
||||
E(OP_CastSpell)
|
||||
E(OP_ChannelMessage)
|
||||
@@ -149,11 +150,13 @@ D(OP_Animation)
|
||||
D(OP_ApplyPoison)
|
||||
D(OP_AugmentInfo)
|
||||
D(OP_AugmentItem)
|
||||
D(OP_Barter)
|
||||
D(OP_BazaarSearch)
|
||||
D(OP_BlockedBuffs)
|
||||
D(OP_BookButton)
|
||||
D(OP_Buff)
|
||||
D(OP_BuffRemoveRequest)
|
||||
D(OP_BuyerItems)
|
||||
D(OP_CastSpell)
|
||||
D(OP_ChannelMessage)
|
||||
D(OP_CharacterCreate)
|
||||
|
||||
+142
-22
@@ -354,15 +354,15 @@ struct Spawn_Struct_Bitfields
|
||||
/*29*/ unsigned showname:1;
|
||||
/*30*/ unsigned idleanimationsoff:1; // what we called statue?
|
||||
/*31*/ unsigned untargetable:1; // bClickThrough
|
||||
/* do these later
|
||||
32 unsigned buyer:1;
|
||||
33 unsigned offline:1;
|
||||
34 unsigned interactiveobject:1;
|
||||
35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables
|
||||
36 unsigned title:1;
|
||||
37 unsigned suffix:1;
|
||||
38 unsigned padding1:1;
|
||||
39 unsigned padding2:1;
|
||||
// byte 5
|
||||
/*32 unsigned buyer:1;
|
||||
/*33 unsigned offline:1;
|
||||
/*34 unsigned interactiveobject:1;
|
||||
/*35 unsigned flung:1; // hmm this vfunc appears to do stuff with leve and flung variables
|
||||
/*36 unsigned title:1;
|
||||
/*37 unsigned suffix:1;
|
||||
/*38 unsigned padding1:1;
|
||||
/*39 unsigned padding2:1;
|
||||
40 unsinged padding3:1;
|
||||
*/
|
||||
/*
|
||||
@@ -3107,19 +3107,139 @@ struct EnvDamage2_Struct {
|
||||
|
||||
//Bazaar Stuff
|
||||
enum RoF2BazaarTraderBuyerActions {
|
||||
Zero = 0,
|
||||
BeginTraderMode = 1,
|
||||
EndTraderMode = 2,
|
||||
PriceUpdate = 3,
|
||||
EndTransaction = 4,
|
||||
BazaarSearch = 7,
|
||||
WelcomeMessage = 9,
|
||||
BuyTraderItem = 10,
|
||||
ListTraderItems = 11,
|
||||
BazaarInspect = 18,
|
||||
ClickTrader = 28,
|
||||
ItemMove = 19,
|
||||
ReconcileItems = 20
|
||||
Zero = 0,
|
||||
BeginTraderMode = 1,
|
||||
EndTraderMode = 2,
|
||||
PriceUpdate = 3,
|
||||
EndTransaction = 4,
|
||||
BazaarSearch = 7,
|
||||
WelcomeMessage = 9,
|
||||
BuyTraderItem = 10,
|
||||
ListTraderItems = 11,
|
||||
BazaarInspect = 18,
|
||||
ClickTrader = 28,
|
||||
ItemMove = 19,
|
||||
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 {
|
||||
|
||||
@@ -21,7 +21,7 @@ public:
|
||||
struct BotSpellsEntries {
|
||||
uint32_t id;
|
||||
int32_t npc_spells_id;
|
||||
int16_t spellid;
|
||||
uint16_t spell_id;
|
||||
uint32_t type;
|
||||
uint8_t minlevel;
|
||||
uint8_t maxlevel;
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
return {
|
||||
"id",
|
||||
"npc_spells_id",
|
||||
"spellid",
|
||||
"spell_id",
|
||||
"type",
|
||||
"minlevel",
|
||||
"maxlevel",
|
||||
@@ -67,7 +67,7 @@ public:
|
||||
return {
|
||||
"id",
|
||||
"npc_spells_id",
|
||||
"spellid",
|
||||
"spell_id",
|
||||
"type",
|
||||
"minlevel",
|
||||
"maxlevel",
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
|
||||
e.id = 0;
|
||||
e.npc_spells_id = 0;
|
||||
e.spellid = 0;
|
||||
e.spell_id = 0;
|
||||
e.type = 0;
|
||||
e.minlevel = 0;
|
||||
e.maxlevel = 255;
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
|
||||
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.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.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;
|
||||
@@ -220,7 +220,7 @@ public:
|
||||
auto columns = Columns();
|
||||
|
||||
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[4] + " = " + std::to_string(e.minlevel));
|
||||
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.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.minlevel));
|
||||
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.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.minlevel));
|
||||
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.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.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;
|
||||
@@ -387,7 +387,7 @@ public:
|
||||
|
||||
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.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.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;
|
||||
@@ -476,7 +476,7 @@ public:
|
||||
|
||||
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.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.minlevel));
|
||||
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.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.minlevel));
|
||||
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 {
|
||||
public:
|
||||
struct Buyer {
|
||||
int32_t charid;
|
||||
int32_t buyslot;
|
||||
int32_t itemid;
|
||||
std::string itemname;
|
||||
int32_t quantity;
|
||||
int32_t price;
|
||||
uint64_t id;
|
||||
uint32_t char_id;
|
||||
uint32_t char_entity_id;
|
||||
std::string char_name;
|
||||
uint32_t char_zone_id;
|
||||
uint32_t char_zone_instance_id;
|
||||
time_t transaction_date;
|
||||
std::string welcome_message;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("charid");
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"charid",
|
||||
"buyslot",
|
||||
"itemid",
|
||||
"itemname",
|
||||
"quantity",
|
||||
"price",
|
||||
"id",
|
||||
"char_id",
|
||||
"char_entity_id",
|
||||
"char_name",
|
||||
"char_zone_id",
|
||||
"char_zone_instance_id",
|
||||
"transaction_date",
|
||||
"welcome_message",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"charid",
|
||||
"buyslot",
|
||||
"itemid",
|
||||
"itemname",
|
||||
"quantity",
|
||||
"price",
|
||||
"id",
|
||||
"char_id",
|
||||
"char_entity_id",
|
||||
"char_name",
|
||||
"char_zone_id",
|
||||
"char_zone_instance_id",
|
||||
"UNIX_TIMESTAMP(transaction_date)",
|
||||
"welcome_message",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,12 +99,14 @@ public:
|
||||
{
|
||||
Buyer e{};
|
||||
|
||||
e.charid = 0;
|
||||
e.buyslot = 0;
|
||||
e.itemid = 0;
|
||||
e.itemname = "";
|
||||
e.quantity = 0;
|
||||
e.price = 0;
|
||||
e.id = 0;
|
||||
e.char_id = 0;
|
||||
e.char_entity_id = 0;
|
||||
e.char_name = "";
|
||||
e.char_zone_id = 0;
|
||||
e.char_zone_instance_id = 0;
|
||||
e.transaction_date = 0;
|
||||
e.welcome_message = "";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -109,7 +117,7 @@ public:
|
||||
)
|
||||
{
|
||||
for (auto &buyer : buyers) {
|
||||
if (buyer.charid == buyer_id) {
|
||||
if (buyer.id == buyer_id) {
|
||||
return buyer;
|
||||
}
|
||||
}
|
||||
@@ -135,12 +143,14 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
Buyer e{};
|
||||
|
||||
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.itemname = row[3] ? row[3] : "";
|
||||
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.char_name = row[3] ? row[3] : "";
|
||||
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 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;
|
||||
}
|
||||
@@ -174,12 +184,13 @@ public:
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.charid));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.buyslot));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.itemid));
|
||||
v.push_back(columns[3] + " = '" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.quantity));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.price));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.char_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.char_entity_id));
|
||||
v.push_back(columns[3] + " = '" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.char_zone_id));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.char_zone_instance_id));
|
||||
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(
|
||||
fmt::format(
|
||||
@@ -187,7 +198,7 @@ public:
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.charid
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -201,12 +212,14 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.buyslot));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
v.push_back(std::to_string(e.price));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
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(
|
||||
fmt::format(
|
||||
@@ -217,7 +230,7 @@ public:
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.charid = results.LastInsertedID();
|
||||
e.id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -236,12 +249,14 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.buyslot));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
v.push_back(std::to_string(e.price));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
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) + ")");
|
||||
}
|
||||
@@ -275,12 +290,14 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Buyer e{};
|
||||
|
||||
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.itemname = row[3] ? row[3] : "";
|
||||
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.char_name = row[3] ? row[3] : "";
|
||||
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 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);
|
||||
}
|
||||
@@ -305,12 +322,14 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Buyer e{};
|
||||
|
||||
e.charid = row[0] ? static_cast<int32_t>(atoi(row[0])) : 0;
|
||||
e.buyslot = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.itemid = row[2] ? static_cast<int32_t>(atoi(row[2])) : 0;
|
||||
e.itemname = row[3] ? row[3] : "";
|
||||
e.quantity = row[4] ? static_cast<int32_t>(atoi(row[4])) : 0;
|
||||
e.price = row[5] ? static_cast<int32_t>(atoi(row[5])) : 0;
|
||||
e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.char_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.char_entity_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.char_name = row[3] ? row[3] : "";
|
||||
e.char_zone_id = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 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);
|
||||
}
|
||||
@@ -385,12 +404,14 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.buyslot));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
v.push_back(std::to_string(e.price));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
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(
|
||||
fmt::format(
|
||||
@@ -413,12 +434,14 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.charid));
|
||||
v.push_back(std::to_string(e.buyslot));
|
||||
v.push_back(std::to_string(e.itemid));
|
||||
v.push_back("'" + Strings::Escape(e.itemname) + "'");
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
v.push_back(std::to_string(e.price));
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back("'" + Strings::Escape(e.char_name) + "'");
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
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) + ")");
|
||||
}
|
||||
|
||||
@@ -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 ornamentidfile;
|
||||
int32_t ornament_hero_model;
|
||||
uint64_t guid;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -61,6 +62,7 @@ public:
|
||||
"ornamenticon",
|
||||
"ornamentidfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -83,6 +85,7 @@ public:
|
||||
"ornamenticon",
|
||||
"ornamentidfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -139,6 +142,7 @@ public:
|
||||
e.ornamenticon = 0;
|
||||
e.ornamentidfile = 0;
|
||||
e.ornament_hero_model = 0;
|
||||
e.guid = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -191,6 +195,7 @@ public:
|
||||
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.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;
|
||||
}
|
||||
@@ -240,6 +245,7 @@ public:
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.ornamenticon));
|
||||
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[16] + " = " + std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -277,6 +283,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
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.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -322,6 +329,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
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.guid));
|
||||
|
||||
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.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.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
|
||||
|
||||
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.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.guid = row[16] ? strtoull(row[16], nullptr, 10) : 0;
|
||||
|
||||
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.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -539,6 +550,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
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.guid));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ public:
|
||||
uint32_t ornamenticon;
|
||||
uint32_t ornamentidfile;
|
||||
int32_t ornament_hero_model;
|
||||
uint64_t guid;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -63,6 +64,7 @@ public:
|
||||
"ornamenticon",
|
||||
"ornamentidfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -86,6 +88,7 @@ public:
|
||||
"ornamenticon",
|
||||
"ornamentidfile",
|
||||
"ornament_hero_model",
|
||||
"guid",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -143,6 +146,7 @@ public:
|
||||
e.ornamenticon = 0;
|
||||
e.ornamentidfile = 0;
|
||||
e.ornament_hero_model = 0;
|
||||
e.guid = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -196,6 +200,7 @@ public:
|
||||
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.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;
|
||||
}
|
||||
@@ -246,6 +251,7 @@ public:
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.ornamenticon));
|
||||
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[17] + " = " + std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -284,6 +290,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
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.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -330,6 +337,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
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.guid));
|
||||
|
||||
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.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.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
|
||||
|
||||
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.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.guid = row[17] ? strtoull(row[17], nullptr, 10) : 0;
|
||||
|
||||
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.ornamentidfile));
|
||||
v.push_back(std::to_string(e.ornament_hero_model));
|
||||
v.push_back(std::to_string(e.guid));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -551,6 +562,7 @@ public:
|
||||
v.push_back(std::to_string(e.ornamenticon));
|
||||
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.guid));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -19,31 +19,34 @@
|
||||
class BaseSpellBucketsRepository {
|
||||
public:
|
||||
struct SpellBuckets {
|
||||
uint64_t spellid;
|
||||
std::string key_;
|
||||
std::string value;
|
||||
uint32_t spell_id;
|
||||
std::string bucket_name;
|
||||
std::string bucket_value;
|
||||
uint8_t bucket_comparison;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("spellid");
|
||||
return std::string("spell_id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"spellid",
|
||||
"`key`",
|
||||
"value",
|
||||
"spell_id",
|
||||
"bucket_name",
|
||||
"bucket_value",
|
||||
"bucket_comparison",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"spellid",
|
||||
"`key`",
|
||||
"value",
|
||||
"spell_id",
|
||||
"bucket_name",
|
||||
"bucket_value",
|
||||
"bucket_comparison",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -84,9 +87,10 @@ public:
|
||||
{
|
||||
SpellBuckets e{};
|
||||
|
||||
e.spellid = 0;
|
||||
e.key_ = "";
|
||||
e.value = "";
|
||||
e.spell_id = 0;
|
||||
e.bucket_name = "";
|
||||
e.bucket_value = "";
|
||||
e.bucket_comparison = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -97,7 +101,7 @@ public:
|
||||
)
|
||||
{
|
||||
for (auto &spell_buckets : spell_bucketss) {
|
||||
if (spell_buckets.spellid == spell_buckets_id) {
|
||||
if (spell_buckets.spell_id == spell_buckets_id) {
|
||||
return spell_buckets;
|
||||
}
|
||||
}
|
||||
@@ -123,9 +127,10 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
SpellBuckets e{};
|
||||
|
||||
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.key_ = row[1] ? row[1] : "";
|
||||
e.value = row[2] ? row[2] : "";
|
||||
e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.bucket_name = row[1] ? row[1] : "";
|
||||
e.bucket_value = row[2] ? row[2] : "";
|
||||
e.bucket_comparison = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -159,9 +164,10 @@ public:
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.spellid));
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back(columns[2] + " = '" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.spell_id));
|
||||
v.push_back(columns[1] + " = '" + Strings::Escape(e.bucket_name) + "'");
|
||||
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(
|
||||
fmt::format(
|
||||
@@ -169,7 +175,7 @@ public:
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.spellid
|
||||
e.spell_id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -183,9 +189,10 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(std::to_string(e.bucket_comparison));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -196,7 +203,7 @@ public:
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.spellid = results.LastInsertedID();
|
||||
e.spell_id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -215,9 +222,10 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(std::to_string(e.bucket_comparison));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -251,9 +259,10 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
SpellBuckets e{};
|
||||
|
||||
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.key_ = row[1] ? row[1] : "";
|
||||
e.value = row[2] ? row[2] : "";
|
||||
e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.bucket_name = row[1] ? row[1] : "";
|
||||
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);
|
||||
}
|
||||
@@ -278,9 +287,10 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
SpellBuckets e{};
|
||||
|
||||
e.spellid = row[0] ? strtoull(row[0], nullptr, 10) : 0;
|
||||
e.key_ = row[1] ? row[1] : "";
|
||||
e.value = row[2] ? row[2] : "";
|
||||
e.spell_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.bucket_name = row[1] ? row[1] : "";
|
||||
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);
|
||||
}
|
||||
@@ -355,9 +365,10 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(std::to_string(e.bucket_comparison));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -380,9 +391,10 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.spellid));
|
||||
v.push_back("'" + Strings::Escape(e.key_) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.value) + "'");
|
||||
v.push_back(std::to_string(e.spell_id));
|
||||
v.push_back("'" + Strings::Escape(e.bucket_name) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.bucket_value) + "'");
|
||||
v.push_back(std::to_string(e.bucket_comparison));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -44,7 +44,24 @@ public:
|
||||
*/
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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 "../strings.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 {
|
||||
public:
|
||||
@@ -45,6 +47,93 @@ public:
|
||||
|
||||
// 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
|
||||
|
||||
@@ -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
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
// these are the base definitions for command_subsettings and can be over-ridden by the database
|
||||
std::vector<CommandSubsettingsRepository::CommandSubsettings> static_records = {
|
||||
{.parent_command = "find", .sub_command = "aa", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findaa"},
|
||||
{.parent_command = "find", .sub_command = "body_type", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findbodytype"},
|
||||
{.parent_command = "find", .sub_command = "bug_category", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findbugcategory"},
|
||||
{.parent_command = "find", .sub_command = "character", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findcharacter"},
|
||||
{.parent_command = "find", .sub_command = "class", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findclass"},
|
||||
@@ -60,10 +61,12 @@ public:
|
||||
{.parent_command = "find", .sub_command = "item", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fi|finditem|itemsearch"},
|
||||
{.parent_command = "find", .sub_command = "language", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findlanguage"},
|
||||
{.parent_command = "find", .sub_command = "npc_type", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fn|findnpc|findnpctype"},
|
||||
{.parent_command = "find", .sub_command = "object_type", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findobjecttype"},
|
||||
{.parent_command = "find", .sub_command = "race", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findrace"},
|
||||
{.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 = "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 = "task", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "findtask"},
|
||||
{.parent_command = "find", .sub_command = "zone", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "fz|findzone"},
|
||||
@@ -82,7 +85,6 @@ public:
|
||||
{.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 = "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 = "freeze", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "freeze|unfreeze"},
|
||||
{.parent_command = "set", .sub_command = "gender", .access_level = AccountStatus::QuestTroupe, .top_level_aliases = "gender"},
|
||||
|
||||
@@ -44,7 +44,24 @@ public:
|
||||
*/
|
||||
|
||||
// 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
|
||||
|
||||
@@ -44,7 +44,23 @@ public:
|
||||
*/
|
||||
|
||||
// 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
|
||||
|
||||
@@ -44,7 +44,25 @@ public:
|
||||
*/
|
||||
|
||||
// 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
|
||||
|
||||
@@ -189,6 +189,8 @@ void RuleManager::ResetRules(bool reload) {
|
||||
m_RuleRealValues[ Real__##rule_name ] = default_value;
|
||||
#define RULE_BOOL(category_name, rule_name, default_value, notes) \
|
||||
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"
|
||||
|
||||
// restore these rules to their pre-reset values
|
||||
|
||||
+24
-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, 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, 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, EnableCharacterEXPMods, false, "Enables character zone-based experience modifiers.")
|
||||
RULE_BOOL(Character, PVPEnableGuardFactionAssist, true, "Enables faction based assisting against the aggresor in pvp.")
|
||||
@@ -226,6 +227,9 @@ RULE_INT(Character, ClearXTargetDelay, 10, "Seconds between uses of the #clearxt
|
||||
RULE_BOOL(Character, PreventMountsFromZoning, false, "Enable to prevent mounts from zoning - Prior to December 15, 2004 this is enabled.")
|
||||
RULE_BOOL(Character, GroupInvitesRequireTarget, false, "Enable to require players to have invitee on target (Disables /invite name) - Classic Style")
|
||||
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_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(Mercs)
|
||||
@@ -240,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, ScaleRate, 100, "Merc scale factor")
|
||||
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(Guild)
|
||||
@@ -503,9 +510,11 @@ RULE_BOOL(Spells, RequireMnemonicRetention, true, "Enabling will require spell s
|
||||
RULE_BOOL(Spells, EvacClearCharmPet, false, "Enable to have evac in zone clear charm from charm pets and detach buffs.")
|
||||
RULE_BOOL(Spells, ManaTapsRequireNPCMana, false, "Enabling will require target to have mana to tap. Default off as many npc's are caster class with 0 mana and need fixed.")
|
||||
RULE_INT(Spells, HarmTouchCritRatio, 200, "Harmtouch crit bonus, on top of BaseCritRatio")
|
||||
RULE_BOOL(Spells, UseClassicHarmTouchDamage, false, "Use pre 2007 Harm Touch calculations - Default: False")
|
||||
RULE_BOOL(Spells, UseClassicSpellFocus, false, "Enabling will tell the server to handle random focus damage as classic spell imports lack the limit values.")
|
||||
RULE_BOOL(Spells, ManaTapsOnAnyClass, false, "Enabling this will allow you to cast mana taps on any class, this will bypass ManaTapsRequireNPCMana rule.")
|
||||
RULE_INT(Spells, HealAmountMessageFilterThreshold, 100, "Lifetaps below this threshold will not have a message sent to the client (Heal will still process) 0 to Disable.")
|
||||
RULE_BOOL(Spells, SnareOverridesSpeedBonuses, false, "Enabling will allow snares to override any speed bonuses the entity may have. Default: False")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Combat)
|
||||
@@ -615,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, 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_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, ArcheryBaseDamage, 0, "Archery base damage, default is 0")
|
||||
RULE_INT(Combat, BackstabBaseDamage, 0, "Backstab base damage, default is 0")
|
||||
@@ -663,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_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, 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(Aggro)
|
||||
@@ -688,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, InitialAggroBonus, 100, "Initial 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(TaskSystem)
|
||||
@@ -702,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, 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_INT(TaskSystem, MaxUpdateMessages, 50, "Maximum update messages for non-GiveCash activity types in IncrementDoneCount")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Range)
|
||||
@@ -713,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, 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, 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, CriticalDamage, 80, "The packet range in which critical hit messages are sent")
|
||||
RULE_INT(Range, MobCloseScanDistance, 600, "Close scan distance")
|
||||
@@ -754,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_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, 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(Chat)
|
||||
@@ -800,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_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_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(Mail)
|
||||
@@ -891,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, 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_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(Client)
|
||||
@@ -993,6 +1014,8 @@ RULE_BOOL(Items, DisableNoRent, false, "Enable this to disable No Rent Items")
|
||||
RULE_BOOL(Items, DisableNoTransfer, false, "Enable this to disable No Transfer Items")
|
||||
RULE_BOOL(Items, DisablePotionBelt, false, "Enable this to disable Potion Belt Items")
|
||||
RULE_BOOL(Items, DisableSpellFocusEffects, false, "Enable this to disable Spell Focus Effects on Items")
|
||||
RULE_BOOL(Items, SummonItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots of items in Client::SummonItem")
|
||||
RULE_BOOL(Items, AugmentItemAllowInvisibleAugments, false, "Enable this to allow augments to be put in invisible augment slots by players")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Parcel)
|
||||
|
||||
@@ -140,6 +140,7 @@
|
||||
|
||||
#define ServerOP_TraderMessaging 0x0120
|
||||
#define ServerOP_BazaarPurchase 0x0121
|
||||
#define ServerOP_BuyerMessaging 0x0122
|
||||
|
||||
#define ServerOP_InstanceUpdateTime 0x014F
|
||||
#define ServerOP_AdventureRequest 0x0150
|
||||
|
||||
+99
-67
@@ -46,6 +46,7 @@
|
||||
#include "repositories/character_item_recast_repository.h"
|
||||
#include "repositories/character_corpses_repository.h"
|
||||
#include "repositories/skill_caps_repository.h"
|
||||
#include "repositories/inventory_repository.h"
|
||||
|
||||
namespace ItemField
|
||||
{
|
||||
@@ -300,15 +301,15 @@ bool SharedDatabase::UpdateInventorySlot(uint32 char_id, const EQ::ItemInstance*
|
||||
// Update/Insert item
|
||||
const std::string query = StringFormat("REPLACE INTO inventory "
|
||||
"(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, "
|
||||
"%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>(charges), static_cast<unsigned long>(inst->IsAttuned() ? 1 : 0),
|
||||
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[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);
|
||||
|
||||
// Save bag contents, if slot supports bag contents
|
||||
@@ -651,48 +652,67 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
||||
return false;
|
||||
|
||||
// Retrieve character inventory
|
||||
const std::string query =
|
||||
StringFormat("SELECT slotid, itemid, charges, color, augslot1, augslot2, augslot3, augslot4, augslot5, "
|
||||
"augslot6, instnodrop, custom_data, ornamenticon, ornamentidfile, ornament_hero_model FROM "
|
||||
"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");
|
||||
auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`;", char_id));
|
||||
if (results.empty()) {
|
||||
LogError("Error loading inventory for char_id {} from the database.", char_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto const &row: results) {
|
||||
if (row.guid != 0) {
|
||||
EQ::ItemInstance::AddGUIDToMap(row.guid);
|
||||
}
|
||||
}
|
||||
|
||||
const auto timestamps = GetItemRecastTimestamps(char_id);
|
||||
auto cv_conflict = false;
|
||||
const auto pmask = inv->GetLookup()->PossessionsBitmask;
|
||||
const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank;
|
||||
|
||||
auto cv_conflict = false;
|
||||
const auto pmask = inv->GetLookup()->PossessionsBitmask;
|
||||
const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank;
|
||||
std::vector<InventoryRepository::Inventory> queue{};
|
||||
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;
|
||||
|
||||
for (auto& row = results.begin(); row != results.end(); ++row) {
|
||||
int16 slot_id = Strings::ToInt(row[0]);
|
||||
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 (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) {
|
||||
cv_conflict = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) { // Titanium thru UF check
|
||||
const auto parent_slot = EQ::invslot::GENERAL_BEGIN + ((slot_id - EQ::invbag::GENERAL_BAGS_BEGIN) / EQ::invbag::SLOT_COUNT);
|
||||
else if (slot_id <= EQ::invbag::GENERAL_BAGS_END && slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN) {
|
||||
// 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) {
|
||||
cv_conflict = true;
|
||||
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) {
|
||||
cv_conflict = true;
|
||||
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);
|
||||
if (parent_index >= bank_size) {
|
||||
cv_conflict = true;
|
||||
@@ -700,64 +720,55 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
||||
}
|
||||
}
|
||||
|
||||
uint32 item_id = Strings::ToUnsignedInt(row[1]);
|
||||
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);
|
||||
|
||||
auto *item = GetItem(item_id);
|
||||
if (!item) {
|
||||
LogError("Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]", char_id, item_id,
|
||||
slot_id);
|
||||
LogError(
|
||||
"Warning: charid [{}] has an invalid item_id [{}] in inventory slot [{}]",
|
||||
char_id,
|
||||
item_id,
|
||||
slot_id
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
EQ::ItemInstance *inst = CreateBaseItem(item, charges);
|
||||
|
||||
if (inst == nullptr)
|
||||
auto *inst = CreateBaseItem(item, charges);
|
||||
if (!inst) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (row[11]) {
|
||||
std::string data_str(row[11]);
|
||||
inst->SetCustomDataString(data_str);
|
||||
if (!row.custom_data.empty()) {
|
||||
inst->SetCustomDataString(row.custom_data);
|
||||
}
|
||||
|
||||
inst->SetOrnamentIcon(ornament_icon);
|
||||
inst->SetOrnamentationIDFile(ornament_idfile);
|
||||
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);
|
||||
}
|
||||
|
||||
if (color > 0)
|
||||
if (color > 0) {
|
||||
inst->SetColor(color);
|
||||
}
|
||||
|
||||
if (charges == 0x7FFF)
|
||||
if (charges == 0x7FFF) {
|
||||
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);
|
||||
else
|
||||
}
|
||||
else {
|
||||
inst->SetCharges(charges);
|
||||
}
|
||||
|
||||
if (item->RecastDelay) {
|
||||
if (item->RecastType != RECAST_TYPE_UNLINKED_ITEM && timestamps.count(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));
|
||||
}
|
||||
else {
|
||||
@@ -767,35 +778,50 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv)
|
||||
|
||||
if (item->IsClassCommon()) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int16 put_slot_id;
|
||||
if (slot_id >= 8000 && slot_id <= 8999) {
|
||||
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
|
||||
LogError("Warning: Defunct location for item in inventory: charid={}, item_id={}, slot_id={} .. pushing to cursor...",
|
||||
char_id, item_id, slot_id);
|
||||
LogError(
|
||||
"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);
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
put_slot_id = inv->PutItem(slot_id, *inst);
|
||||
}
|
||||
|
||||
row.guid = inst->GetSerialNumber();
|
||||
queue.push_back(row);
|
||||
|
||||
safe_delete(inst);
|
||||
|
||||
// Save ptr to item in inventory
|
||||
if (put_slot_id == INVALID_INDEX) {
|
||||
LogError("Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]",
|
||||
char_id, item_id, slot_id);
|
||||
LogError(
|
||||
"Warning: Invalid slot_id for item in inventory: charid=[{}], item_id=[{}], slot_id=[{}]",
|
||||
char_id,
|
||||
item_id,
|
||||
slot_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (cv_conflict) {
|
||||
const std::string& char_name = GetCharName(char_id);
|
||||
LogError("ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
|
||||
const std::string &char_name = GetCharName(char_id);
|
||||
LogError(
|
||||
"ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])",
|
||||
char_name,
|
||||
char_id,
|
||||
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
|
||||
return GetSharedBank(char_id, inv, true);
|
||||
}
|
||||
|
||||
@@ -431,6 +431,16 @@ bool IsCharmSpell(uint16 spell_id)
|
||||
return IsEffectInSpell(spell_id, SE_Charm);
|
||||
}
|
||||
|
||||
bool IsResurrectionSicknessSpell(uint16 spell_id) {
|
||||
return (
|
||||
spell_id == SPELL_RESURRECTION_SICKNESS ||
|
||||
spell_id == SPELL_RESURRECTION_SICKNESS2 ||
|
||||
spell_id == SPELL_RESURRECTION_SICKNESS3 ||
|
||||
spell_id == SPELL_RESURRECTION_SICKNESS4 ||
|
||||
spell_id == SPELL_REVIVAL_SICKNESS
|
||||
);
|
||||
}
|
||||
|
||||
bool IsBlindSpell(uint16 spell_id)
|
||||
{
|
||||
return IsEffectInSpell(spell_id, SE_Blind);
|
||||
@@ -2315,3 +2325,108 @@ bool IsCastNotStandingSpell(uint16 spell_id) {
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1522,6 +1522,7 @@ bool IsSummonPetSpell(uint16 spell_id);
|
||||
bool IsSummonPCSpell(uint16 spell_id);
|
||||
bool IsPetSpell(uint16 spell_id);
|
||||
bool IsCharmSpell(uint16 spell_id);
|
||||
bool IsResurrectionSicknessSpell(uint16 spell_id);
|
||||
bool IsBlindSpell(uint16 spell_id);
|
||||
bool IsHealthSpell(uint16 spell_id);
|
||||
bool IsCastTimeReductionSpell(uint16 spell_id);
|
||||
@@ -1624,5 +1625,8 @@ bool IsSpellUsableInThisZoneType(uint16 spell_id, uint8 zone_type);
|
||||
const char *GetSpellName(uint16 spell_id);
|
||||
int GetSpellStatValue(uint16 spell_id, const char* stat_identifier, uint8 slot = 0);
|
||||
bool IsCastRestrictedSpell(uint16 spell_id);
|
||||
bool IsAegolismSpell(uint16 spell_id);
|
||||
bool AegolismStackingIsSymbolSpell(uint16 spell_id);
|
||||
bool AegolismStackingIsArmorClassSpell(uint16 spell_id);
|
||||
|
||||
#endif
|
||||
|
||||
+3
-3
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// these get injected during the build pipeline
|
||||
#define CURRENT_VERSION "22.52.0-dev" // always append -dev to the current version for custom-builds
|
||||
#define CURRENT_VERSION "22.56.0-dev" // always append -dev to the current version for custom-builds
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
@@ -42,8 +42,8 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9280
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9044
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9283
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045
|
||||
|
||||
#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)) {
|
||||
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"));
|
||||
}
|
||||
else {
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "22.52.0",
|
||||
"version": "22.56.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
@@ -433,6 +433,7 @@ OP_TraderShop=0x31df
|
||||
OP_TraderBulkSend=0x6a96
|
||||
OP_Trader=0x4ef5
|
||||
OP_Barter=0x243a
|
||||
OP_BuyerItems=0x1a6a
|
||||
OP_TraderBuy=0x0000
|
||||
OP_ShopItem=0x0000
|
||||
OP_BazaarInspect=0x0000
|
||||
|
||||
+279
-234
@@ -528,276 +528,321 @@ void ClientList::SendOnlineGuildMembers(uint32 FromID, uint32 GuildID)
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
|
||||
void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_Struct* whom, WorldTCPConnection* connection) {
|
||||
try{
|
||||
LinkedListIterator<ClientListEntry*> iterator(clientlist);
|
||||
LinkedListIterator<ClientListEntry*> countclients(clientlist);
|
||||
ClientListEntry* cle = 0;
|
||||
ClientListEntry* countcle = 0;
|
||||
//char tmpgm[25] = "";
|
||||
//char accinfo[150] = "";
|
||||
char line[300] = "";
|
||||
//char tmpguild[50] = "";
|
||||
//char LFG[10] = "";
|
||||
//uint32 x = 0;
|
||||
int whomlen = 0;
|
||||
if (whom) {
|
||||
// fixes for client converting some queries into a race query instead of zone
|
||||
if (whom->wrace == 221) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "scarlet");
|
||||
}
|
||||
if (whom->wrace == 327) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "crystal");
|
||||
}
|
||||
if (whom->wrace == 103) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "kedge");
|
||||
}
|
||||
if (whom->wrace == 230) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "akheva");
|
||||
}
|
||||
if (whom->wrace == 229) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "netherbian");
|
||||
}
|
||||
try {
|
||||
LinkedListIterator<ClientListEntry*> iterator(clientlist);
|
||||
LinkedListIterator<ClientListEntry*> countclients(clientlist);
|
||||
ClientListEntry* cle = 0;
|
||||
ClientListEntry* countcle = 0;
|
||||
//char tmpgm[25] = "";
|
||||
//char accinfo[150] = "";
|
||||
char line[300] = "";
|
||||
//char tmpguild[50] = "";
|
||||
//char LFG[10] = "";
|
||||
//uint32 x = 0;
|
||||
int whomlen = 0;
|
||||
|
||||
whomlen = strlen(whom->whom);
|
||||
if(whom->wrace == 0x001A) // 0x001A is the old Froglok race number and is sent by the client for /who all froglok
|
||||
whom->wrace = FROGLOK; // This is what EQEmu uses for the Froglok Race number.
|
||||
}
|
||||
|
||||
uint32 totalusers=0;
|
||||
uint32 totallength=0;
|
||||
countclients.Reset();
|
||||
while(countclients.MoreElements()){
|
||||
countcle = countclients.GetData();
|
||||
const char* tmpZone = ZoneName(countcle->zone());
|
||||
if (
|
||||
(countcle->Online() >= CLE_Status::Zoning) &&
|
||||
(!countcle->GetGM() || countcle->Anon() != 1 || admin >= countcle->Admin()) &&
|
||||
(whom == 0 || (
|
||||
((countcle->Admin() >= AccountStatus::QuestTroupe && countcle->GetGM()) || whom->gmlookup == 0xFFFF) &&
|
||||
(whom->lvllow == 0xFFFF || (countcle->level() >= whom->lvllow && countcle->level() <= whom->lvlhigh && (countcle->Anon()==0 || admin > countcle->Admin()))) &&
|
||||
(whom->wclass == 0xFFFF || (countcle->class_() == whom->wclass && (countcle->Anon()==0 || admin > countcle->Admin()))) &&
|
||||
(whom->wrace == 0xFFFF || (countcle->race() == whom->wrace && (countcle->Anon()==0 || admin > countcle->Admin()))) &&
|
||||
(whomlen == 0 || (
|
||||
(tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) ||
|
||||
strncasecmp(countcle->name(),whom->whom, whomlen) == 0 ||
|
||||
(strncasecmp(guild_mgr.GetGuildName(countcle->GuildID()), whom->whom, whomlen) == 0) ||
|
||||
(admin >= AccountStatus::GMAdmin && strncasecmp(countcle->AccountName(), whom->whom, whomlen) == 0)
|
||||
))
|
||||
))
|
||||
) {
|
||||
if((countcle->Anon()>0 && admin >= countcle->Admin() && admin > AccountStatus::Player) || countcle->Anon()==0 ){
|
||||
totalusers++;
|
||||
if(totalusers<=20 || admin >= AccountStatus::GMAdmin)
|
||||
totallength=totallength+strlen(countcle->name())+strlen(countcle->AccountName())+strlen(guild_mgr.GetGuildName(countcle->GuildID()))+5;
|
||||
if (whom) {
|
||||
// fixes for client converting some queries into a race query instead of zone
|
||||
if (whom->wrace == 221) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "scarlet");
|
||||
}
|
||||
else if((countcle->Anon()>0 && admin<=countcle->Admin()) || (countcle->Anon()==0 && !countcle->GetGM())) {
|
||||
totalusers++;
|
||||
if(totalusers<=20 || admin >= AccountStatus::GMAdmin)
|
||||
totallength=totallength+strlen(countcle->name())+strlen(guild_mgr.GetGuildName(countcle->GuildID()))+5;
|
||||
|
||||
if (whom->wrace == 327) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "crystal");
|
||||
}
|
||||
|
||||
if (whom->wrace == 103) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "kedge");
|
||||
}
|
||||
|
||||
if (whom->wrace == 230) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "akheva");
|
||||
}
|
||||
|
||||
if (whom->wrace == 229) {
|
||||
whom->wrace = 0xFFFF;
|
||||
strcpy(whom->whom, "netherbian");
|
||||
}
|
||||
|
||||
whomlen = strlen(whom->whom);
|
||||
|
||||
if (whom->wrace == 0x001A) { // 0x001A is the old Froglok race number and is sent by the client for /who all froglok
|
||||
whom->wrace = FROGLOK; // This is what EQEmu uses for the Froglok Race number.
|
||||
}
|
||||
}
|
||||
countclients.Advance();
|
||||
}
|
||||
uint32 plid=fromid;
|
||||
uint32 playerineqstring=5001;
|
||||
const char line2[]="---------------------------";
|
||||
uint8 unknown35=0x0A;
|
||||
uint32 unknown36=0;
|
||||
uint32 playersinzonestring=5028;
|
||||
if(totalusers>20 && admin<AccountStatus::GMAdmin){
|
||||
totalusers=20;
|
||||
playersinzonestring=5033;
|
||||
}
|
||||
else if(totalusers>1)
|
||||
playersinzonestring=5036;
|
||||
uint32 unknown44[2];
|
||||
unknown44[0]=0;
|
||||
unknown44[1]=0;
|
||||
uint32 unknown52=totalusers;
|
||||
uint32 unknown56=1;
|
||||
auto pack2 = new ServerPacket(ServerOP_WhoAllReply, 64 + totallength + (49 * totalusers));
|
||||
memset(pack2->pBuffer,0,pack2->size);
|
||||
uchar *buffer=pack2->pBuffer;
|
||||
uchar *bufptr=buffer;
|
||||
//memset(buffer,0,pack2->size);
|
||||
memcpy(bufptr,&plid, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&playerineqstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&line2, strlen(line2));
|
||||
bufptr+=strlen(line2);
|
||||
memcpy(bufptr,&unknown35, sizeof(uint8));
|
||||
bufptr+=sizeof(uint8);
|
||||
memcpy(bufptr,&unknown36, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&playersinzonestring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown44[0], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown44[1], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown52, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown56, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&totalusers, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
|
||||
iterator.Reset();
|
||||
int idx=-1;
|
||||
while(iterator.MoreElements()) {
|
||||
cle = iterator.GetData();
|
||||
const char* tmpZone = ZoneName(cle->zone());
|
||||
uint32 totalusers=0;
|
||||
uint32 totallength=0;
|
||||
countclients.Reset();
|
||||
while (countclients.MoreElements()) {
|
||||
countcle = countclients.GetData();
|
||||
const char* tmpZone = ZoneName(countcle->zone());
|
||||
if (
|
||||
(countcle->Online() >= CLE_Status::Zoning) &&
|
||||
(!countcle->GetGM() || countcle->Anon() != 1 || admin >= countcle->Admin()) &&
|
||||
(whom == 0 || (
|
||||
((countcle->Admin() >= AccountStatus::QuestTroupe && countcle->GetGM()) || whom->gmlookup == 0xFFFF) &&
|
||||
(whom->lvllow == 0xFFFF ||
|
||||
(countcle->level() >= whom->lvllow && countcle->level() <= whom->lvlhigh &&
|
||||
(countcle->Anon() == 0 || admin > countcle->Admin()))) &&
|
||||
(whom->wclass == 0xFFFF || (countcle->class_() == whom->wclass &&
|
||||
(countcle->Anon() == 0 || admin > countcle->Admin()))) &&
|
||||
(whom->wrace == 0xFFFF ||
|
||||
(countcle->race() == whom->wrace && (countcle->Anon() == 0 || admin > countcle->Admin()))) &&
|
||||
(whomlen == 0 || (
|
||||
(tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) ||
|
||||
strncasecmp(countcle->name(),whom->whom, whomlen) == 0 ||
|
||||
(strncasecmp(guild_mgr.GetGuildName(countcle->GuildID()), whom->whom, whomlen) == 0) ||
|
||||
(admin >= AccountStatus::GMAdmin && strncasecmp(countcle->AccountName(), whom->whom, whomlen) == 0)
|
||||
))
|
||||
))
|
||||
) {
|
||||
// these blocks can all be condensed but it's simpler to conceptualize this way
|
||||
if ((countcle->Anon()>0 && admin >= countcle->Admin() && admin > AccountStatus::Player) || countcle->Anon()==0 ) {
|
||||
totalusers++;
|
||||
if (totalusers<=20 || admin >= AccountStatus::GMAdmin) {
|
||||
totallength = totallength + strlen(countcle->name()) + strlen(countcle->AccountName()) +
|
||||
strlen(guild_mgr.GetGuildName(countcle->GuildID())) + 5;
|
||||
}
|
||||
} else if (((countcle->Anon() == 1 && admin <= countcle->Admin()) && whomlen != 0 &&
|
||||
strncasecmp(countcle->name(), whom->whom, whomlen) == 0)) {
|
||||
totalusers++;
|
||||
if (totalusers <= 20 || admin >= AccountStatus::GMAdmin) {
|
||||
totallength = totallength + strlen(countcle->name()) + strlen(countcle->AccountName()) +
|
||||
strlen(guild_mgr.GetGuildName(countcle->GuildID())) + 5;
|
||||
}
|
||||
} else if (((countcle->Anon() == 2 && admin <= countcle->Admin()) && whomlen != 0 &&
|
||||
(strncasecmp(countcle->name(), whom->whom, whomlen) == 0 ||
|
||||
strncasecmp(guild_mgr.GetGuildName(countcle->GuildID()), whom->whom, whomlen) == 0))) {
|
||||
totalusers++;
|
||||
if (totalusers <= 20 || admin >= AccountStatus::GMAdmin) {
|
||||
totallength = totallength + strlen(countcle->name()) + strlen(countcle->AccountName()) +
|
||||
strlen(guild_mgr.GetGuildName(countcle->GuildID())) + 5;
|
||||
}
|
||||
}
|
||||
}
|
||||
countclients.Advance();
|
||||
}
|
||||
|
||||
if (
|
||||
(cle->Online() >= CLE_Status::Zoning) &&
|
||||
(!cle->GetGM() || cle->Anon() != 1 || admin >= cle->Admin()) &&
|
||||
(whom == 0 || (
|
||||
((cle->Admin() >= AccountStatus::QuestTroupe && cle->GetGM()) || whom->gmlookup == 0xFFFF) &&
|
||||
(whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whom->wclass == 0xFFFF || (cle->class_() == whom->wclass && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whom->wrace == 0xFFFF || (cle->race() == whom->wrace && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whomlen == 0 || (
|
||||
(tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) ||
|
||||
strncasecmp(cle->name(),whom->whom, whomlen) == 0 ||
|
||||
(strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) ||
|
||||
(admin >= AccountStatus::GMAdmin && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0)
|
||||
))
|
||||
))
|
||||
) {
|
||||
line[0] = 0;
|
||||
uint32 rankstring = 0xFFFFFFFF;
|
||||
if((cle->Anon()==1 && cle->GetGM() && cle->Admin()>admin) || (idx>=20 && admin < AccountStatus::GMAdmin)){ //hide gms that are anon from lesser gms and normal players, cut off at 20
|
||||
uint32 plid=fromid;
|
||||
uint32 playerineqstring=5001;
|
||||
const char line2[]="---------------------------";
|
||||
uint8 unknown35=0x0A;
|
||||
uint32 unknown36=0;
|
||||
uint32 playersinzonestring=5028;
|
||||
|
||||
if (totalusers>20 && admin<AccountStatus::GMAdmin) {
|
||||
totalusers=20;
|
||||
playersinzonestring=5033;
|
||||
} else if(totalusers>1) {
|
||||
playersinzonestring=5036;
|
||||
}
|
||||
|
||||
uint32 unknown44[2];
|
||||
unknown44[0]=0;
|
||||
unknown44[1]=0;
|
||||
uint32 unknown52=totalusers;
|
||||
uint32 unknown56=1;
|
||||
auto pack2 = new ServerPacket(ServerOP_WhoAllReply, 64 + totallength + (49 * totalusers));
|
||||
memset(pack2->pBuffer,0,pack2->size);
|
||||
uchar *buffer=pack2->pBuffer;
|
||||
uchar *bufptr=buffer;
|
||||
//memset(buffer,0,pack2->size);
|
||||
memcpy(bufptr,&plid, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&playerineqstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&line2, strlen(line2));
|
||||
bufptr+=strlen(line2);
|
||||
memcpy(bufptr,&unknown35, sizeof(uint8));
|
||||
bufptr+=sizeof(uint8);
|
||||
memcpy(bufptr,&unknown36, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&playersinzonestring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown44[0], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown44[1], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown52, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown56, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&totalusers, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
|
||||
iterator.Reset();
|
||||
int idx=-1;
|
||||
while(iterator.MoreElements()) {
|
||||
cle = iterator.GetData();
|
||||
const char* tmpZone = ZoneName(cle->zone());
|
||||
|
||||
if (
|
||||
(cle->Online() >= CLE_Status::Zoning) &&
|
||||
(!cle->GetGM() || cle->Anon() != 1 || admin >= cle->Admin()) &&
|
||||
(whom == 0 || (
|
||||
((cle->Admin() >= AccountStatus::QuestTroupe && cle->GetGM()) || whom->gmlookup == 0xFFFF) &&
|
||||
(whom->lvllow == 0xFFFF || (cle->level() >= whom->lvllow && cle->level() <= whom->lvlhigh && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whom->wclass == 0xFFFF || (cle->class_() == whom->wclass && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whom->wrace == 0xFFFF || (cle->race() == whom->wrace && (cle->Anon()==0 || admin>cle->Admin()))) &&
|
||||
(whomlen == 0 || (
|
||||
(tmpZone != 0 && strncasecmp(tmpZone, whom->whom, whomlen) == 0) ||
|
||||
strncasecmp(cle->name(),whom->whom, whomlen) == 0 ||
|
||||
(strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) == 0) ||
|
||||
(admin >= AccountStatus::GMAdmin && strncasecmp(cle->AccountName(), whom->whom, whomlen) == 0)
|
||||
))
|
||||
))
|
||||
) {
|
||||
line[0] = 0;
|
||||
uint32 rankstring = 0xFFFFFFFF;
|
||||
// These lines can be simplified but easier to conceptualize this way
|
||||
if ((cle->Anon()==1 && cle->GetGM() && cle->Admin()>admin) || (idx>=20 && admin < AccountStatus::GMAdmin)) { //hide gms that are anon from lesser gms and normal players, cut off at 20
|
||||
rankstring = 0;
|
||||
iterator.Advance();
|
||||
continue;
|
||||
} else if (cle->Anon() == 1 && cle->Admin()>=admin && (whomlen == 0 || (whomlen !=0 && strncasecmp(cle->name(), whom->whom, whomlen) != 0))) {
|
||||
rankstring = 0;
|
||||
iterator.Advance();
|
||||
continue;
|
||||
} else if (cle->Anon() == 2 && cle->Admin()>=admin && (whomlen == 0 || (whomlen !=0 && strncasecmp(cle->name(), whom->whom, whomlen) != 0 && strncasecmp(guild_mgr.GetGuildName(cle->GuildID()), whom->whom, whomlen) != 0))) {
|
||||
rankstring = 0;
|
||||
iterator.Advance();
|
||||
continue;
|
||||
} else if (cle->GetGM()) {
|
||||
if (cle->Admin() >= AccountStatus::GMImpossible)
|
||||
if (cle->Admin() >= AccountStatus::GMImpossible) {
|
||||
rankstring = 5021;
|
||||
else if (cle->Admin() >= AccountStatus::GMMgmt)
|
||||
} else if (cle->Admin() >= AccountStatus::GMMgmt) {
|
||||
rankstring = 5020;
|
||||
else if (cle->Admin() >= AccountStatus::GMCoder)
|
||||
} else if (cle->Admin() >= AccountStatus::GMCoder) {
|
||||
rankstring = 5019;
|
||||
else if (cle->Admin() >= AccountStatus::GMAreas)
|
||||
} else if (cle->Admin() >= AccountStatus::GMAreas) {
|
||||
rankstring = 5018;
|
||||
else if (cle->Admin() >= AccountStatus::QuestMaster)
|
||||
} else if (cle->Admin() >= AccountStatus::QuestMaster) {
|
||||
rankstring = 5017;
|
||||
else if (cle->Admin() >= AccountStatus::GMLeadAdmin)
|
||||
} else if (cle->Admin() >= AccountStatus::GMLeadAdmin) {
|
||||
rankstring = 5016;
|
||||
else if (cle->Admin() >= AccountStatus::GMAdmin)
|
||||
} else if (cle->Admin() >= AccountStatus::GMAdmin) {
|
||||
rankstring = 5015;
|
||||
else if (cle->Admin() >= AccountStatus::GMStaff)
|
||||
} else if (cle->Admin() >= AccountStatus::GMStaff) {
|
||||
rankstring = 5014;
|
||||
else if (cle->Admin() >= AccountStatus::EQSupport)
|
||||
} else if (cle->Admin() >= AccountStatus::EQSupport) {
|
||||
rankstring = 5013;
|
||||
else if (cle->Admin() >= AccountStatus::GMTester)
|
||||
} else if (cle->Admin() >= AccountStatus::GMTester) {
|
||||
rankstring = 5012;
|
||||
else if (cle->Admin() >= AccountStatus::SeniorGuide)
|
||||
} else if (cle->Admin() >= AccountStatus::SeniorGuide) {
|
||||
rankstring = 5011;
|
||||
else if (cle->Admin() >= AccountStatus::QuestTroupe)
|
||||
} else if (cle->Admin() >= AccountStatus::QuestTroupe) {
|
||||
rankstring = 5010;
|
||||
else if (cle->Admin() >= AccountStatus::Guide)
|
||||
} else if (cle->Admin() >= AccountStatus::Guide) {
|
||||
rankstring = 5009;
|
||||
else if (cle->Admin() >= AccountStatus::ApprenticeGuide)
|
||||
} else if (cle->Admin() >= AccountStatus::ApprenticeGuide) {
|
||||
rankstring = 5008;
|
||||
else if (cle->Admin() >= AccountStatus::Steward)
|
||||
} else if (cle->Admin() >= AccountStatus::Steward) {
|
||||
rankstring = 5007;
|
||||
}
|
||||
}
|
||||
idx++;
|
||||
char guildbuffer[67]={0};
|
||||
if (cle->GuildID() != GUILD_NONE && cle->GuildID()>0)
|
||||
sprintf(guildbuffer,"<%s>", guild_mgr.GetGuildName(cle->GuildID()));
|
||||
uint32 formatstring=5025;
|
||||
if(cle->Anon()==1 && (admin<cle->Admin() || admin == AccountStatus::Player))
|
||||
formatstring=5024;
|
||||
else if(cle->Anon()==1 && admin>=cle->Admin() && admin > AccountStatus::Player)
|
||||
formatstring=5022;
|
||||
else if(cle->Anon()==2 && (admin<cle->Admin() || admin == AccountStatus::Player))
|
||||
formatstring=5023;//display guild
|
||||
else if(cle->Anon()==2 && admin>=cle->Admin() && admin > AccountStatus::Player)
|
||||
formatstring=5022;//display everything
|
||||
|
||||
//war* wars2 = (war*)pack2->pBuffer;
|
||||
idx++;
|
||||
char guildbuffer[67]={0};
|
||||
|
||||
uint32 plclass_=0;
|
||||
uint32 pllevel=0;
|
||||
uint32 pidstring=0xFFFFFFFF;//5003;
|
||||
uint32 plrace=0;
|
||||
uint32 zonestring=0xFFFFFFFF;
|
||||
uint32 plzone=0;
|
||||
uint32 unknown80[2];
|
||||
if(cle->Anon()==0 || (admin>=cle->Admin() && admin> AccountStatus::Player)){
|
||||
plclass_=cle->class_();
|
||||
pllevel=cle->level();
|
||||
if(admin>=AccountStatus::GMAdmin)
|
||||
pidstring=5003;
|
||||
plrace=cle->race();
|
||||
zonestring=5006;
|
||||
plzone=cle->zone();
|
||||
}
|
||||
if (cle->GuildID() != GUILD_NONE && cle->GuildID()>0) {
|
||||
sprintf(guildbuffer,"<%s>", guild_mgr.GetGuildName(cle->GuildID()));
|
||||
}
|
||||
|
||||
uint32 formatstring=5025;
|
||||
|
||||
if(admin>=cle->Admin() && admin > AccountStatus::Player)
|
||||
unknown80[0]=cle->Admin();
|
||||
else
|
||||
unknown80[0]=0xFFFFFFFF;
|
||||
unknown80[1]=0xFFFFFFFF;//1035
|
||||
if (cle->Anon()==1 && (admin<cle->Admin() || admin == AccountStatus::Player)) {
|
||||
formatstring=5024;
|
||||
} else if(cle->Anon()==1 && admin>=cle->Admin() && admin > AccountStatus::Player) {
|
||||
formatstring=5022;
|
||||
} else if(cle->Anon()==2 && (admin<cle->Admin() || admin == AccountStatus::Player)) {
|
||||
formatstring=5023;//display guild
|
||||
} else if(cle->Anon()==2 && admin>=cle->Admin() && admin > AccountStatus::Player) {
|
||||
formatstring=5022;//display everything
|
||||
}
|
||||
|
||||
//war* wars2 = (war*)pack2->pBuffer;
|
||||
|
||||
//char plstatus[20]={0};
|
||||
//sprintf(plstatus, "Status %i",cle->Admin());
|
||||
char plname[64]={0};
|
||||
strcpy(plname,cle->name());
|
||||
uint32 plclass_=0;
|
||||
uint32 pllevel=0;
|
||||
uint32 pidstring=0xFFFFFFFF;//5003;
|
||||
uint32 plrace=0;
|
||||
uint32 zonestring=0xFFFFFFFF;
|
||||
uint32 plzone=0;
|
||||
uint32 unknown80[2];
|
||||
|
||||
char placcount[30]={0};
|
||||
if(admin>=cle->Admin() && admin > AccountStatus::Player)
|
||||
strcpy(placcount,cle->AccountName());
|
||||
if (cle->Anon()==0 || (admin>=cle->Admin() && admin> AccountStatus::Player)) {
|
||||
plclass_=cle->class_();
|
||||
pllevel=cle->level();
|
||||
|
||||
memcpy(bufptr,&formatstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&pidstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plname, strlen(plname)+1);
|
||||
bufptr+=strlen(plname)+1;
|
||||
memcpy(bufptr,&rankstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&guildbuffer, strlen(guildbuffer)+1);
|
||||
bufptr+=strlen(guildbuffer)+1;
|
||||
memcpy(bufptr,&unknown80[0], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown80[1], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&zonestring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plzone, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plclass_, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&pllevel, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plrace, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
uint32 ending=0;
|
||||
memcpy(bufptr,&placcount, strlen(placcount)+1);
|
||||
bufptr+=strlen(placcount)+1;
|
||||
ending=207;
|
||||
memcpy(bufptr,&ending, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
if(admin>=AccountStatus::GMAdmin) {
|
||||
pidstring=5003;
|
||||
}
|
||||
plrace=cle->race();
|
||||
zonestring=5006;
|
||||
plzone=cle->zone();
|
||||
}
|
||||
|
||||
if (admin>=cle->Admin() && admin > AccountStatus::Player) {
|
||||
unknown80[0]=cle->Admin();
|
||||
} else {
|
||||
unknown80[0]=0xFFFFFFFF;
|
||||
}
|
||||
|
||||
unknown80[1]=0xFFFFFFFF;//1035
|
||||
|
||||
//char plstatus[20]={0};
|
||||
//sprintf(plstatus, "Status %i",cle->Admin());
|
||||
char plname[64]={0};
|
||||
strcpy(plname,cle->name());
|
||||
|
||||
char placcount[30]={0};
|
||||
if (admin>=cle->Admin() && admin > AccountStatus::Player) {
|
||||
strcpy(placcount,cle->AccountName());
|
||||
}
|
||||
|
||||
memcpy(bufptr,&formatstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&pidstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plname, strlen(plname)+1);
|
||||
bufptr+=strlen(plname)+1;
|
||||
memcpy(bufptr,&rankstring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&guildbuffer, strlen(guildbuffer)+1);
|
||||
bufptr+=strlen(guildbuffer)+1;
|
||||
memcpy(bufptr,&unknown80[0], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&unknown80[1], sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&zonestring, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plzone, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plclass_, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&pllevel, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
memcpy(bufptr,&plrace, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
uint32 ending=0;
|
||||
memcpy(bufptr,&placcount, strlen(placcount)+1);
|
||||
bufptr+=strlen(placcount)+1;
|
||||
ending=207;
|
||||
memcpy(bufptr,&ending, sizeof(uint32));
|
||||
bufptr+=sizeof(uint32);
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
iterator.Advance();
|
||||
}
|
||||
//zoneserver_list.SendPacket(pack2); // NO NO NO WHY WOULD YOU SEND IT TO EVERY ZONE SERVER?!?
|
||||
SendPacket(to,pack2);
|
||||
safe_delete(pack2);
|
||||
}
|
||||
catch(...){
|
||||
|
||||
SendPacket(to,pack2);
|
||||
safe_delete(pack2);
|
||||
} catch(...) {
|
||||
LogInfo("Unknown error in world's SendWhoAll (probably mem error), ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -289,6 +289,10 @@ bool WorldBoot::DatabaseLoadRoutines(int argc, char **argv)
|
||||
LogInfo("Clearing inventory snapshots");
|
||||
database.ClearInvSnapshots();
|
||||
LogInfo("Loading items");
|
||||
LogInfo("Clearing trader table details");
|
||||
database.ClearTraderDetails();
|
||||
database.ClearBuyerDetails();
|
||||
LogInfo("Clearing buyer table details");
|
||||
|
||||
if (!content_db.LoadItems(hotfix_name)) {
|
||||
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);
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerOP_BazaarPurchase: {
|
||||
@@ -1753,9 +1752,40 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
"ServerOP_BazaarPurchase",
|
||||
in->trader_buy_struct.trader_id
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
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: {
|
||||
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);
|
||||
|
||||
+95
-7
@@ -222,7 +222,7 @@ void NPC::DescribeAggro(Client *to_who, Mob *mob, bool verbose) {
|
||||
if (
|
||||
GetLevel() < RuleI(Aggro, MinAggroLevel) &&
|
||||
mob->GetLevelCon(GetLevel()) == ConsiderColor::Gray &&
|
||||
GetBodyType() != BT_Undead &&
|
||||
GetBodyType() != BodyType::Undead &&
|
||||
!AlwaysAggro()
|
||||
) {
|
||||
to_who->Message(
|
||||
@@ -452,8 +452,26 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't aggro new clients if we are already engaged unless SpecialAbility::ProximityAggro is set
|
||||
if (IsEngaged() && (!GetSpecialAbility(SpecialAbility::ProximityAggro) || (GetSpecialAbility(SpecialAbility::ProximityAggro) && !CombatRange(mob)))) {
|
||||
// Don't aggro new clients if we are already engaged unless PROX_AGGRO is set
|
||||
// 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(
|
||||
"[{}] is in combat, and does not have prox_aggro, or does and is out of combat range with [{}]",
|
||||
GetName(),
|
||||
@@ -496,7 +514,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
RuleB(Aggro, UseLevelAggro) &&
|
||||
(
|
||||
GetLevel() >= RuleI(Aggro, MinAggroLevel) ||
|
||||
GetBodyType() == BT_Undead ||
|
||||
GetBodyType() == BodyType::Undead ||
|
||||
AlwaysAggro() ||
|
||||
(
|
||||
mob->IsClient() &&
|
||||
@@ -524,7 +542,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
} else {
|
||||
if (
|
||||
(
|
||||
(RuleB(Aggro, UndeadAlwaysAggro) && GetBodyType() == BT_Undead) ||
|
||||
(RuleB(Aggro, UndeadAlwaysAggro) && GetBodyType() == BodyType::Undead) ||
|
||||
(GetINT() <= RuleI(Aggro, IntAggroThreshold)) ||
|
||||
AlwaysAggro() ||
|
||||
(
|
||||
@@ -564,6 +582,76 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
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)
|
||||
{
|
||||
// Return a list of how many non-feared, non-mezzed, non-green mobs, within aggro range, hate *attacker
|
||||
@@ -671,9 +759,9 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack)
|
||||
target_owner = nullptr;
|
||||
|
||||
//cannot hurt untargetable mobs
|
||||
bodyType bt = target->GetBodyType();
|
||||
uint8 bt = target->GetBodyType();
|
||||
|
||||
if(bt == BT_NoTarget || bt == BT_NoTarget2) {
|
||||
if(bt == BodyType::NoTarget || bt == BodyType::NoTarget2) {
|
||||
if (RuleB(Pets, UnTargetableSwarmPet)) {
|
||||
if (target->IsNPC()) {
|
||||
if (!target->CastToNPC()->GetSwarmOwner()) {
|
||||
|
||||
@@ -651,7 +651,6 @@ Json::Value ApiGetClientListDetail(EQ::Net::WebsocketServerConnection *connectio
|
||||
row["base_wis"] = client->GetBaseWIS();
|
||||
row["become_npc_level"] = client->GetBecomeNPCLevel();
|
||||
row["boat_id"] = client->GetBoatID();
|
||||
row["buyer_welcome_message"] = client->GetBuyerWelcomeMessage();
|
||||
row["calc_atk"] = client->CalcATK();
|
||||
row["calc_base_mana"] = client->CalcBaseMana();
|
||||
row["calc_current_weight"] = client->CalcCurrentWeight();
|
||||
|
||||
+312
-229
@@ -1478,8 +1478,10 @@ int64 Mob::DoDamageCaps(int64 base_damage)
|
||||
//SYNC WITH: tune.cpp, mob.h TuneDoAttack
|
||||
void Mob::DoAttack(Mob *other, DamageHitInfo &hit, ExtraAttackOptions *opts, bool FromRiposte)
|
||||
{
|
||||
if (!other)
|
||||
if (!other) {
|
||||
return;
|
||||
}
|
||||
|
||||
LogCombat("[{}]::DoAttack vs [{}] base [{}] min [{}] offense [{}] tohit [{}] skill [{}]", GetName(),
|
||||
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 (int strike_through = itembonuses.StrikeThrough + spellbonuses.StrikeThrough + aabonuses.StrikeThrough;
|
||||
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
|
||||
}
|
||||
|
||||
if (hit.damage_done == DMG_RIPOSTED) {
|
||||
DoRiposte(other);
|
||||
return;
|
||||
}
|
||||
|
||||
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_resist = other->spellbonuses.StunResist + other->itembonuses.StunResist + other->aabonuses.StunResist;
|
||||
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)) {
|
||||
other->MessageString(Chat::Stun, SHAKE_OFF_STUN);
|
||||
other->FilteredMessageString(
|
||||
this,
|
||||
Chat::Stun,
|
||||
FilterStuns,
|
||||
SHAKE_OFF_STUN
|
||||
);
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
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...
|
||||
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)
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
bool died = false;
|
||||
if (damage > 0) {
|
||||
//if there is some damage being done and theres an attacker involved
|
||||
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.
|
||||
SetHP(int64(GetHP() - damage));
|
||||
int old_hp_ratio = (int)GetHPRatio();
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
int64 damage_override = 0;
|
||||
|
||||
if (has_given_event && attacker) {
|
||||
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()) {
|
||||
bool IsSaved = false;
|
||||
|
||||
if (TryDivineSave())
|
||||
if (TryDivineSave()) {
|
||||
IsSaved = true;
|
||||
}
|
||||
|
||||
if (!IsSaved && !TrySpellOnDeath()) {
|
||||
SetHP(-500);
|
||||
if (IsNPC()) {
|
||||
died = !CastToNPC()->GetDepop();
|
||||
} else if (IsClient()) {
|
||||
died = CastToClient()->CheckIfAlreadyDead();
|
||||
}
|
||||
|
||||
if (died) {
|
||||
SetHP(-500);
|
||||
}
|
||||
|
||||
// killedByType is clarified in Client::Death if we are client.
|
||||
if (Death(attacker, damage, spell_id, skill_used, KilledByTypes::Killed_NPC, iBuffTic)) {
|
||||
return;
|
||||
@@ -4485,32 +4533,59 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
Stun(RuleI(Combat, StunDuration));
|
||||
if (RuleB(Combat, ClientStunMessage) && attacker->IsClient()) {
|
||||
if (attacker) {
|
||||
entity_list.MessageClose(this, true, 500, Chat::Emote, "%s is stunned after being bashed by %s.", GetCleanName(), attacker->GetCleanName());
|
||||
}
|
||||
else {
|
||||
entity_list.MessageClose(this, true, 500, Chat::Emote, "%s is stunned by a bash to the head.", GetCleanName());
|
||||
entity_list.FilteredMessageClose(
|
||||
this,
|
||||
true,
|
||||
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!
|
||||
if (IsClient())
|
||||
MessageString(Chat::Stun, SHAKE_OFF_STUN);
|
||||
if (IsClient()) {
|
||||
FilteredMessageString(
|
||||
this,
|
||||
Chat::Stun,
|
||||
FilterStuns,
|
||||
SHAKE_OFF_STUN
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// stun resist 2 passed!
|
||||
if (IsClient()) {
|
||||
FilteredMessageString(
|
||||
this,
|
||||
Chat::Stun,
|
||||
FilterStuns,
|
||||
AVOID_STUNNING_BLOW
|
||||
);
|
||||
}
|
||||
}
|
||||
else {
|
||||
// stun resist 2 passed!
|
||||
if (IsClient())
|
||||
MessageString(Chat::Stun, AVOID_STUNNING_BLOW);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
// main stun failed -- extra interrupt roll
|
||||
if (IsCasting() &&
|
||||
!EQ::ValueWithin(casting_spell_id, 859, 1023)) // these spells are excluded
|
||||
// 90% chance >< -- stun immune won't reach this branch though :(
|
||||
if (zone->random.Int(0, 9) > 1)
|
||||
// these spells are excluded
|
||||
// 90% chance >< -- stun immune won't reach this branch though :(
|
||||
if (IsCasting() && !EQ::ValueWithin(casting_spell_id, 859, 1023)) {
|
||||
if (zone->random.Int(0, 9) > 1) {
|
||||
InterruptSpell();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4529,8 +4604,21 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
}
|
||||
|
||||
//send an HP update if we are hurt
|
||||
if (GetHP() < GetMaxHP()) {
|
||||
SendHPUpdate(); // the OP_Damage actually updates the client in these cases, so we skip the HP update for them
|
||||
if(GetHP() < GetMaxHP())
|
||||
{
|
||||
// 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`
|
||||
|
||||
@@ -4593,10 +4681,10 @@ void Mob::CommonDamage(Mob* attacker, int64 &damage, const uint16 spell_id, cons
|
||||
//this was done to simplify the code here (since we can only effectively skip one mob on queue)
|
||||
eqFilterType filter;
|
||||
Mob* skip = attacker;
|
||||
if (attacker && attacker->IsPet() && !attacker->IsBot()) {
|
||||
Mob* owner = attacker ? attacker->GetOwner() : nullptr;
|
||||
if (attacker && owner && !attacker->IsBot()) {
|
||||
//attacker is a pet, let pet owners see their pet's damage
|
||||
Mob* owner = attacker->GetOwner();
|
||||
if (owner && owner->IsClient()) {
|
||||
if (owner->IsClient()) {
|
||||
if (FromDamageShield && damage > 0) {
|
||||
//special crap for spell damage, looks hackish to me
|
||||
char val1[20] = { 0 };
|
||||
@@ -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)
|
||||
{
|
||||
#ifdef LUA_EQEMU
|
||||
@@ -5494,8 +5487,9 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
||||
}
|
||||
#endif
|
||||
|
||||
if (hit.damage_done < 1 || !defender)
|
||||
if (hit.damage_done < 1 || !defender) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
@@ -5513,112 +5507,201 @@ void Mob::TryCriticalHit(Mob *defender, DamageHitInfo &hit, ExtraAttackOptions *
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1: Check if we are critting
|
||||
if (!RollMeleeCritCheck(defender, hit.skill)) {
|
||||
return;
|
||||
}
|
||||
// 1: Try Slay Undead
|
||||
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];
|
||||
|
||||
int crit_mod = EQ::ClampLower((170 + GetCritDmgMod(hit.skill)), 100);
|
||||
LogCombatDetail("Slayundead hit rate [{}]", slay_rate_bonus);
|
||||
|
||||
// Step 2: Calculate damage
|
||||
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
||||
int og_damage = hit.damage_done;
|
||||
hit.damage_done = hit.damage_done * crit_mod / 100;
|
||||
if (slay_rate_bonus) {
|
||||
float slay_chance = ((static_cast<float>(slay_rate_bonus) / 10000.0f) * RuleR(Combat, SlayRateMultiplier));
|
||||
|
||||
LogCombatDetail("Crit info: [{}] scaled from: [{}] - IsUndeadForSlay: [{}]", hit.damage_done, og_damage, IsUndeadForSlay() ? "true" : "false");
|
||||
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];
|
||||
|
||||
// Try Slay Undead
|
||||
if (defender->IsUndeadForSlay()) {
|
||||
float chance = GetUndeadSlayRate() / 100.0f;
|
||||
LogCombatDetail("Trying Undead slay: Chance: [{}]", chance);
|
||||
LogCombatDetail("Slayundead damage bonus [{}]", slay_damage_bonus);
|
||||
|
||||
if(zone->random.Roll(chance)) {
|
||||
DoUndeadSlay(hit, crit_mod);
|
||||
return;
|
||||
}
|
||||
}
|
||||
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));
|
||||
|
||||
// Step 3: Check deadly strike
|
||||
if (GetClass() == Class::Rogue && hit.skill == EQ::skills::SkillThrowing && BehindMob(defender, GetX(), GetY())) {
|
||||
int chance = GetLevel() * 12;
|
||||
if (zone->random.Int(1, 1000) < chance) {
|
||||
// Check assassinate
|
||||
int assassinate_damage = TryAssassinate(defender, hit.skill);
|
||||
int min_slay = (hit.min_damage + 5) * slay_damage_bonus / 100;
|
||||
|
||||
if (assassinate_damage) {
|
||||
hit.damage_done = assassinate_damage;
|
||||
return;
|
||||
}
|
||||
LogCombatDetail(" Calculated Slayundead damage [{}] - Min Slay Undead Damage [{}]", hit.damage_done, min_slay);
|
||||
|
||||
hit.damage_done = hit.damage_done * 200 / 100;
|
||||
if (hit.damage_done < min_slay) {
|
||||
hit.damage_done = min_slay;
|
||||
}
|
||||
|
||||
entity_list.FilteredMessageCloseString(
|
||||
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 */
|
||||
DEADLY_STRIKE, /* MessageFormat: %1 scores a Deadly Strike!(%2) */
|
||||
slay_sex,
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done) /* Message2 */
|
||||
);
|
||||
return;
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4: check cripple
|
||||
bool berserk = spellbonuses.BerserkSPA || itembonuses.BerserkSPA || aabonuses.BerserkSPA;
|
||||
// 2: Try Melee Critical
|
||||
// a lot of good info: http://giline.versus.jp/shiden/damage_e.htm, http://giline.versus.jp/shiden/su.htm
|
||||
|
||||
if (!berserk && zone->random.Roll(GetCrippBlowChance())) {
|
||||
berserk = true;
|
||||
// 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;
|
||||
}
|
||||
|
||||
if (IsBerserk() || berserk) {
|
||||
hit.damage_done += og_damage * 119 / 100;
|
||||
LogCombatDetail("Crippling damage [{}]", hit.damage_done);
|
||||
// we have a chance to crit!
|
||||
if (innate_crit || crit_chance) {
|
||||
int difficulty = 0;
|
||||
|
||||
entity_list.FilteredMessageCloseString(
|
||||
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;
|
||||
}
|
||||
|
||||
// step 2: calculate damage
|
||||
hit.damage_done = std::max(hit.damage_done, hit.base_damage) + 5;
|
||||
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;
|
||||
LogCombatDetail("Crit success roll [{}] dex chance [{}] og dmg [{}] crit_mod [{}] new dmg [{}]", roll, dex_bonus, og_damage, crit_mod, hit.damage_done);
|
||||
|
||||
// step 3: check deadly strike
|
||||
if (GetClass() == Class::Rogue && hit.skill == EQ::skills::SkillThrowing) {
|
||||
if (BehindMob(defender, GetX(), GetY())) {
|
||||
int chance = GetLevel() * 12;
|
||||
if (zone->random.Int(1, 1000) < chance) {
|
||||
// step 3a: check assassinate
|
||||
int assassinate_damage = TryAssassinate(defender, hit.skill); // I don't think this is right
|
||||
if (assassinate_damage) {
|
||||
hit.damage_done = assassinate_damage;
|
||||
return;
|
||||
}
|
||||
hit.damage_done = hit.damage_done * 200 / 100;
|
||||
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
RuleI(Range, CriticalDamage),
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
DEADLY_STRIKE, /* MessageFormat: %1 scores a Deadly Strike!(%2) */
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done + hit.min_damage) /* Message2 */
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// step 4: check crips
|
||||
// this SPA was reused on live ...
|
||||
bool berserk = spellbonuses.BerserkSPA || itembonuses.BerserkSPA || aabonuses.BerserkSPA;
|
||||
if (!berserk) {
|
||||
if (zone->random.Roll(GetCrippBlowChance())) {
|
||||
berserk = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsBerserk() || berserk) {
|
||||
hit.damage_done += og_damage * 119 / 100;
|
||||
LogCombat("Crip damage [{}]", hit.damage_done);
|
||||
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
RuleI(Range, CriticalDamage),
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
CRIPPLING_BLOW, /* MessageFormat: %1 lands a Crippling Blow!(%2) */
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done + hit.min_damage) /* Message2 */
|
||||
);
|
||||
|
||||
// Crippling blows also have a chance to stun
|
||||
// Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a
|
||||
// staggers message.
|
||||
if (defender->GetLevel() <= 55 && !defender->GetSpecialAbility(SpecialAbility::StunImmunity)) {
|
||||
entity_list.MessageCloseString(
|
||||
defender,
|
||||
true,
|
||||
RuleI(Range, Emote),
|
||||
Chat::Emote,
|
||||
STAGGERS,
|
||||
GetName()
|
||||
);
|
||||
defender->Stun(RuleI(Combat, StunDuration));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Normal Critical hit message */
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
RuleI(Range, CriticalDamage),
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
CRIPPLING_BLOW, /* MessageFormat: %1 lands a Crippling Blow!(%2) */
|
||||
CRITICAL_HIT, /* MessageFormat: %1 scores a critical hit! (%2) */
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done) /* Message2 */
|
||||
);
|
||||
|
||||
// Crippling blows also have a chance to stun
|
||||
// Kayen: Crippling Blow would cause a chance to interrupt for npcs < 55, with a
|
||||
// staggers message.
|
||||
if (defender->GetLevel() <= RuleI(Combat, MaximumLevelStunsCripplingBlow) && !defender->GetSpecialAbility(SpecialAbility::StunImmunity)) {
|
||||
entity_list.MessageCloseString(
|
||||
defender,
|
||||
true,
|
||||
RuleI(Range, Emote),
|
||||
Chat::Emote,
|
||||
STAGGERS,
|
||||
GetName()
|
||||
itoa(hit.damage_done + hit.min_damage) /* Message2 */
|
||||
);
|
||||
defender->Stun(RuleI(Combat, StunDuration));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Normal Critical hit message */
|
||||
entity_list.FilteredMessageCloseString(
|
||||
this, /* Sender */
|
||||
false, /* Skip Sender */
|
||||
RuleI(Range, CriticalDamage),
|
||||
Chat::MeleeCrit, /* Type: 301 */
|
||||
FilterMeleeCrits, /* FilterType: 12 */
|
||||
CRITICAL_HIT, /* MessageFormat: %1 scores a critical hit! (%2) */
|
||||
0,
|
||||
GetCleanName(), /* Message1 */
|
||||
itoa(hit.damage_done) /* Message2 */
|
||||
);
|
||||
}
|
||||
|
||||
bool Mob::TryFinishingBlow(Mob *defender, int64 &damage)
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ Beacon::Beacon(const glm::vec4 &in_pos, int lifetime) : Mob(
|
||||
Gender::Male, // in_gender
|
||||
Race::InvisibleMan, // in_race
|
||||
Class::None, // in_class
|
||||
BT_NoTarget, // in_bodytype
|
||||
BodyType::NoTarget, // in_bodytype
|
||||
Deity::Unknown, // in_deity
|
||||
0, // in_level
|
||||
0, // in_npctype_id
|
||||
|
||||
+22
-12
@@ -182,14 +182,20 @@ void Mob::CalcItemBonuses(StatBonuses* b) {
|
||||
SetDualWeaponsEquipped(true);
|
||||
}
|
||||
|
||||
if (IsOfClientBot()) {
|
||||
for (i = EQ::invslot::TRIBUTE_BEGIN; i <= EQ::invslot::TRIBUTE_END; i++) {
|
||||
const EQ::ItemInstance* inst = m_inv[i];
|
||||
if (!inst) {
|
||||
continue;
|
||||
}
|
||||
if (IsClient()) {
|
||||
if (CastToClient()->GetPP().tribute_active) {
|
||||
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) {
|
||||
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: {
|
||||
if (newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] < base_value) {
|
||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = base_value; // Rate
|
||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = limit_value; // Damage Modifier
|
||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = base_value; // Rate
|
||||
newbon->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = limit_value; // Damage Modifier
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2438,6 +2444,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
case SE_CastingLevel2:
|
||||
{
|
||||
new_bonus->effective_casting_level += effect_value;
|
||||
|
||||
if (RuleB(Spells, SnareOverridesSpeedBonuses) && effect_value < 0) {
|
||||
new_bonus->movementspeed = effect_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3356,7 +3366,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
break;
|
||||
|
||||
case SE_Blind:
|
||||
if (RuleB(Combat, AllowRaidTargetBlind) && IsRaidTarget()) { // do not blind raid targets
|
||||
if (!RuleB(Combat, AllowRaidTargetBlind) && IsRaidTarget()) { // do not blind raid targets
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3585,8 +3595,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
|
||||
case SE_SlayUndead: {
|
||||
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_DMG_MOD] = limit_value; // Damage Modifier
|
||||
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_RATE_MOD] = limit_value; // Rate
|
||||
new_bonus->SlayUndead[SBIndex::SLAYUNDEAD_DMG_MOD] = effect_value; // Damage Modifier
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
+13
-17
@@ -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);
|
||||
|
||||
m_auto_defend_timer.Disable();
|
||||
@@ -1899,8 +1899,8 @@ void Bot::AI_Process()
|
||||
#define NOT_GUARDING (!GetGuardFlag())
|
||||
#define HOLDING (GetHoldFlag())
|
||||
#define NOT_HOLDING (!GetHoldFlag())
|
||||
#define PASSIVE (GetBotStance() == EQ::constants::stancePassive)
|
||||
#define NOT_PASSIVE (GetBotStance() != EQ::constants::stancePassive)
|
||||
#define PASSIVE (GetBotStance() == Stance::Passive)
|
||||
#define NOT_PASSIVE (GetBotStance() != Stance::Passive)
|
||||
|
||||
Client* bot_owner = (GetBotOwner() && GetBotOwner()->IsClient() ? GetBotOwner()->CastToClient() : nullptr);
|
||||
|
||||
@@ -5481,13 +5481,13 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) {
|
||||
if (!Result) {
|
||||
if (caster->IsBot()) {
|
||||
if (spells[spell_id].target_type == ST_Undead) {
|
||||
if ((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Undead) && (GetBodyType() != BT_Vampire)) {
|
||||
if ((GetBodyType() != BodyType::SummonedUndead) && (GetBodyType() != BodyType::Undead) && (GetBodyType() != BodyType::Vampire)) {
|
||||
LogSpellsDetail("Bot's target is not an undead");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (spells[spell_id].target_type == ST_Summoned) {
|
||||
if ((GetBodyType() != BT_SummonedUndead) && (GetBodyType() != BT_Summoned) && (GetBodyType() != BT_Summoned2) && (GetBodyType() != BT_Summoned3)) {
|
||||
if ((GetBodyType() != BodyType::SummonedUndead) && (GetBodyType() != BodyType::Summoned) && (GetBodyType() != BodyType::Summoned2) && (GetBodyType() != BodyType::Summoned3)) {
|
||||
LogSpellsDetail("Bot's target is not a summoned creature");
|
||||
return true;
|
||||
}
|
||||
@@ -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())) {
|
||||
float hpRatioToHeal = 25.0f;
|
||||
switch(caster->GetBotStance()) {
|
||||
case EQ::constants::stanceReactive:
|
||||
case EQ::constants::stanceBalanced:
|
||||
case Stance::Reactive:
|
||||
case Stance::Balanced:
|
||||
hpRatioToHeal = 50.0f;
|
||||
break;
|
||||
case EQ::constants::stanceBurn:
|
||||
case EQ::constants::stanceBurnAE:
|
||||
case Stance::Burn:
|
||||
case Stance::AEBurn:
|
||||
hpRatioToHeal = 20.0f;
|
||||
break;
|
||||
case EQ::constants::stanceAggressive:
|
||||
case EQ::constants::stanceEfficient:
|
||||
case Stance::Aggressive:
|
||||
case Stance::Efficient:
|
||||
default:
|
||||
hpRatioToHeal = 25.0f;
|
||||
break;
|
||||
@@ -7655,11 +7655,7 @@ bool Bot::HasOrMayGetAggro() {
|
||||
}
|
||||
|
||||
void Bot::SetDefaultBotStance() {
|
||||
EQ::constants::StanceType defaultStance = EQ::constants::stanceBalanced;
|
||||
if (GetClass() == Class::Warrior)
|
||||
defaultStance = EQ::constants::stanceAggressive;
|
||||
|
||||
_botStance = defaultStance;
|
||||
_botStance = GetClass() == Class::Warrior ? Stance::Aggressive : Stance::Balanced;
|
||||
}
|
||||
|
||||
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 GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
|
||||
EQ::constants::StanceType GetBotStance() { return _botStance; }
|
||||
uint8 GetBotStance() { return _botStance; }
|
||||
uint8 GetChanceToCastBySpellType(uint32 spellType);
|
||||
bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; }
|
||||
float GetBotCasterMaxRange(float melee_distance_max);
|
||||
@@ -605,12 +605,7 @@ public:
|
||||
void SetPetChooser(bool p) { _petChooser = p; }
|
||||
void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; }
|
||||
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == Class::Ranger ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
|
||||
void SetBotStance(EQ::constants::StanceType botStance) {
|
||||
if (botStance >= EQ::constants::stancePassive && botStance <= EQ::constants::stanceBurnAE)
|
||||
_botStance = botStance;
|
||||
else
|
||||
_botStance = EQ::constants::stancePassive;
|
||||
}
|
||||
void SetBotStance(uint8 stance_id) { _botStance = Stance::IsValid(stance_id) ? stance_id : Stance::Passive; }
|
||||
void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; }
|
||||
uint32 GetSpellRecastTimer(uint16 spell_id = 0);
|
||||
bool CheckSpellRecastTimer(uint16 spell_id = 0);
|
||||
@@ -753,7 +748,7 @@ public:
|
||||
//Raid additions
|
||||
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 BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid);
|
||||
@@ -870,7 +865,7 @@ private:
|
||||
std::string _suffix;
|
||||
uint32 _lastZoneId;
|
||||
bool _rangerAutoWeaponSelect;
|
||||
EQ::constants::StanceType _botStance;
|
||||
uint8 _botStance;
|
||||
unsigned int RestRegenHP;
|
||||
unsigned int RestRegenMana;
|
||||
unsigned int RestRegenEndurance;
|
||||
|
||||
+8
-8
@@ -753,19 +753,19 @@ namespace ActionableTarget
|
||||
verified_friendly = target_mob;
|
||||
break;
|
||||
case BCEnum::TT_Animal:
|
||||
if (target_mob && target_mob->GetBodyType() == BT_Animal)
|
||||
if (target_mob && target_mob->GetBodyType() == BodyType::Animal)
|
||||
verified_friendly = target_mob;
|
||||
break;
|
||||
case BCEnum::TT_Undead:
|
||||
if (target_mob && target_mob->GetBodyType() == BT_Undead)
|
||||
if (target_mob && target_mob->GetBodyType() == BodyType::Undead)
|
||||
verified_friendly = target_mob;
|
||||
break;
|
||||
case BCEnum::TT_Summoned:
|
||||
if (target_mob && target_mob->GetBodyType() == BT_Summoned)
|
||||
if (target_mob && target_mob->GetBodyType() == BodyType::Summoned)
|
||||
verified_friendly = target_mob;
|
||||
break;
|
||||
case BCEnum::TT_Plant:
|
||||
if (target_mob && target_mob->GetBodyType() == BT_Plant)
|
||||
if (target_mob && target_mob->GetBodyType() == BodyType::Plant)
|
||||
verified_friendly = target_mob;
|
||||
break;
|
||||
case BCEnum::TT_Corpse:
|
||||
@@ -800,19 +800,19 @@ namespace ActionableTarget
|
||||
Mob* verified_enemy = nullptr;
|
||||
switch (target_type) {
|
||||
case BCEnum::TT_Animal:
|
||||
if (target_mob->GetBodyType() == BT_Animal)
|
||||
if (target_mob->GetBodyType() == BodyType::Animal)
|
||||
verified_enemy = target_mob;
|
||||
break;
|
||||
case BCEnum::TT_Undead:
|
||||
if (target_mob->GetBodyType() == BT_Undead)
|
||||
if (target_mob->GetBodyType() == BodyType::Undead)
|
||||
verified_enemy = target_mob;
|
||||
break;
|
||||
case BCEnum::TT_Summoned:
|
||||
if (target_mob->GetBodyType() == BT_Summoned)
|
||||
if (target_mob->GetBodyType() == BodyType::Summoned)
|
||||
verified_enemy = target_mob;
|
||||
break;
|
||||
case BCEnum::TT_Plant:
|
||||
if (target_mob->GetBodyType() == BT_Plant)
|
||||
if (target_mob->GetBodyType() == BodyType::Plant)
|
||||
verified_enemy = target_mob;
|
||||
break;
|
||||
case BCEnum::TT_Single:
|
||||
|
||||
@@ -40,7 +40,7 @@ void bot_command_attack(Client *c, const Seperator *sep)
|
||||
sbl.remove(nullptr);
|
||||
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) {
|
||||
first_attacker = bot_iter;
|
||||
|
||||
+32
-18
@@ -198,7 +198,7 @@ void bot_command_clone(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
int clone_stance = EQ::constants::stancePassive;
|
||||
int clone_stance = Stance::Passive;
|
||||
if (!database.botdb.LoadStance(my_bot->GetBotID(), clone_stance)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
@@ -1058,33 +1058,47 @@ void bot_command_stance(Client *c, const Seperator *sep)
|
||||
return;
|
||||
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, "value: %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s)",
|
||||
EQ::constants::stancePassive, EQ::constants::GetStanceName(EQ::constants::stancePassive),
|
||||
EQ::constants::stanceBalanced, EQ::constants::GetStanceName(EQ::constants::stanceBalanced),
|
||||
EQ::constants::stanceEfficient, EQ::constants::GetStanceName(EQ::constants::stanceEfficient),
|
||||
EQ::constants::stanceReactive, EQ::constants::GetStanceName(EQ::constants::stanceReactive),
|
||||
EQ::constants::stanceAggressive, EQ::constants::GetStanceName(EQ::constants::stanceAggressive),
|
||||
EQ::constants::stanceAssist, EQ::constants::GetStanceName(EQ::constants::stanceAssist),
|
||||
EQ::constants::stanceBurn, EQ::constants::GetStanceName(EQ::constants::stanceBurn),
|
||||
EQ::constants::stanceEfficient2, EQ::constants::GetStanceName(EQ::constants::stanceEfficient2),
|
||||
EQ::constants::stanceBurnAE, EQ::constants::GetStanceName(EQ::constants::stanceBurnAE)
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Value: {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({})",
|
||||
Stance::Passive,
|
||||
Stance::GetName(Stance::Passive),
|
||||
Stance::Balanced,
|
||||
Stance::GetName(Stance::Balanced),
|
||||
Stance::Efficient,
|
||||
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;
|
||||
}
|
||||
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
|
||||
|
||||
bool current_flag = false;
|
||||
auto bst = EQ::constants::stanceUnknown;
|
||||
uint8 bst = Stance::Unknown;
|
||||
|
||||
if (!strcasecmp(sep->arg[1], "current"))
|
||||
current_flag = true;
|
||||
else if (sep->IsNumber(1)) {
|
||||
bst = (EQ::constants::StanceType)Strings::ToInt(sep->arg[1]);
|
||||
if (bst < EQ::constants::stanceUnknown || bst > EQ::constants::stanceBurnAE)
|
||||
bst = EQ::constants::stanceUnknown;
|
||||
bst = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[1]));
|
||||
if (!Stance::IsValid(bst)) {
|
||||
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");
|
||||
return;
|
||||
}
|
||||
@@ -1106,8 +1120,8 @@ void bot_command_stance(Client *c, const Seperator *sep)
|
||||
bot_iter,
|
||||
fmt::format(
|
||||
"My current stance is {} ({}).",
|
||||
EQ::constants::GetStanceName(bot_iter->GetBotStance()),
|
||||
static_cast<int>(bot_iter->GetBotStance())
|
||||
Stance::GetName(bot_iter->GetBotStance()),
|
||||
bot_iter->GetBotStance()
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ void bot_command_pull(Client *c, const Seperator *sep)
|
||||
Bot* bot_puller = nullptr;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ bool BotDatabase::LoadBotSpellCastingChances()
|
||||
if (
|
||||
e.spell_type_index >= Bot::SPELL_TYPE_COUNT ||
|
||||
!IsPlayerClass(e.class_id) ||
|
||||
e.stance_index >= EQ::constants::STANCE_TYPE_COUNT
|
||||
e.stance_index >= Stance::AEBurn
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
@@ -761,7 +761,7 @@ bool BotDatabase::LoadStance(Bot* b, bool& stance_flag)
|
||||
|
||||
auto e = l.front();
|
||||
|
||||
b->SetBotStance(static_cast<EQ::constants::StanceType>(e.stance_id));
|
||||
b->SetBotStance(e.stance_id);
|
||||
|
||||
stance_flag = true;
|
||||
|
||||
@@ -793,7 +793,7 @@ bool BotDatabase::SaveStance(Bot* b)
|
||||
database,
|
||||
BotStancesRepository::BotStances{
|
||||
.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 (
|
||||
spell_type_index >= Bot::SPELL_TYPE_COUNT ||
|
||||
class_index >= Class::PLAYER_CLASS_COUNT ||
|
||||
stance_index >= EQ::constants::STANCE_TYPE_COUNT ||
|
||||
stance_index >= Stance::AEBurn ||
|
||||
conditional_index >= cntHSND
|
||||
) {
|
||||
return 0;
|
||||
|
||||
+45
-43
@@ -629,16 +629,16 @@ bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
||||
float manaRatioToCast = 75.0f;
|
||||
|
||||
switch(GetBotStance()) {
|
||||
case EQ::constants::stanceEfficient:
|
||||
case Stance::Efficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case EQ::constants::stanceBalanced:
|
||||
case EQ::constants::stanceAggressive:
|
||||
case Stance::Balanced:
|
||||
case Stance::Aggressive:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case EQ::constants::stanceReactive:
|
||||
case EQ::constants::stanceBurn:
|
||||
case EQ::constants::stanceBurnAE:
|
||||
case Stance::Reactive:
|
||||
case Stance::Burn:
|
||||
case Stance::AEBurn:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
default:
|
||||
@@ -746,18 +746,18 @@ bool Bot::BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
|
||||
float manaRatioToCast = 75.0f;
|
||||
|
||||
switch(GetBotStance()) {
|
||||
case EQ::constants::stanceEfficient:
|
||||
case Stance::Efficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case EQ::constants::stanceBalanced:
|
||||
case Stance::Balanced:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case EQ::constants::stanceReactive:
|
||||
case EQ::constants::stanceAggressive:
|
||||
case Stance::Reactive:
|
||||
case Stance::Aggressive:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
case EQ::constants::stanceBurn:
|
||||
case EQ::constants::stanceBurnAE:
|
||||
case Stance::Burn:
|
||||
case Stance::AEBurn:
|
||||
manaRatioToCast = 25.0f;
|
||||
break;
|
||||
default:
|
||||
@@ -771,9 +771,9 @@ bool Bot::BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
|
||||
}
|
||||
|
||||
if (botClass == Class::Magician || botClass == Class::ShadowKnight || botClass == Class::Necromancer || botClass == Class::Paladin || botClass == Class::Ranger || botClass == Class::Druid || botClass == Class::Cleric) {
|
||||
if (tar->GetBodyType() == BT_Undead || tar->GetBodyType() == BT_SummonedUndead || tar->GetBodyType() == BT_Vampire)
|
||||
if (tar->GetBodyType() == BodyType::Undead || tar->GetBodyType() == BodyType::SummonedUndead || tar->GetBodyType() == BodyType::Vampire)
|
||||
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Undead);
|
||||
else if (tar->GetBodyType() == BT_Summoned || tar->GetBodyType() == BT_Summoned2 || tar->GetBodyType() == BT_Summoned3)
|
||||
else if (tar->GetBodyType() == BodyType::Summoned || tar->GetBodyType() == BodyType::Summoned2 || tar->GetBodyType() == BodyType::Summoned3)
|
||||
botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Summoned);
|
||||
}
|
||||
|
||||
@@ -924,16 +924,16 @@ bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) {
|
||||
float manaRatioToCast = 75.0f;
|
||||
|
||||
switch (GetBotStance()) {
|
||||
case EQ::constants::stanceEfficient:
|
||||
case Stance::Efficient:
|
||||
manaRatioToCast = 90.0f;
|
||||
break;
|
||||
case EQ::constants::stanceBalanced:
|
||||
case EQ::constants::stanceAggressive:
|
||||
case Stance::Balanced:
|
||||
case Stance::Aggressive:
|
||||
manaRatioToCast = 75.0f;
|
||||
break;
|
||||
case EQ::constants::stanceReactive:
|
||||
case EQ::constants::stanceBurn:
|
||||
case EQ::constants::stanceBurnAE:
|
||||
case Stance::Reactive:
|
||||
case Stance::Burn:
|
||||
case Stance::AEBurn:
|
||||
manaRatioToCast = 50.0f;
|
||||
break;
|
||||
default:
|
||||
@@ -1088,18 +1088,18 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpe
|
||||
float hpRatioToCast = 0.0f;
|
||||
|
||||
switch (GetBotStance()) {
|
||||
case EQ::constants::stanceEfficient:
|
||||
case EQ::constants::stanceAggressive:
|
||||
case Stance::Efficient:
|
||||
case Stance::Aggressive:
|
||||
hpRatioToCast = isPrimaryHealer ? 90.0f : 50.0f;
|
||||
break;
|
||||
case EQ::constants::stanceBalanced:
|
||||
case Stance::Balanced:
|
||||
hpRatioToCast = isPrimaryHealer ? 95.0f : 75.0f;
|
||||
break;
|
||||
case EQ::constants::stanceReactive:
|
||||
case Stance::Reactive:
|
||||
hpRatioToCast = isPrimaryHealer ? 100.0f : 90.0f;
|
||||
break;
|
||||
case EQ::constants::stanceBurn:
|
||||
case EQ::constants::stanceBurnAE:
|
||||
case Stance::Burn:
|
||||
case Stance::AEBurn:
|
||||
hpRatioToCast = isPrimaryHealer ? 75.0f : 25.0f;
|
||||
break;
|
||||
default:
|
||||
@@ -2939,11 +2939,12 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
||||
return 0;
|
||||
--class_index;
|
||||
|
||||
EQ::constants::StanceType stance_type = GetBotStance();
|
||||
if (stance_type < EQ::constants::stancePassive || stance_type > EQ::constants::stanceBurnAE)
|
||||
uint32 stance_id = GetBotStance();
|
||||
if (!Stance::IsValid(stance_id)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8 stance_index = EQ::constants::ConvertStanceTypeToIndex(stance_type);
|
||||
uint8 stance_index = Stance::GetIndex(stance_id);
|
||||
uint8 type_index = nHSND;
|
||||
|
||||
if (HasGroup()) {
|
||||
@@ -3323,18 +3324,19 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id)
|
||||
if (!bse.empty()) {
|
||||
for (const auto& e : bse) {
|
||||
DBbotspells_entries_Struct entry;
|
||||
entry.spellid = e.spellid;
|
||||
entry.type = e.type;
|
||||
entry.minlevel = e.minlevel;
|
||||
entry.maxlevel = e.maxlevel;
|
||||
entry.manacost = e.manacost;
|
||||
entry.recast_delay = e.recast_delay;
|
||||
entry.priority = e.priority;
|
||||
entry.min_hp = e.min_hp;
|
||||
entry.max_hp = e.max_hp;
|
||||
entry.resist_adjust = e.resist_adjust;
|
||||
entry.bucket_name = e.bucket_name;
|
||||
entry.bucket_value = e.bucket_value;
|
||||
|
||||
entry.spellid = e.spell_id;
|
||||
entry.type = e.type;
|
||||
entry.minlevel = e.minlevel;
|
||||
entry.maxlevel = e.maxlevel;
|
||||
entry.manacost = e.manacost;
|
||||
entry.recast_delay = e.recast_delay;
|
||||
entry.priority = e.priority;
|
||||
entry.min_hp = e.min_hp;
|
||||
entry.max_hp = e.max_hp;
|
||||
entry.resist_adjust = e.resist_adjust;
|
||||
entry.bucket_name = e.bucket_name;
|
||||
entry.bucket_value = e.bucket_value;
|
||||
entry.bucket_comparison = e.bucket_comparison;
|
||||
|
||||
// some spell types don't make much since to be priority 0, so fix that
|
||||
@@ -3344,8 +3346,8 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id)
|
||||
|
||||
if (e.resist_adjust) {
|
||||
entry.resist_adjust = e.resist_adjust;
|
||||
} else if (IsValidSpell(e.spellid)) {
|
||||
entry.resist_adjust = spells[e.spellid].resist_difficulty;
|
||||
} else if (IsValidSpell(e.spell_id)) {
|
||||
entry.resist_adjust = spells[e.spell_id].resist_difficulty;
|
||||
}
|
||||
|
||||
spell_set.entries.push_back(entry);
|
||||
|
||||
+179
-24
@@ -95,7 +95,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
Gender::Male, // in_gender
|
||||
Race::Doug, // in_race
|
||||
Class::None, // in_class
|
||||
BT_Humanoid, // in_bodytype
|
||||
BodyType::Humanoid, // in_bodytype
|
||||
Deity::Unknown, // in_deity
|
||||
0, // in_level
|
||||
0, // in_npctype_id
|
||||
@@ -185,7 +185,9 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
position_update_timer(10000),
|
||||
consent_throttle_timer(2000),
|
||||
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)) {
|
||||
SetFilter(client_filter, FilterShow);
|
||||
@@ -203,7 +205,6 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
port = ntohs(eqs->GetRemotePort());
|
||||
client_state = CLIENT_CONNECTING;
|
||||
SetTrader(false);
|
||||
Buyer = false;
|
||||
Haste = 0;
|
||||
SetCustomerID(0);
|
||||
SetTraderID(0);
|
||||
@@ -215,6 +216,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
guild_id = GUILD_NONE;
|
||||
guildrank = 0;
|
||||
guild_tribute_opt_in = 0;
|
||||
SetGuildListDirty(false);
|
||||
GuildBanker = false;
|
||||
memset(lskey, 0, sizeof(lskey));
|
||||
strcpy(account_name, "");
|
||||
@@ -240,7 +242,6 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
runmode = false;
|
||||
linkdead_timer.Disable();
|
||||
zonesummon_id = 0;
|
||||
zonesummon_instance_id = 0;
|
||||
zonesummon_ignorerestrictions = 0;
|
||||
bZoning = false;
|
||||
m_lock_save_position = false;
|
||||
@@ -285,6 +286,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
memset(&m_epp, 0, sizeof(m_epp));
|
||||
PendingTranslocate = false;
|
||||
PendingSacrifice = false;
|
||||
sacrifice_caster_id = 0;
|
||||
controlling_boat_id = 0;
|
||||
controlled_mob_id = 0;
|
||||
qGlobals = nullptr;
|
||||
@@ -386,11 +388,12 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
m_parcel_merchant_engaged = false;
|
||||
m_parcels.clear();
|
||||
|
||||
m_buyer_id = 0;
|
||||
|
||||
SetBotPulling(false);
|
||||
SetBotPrecombat(false);
|
||||
|
||||
AI_Init();
|
||||
|
||||
}
|
||||
|
||||
Client::~Client() {
|
||||
@@ -410,8 +413,12 @@ Client::~Client() {
|
||||
zone->ClearEXPModifier(this);
|
||||
}
|
||||
|
||||
if(IsInAGuild())
|
||||
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr));
|
||||
if (!IsZoning()) {
|
||||
if(IsInAGuild()) {
|
||||
guild_mgr.UpdateDbMemberOnline(CharacterID(), false);
|
||||
guild_mgr.SendGuildMemberUpdateToWorld(GetName(), GuildID(), 0, time(nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
Mob* horse = entity_list.GetMob(CastToClient()->GetHorseId());
|
||||
if (horse)
|
||||
@@ -425,8 +432,9 @@ Client::~Client() {
|
||||
TraderEndTrader();
|
||||
}
|
||||
|
||||
if(Buyer)
|
||||
if(IsBuyer()) {
|
||||
ToggleBuyerMode(false);
|
||||
}
|
||||
|
||||
if(conn_state != ClientConnectFinished) {
|
||||
LogDebug("Client [{}] was destroyed before reaching the connected state:", GetName());
|
||||
@@ -2159,12 +2167,13 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
Mob::FillSpawnStruct(ns, ForWho);
|
||||
|
||||
// Populate client-specific spawn information
|
||||
ns->spawn.afk = AFK;
|
||||
ns->spawn.lfg = LFG; // afk and lfg are cleared on zoning on live
|
||||
ns->spawn.anon = m_pp.anon;
|
||||
ns->spawn.gm = GetGM() ? 1 : 0;
|
||||
ns->spawn.guildID = GuildID();
|
||||
ns->spawn.trader = IsTrader();
|
||||
ns->spawn.afk = AFK;
|
||||
ns->spawn.lfg = LFG; // afk and lfg are cleared on zoning on live
|
||||
ns->spawn.anon = m_pp.anon;
|
||||
ns->spawn.gm = GetGM() ? 1 : 0;
|
||||
ns->spawn.guildID = GuildID();
|
||||
ns->spawn.trader = IsTrader();
|
||||
ns->spawn.buyer = IsBuyer();
|
||||
// ns->spawn.linkdead = IsLD() ? 1 : 0;
|
||||
// ns->spawn.pvp = GetPVP(false) ? 1 : 0;
|
||||
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
|
||||
Filter1(FilterHealOverTime);
|
||||
}
|
||||
|
||||
Filter1(FilterItemSpeech);
|
||||
Filter1(FilterStrikethrough);
|
||||
Filter1(FilterStuns);
|
||||
Filter1(FilterBardSongsOnPets);
|
||||
}
|
||||
|
||||
// this version is for messages with no parameters
|
||||
@@ -3958,7 +3972,7 @@ void Client::SetEndurance(int32 newEnd)
|
||||
CheckManaEndUpdate();
|
||||
}
|
||||
|
||||
void Client::SacrificeConfirm(Client *caster)
|
||||
void Client::SacrificeConfirm(Mob *caster)
|
||||
{
|
||||
auto outapp = new EQApplicationPacket(OP_Sacrifice, sizeof(Sacrifice_Struct));
|
||||
Sacrifice_Struct *ss = (Sacrifice_Struct *)outapp->pBuffer;
|
||||
@@ -3985,14 +3999,14 @@ void Client::SacrificeConfirm(Client *caster)
|
||||
ss->Confirm = 0;
|
||||
QueuePacket(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.
|
||||
SacrificeCaster += caster->GetName();
|
||||
sacrifice_caster_id = caster->GetID();
|
||||
PendingSacrifice = true;
|
||||
}
|
||||
|
||||
//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)) {
|
||||
int exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
|
||||
@@ -4040,8 +4054,11 @@ void Client::Sacrifice(Client *caster)
|
||||
}
|
||||
Save();
|
||||
GoToDeath();
|
||||
if (caster) // I guess it's possible?
|
||||
caster->SummonItem(RuleI(Spells, SacrificeItemID));
|
||||
if (caster && caster->IsClient()) {
|
||||
caster->CastToClient()->SummonItem(RuleI(Spells, SacrificeItemID));
|
||||
} else if (caster && caster->IsNPC()) {
|
||||
caster->CastToNPC()->AddItem(RuleI(Spells, SacrificeItemID), 1, false);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
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_nine;
|
||||
std::string menu_toggle;
|
||||
std::string window_toggle;
|
||||
|
||||
/**
|
||||
* Search entity commands
|
||||
@@ -9398,9 +9416,14 @@ void Client::ShowDevToolsMenu()
|
||||
/**
|
||||
* Show window status
|
||||
*/
|
||||
menu_toggle = Saylink::Silent("#devtools enable", "Enable");
|
||||
menu_toggle = Saylink::Silent("#devtools menu enable", "Enable");
|
||||
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(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Toggle | {}",
|
||||
"Toggle Menu | {}",
|
||||
menu_toggle
|
||||
).c_str()
|
||||
);
|
||||
|
||||
Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Toggle Window | {}",
|
||||
window_toggle
|
||||
).c_str()
|
||||
);
|
||||
|
||||
Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
@@ -11975,7 +12006,7 @@ void Client::SendPath(Mob* target)
|
||||
target->IsClient() &&
|
||||
(
|
||||
target->CastToClient()->IsTrader() ||
|
||||
target->CastToClient()->Buyer
|
||||
target->CastToClient()->IsBuyer()
|
||||
)
|
||||
) {
|
||||
Message(
|
||||
@@ -12579,3 +12610,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+57
-14
@@ -70,6 +70,8 @@ namespace EQ
|
||||
#include "../common/data_verification.h"
|
||||
#include "../common/repositories/character_parcels_repository.h"
|
||||
#include "../common/repositories/trader_repository.h"
|
||||
#include "../common/guild_base.h"
|
||||
#include "../common/repositories/buyer_buy_lines_repository.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
// since windows defines these within windef.h (which windows.h include)
|
||||
@@ -276,6 +278,8 @@ public:
|
||||
|
||||
std::vector<Mob*> GetRaidOrGroupOrSelf(bool clients_only = false);
|
||||
|
||||
bool CheckIfAlreadyDead();
|
||||
|
||||
void AI_Init();
|
||||
void AI_Start(uint32 iMoveDelay = 0);
|
||||
void AI_Stop();
|
||||
@@ -288,6 +292,7 @@ public:
|
||||
void TraderPriceUpdate(const EQApplicationPacket *app);
|
||||
void SendBazaarDone(uint32 trader_id);
|
||||
void SendBulkBazaarTraders();
|
||||
void SendBulkBazaarBuyers();
|
||||
void DoBazaarInspect(const BazaarInspect_Struct &in);
|
||||
void SendBazaarDeliveryCosts();
|
||||
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);
|
||||
void SendTraderPacket(Client* trader, uint32 Unknown72 = 51);
|
||||
void SendBuyerPacket(Client* Buyer);
|
||||
void SendBuyerToBarterWindow(Client* buyer, uint32 action);
|
||||
GetItems_Struct* GetTraderItems();
|
||||
void SendBazaarWelcome();
|
||||
void SendBarterWelcome();
|
||||
void DyeArmor(EQ::TintProfile* dye);
|
||||
void DyeArmorBySlot(uint8 slot, uint8 red, uint8 green, uint8 blue, uint8 use_tint = 0x00);
|
||||
uint8 SlotConvert(uint8 slot,bool bracer=false);
|
||||
@@ -375,14 +382,35 @@ public:
|
||||
uint32 GetCustomerID() { return customer_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 SellToBuyer(const EQApplicationPacket *app);
|
||||
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 SetBuyerWelcomeMessage(const char* WelcomeMessage) { BuyerWelcomeMessage = WelcomeMessage; }
|
||||
const char* GetBuyerWelcomeMessage() { return BuyerWelcomeMessage.c_str(); }
|
||||
void SendWindowUpdatesToSellerAndBuyer(BuyerLineSellItem_Struct& blsi);
|
||||
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);
|
||||
bool ShouldISpawnFor(Client *c) { return !GMHideMe(c) && !IsHoveringForRespawn(); }
|
||||
@@ -734,11 +762,12 @@ public:
|
||||
void GetRaidAAs(RaidLeadershipAA_Struct *into) const;
|
||||
void ClearGroupAAs();
|
||||
void UpdateGroupAAs(int32 points, uint32 type);
|
||||
void SacrificeConfirm(Client* caster);
|
||||
void Sacrifice(Client* caster);
|
||||
void SacrificeConfirm(Mob* caster);
|
||||
void Sacrifice(Mob* caster);
|
||||
void GoToDeath();
|
||||
inline const int32 GetInstanceID() const { return zone->GetInstanceID(); }
|
||||
void SetZoning(bool in) { bZoning = in; }
|
||||
bool IsZoning() { return bZoning; }
|
||||
|
||||
void ShowSpells(Client* c, ShowSpellType show_spell_type);
|
||||
|
||||
@@ -825,9 +854,11 @@ public:
|
||||
void QuestReadBook(const char* text, uint8 type);
|
||||
void SendMoneyUpdate();
|
||||
bool TakeMoneyFromPP(uint64 copper, bool update_client = false);
|
||||
bool TakeMoneyFromPPWithOverFlow(uint64 copper, bool update_client);
|
||||
bool TakePlatinum(uint32 platinum, 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 AddMoneyToPPWithOverflow(uint64 copper, bool update_client);
|
||||
void AddPlatinum(uint32 platinu, bool update_client = false);
|
||||
bool HasMoney(uint64 copper);
|
||||
uint64 GetCarriedMoney();
|
||||
@@ -966,6 +997,8 @@ public:
|
||||
void ChangeTributeSettings(TributeInfo_Struct *t);
|
||||
void SendTributeTimer();
|
||||
void ToggleTribute(bool enabled);
|
||||
std::map<uint32, TributeData> GetTributeList();
|
||||
uint32 LookupTributeItemID(uint32 tribute_id, uint32 tier);
|
||||
void SendPathPacket(const std::vector<FindPerson_Point> &path);
|
||||
|
||||
inline PTimerList &GetPTimers() { return(p_timers); }
|
||||
@@ -1054,6 +1087,8 @@ public:
|
||||
int32 GetItemIDAt(int16 slot_id);
|
||||
int32 GetAugmentIDAt(int16 slot_id, uint8 augslot);
|
||||
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);
|
||||
void SendCursorBuffer();
|
||||
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; }
|
||||
void SetTraderID(uint16 id) { trader_id = id; }
|
||||
|
||||
inline bool IsBuyer() const { return(Buyer); }
|
||||
eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; }
|
||||
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 RemoveDuplicateLore(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);
|
||||
void DoClassAttacks(Mob *ca_target, uint16 skill = -1, bool IsRiposte=false);
|
||||
|
||||
@@ -1211,7 +1245,7 @@ public:
|
||||
bool PendingTranslocate;
|
||||
time_t TranslocateTime;
|
||||
bool PendingSacrifice;
|
||||
std::string SacrificeCaster;
|
||||
uint16 sacrifice_caster_id;
|
||||
PendingTranslocate_Struct PendingTranslocateData;
|
||||
void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID);
|
||||
|
||||
@@ -1386,7 +1420,11 @@ public:
|
||||
{
|
||||
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 CancelAllTasks() { if (task_state) { task_state->CancelAllTasks(this); }}
|
||||
inline int GetActiveTaskCount() { return (task_state ? task_state->GetActiveTaskCount() : 0); }
|
||||
@@ -1909,8 +1947,8 @@ private:
|
||||
uint8 firstlogon;
|
||||
uint32 mercid; // current merc
|
||||
uint8 mercSlot; // selected merc slot
|
||||
bool Buyer;
|
||||
std::string BuyerWelcomeMessage;
|
||||
uint32 m_buyer_id;
|
||||
uint32 m_barter_time;
|
||||
int32 m_parcel_platinum;
|
||||
int32 m_parcel_gold;
|
||||
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 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;
|
||||
uint16 zonesummon_id;
|
||||
uint8 zonesummon_instance_id;
|
||||
uint8 zonesummon_ignorerestrictions;
|
||||
ZoneMode zone_mode;
|
||||
|
||||
@@ -2019,6 +2058,11 @@ private:
|
||||
Timer task_request_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 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::vec4 last_position_before_bulk_update;
|
||||
@@ -2138,7 +2182,6 @@ private:
|
||||
bool m_has_quest_compass = false;
|
||||
std::vector<uint32_t> m_dynamic_zone_ids;
|
||||
|
||||
|
||||
public:
|
||||
enum BotOwnerOption : size_t {
|
||||
booDeathMarquee,
|
||||
|
||||
+290
-146
@@ -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/character_corpses_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/repositories/character_stats_record_repository.h"
|
||||
@@ -368,13 +369,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_Save] = &Client::Handle_OP_Save;
|
||||
ConnectedOpcodes[OP_SaveOnZoneReq] = &Client::Handle_OP_SaveOnZoneReq;
|
||||
ConnectedOpcodes[OP_SelectTribute] = &Client::Handle_OP_SelectTribute;
|
||||
|
||||
// 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_SenseHeading] = &Client::Handle_OP_SenseHeading;
|
||||
ConnectedOpcodes[OP_SenseTraps] = &Client::Handle_OP_SenseTraps;
|
||||
ConnectedOpcodes[OP_SetGuildMOTD] = &Client::Handle_OP_SetGuildMOTD;
|
||||
ConnectedOpcodes[OP_SetRunMode] = &Client::Handle_OP_SetRunMode;
|
||||
@@ -764,8 +759,6 @@ void Client::CompleteConnect()
|
||||
|
||||
entity_list.SendIllusionWearChange(this);
|
||||
|
||||
entity_list.SendTraders(this);
|
||||
|
||||
SendWearChangeAndLighting(EQ::textures::LastTexture);
|
||||
Mob* pet = GetPet();
|
||||
if (pet) {
|
||||
@@ -793,6 +786,8 @@ void Client::CompleteConnect()
|
||||
parse->EventPlayer(EVENT_ENTER_ZONE, this, "", 0);
|
||||
}
|
||||
|
||||
DeleteEntityVariable(SEE_BUFFS_FLAG);
|
||||
|
||||
// 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
|
||||
// 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.LoadCharacterDisciplines(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.LoadCharacterSpellBook(cid, &m_pp); /* Load Character Spell Book */
|
||||
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.LoadCharacterLeadershipAbilities(cid, &m_pp); /* Load Character Leadership AA's */
|
||||
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->UpdateOrnamentationInfo();
|
||||
|
||||
@@ -3643,29 +3642,39 @@ void Client::Handle_OP_AutoFire(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.
|
||||
//
|
||||
if (app->size != sizeof(BandolierCreate_Struct)) {
|
||||
LogDebug("Size mismatch in OP_Bandolier expected [{}] got [{}]", sizeof(BandolierCreate_Struct), app->size);
|
||||
DumpPacket(app);
|
||||
return;
|
||||
}
|
||||
|
||||
BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer;
|
||||
auto bs = (BandolierCreate_Struct*) app->pBuffer;
|
||||
|
||||
switch (bs->Action)
|
||||
{
|
||||
case bandolierCreate:
|
||||
CreateBandolier(app);
|
||||
break;
|
||||
case bandolierRemove:
|
||||
RemoveBandolier(app);
|
||||
break;
|
||||
case bandolierSet:
|
||||
SetBandolier(app);
|
||||
break;
|
||||
default:
|
||||
LogDebug("Unknown Bandolier action [{}]", bs->Action);
|
||||
break;
|
||||
switch (bs->Action) {
|
||||
case bandolierCreate:
|
||||
CreateBandolier(app);
|
||||
break;
|
||||
case bandolierRemove:
|
||||
RemoveBandolier(app);
|
||||
break;
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
LogDebug("Unknown Bandolier action [{}]", bs->Action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3763,116 +3772,104 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
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
|
||||
// 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:
|
||||
{
|
||||
BuyerItemSearch(app);
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_SellerSearch:
|
||||
{
|
||||
BarterSearchRequest_Struct *bsr = (BarterSearchRequest_Struct*)app->pBuffer;
|
||||
SendBuyerResults(bsr->SearchString, bsr->SearchID);
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_BuyerModeOn:
|
||||
{
|
||||
if (!IsTrader()) {
|
||||
ToggleBuyerMode(true);
|
||||
case Barter_BuyerSearch: {
|
||||
BuyerItemSearch(app);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
Buf = (char *)app->pBuffer;
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, Buf, Barter_BuyerModeOff);
|
||||
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
|
||||
|
||||
case Barter_SellerSearch: {
|
||||
auto bsr = (BarterSearchRequest_Struct *) app->pBuffer;
|
||||
SendBuyerResults(*bsr);
|
||||
break;
|
||||
}
|
||||
QueuePacket(app);
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_BuyerModeOff:
|
||||
{
|
||||
QueuePacket(app);
|
||||
ToggleBuyerMode(false);
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerModeOn: {
|
||||
if (!IsTrader()) {
|
||||
ToggleBuyerMode(true);
|
||||
}
|
||||
else {
|
||||
ToggleBuyerMode(false);
|
||||
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_BuyerItemUpdate:
|
||||
{
|
||||
UpdateBuyLine(app);
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerModeOff: {
|
||||
ToggleBuyerMode(false);
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_BuyerItemRemove:
|
||||
{
|
||||
BuyerRemoveItem_Struct* bris = (BuyerRemoveItem_Struct*)app->pBuffer;
|
||||
database.RemoveBuyLine(CharacterID(), bris->BuySlot);
|
||||
QueuePacket(app);
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerItemUpdate: {
|
||||
ModifyBuyLine(app);
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_SellItem:
|
||||
{
|
||||
SellToBuyer(app);
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerItemStart: {
|
||||
CreateStartingBuyLines(app);
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_BuyerInspectBegin:
|
||||
{
|
||||
ShowBuyLines(app);
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerItemRemove: {
|
||||
auto bris = (BuyerRemoveItem_Struct *) app->pBuffer;
|
||||
BuyerBuyLinesRepository::DeleteBuyLine(database, CharacterID(), bris->buy_slot_id);
|
||||
QueuePacket(app);
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_BuyerInspectEnd:
|
||||
{
|
||||
BuyerInspectRequest_Struct* bir = (BuyerInspectRequest_Struct*)app->pBuffer;
|
||||
Client *Buyer = entity_list.GetClientByID(bir->BuyerID);
|
||||
if (Buyer)
|
||||
Buyer->WithCustomer(0);
|
||||
case Barter_SellItem: {
|
||||
SellToBuyer(app);
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case Barter_BuyerInspectBegin: {
|
||||
ShowBuyLines(app);
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_BarterItemInspect:
|
||||
{
|
||||
BarterItemSearchLinkRequest_Struct* bislr = (BarterItemSearchLinkRequest_Struct*)app->pBuffer;
|
||||
case Barter_BuyerInspectEnd: {
|
||||
auto bir = (BuyerInspectRequest_Struct *) app->pBuffer;
|
||||
auto buyer = entity_list.GetClientByID(bir->buyer_id);
|
||||
if (buyer) {
|
||||
buyer->WithCustomer(0);
|
||||
}
|
||||
|
||||
const EQ::ItemData* item = database.GetItem(bislr->ItemID);
|
||||
break;
|
||||
}
|
||||
case Barter_BarterItemInspect: {
|
||||
auto bislr = (BarterItemSearchLinkRequest_Struct *) app->pBuffer;
|
||||
const EQ::ItemData *item = database.GetItem(bislr->item_id);
|
||||
|
||||
if (!item)
|
||||
Message(Chat::Red, "Error: This item does not exist!");
|
||||
else
|
||||
{
|
||||
EQ::ItemInstance* inst = database.CreateItem(item);
|
||||
if (inst)
|
||||
{
|
||||
if (!item) {
|
||||
Message(Chat::Red, "Error: This item does not exist!");
|
||||
return;
|
||||
}
|
||||
|
||||
EQ::ItemInstance *inst = database.CreateItem(item);
|
||||
if (inst) {
|
||||
SendItemPacket(0, inst, ItemPacketViewLink);
|
||||
safe_delete(inst);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_Welcome:
|
||||
{
|
||||
//SendBazaarWelcome();
|
||||
SendBarterWelcome();
|
||||
break;
|
||||
}
|
||||
|
||||
case Barter_WelcomeMessageUpdate:
|
||||
{
|
||||
BuyerWelcomeMessageUpdate_Struct* bwmu = (BuyerWelcomeMessageUpdate_Struct*)app->pBuffer;
|
||||
SetBuyerWelcomeMessage(bwmu->WelcomeMessage);
|
||||
case Barter_WelcomeMessageUpdate: {
|
||||
auto bwmu = (BuyerWelcomeMessageUpdate_Struct *) app->pBuffer;
|
||||
SetBuyerWelcomeMessage(bwmu->welcome_message);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3896,15 +3893,20 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app)
|
||||
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;
|
||||
}
|
||||
|
||||
default:
|
||||
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();
|
||||
}
|
||||
|
||||
if (IsBuyer()) {
|
||||
ToggleBuyerMode(false);
|
||||
}
|
||||
|
||||
/* Break Hide if moving without sneaking and set rewind timer if moved */
|
||||
if ((hidden || improved_hidden) && !sneaking) {
|
||||
hidden = false;
|
||||
@@ -10434,7 +10440,7 @@ void Client::Handle_OP_Mend(const EQApplicationPacket *app)
|
||||
|
||||
int mendhp = GetMaxHP() / 4;
|
||||
int currenthp = GetHP();
|
||||
if (zone->random.Int(0, 199) < (int)GetSkill(EQ::skills::SkillMend)) {
|
||||
if (zone->random.Int(0, RuleI(Character, MendAlwaysSucceedValue)) < (int)GetSkill(EQ::skills::SkillMend)) {
|
||||
|
||||
int criticalchance = spellbonuses.CriticalMend + itembonuses.CriticalMend + aabonuses.CriticalMend;
|
||||
|
||||
@@ -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)
|
||||
if (option >= 0 && option < numStances)
|
||||
{
|
||||
merc->SetStance((EQ::constants::StanceType)mercTemplate->Stances[option]);
|
||||
merc->SetStance(mercTemplate->Stances[option]);
|
||||
GetMercInfo().Stance = mercTemplate->Stances[option];
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
@@ -11882,8 +12002,8 @@ void Client::Handle_OP_PickPocket(const EQApplicationPacket *app)
|
||||
}
|
||||
else if (victim->IsNPC()) {
|
||||
auto body = victim->GetBodyType();
|
||||
if (body == BT_Humanoid || body == BT_Monster || body == BT_Giant ||
|
||||
body == BT_Lycanthrope) {
|
||||
if (body == BodyType::Humanoid || body == BodyType::Monster || body == BodyType::Giant ||
|
||||
body == BodyType::Lycanthrope) {
|
||||
victim->CastToNPC()->PickPocket(this);
|
||||
return;
|
||||
}
|
||||
@@ -13561,11 +13681,11 @@ void Client::Handle_OP_Sacrifice(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
if (ss->Confirm) {
|
||||
Client *Caster = entity_list.GetClientByName(SacrificeCaster.c_str());
|
||||
Mob *Caster = entity_list.GetMob(sacrifice_caster_id);
|
||||
if (Caster) Sacrifice(Caster);
|
||||
}
|
||||
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)
|
||||
@@ -13608,14 +13728,24 @@ void Client::Handle_OP_SelectTribute(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_SenseHeading(const EQApplicationPacket *app)
|
||||
{
|
||||
if (!HasSkill(EQ::skills::SkillSenseHeading))
|
||||
if (!HasSkill(EQ::skills::SkillSenseHeading)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int chancemod = 0;
|
||||
if (RuleB(Skills, TrainSenseHeading)) {
|
||||
CheckIncreaseSkill(EQ::skills::SkillSenseHeading, nullptr, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
CheckIncreaseSkill(EQ::skills::SkillSenseHeading, nullptr, chancemod);
|
||||
if (parse->PlayerHasQuestSub(EVENT_USE_SKILL)) {
|
||||
const auto& export_string = fmt::format(
|
||||
"{} {}",
|
||||
EQ::skills::SkillSenseHeading,
|
||||
GetRawSkill(EQ::skills::SkillSenseHeading)
|
||||
);
|
||||
|
||||
return;
|
||||
parse->EventPlayer(EVENT_USE_SKILL, this, export_string, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app)
|
||||
@@ -14644,12 +14774,18 @@ void Client::Handle_OP_Sneak(const EQApplicationPacket *app)
|
||||
sa_out->parameter = 0;
|
||||
entity_list.QueueClients(this, outapp, true);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
CheckIncreaseSkill(EQ::skills::SkillSneak, nullptr, 5);
|
||||
}
|
||||
|
||||
float hidechance = ((GetSkill(EQ::skills::SkillSneak) / 300.0f) + .25) * 100;
|
||||
|
||||
if (RuleB(Character, SneakAlwaysSucceedOver100)) {
|
||||
hidechance = std::max(10, (int)GetSkill(EQ::skills::SkillSneak));
|
||||
}
|
||||
|
||||
float random = zone->random.Real(0, 99);
|
||||
|
||||
if (!was && random < hidechance) {
|
||||
sneaking = true;
|
||||
}
|
||||
@@ -14862,14 +14998,15 @@ void Client::Handle_OP_Split(const EQApplicationPacket *app)
|
||||
Group *group = nullptr;
|
||||
Raid *raid = nullptr;
|
||||
|
||||
if (IsRaidGrouped())
|
||||
if (IsRaidGrouped()) {
|
||||
raid = GetRaid();
|
||||
else if (IsGrouped())
|
||||
} else if (IsGrouped()) {
|
||||
group = GetGroup();
|
||||
}
|
||||
|
||||
// is there an actual error message for this?
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -14877,14 +15014,15 @@ void Client::Handle_OP_Split(const EQApplicationPacket *app)
|
||||
10 * static_cast<uint64>(split->silver) +
|
||||
100 * static_cast<uint64>(split->gold) +
|
||||
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;
|
||||
}
|
||||
|
||||
if (raid)
|
||||
if (raid) {
|
||||
raid->SplitMoney(raid->GetGroup(this), split->copper, split->silver, split->gold, split->platinum);
|
||||
else if (group)
|
||||
group->SplitMoney(split->copper, split->silver, split->gold, split->platinum, this);
|
||||
} else if (group) {
|
||||
group->SplitMoney(split->copper, split->silver, split->gold, split->platinum, this, true);
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
@@ -14999,9 +15137,9 @@ void Client::Handle_OP_TargetCommand(const EQApplicationPacket *app)
|
||||
|
||||
if (nt) {
|
||||
if (GetGM() || (!nt->IsInvisible(this) && (DistanceSquared(m_Position, nt->GetPosition()) <= TARGETING_RANGE*TARGETING_RANGE))) {
|
||||
if (nt->GetBodyType() == BT_NoTarget2 ||
|
||||
nt->GetBodyType() == BT_Special ||
|
||||
nt->GetBodyType() == BT_NoTarget) {
|
||||
if (nt->GetBodyType() == BodyType::NoTarget2 ||
|
||||
nt->GetBodyType() == BodyType::Special ||
|
||||
nt->GetBodyType() == BodyType::NoTarget) {
|
||||
can_target = false;
|
||||
}
|
||||
else {
|
||||
@@ -15144,8 +15282,8 @@ void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app)
|
||||
GetTarget()->IsTargeted(1);
|
||||
return;
|
||||
}
|
||||
else if (GetTarget()->GetBodyType() == BT_NoTarget2 || GetTarget()->GetBodyType() == BT_Special
|
||||
|| GetTarget()->GetBodyType() == BT_NoTarget)
|
||||
else if (GetTarget()->GetBodyType() == BodyType::NoTarget2 || GetTarget()->GetBodyType() == BodyType::Special
|
||||
|| GetTarget()->GetBodyType() == BodyType::NoTarget)
|
||||
{
|
||||
auto message = fmt::format(
|
||||
"[{}] attempting to target something untargetable [{}] bodytype [{}]",
|
||||
@@ -15512,7 +15650,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
|
||||
break;
|
||||
}
|
||||
case TraderOn: {
|
||||
if (Buyer) {
|
||||
if (IsBuyer()) {
|
||||
TraderEndTrader();
|
||||
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
|
||||
return;
|
||||
@@ -15558,7 +15696,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
||||
auto trader = entity_list.GetClientByID(in->trader_id);
|
||||
|
||||
switch (in->method) {
|
||||
case ByVendor: {
|
||||
case BazaarByVendor: {
|
||||
if (trader) {
|
||||
LogTrading("Buy item directly from vendor id <green>[{}] item_id <green>[{}] quantity <green>[{}] "
|
||||
"serial_number <green>[{}]",
|
||||
@@ -15571,7 +15709,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ByParcel: {
|
||||
case BazaarByParcel: {
|
||||
if (!RuleB(Parcel, EnableParcelMerchants) || !RuleB(Bazaar, EnableParcelDelivery)) {
|
||||
LogTrading(
|
||||
"Bazaar purchase attempt by parcel delivery though 'Parcel:EnableParcelMerchants' or "
|
||||
@@ -15581,7 +15719,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
||||
Chat::Yellow,
|
||||
"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;
|
||||
TradeRequestFailed(app);
|
||||
return;
|
||||
@@ -15596,7 +15734,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
||||
BuyTraderItemOutsideBazaar(in, app);
|
||||
break;
|
||||
}
|
||||
case ByDirectToInventory: {
|
||||
case BazaarByDirectToInventory: {
|
||||
if (!RuleB(Parcel, EnableDirectToInventoryDelivery)) {
|
||||
LogTrading("Bazaar purchase attempt by direct inventory delivery though "
|
||||
"'Parcel:EnableDirectToInventoryDelivery' not enabled."
|
||||
@@ -15605,7 +15743,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
||||
Chat::Yellow,
|
||||
"Direct inventory delivey is not enabled on this server. Please visit the vendor directly."
|
||||
);
|
||||
in->method = ByDirectToInventory;
|
||||
in->method = BazaarByDirectToInventory;
|
||||
in->sub_action = Failed;
|
||||
TradeRequestFailed(app);
|
||||
return;
|
||||
@@ -15622,7 +15760,7 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
||||
Chat::Yellow,
|
||||
"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;
|
||||
TradeRequestFailed(app);
|
||||
break;
|
||||
@@ -16021,7 +16159,8 @@ void Client::Handle_OP_WearChange(const EQApplicationPacket *app)
|
||||
wc->hero_forge_model = GetHerosForgeModel(wc->wear_slot_id);
|
||||
|
||||
// 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)
|
||||
@@ -16768,9 +16907,14 @@ void Client::Handle_OP_RaidClearNPCMarks(const EQApplicationPacket* app)
|
||||
|
||||
void Client::RecordStats()
|
||||
{
|
||||
const uint32 character_id = CharacterID();
|
||||
if (!character_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto r = CharacterStatsRecordRepository::FindOne(
|
||||
database,
|
||||
CharacterID()
|
||||
character_id
|
||||
);
|
||||
|
||||
r.status = Admin();
|
||||
@@ -16848,8 +16992,8 @@ void Client::RecordStats()
|
||||
if (r.character_id > 0) {
|
||||
CharacterStatsRecordRepository::UpdateOne(database, r);
|
||||
} else {
|
||||
r.character_id = CharacterID();
|
||||
r.created_at = std::time(nullptr);
|
||||
r.character_id = character_id;
|
||||
r.created_at = std::time(nullptr);
|
||||
CharacterStatsRecordRepository::InsertOne(database, r);
|
||||
}
|
||||
}
|
||||
|
||||
+118
-57
@@ -289,6 +289,37 @@ bool Client::Process() {
|
||||
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;
|
||||
/*
|
||||
Things which prevent us from attacking:
|
||||
@@ -321,44 +352,42 @@ bool Client::Process() {
|
||||
auto_fire = false;
|
||||
}
|
||||
EQ::ItemInstance *ranged = GetInv().GetItem(EQ::invslot::slotRange);
|
||||
if (ranged)
|
||||
{
|
||||
if (ranged) {
|
||||
if (ranged->GetItem() && ranged->GetItem()->ItemType == EQ::item::ItemTypeBow) {
|
||||
if (ranged_timer.Check(false)) {
|
||||
if (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
||||
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
||||
if (CheckLosFN(GetTarget()) && CheckWaterAutoFireLoS(GetTarget())) {
|
||||
//client has built in los check, but auto fire does not.. done last.
|
||||
RangedAttack(GetTarget());
|
||||
if (CheckDoubleRangedAttack())
|
||||
if (RangedAttack(GetTarget()) && CheckDoubleRangedAttack()) {
|
||||
RangedAttack(GetTarget(), true);
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
ranged_timer.Start();
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
ranged_timer.Start();
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
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 (GetTarget() && (GetTarget()->IsNPC() || GetTarget()->IsClient()) && IsAttackAllowed(GetTarget())) {
|
||||
if (GetTarget()->InFrontMob(this, GetTarget()->GetX(), GetTarget()->GetY())) {
|
||||
if (CheckLosFN(GetTarget()) && CheckWaterAutoFireLoS(GetTarget())) {
|
||||
//client has built in los check, but auto fire does not.. done last.
|
||||
ThrowingAttack(GetTarget());
|
||||
}
|
||||
else
|
||||
} else {
|
||||
ranged_timer.Start();
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
ranged_timer.Start();
|
||||
}
|
||||
else
|
||||
}
|
||||
} else {
|
||||
ranged_timer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -782,39 +811,41 @@ void Client::BulkSendInventoryItems()
|
||||
last_pos = ob.tellp();
|
||||
}
|
||||
|
||||
// Bank items
|
||||
for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) {
|
||||
const EQ::ItemInstance* inst = m_inv[slot_id];
|
||||
if (!inst)
|
||||
continue;
|
||||
if (!RuleB(Inventory, LazyLoadBank)) {
|
||||
// Bank items
|
||||
for (int16 slot_id = EQ::invslot::BANK_BEGIN; slot_id <= EQ::invslot::BANK_END; slot_id++) {
|
||||
const EQ::ItemInstance* inst = m_inv[slot_id];
|
||||
if (!inst)
|
||||
continue;
|
||||
|
||||
inst->Serialize(ob, slot_id);
|
||||
inst->Serialize(ob, slot_id);
|
||||
|
||||
if (ob.tellp() == last_pos)
|
||||
LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id);
|
||||
if (ob.tellp() == last_pos)
|
||||
LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id);
|
||||
|
||||
last_pos = ob.tellp();
|
||||
}
|
||||
last_pos = ob.tellp();
|
||||
}
|
||||
|
||||
// SharedBank items
|
||||
for (int16 slot_id = EQ::invslot::SHARED_BANK_BEGIN; slot_id <= EQ::invslot::SHARED_BANK_END; slot_id++) {
|
||||
const EQ::ItemInstance* inst = m_inv[slot_id];
|
||||
if (!inst)
|
||||
continue;
|
||||
// SharedBank items
|
||||
for (int16 slot_id = EQ::invslot::SHARED_BANK_BEGIN; slot_id <= EQ::invslot::SHARED_BANK_END; slot_id++) {
|
||||
const EQ::ItemInstance* inst = m_inv[slot_id];
|
||||
if (!inst)
|
||||
continue;
|
||||
|
||||
inst->Serialize(ob, slot_id);
|
||||
inst->Serialize(ob, slot_id);
|
||||
|
||||
if (ob.tellp() == last_pos)
|
||||
LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id);
|
||||
if (ob.tellp() == last_pos)
|
||||
LogInventory("Serialization failed on item slot [{}] during BulkSendInventoryItems. Item skipped", slot_id);
|
||||
|
||||
last_pos = ob.tellp();
|
||||
}
|
||||
last_pos = ob.tellp();
|
||||
}
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_CharInventory);
|
||||
outapp->size = ob.size();
|
||||
outapp->pBuffer = ob.detach();
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
auto outapp = new EQApplicationPacket(OP_CharInventory);
|
||||
outapp->size = ob.size();
|
||||
outapp->pBuffer = ob.detach();
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
||||
@@ -1034,32 +1065,62 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I
|
||||
name, (uint16)spells[SpellID].base_value[0],
|
||||
SpellID, ZoneID, InstanceID);
|
||||
|
||||
if (RuleB(Spells, BuffsFadeOnDeath)) {
|
||||
BuffFadeNonPersistDeath();
|
||||
}
|
||||
const bool use_old_resurrection = (
|
||||
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);
|
||||
// Rez spells with Rez effects have this DescNum (first is Titanium, second is 6.2 Client)
|
||||
if(RuleB(Character, UseResurrectionSickness) && SpellEffectDescNum == 82 || SpellEffectDescNum == 39067) {
|
||||
SetHP(GetMaxHP() / 5);
|
||||
SetMana(0);
|
||||
int resurrection_sickness_spell_id = (
|
||||
RuleB(Character, UseOldRaceRezEffects) &&
|
||||
(
|
||||
GetRace() == BARBARIAN ||
|
||||
GetRace() == DWARF ||
|
||||
GetRace() == TROLL ||
|
||||
GetRace() == OGRE
|
||||
) ?
|
||||
RuleI(Character, OldResurrectionSicknessSpellID) :
|
||||
RuleI(Character, ResurrectionSicknessSpellID)
|
||||
);
|
||||
|
||||
if (RuleB(Spells, BuffsFadeOnDeath)) {
|
||||
BuffFadeNonPersistDeath();
|
||||
}
|
||||
|
||||
SpellOnTarget(resurrection_sickness_spell_id, this);
|
||||
} else if (SpellID == SPELL_DIVINE_REZ) {
|
||||
if (RuleB(Spells, BuffsFadeOnDeath)) {
|
||||
BuffFadeNonPersistDeath();
|
||||
}
|
||||
|
||||
RestoreHealth();
|
||||
RestoreMana();
|
||||
RestoreEndurance();
|
||||
} 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);
|
||||
SetMana(GetMaxMana() / 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("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("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("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) ||
|
||||
@@ -134,6 +134,7 @@ int command_init(void)
|
||||
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("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("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) ||
|
||||
@@ -829,6 +830,7 @@ void command_bot(Client *c, const Seperator *sep)
|
||||
#include "gm_commands/fish.cpp"
|
||||
#include "gm_commands/fixmob.cpp"
|
||||
#include "gm_commands/flagedit.cpp"
|
||||
#include "gm_commands/fleeinfo.cpp"
|
||||
#include "gm_commands/forage.cpp"
|
||||
#include "gm_commands/gearup.cpp"
|
||||
#include "gm_commands/giveitem.cpp"
|
||||
|
||||
+2
-1
@@ -87,6 +87,7 @@ void command_find(Client *c, const Seperator *sep);
|
||||
void command_fish(Client* c, const Seperator* sep);
|
||||
void command_fixmob(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_gearup(Client *c, const Seperator *sep);
|
||||
void command_giveitem(Client *c, const Seperator *sep);
|
||||
@@ -136,7 +137,7 @@ void command_nukebuffs(Client *c, const Seperator *sep);
|
||||
void command_nukeitem(Client *c, const Seperator *sep);
|
||||
void command_object(Client *c, const Seperator *sep);
|
||||
void command_oocmute(Client *c, const Seperator *sep);
|
||||
void command_parcels(Client *c, const Seperator *sep);
|
||||
void command_parcels(Client *c, const Seperator *sep);
|
||||
void command_path(Client *c, const Seperator *sep);
|
||||
void command_peqzone(Client *c, const Seperator *sep);
|
||||
void command_petitems(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_CHANCE = 1; // SPA 288
|
||||
constexpr uint16 SKILLATK_PROC_SKILL = 2; // SPA 288
|
||||
constexpr uint16 SLAYUNDEAD_RATE_MOD = 0; // SPA 219
|
||||
constexpr uint16 SLAYUNDEAD_DMG_MOD = 1; // SPA 219
|
||||
constexpr uint16 SLAYUNDEAD_DMG_MOD = 0; // SPA 219
|
||||
constexpr uint16 SLAYUNDEAD_RATE_MOD = 1; // SPA 219
|
||||
constexpr uint16 DOUBLE_RIPOSTE_CHANCE = 0; // SPA 223
|
||||
constexpr uint16 DOUBLE_RIPOSTE_SKILL_ATK_CHANCE = 1; // SPA 223
|
||||
constexpr uint16 DOUBLE_RIPOSTE_SKILL = 2; // SPA 223
|
||||
|
||||
+3
-3
@@ -70,7 +70,7 @@ Corpse::Corpse(
|
||||
npc->GetGender(), // in_gender
|
||||
npc->GetRace(), // in_race
|
||||
npc->GetClass(), // in_class
|
||||
BT_Humanoid, // in_bodytype
|
||||
BodyType::Humanoid, // in_bodytype
|
||||
npc->GetDeity(), // in_deity
|
||||
npc->GetLevel(), // in_level
|
||||
npc->GetNPCTypeID(), // in_npctype_id
|
||||
@@ -189,7 +189,7 @@ Corpse::Corpse(Client *c, int32 rez_exp, KilledByTypes in_killed_by) : Mob(
|
||||
c->GetGender(), // in_gender
|
||||
c->GetRace(), // in_race
|
||||
c->GetClass(), // in_class
|
||||
BT_Humanoid, // in_bodytype
|
||||
BodyType::Humanoid, // in_bodytype
|
||||
c->GetDeity(), // in_deity
|
||||
c->GetLevel(), // in_level
|
||||
0, // in_npctype_id
|
||||
@@ -495,7 +495,7 @@ Corpse::Corpse(
|
||||
gender, // in_gender
|
||||
race, // in_race
|
||||
class_, // in_class
|
||||
BT_Humanoid, // in_bodytype
|
||||
BodyType::Humanoid, // in_bodytype
|
||||
deity, // in_deity
|
||||
level, // in_level
|
||||
0, // in_npctype_id
|
||||
|
||||
@@ -428,8 +428,6 @@ void DataBucket::BulkLoadEntities(DataBucketLoadType::Type t, std::vector<uint32
|
||||
return;
|
||||
}
|
||||
|
||||
size_t size_before = g_data_bucket_cache.size();
|
||||
|
||||
LogDataBucketsDetail("cache size before [{}] l size [{}]", g_data_bucket_cache.size(), l.size());
|
||||
|
||||
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) {
|
||||
if (!ExistsInCache(e)) {
|
||||
LogDataBucketsDetail("bucket id [{}] bucket key [{}] bucket value [{}]", e.id, e.key_, e.value);
|
||||
|
||||
@@ -60,6 +60,12 @@ public:
|
||||
void SetPosition(const glm::vec4 &position);
|
||||
void SetSize(uint16 size);
|
||||
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 GetY();
|
||||
|
||||
+112
-155
@@ -71,20 +71,20 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
|
||||
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
|
||||
|
||||
//Improved Harm Touch is a guaranteed crit if you have at least one level of SCF.
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBot() && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0))
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBot() && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0)) {
|
||||
chance = 100;
|
||||
}
|
||||
|
||||
if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance)
|
||||
if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance) {
|
||||
chance = spells[spell_id].override_crit_chance;
|
||||
}
|
||||
|
||||
if (zone->random.Roll(chance)) {
|
||||
Critical = true;
|
||||
ratio += itembonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncrease + aabonuses.SpellCritDmgIncrease;
|
||||
ratio += itembonuses.SpellCritDmgIncNoStack + spellbonuses.SpellCritDmgIncNoStack + aabonuses.SpellCritDmgIncNoStack;
|
||||
}
|
||||
|
||||
else if ((IsOfClientBot() && GetClass() == Class::Wizard) || (IsMerc() && GetClass() == CASTERDPS)) {
|
||||
if ((GetLevel() >= RuleI(Spells, WizCritLevel)) && zone->random.Roll(RuleI(Spells, WizCritChance))){
|
||||
} else if ((IsOfClientBot() && GetClass() == Class::Wizard) || (IsMerc() && GetClass() == CASTERDPS)) {
|
||||
if ((GetLevel() >= RuleI(Spells, WizCritLevel)) && zone->random.Roll(RuleI(Spells, WizCritChance))) {
|
||||
//Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
|
||||
ratio += zone->random.Int(RuleI(Spells, WizardCritMinimumRandomRatio), RuleI(Spells, WizardCritMaximumRandomRatio));
|
||||
Critical = true;
|
||||
@@ -100,21 +100,32 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
|
||||
}
|
||||
|
||||
if (Critical) {
|
||||
|
||||
value = base_value*ratio/100;
|
||||
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
|
||||
if (RuleB(Spells, UseClassicHarmTouchDamage)) {
|
||||
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
|
||||
if (IsHarmTouchSpell(spell_id) && GetLevel() > 40) {
|
||||
value -= (GetLevel() - 40) * 20;
|
||||
}
|
||||
|
||||
value += int(base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
|
||||
value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100)*ratio / 100;
|
||||
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm TOuch.
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBotMerc()) { //Improved Harm Touch
|
||||
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
|
||||
}
|
||||
}
|
||||
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id) / 100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id) / 100;
|
||||
|
||||
value += int(base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id) / 100) * ratio / 100;
|
||||
value += int(base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100) * ratio / 100;
|
||||
|
||||
if (target) {
|
||||
value += int(base_value*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
|
||||
value += int(base_value*target->GetVulnerability(this, spell_id, 0) / 100) * ratio / 100;
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id);
|
||||
}
|
||||
|
||||
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100;
|
||||
value -= GetFocusEffect(focusFcDamageAmtCrit, spell_id) * ratio / 100;
|
||||
|
||||
value -= GetFocusEffect(focusFcDamageAmt, spell_id);
|
||||
value -= GetFocusEffect(focusFcDamageAmt2, spell_id);
|
||||
@@ -127,9 +138,7 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
|
||||
if (RuleB(Spells, IgnoreSpellDmgLvlRestriction) && !spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg) {
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value) * ratio / 100;
|
||||
|
||||
}
|
||||
|
||||
else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
|
||||
} else if (!spells[spell_id].no_heal_damage_item_mod && itembonuses.SpellDmg && spells[spell_id].classes[(GetClass() % 17) - 1] >= GetLevel() - 5) {
|
||||
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, base_value) * ratio / 100;
|
||||
}
|
||||
|
||||
@@ -148,30 +157,20 @@ int64 Mob::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
|
||||
MessageString(Chat::SpellCrit, YOU_CRIT_BLAST, itoa(-value));
|
||||
}
|
||||
|
||||
// Need to scale HT damage differently after level 40! It no longer scales by the constant value in the spell file. It scales differently, instead of 10 more damage per level, it does 30 more damage per level. So we multiply the level minus 40 times 20 if they are over level 40.
|
||||
if (IsHarmTouchSpell(spell_id) && GetLevel() > 40) {
|
||||
value -= (GetLevel() - 40) * 20;
|
||||
}
|
||||
|
||||
//This adds the extra damage from the AA Unholy Touch, 450 per level to the AA Improved Harm Touch.
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && IsOfClientBot()) { //Improved Harm Touch
|
||||
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
}
|
||||
//Non Crtical Hit Calculation pathway
|
||||
value = base_value;
|
||||
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage, spell_id) / 100;
|
||||
value += base_value*GetFocusEffect(focusImprovedDamage2, spell_id) / 100;
|
||||
|
||||
value += base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusFcAmplifyMod, spell_id)/100;
|
||||
value += base_value*GetFocusEffect(focusFcDamagePctCrit, spell_id) / 100;
|
||||
value += base_value*GetFocusEffect(focusFcAmplifyMod, spell_id) / 100;
|
||||
|
||||
if (target) {
|
||||
value += base_value*target->GetVulnerability(this, spell_id, 0)/100;
|
||||
value += base_value*target->GetVulnerability(this, spell_id, 0) / 100;
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id);
|
||||
}
|
||||
|
||||
@@ -614,7 +613,7 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration)
|
||||
{
|
||||
// focuses don't affect discipline duration (Except War Cries)
|
||||
if (
|
||||
IsDiscipline(spell_id) &&
|
||||
IsDiscipline(spell_id) &&
|
||||
(
|
||||
spell_id != SPELL_BATTLE_CRY &&
|
||||
spell_id != SPELL_WAR_CRY &&
|
||||
@@ -1029,24 +1028,15 @@ void Client::SendDisciplineTimer(uint32 timer_id, uint32 duration)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param taunter
|
||||
* @param range
|
||||
* @param bonus_hate
|
||||
*/
|
||||
void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate)
|
||||
void EntityList::AETaunt(Client* taunter, float range, int bonus_hate)
|
||||
{
|
||||
|
||||
/**
|
||||
* Live AE taunt range - Hardcoded.
|
||||
*/
|
||||
if (range == 0) {
|
||||
range = 40;
|
||||
}
|
||||
|
||||
float range_squared = range * range;
|
||||
|
||||
for (auto &it : entity_list.GetCloseMobList(taunter, range)) {
|
||||
for (auto& it: entity_list.GetCloseMobList(taunter, range)) {
|
||||
Mob *them = it.second;
|
||||
if (!them) {
|
||||
continue;
|
||||
@@ -1061,9 +1051,11 @@ void EntityList::AETaunt(Client *taunter, float range, int32 bonus_hate)
|
||||
z_difference *= -1;
|
||||
}
|
||||
|
||||
if (z_difference < 10
|
||||
&& taunter->IsAttackAllowed(them)
|
||||
&& DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared) {
|
||||
if (
|
||||
z_difference < 10 &&
|
||||
taunter->IsAttackAllowed(them) &&
|
||||
DistanceSquaredNoZ(taunter->GetPosition(), them->GetPosition()) <= range_squared
|
||||
) {
|
||||
if (taunter->CheckLosFN(them)) {
|
||||
taunter->Taunt(them->CastToNPC(), true, 0, true, bonus_hate);
|
||||
}
|
||||
@@ -1071,38 +1063,31 @@ 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(
|
||||
Mob *caster_mob,
|
||||
Mob *center_mob,
|
||||
Mob* caster_mob,
|
||||
Mob* center_mob,
|
||||
uint16 spell_id,
|
||||
bool affect_caster,
|
||||
int16 resist_adjust,
|
||||
int *max_targets
|
||||
int* max_targets,
|
||||
bool is_scripted
|
||||
)
|
||||
{
|
||||
const auto &cast_target_position =
|
||||
spells[spell_id].target_type == ST_Ring ?
|
||||
caster_mob->GetTargetRingLocation() :
|
||||
static_cast<glm::vec3>(center_mob->GetPosition());
|
||||
const auto& cast_target_position = (
|
||||
(!is_scripted && spells[spell_id].target_type == ST_Ring) ?
|
||||
caster_mob->GetTargetRingLocation() :
|
||||
static_cast<glm::vec3>(center_mob->GetPosition())
|
||||
);
|
||||
|
||||
Mob* current_mob = nullptr;
|
||||
|
||||
Mob *current_mob = nullptr;
|
||||
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
|
||||
bool is_npc = caster_mob->IsNPC();
|
||||
float distance = caster_mob->GetAOERange(spell_id);
|
||||
float distance_squared = distance * distance;
|
||||
float min_range2 = 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 max = {cast_target_position.x + distance, cast_target_position.y + distance};
|
||||
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 max = { cast_target_position.x + distance, cast_target_position.y + distance };
|
||||
|
||||
/**
|
||||
* If using Old Rain Targets - there is no max target limitation
|
||||
@@ -1111,17 +1096,18 @@ void EntityList::AESpell(
|
||||
max_targets = nullptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Max AOE targets
|
||||
*/
|
||||
int max_targets_allowed = RuleI(Range, AOEMaxTargets); // unlimited
|
||||
if (max_targets) { // rains pass this in since they need to preserve the count through waves
|
||||
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;
|
||||
}
|
||||
else if (IsTargetableAESpell(spell_id) && is_detrimental_spell && !is_npc && !IsEffectInSpell(spell_id, SE_Lull) && !IsEffectInSpell(spell_id, SE_Mez)) {
|
||||
} else if (
|
||||
IsTargetableAESpell(spell_id) &&
|
||||
is_detrimental_spell &&
|
||||
!is_npc &&
|
||||
!IsEffectInSpell(spell_id, SE_Lull) &&
|
||||
!IsEffectInSpell(spell_id, SE_Mez)
|
||||
) {
|
||||
max_targets_allowed = 4;
|
||||
}
|
||||
|
||||
@@ -1134,7 +1120,7 @@ void EntityList::AESpell(
|
||||
distance
|
||||
);
|
||||
|
||||
for (auto &it : entity_list.GetCloseMobList(caster_mob, distance)) {
|
||||
for (auto& it: entity_list.GetCloseMobList(caster_mob, distance)) {
|
||||
current_mob = it.second;
|
||||
if (!current_mob) {
|
||||
continue;
|
||||
@@ -1162,16 +1148,11 @@ void EntityList::AESpell(
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check PC / NPC
|
||||
* 1 = PC
|
||||
* 2 = NPC
|
||||
*/
|
||||
if (spells[spell_id].pcnpc_only_flag == 1 && !current_mob->IsOfClientBotMerc()) {
|
||||
if (spells[spell_id].pcnpc_only_flag == PCNPCOnlyFlagType::PC && !current_mob->IsOfClientBotMerc()) {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1185,41 +1166,40 @@ void EntityList::AESpell(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (distance_to_target < min_range2) {
|
||||
if (distance_to_target < min_range_squared) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_npc && current_mob->IsNPC() &&
|
||||
spells[spell_id].target_type != ST_AreaNPCOnly) { //check npc->npc casting
|
||||
FACTION_VALUE faction_value = current_mob->GetReverseFactionCon(caster_mob);
|
||||
if (
|
||||
is_npc &&
|
||||
current_mob->IsNPC() &&
|
||||
spells[spell_id].target_type != ST_AreaNPCOnly
|
||||
) {
|
||||
const auto faction_value = current_mob->GetReverseFactionCon(caster_mob);
|
||||
if (is_detrimental_spell) {
|
||||
//affect mobs that are on our hate list, or
|
||||
//which have bad faction with us
|
||||
if (
|
||||
!(caster_mob->CheckAggro(current_mob) ||
|
||||
faction_value == FACTION_THREATENINGLY ||
|
||||
faction_value == FACTION_SCOWLS)) {
|
||||
faction_value == FACTION_THREATENINGLY ||
|
||||
faction_value == FACTION_SCOWLS)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
//only affect mobs we would assist.
|
||||
} else {
|
||||
if (!(faction_value <= FACTION_AMIABLY)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finally, make sure they are within range
|
||||
*/
|
||||
if (is_detrimental_spell) {
|
||||
if (!caster_mob->IsAttackAllowed(current_mob, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (center_mob && !spells[spell_id].npc_no_los && !center_mob->CheckLosFN(current_mob)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!center_mob && !spells[spell_id].npc_no_los && !caster_mob->CheckLosFN(
|
||||
caster_mob->GetTargetRingX(),
|
||||
caster_mob->GetTargetRingY(),
|
||||
@@ -1227,9 +1207,7 @@ void EntityList::AESpell(
|
||||
current_mob->GetSize())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
} else {
|
||||
/**
|
||||
* 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.
|
||||
@@ -1239,6 +1217,7 @@ void EntityList::AESpell(
|
||||
if (caster_mob->IsAttackAllowed(current_mob, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (caster_mob->CheckAggro(current_mob)) {
|
||||
continue;
|
||||
}
|
||||
@@ -1265,40 +1244,29 @@ void EntityList::AESpell(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param caster
|
||||
* @param center
|
||||
* @param spell_id
|
||||
* @param affect_caster
|
||||
*/
|
||||
void EntityList::MassGroupBuff(
|
||||
Mob *caster,
|
||||
Mob *center,
|
||||
Mob* caster,
|
||||
Mob* center,
|
||||
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_squared = distance * distance;
|
||||
bool is_detrimental_spell = IsDetrimentalSpell(spell_id);
|
||||
|
||||
for (auto &it : entity_list.GetCloseMobList(caster, distance)) {
|
||||
for (auto& it: entity_list.GetCloseMobList(caster, distance)) {
|
||||
current_mob = it.second;
|
||||
if (!current_mob) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip center
|
||||
*/
|
||||
if (current_mob == center) {
|
||||
if (current_mob == center) { // Skip Center
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip self
|
||||
*/
|
||||
if (current_mob == caster && !affect_caster) {
|
||||
if (current_mob == caster && !affect_caster) { // Skip Caster
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1306,17 +1274,13 @@ void EntityList::MassGroupBuff(
|
||||
continue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pets
|
||||
*/
|
||||
if (current_mob->IsNPC()) {
|
||||
Mob *owner = current_mob->GetOwner();
|
||||
Mob* owner = current_mob->GetOwner();
|
||||
if (owner) {
|
||||
if (!owner->IsOfClientBot()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -1329,55 +1293,48 @@ 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(
|
||||
Mob *attacker,
|
||||
Mob* attacker,
|
||||
float distance,
|
||||
int Hand,
|
||||
int count,
|
||||
int16 slot_id,
|
||||
int hit_count,
|
||||
bool is_from_spell,
|
||||
int attack_rounds)
|
||||
int attack_rounds
|
||||
)
|
||||
{
|
||||
Mob *current_mob = nullptr;
|
||||
Mob* current_mob = nullptr;
|
||||
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;
|
||||
if (!current_mob) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (current_mob->IsNPC()
|
||||
&& current_mob != attacker //this is not needed unless NPCs can use this
|
||||
&& (attacker->IsAttackAllowed(current_mob))
|
||||
&& !current_mob->IsHorse() /* dont attack mounts */
|
||||
&& (DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared)
|
||||
) {
|
||||
|
||||
if (
|
||||
current_mob->IsNPC() &&
|
||||
current_mob != attacker &&
|
||||
attacker->IsAttackAllowed(current_mob) &&
|
||||
!current_mob->IsHorse() &&
|
||||
DistanceSquared(current_mob->GetPosition(), attacker->GetPosition()) <= distance_squared
|
||||
) {
|
||||
for (int i = 0; i < attack_rounds; i++) {
|
||||
if (!attacker->IsClient() || attacker->GetClass() == Class::Monk || attacker->GetClass() == Class::Ranger) {
|
||||
attacker->Attack(current_mob, Hand, false, false, is_from_spell);
|
||||
if (
|
||||
!attacker->IsClient() ||
|
||||
attacker->GetClass() == Class::Monk ||
|
||||
attacker->GetClass() == Class::Ranger
|
||||
) {
|
||||
attacker->Attack(current_mob, slot_id, false, false, is_from_spell);
|
||||
} else {
|
||||
attacker->CastToClient()->DoAttackRounds(current_mob, Hand, is_from_spell);
|
||||
attacker->CastToClient()->DoAttackRounds(current_mob, slot_id, is_from_spell);
|
||||
}
|
||||
}
|
||||
|
||||
hit_count++;
|
||||
if (count != 0 && hit_count >= count) {
|
||||
current_hits++;
|
||||
if (hit_count != 0 && current_hits >= hit_count) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
+5
-4
@@ -2064,9 +2064,14 @@ void PerlembParser::ExportEventVariables(
|
||||
Corpse* corpse = std::any_cast<Corpse*>(extra_pointers->at(0));
|
||||
if (corpse) {
|
||||
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) {
|
||||
NPC* killed = std::any_cast<NPC*>(extra_pointers->at(1));
|
||||
if (killed) {
|
||||
@@ -2076,10 +2081,6 @@ void PerlembParser::ExportEventVariables(
|
||||
killed->IsBot() ? killed->CastToBot()->GetBotID() : 0
|
||||
);
|
||||
ExportVar(package_name.c_str(), "killed_npc_id", killed->IsNPC() ? killed->GetNPCTypeID() : 0);
|
||||
ExportVar(package_name.c_str(), "killed_x", killed->GetX());
|
||||
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;
|
||||
|
||||
+34
-4
@@ -34,11 +34,14 @@
|
||||
#include "questmgr.h"
|
||||
#include "zone.h"
|
||||
#include "data_bucket.h"
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "worldserver.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
extern Zone *zone;
|
||||
extern QueryServ *QServ;
|
||||
extern WorldServer worldserver;
|
||||
|
||||
#ifdef EMBPERL_XS_CLASSES
|
||||
|
||||
@@ -1273,7 +1276,7 @@ int Perl__tasktimeleft(int 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);
|
||||
}
|
||||
@@ -4740,9 +4743,9 @@ std::string Perl__getlanguagename(uint8 language_id)
|
||||
return quest_manager.getlanguagename(language_id);
|
||||
}
|
||||
|
||||
std::string Perl__getbodytypename(uint32 bodytype_id)
|
||||
std::string Perl__getbodytypename(uint8 body_type_id)
|
||||
{
|
||||
return quest_manager.getbodytypename(bodytype_id);
|
||||
return quest_manager.getbodytypename(body_type_id);
|
||||
}
|
||||
|
||||
std::string Perl__getconsiderlevelname(uint8 consider_level)
|
||||
@@ -5946,7 +5949,33 @@ bool Perl__send_parcel(perl::reference table_ref)
|
||||
e.note = note;
|
||||
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()
|
||||
@@ -6272,6 +6301,7 @@ void perl_register_quest()
|
||||
package.add("addloot", (void(*)(int, int))&Perl__addloot);
|
||||
package.add("addloot", (void(*)(int, int, bool))&Perl__addloot);
|
||||
package.add("addskill", &Perl__addskill);
|
||||
package.add("aretaskscompleted", &Perl__aretaskscompleted);
|
||||
package.add("assigntask", (void(*)(int))&Perl__assigntask);
|
||||
package.add("assigntask", (void(*)(int, bool))&Perl__assigntask);
|
||||
package.add("attack", &Perl__attack);
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ Encounter::Encounter(const char *enc_name) : Mob(
|
||||
Gender::Male, // in_gender
|
||||
Race::InvisibleMan, // in_race
|
||||
Class::None, // in_class
|
||||
BT_NoTarget, // in_bodytype
|
||||
BodyType::NoTarget, // in_bodytype
|
||||
Deity::Unknown, // in_deity
|
||||
0, // in_level
|
||||
0, // in_npcype_id
|
||||
|
||||
+42
-9
@@ -1655,7 +1655,10 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap
|
||||
Send = clear_target_window;
|
||||
if (c->GetGM() || RuleB(Spells, AlwaysSendTargetsBuffs)) {
|
||||
if (c->GetGM()) {
|
||||
c->Message(Chat::White, "Your GM flag allows you to always see your targets' buffs.");
|
||||
if (!c->EntityVariableExists(SEE_BUFFS_FLAG)) {
|
||||
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;
|
||||
@@ -4480,6 +4483,36 @@ void EntityList::QuestJournalledSayClose(
|
||||
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)
|
||||
{
|
||||
if (!sender)
|
||||
@@ -5059,7 +5092,7 @@ uint32 EntityList::CheckNPCsClose(Mob *center)
|
||||
while (it != npc_list.end()) {
|
||||
NPC *cur = it->second;
|
||||
if (!cur || cur == center || cur->IsPet() || cur->GetClass() == Class::LDoNTreasure ||
|
||||
cur->GetBodyType() == BT_NoTarget || cur->GetBodyType() == BT_Special) {
|
||||
cur->GetBodyType() == BodyType::NoTarget || cur->GetBodyType() == BodyType::Special) {
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
@@ -5419,9 +5452,9 @@ void EntityList::AddLootToNPCS(uint32 item_id, uint32 count)
|
||||
while (it != npc_list.end()) {
|
||||
if (!it->second->IsPet()
|
||||
&& it->second->GetClass() != Class::LDoNTreasure
|
||||
&& it->second->GetBodyType() != BT_NoTarget
|
||||
&& it->second->GetBodyType() != BT_NoTarget2
|
||||
&& it->second->GetBodyType() != BT_Special)
|
||||
&& it->second->GetBodyType() != BodyType::NoTarget
|
||||
&& it->second->GetBodyType() != BodyType::NoTarget2
|
||||
&& it->second->GetBodyType() != BodyType::Special)
|
||||
npc_count++;
|
||||
++it;
|
||||
}
|
||||
@@ -5440,9 +5473,9 @@ void EntityList::AddLootToNPCS(uint32 item_id, uint32 count)
|
||||
while (it != npc_list.end()) {
|
||||
if (!it->second->IsPet()
|
||||
&& it->second->GetClass() != Class::LDoNTreasure
|
||||
&& it->second->GetBodyType() != BT_NoTarget
|
||||
&& it->second->GetBodyType() != BT_NoTarget2
|
||||
&& it->second->GetBodyType() != BT_Special)
|
||||
&& it->second->GetBodyType() != BodyType::NoTarget
|
||||
&& it->second->GetBodyType() != BodyType::NoTarget2
|
||||
&& it->second->GetBodyType() != BodyType::Special)
|
||||
npcs[i++] = it->second;
|
||||
++it;
|
||||
}
|
||||
@@ -5520,7 +5553,7 @@ void EntityList::ExpeditionWarning(uint32 minutes_left)
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
Mob *EntityList::GetClosestMobByBodyType(Mob *sender, bodyType BodyType, bool skip_client_pets)
|
||||
Mob *EntityList::GetClosestMobByBodyType(Mob *sender, uint8 BodyType, bool skip_client_pets)
|
||||
{
|
||||
|
||||
if (!sender)
|
||||
|
||||
+15
-10
@@ -60,6 +60,8 @@ class Bot;
|
||||
|
||||
extern EntityList entity_list;
|
||||
|
||||
constexpr const char* SEE_BUFFS_FLAG = "see_buffs_flag";
|
||||
|
||||
class Entity
|
||||
{
|
||||
public:
|
||||
@@ -114,6 +116,7 @@ public:
|
||||
inline const time_t& GetSpawnTimeStamp() const { return spawn_timestamp; }
|
||||
|
||||
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();
|
||||
const Bot* CastToBot() const;
|
||||
@@ -433,23 +436,24 @@ public:
|
||||
void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app);
|
||||
|
||||
void AEAttack(
|
||||
Mob *attacker,
|
||||
Mob* attacker,
|
||||
float distance,
|
||||
int Hand = EQ::invslot::slotPrimary,
|
||||
int count = 0,
|
||||
int16 slot_id = EQ::invslot::slotPrimary,
|
||||
int hit_count = 0,
|
||||
bool is_from_spell = false,
|
||||
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(
|
||||
Mob *caster,
|
||||
Mob *center,
|
||||
Mob* caster,
|
||||
Mob* center,
|
||||
uint16 spell_id,
|
||||
bool affect_caster = true,
|
||||
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);
|
||||
|
||||
//trap stuff
|
||||
Mob* GetTrapTrigger(Trap* trap);
|
||||
@@ -501,11 +505,12 @@ public:
|
||||
Mob* GetTargetForMez(Mob* caster);
|
||||
uint32 CheckNPCsClose(Mob *center);
|
||||
|
||||
int FleeAllyCount(Mob* attacker, Mob* skipped);
|
||||
Corpse* GetClosestCorpse(Mob* sender, const char *Name);
|
||||
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);
|
||||
void CameraEffect(uint32 duration, float intensity);
|
||||
Mob* GetClosestMobByBodyType(Mob* sender, bodyType BodyType, bool skip_client_pets=false);
|
||||
Mob* GetClosestMobByBodyType(Mob* sender, uint8 BodyType, bool skip_client_pets=false);
|
||||
void ForceGroupUpdate(uint32 gid);
|
||||
void SendGroupLeave(uint32 gid, const char *name);
|
||||
void SendGroupJoin(uint32 gid, const char *name);
|
||||
|
||||
+59
-51
@@ -527,7 +527,7 @@ void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, boo
|
||||
// Are we also doing linear AA acceleration?
|
||||
if (RuleB(AA, ModernAAScalingEnabled) && aaexp > 0)
|
||||
{
|
||||
aaexp = ScaleAAXPBasedOnCurrentAATotal(GetAAPoints(), aaexp);
|
||||
aaexp = ScaleAAXPBasedOnCurrentAATotal(GetSpentAA() + GetAAPoints(), aaexp);
|
||||
}
|
||||
|
||||
// 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.");
|
||||
return; // Must be invalid class/race
|
||||
}
|
||||
|
||||
uint32 i = 0;
|
||||
uint32 membercount = 0;
|
||||
if(GetGroup())
|
||||
{
|
||||
if(GetGroup()) {
|
||||
for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
|
||||
if (GetGroup()->members[i] != nullptr) {
|
||||
membercount++;
|
||||
@@ -627,25 +627,29 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
||||
if (RuleI(Character, ShowExpValues) >= 1) {
|
||||
if (exp_gained > 0 && aa_exp_gained > 0) {
|
||||
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);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
exp_amount_message = fmt::format("({}) AA", aa_exp_gained);
|
||||
}
|
||||
}
|
||||
|
||||
std::string exp_percent_message = "";
|
||||
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);
|
||||
else if (exp_gained > 0) exp_percent_message = StringFormat("(%.3f%%)", exp_percent);
|
||||
else exp_percent_message = StringFormat("(%.3f%%AA)", aa_exp_percent);
|
||||
if (exp_gained > 0 && aa_exp_gained > 0) {
|
||||
exp_percent_message = StringFormat("(%.3f%%, %.3f%%AA)", exp_percent, 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 (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());
|
||||
else MessageString(Chat::Experience, REZ_REGAIN);
|
||||
} else {
|
||||
MessageString(Chat::Experience, REZ_REGAIN);
|
||||
}
|
||||
} else {
|
||||
if (membercount > 1) {
|
||||
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;
|
||||
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);
|
||||
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.");
|
||||
if (RuleI(Character, ShowExpValues) == 1 && exp_lost > 0) {
|
||||
Message(Chat::Yellow, "You have lost %i experience.", exp_lost);
|
||||
} 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
|
||||
@@ -698,8 +705,9 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
||||
}
|
||||
level_count++;
|
||||
|
||||
if(GetMercenaryID())
|
||||
if (GetMercenaryID()) {
|
||||
UpdateMercLevel();
|
||||
}
|
||||
}
|
||||
//see if we lost any levels
|
||||
while (set_exp < GetEXPForLevel(check_level-1)) {
|
||||
@@ -709,8 +717,9 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
||||
break;
|
||||
}
|
||||
level_increase = false;
|
||||
if(GetMercenaryID())
|
||||
if (GetMercenaryID()) {
|
||||
UpdateMercLevel();
|
||||
}
|
||||
}
|
||||
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);
|
||||
char val1[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.
|
||||
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.
|
||||
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).
|
||||
}
|
||||
|
||||
if (RuleB(AA, SoundForAAEarned)) {
|
||||
SendSound();
|
||||
@@ -765,7 +776,7 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool
|
||||
RecordPlayerEventLog(PlayerEvent::AA_GAIN, PlayerEvent::AAGainedEvent{gained});
|
||||
|
||||
/* QS: PlayerLogAARate */
|
||||
if (RuleB(QueryServ, PlayerLogAARate)){
|
||||
if (RuleB(QueryServ, PlayerLogAARate)) {
|
||||
int add_points = (m_pp.aapoints - last_unspentAA);
|
||||
std::string query = StringFormat("INSERT INTO `qs_player_aa_rate_hourly` (char_id, aa_count, hour_time) VALUES (%i, %i, UNIX_TIMESTAMP() - MOD(UNIX_TIMESTAMP(), 3600)) ON DUPLICATE KEY UPDATE `aa_count` = `aa_count` + %i", CharacterID(), add_points, add_points);
|
||||
QServ->SendQuery(query.c_str());
|
||||
@@ -774,47 +785,44 @@ 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);
|
||||
}
|
||||
|
||||
uint8 maxlevel = RuleI(Character, MaxExpLevel) + 1;
|
||||
uint8 max_level = RuleI(Character, MaxExpLevel) + 1;
|
||||
|
||||
if(maxlevel <= 1)
|
||||
maxlevel = 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);
|
||||
}
|
||||
if (max_level <= 1) {
|
||||
max_level = RuleI(Character, MaxLevel) + 1;
|
||||
}
|
||||
|
||||
auto client_max_level = GetClientMaxLevel();
|
||||
if (client_max_level) {
|
||||
if (GetLevel() >= client_max_level) {
|
||||
auto exp_needed = GetEXPForLevel(client_max_level);
|
||||
if (set_exp > exp_needed) {
|
||||
set_exp = exp_needed;
|
||||
}
|
||||
max_level = client_max_level + 1;
|
||||
}
|
||||
|
||||
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};
|
||||
if (level_increase)
|
||||
{
|
||||
if (level_count == 1)
|
||||
if (level_increase) {
|
||||
if (level_count == 1) {
|
||||
MessageString(Chat::Experience, GAIN_LEVEL, ConvertArray(check_level, val1));
|
||||
else
|
||||
} else {
|
||||
Message(Chat::Yellow, "Welcome to level %i!", check_level);
|
||||
}
|
||||
|
||||
if (check_level == RuleI(Character, DeathItemLossLevel) &&
|
||||
m_ClientVersionBit & EQ::versions::maskUFAndEarlier)
|
||||
m_ClientVersionBit & EQ::versions::maskUFAndEarlier) {
|
||||
MessageString(Chat::Yellow, CORPSE_ITEM_LOST);
|
||||
}
|
||||
|
||||
if (check_level == RuleI(Character, DeathExpLossLevel))
|
||||
if (check_level == RuleI(Character, DeathExpLossLevel)) {
|
||||
MessageString(Chat::Yellow, CORPSE_EXP_LOST);
|
||||
}
|
||||
}
|
||||
|
||||
uint8 myoldlevel = GetLevel();
|
||||
@@ -828,9 +836,9 @@ 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(GetLevel() == maxlevel - 1){
|
||||
uint32 expneeded = GetEXPForLevel(maxlevel);
|
||||
if(set_exp > expneeded) {
|
||||
if (GetLevel() == max_level - 1){
|
||||
uint32 expneeded = GetEXPForLevel(max_level);
|
||||
if (set_exp > expneeded) {
|
||||
set_exp = expneeded;
|
||||
}
|
||||
}
|
||||
|
||||
+281
-57
@@ -19,6 +19,7 @@
|
||||
#include "../common/rulesys.h"
|
||||
|
||||
#include "map.h"
|
||||
#include "water_map.h"
|
||||
#include "zone.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
@@ -29,17 +30,52 @@ extern Zone* zone;
|
||||
|
||||
#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
|
||||
void Mob::CheckFlee()
|
||||
{
|
||||
|
||||
// if mob is dead why would you run?
|
||||
if (GetHP() == 0) {
|
||||
if (IsPet() || IsCasting() || GetHP() == 0 || GetBodyType() == BodyType::Undead || (IsNPC() && CastToNPC()->IsUnderwaterOnly())) {
|
||||
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) {
|
||||
int flee_speed = GetFearSpeed();
|
||||
if (flee_speed < 1) {
|
||||
flee_speed = 0;
|
||||
}
|
||||
|
||||
SetRunAnimSpeed(flee_speed);
|
||||
|
||||
if (IsMoving() && flee_speed < 1) {
|
||||
StopNavigation();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -49,38 +85,17 @@ void Mob::CheckFlee()
|
||||
return;
|
||||
}
|
||||
|
||||
// Undead do not flee
|
||||
if (GetBodyType() == BT_Undead) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if Flee Timer is cleared
|
||||
if (!flee_timer.Check()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int hp_ratio = GetIntHPRatio();
|
||||
int flee_ratio = GetSpecialAbility(SpecialAbility::FleePercent); // if a special SpecialAbility::FleePercent exists
|
||||
int flee_ratio = GetFleeRatio();
|
||||
Mob *hate_top = GetHateTop();
|
||||
|
||||
LogFlee("Mob [{}] hp_ratio [{}] flee_ratio [{}]", GetCleanName(), hp_ratio, flee_ratio);
|
||||
|
||||
// Sanity Check for race conditions
|
||||
if (hate_top == nullptr) {
|
||||
if(!hate_top) {
|
||||
//this should never happen...
|
||||
StartFleeing();
|
||||
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;
|
||||
if (mob_has_low_enough_health_to_flee) {
|
||||
LogFlee(
|
||||
@@ -141,7 +156,7 @@ void Mob::CheckFlee()
|
||||
// if FleeIfNotAlone is true, we skip alone check
|
||||
// roll chance
|
||||
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))) {
|
||||
|
||||
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()
|
||||
{
|
||||
if (!flee_mode) {
|
||||
return;
|
||||
}
|
||||
|
||||
//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.
|
||||
@@ -170,46 +239,201 @@ void Mob::ProcessFlee()
|
||||
return;
|
||||
}
|
||||
|
||||
int hpratio = GetIntHPRatio();
|
||||
int fleeratio = GetSpecialAbility(SpecialAbility::FleePercent); // if a special SpecialAbility::FleePercent exists
|
||||
Mob *hate_top = GetHateTop();
|
||||
bool dying = GetIntHPRatio() < GetFleeRatio();
|
||||
|
||||
// If no special flee_percent check for Gray or Other con rates
|
||||
if(hate_top != nullptr && GetLevelCon(hate_top->GetLevel(), GetLevel()) == ConsiderColor::Gray && fleeratio == 0 && RuleB(Combat, FleeGray)) {
|
||||
fleeratio = RuleI(Combat, FleeGrayHPRatio);
|
||||
} else if(fleeratio == 0) {
|
||||
fleeratio = RuleI(Combat, FleeHPRatio );
|
||||
// We have stopped fleeing for an unknown reason (couldn't find a node is possible) restart.
|
||||
if (flee_mode && !currently_fleeing) {
|
||||
if(dying) {
|
||||
StartFleeing();
|
||||
}
|
||||
}
|
||||
|
||||
// Mob is still too low. Keep Running
|
||||
if(hpratio < fleeratio) {
|
||||
//see if we are still dying, if so, do nothing
|
||||
if (dying) {
|
||||
return;
|
||||
}
|
||||
|
||||
//we are not dying anymore... see what we do next
|
||||
|
||||
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;
|
||||
}
|
||||
//we are not dying anymore, check to make sure we're not blind or feared and cancel flee.
|
||||
StopFleeing();
|
||||
}
|
||||
|
||||
void Mob::CalculateNewFearpoint()
|
||||
{
|
||||
if (RuleB(Pathing, Fear) && zone->pathing) {
|
||||
auto Node = zone->pathing->GetRandomLocation(glm::vec3(GetX(), GetY(), GetZ()));
|
||||
if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) {
|
||||
m_FearWalkTarget = Node;
|
||||
currently_fleeing = true;
|
||||
|
||||
return;
|
||||
// blind waypoint logic isn't the same as fear's. Has a chance to run toward the player
|
||||
// chance is very high if the player is moving, otherwise it's low
|
||||
if (IsBlind() && !IsFeared() && GetTarget()) {
|
||||
int roll = 20;
|
||||
if (GetTarget()->GetCurrentSpeed() > 0.1f || (GetTarget()->IsClient() && GetTarget()->animation != 0)) {
|
||||
roll = 80;
|
||||
}
|
||||
|
||||
LogPathing("No path found to selected node during CalculateNewFearpoint.");
|
||||
if (zone->random.Roll(roll)) {
|
||||
m_FearWalkTarget = glm::vec3(GetTarget()->GetPosition());
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,11 +3,19 @@
|
||||
|
||||
void command_devtools(Client *c, const Seperator *sep)
|
||||
{
|
||||
bool is_disable = !strcasecmp(sep->arg[1], "disable");
|
||||
bool is_enable = !strcasecmp(sep->arg[1], "enable");
|
||||
const uint16 arguments = sep->argnum;
|
||||
if (arguments != 2) {
|
||||
c->ShowDevToolsMenu();
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_disable || is_enable) {
|
||||
c->SetDevToolsEnabled(is_enable);
|
||||
const std::string& type = sep->arg[1];
|
||||
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();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "../client.h"
|
||||
#include "find/aa.cpp"
|
||||
#include "find/body_type.cpp"
|
||||
#include "find/bug_category.cpp"
|
||||
#include "find/character.cpp"
|
||||
#include "find/class.cpp"
|
||||
@@ -11,9 +12,11 @@
|
||||
#include "find/item.cpp"
|
||||
#include "find/language.cpp"
|
||||
#include "find/npctype.cpp"
|
||||
#include "find/object_type.cpp"
|
||||
#include "find/race.cpp"
|
||||
#include "find/recipe.cpp"
|
||||
#include "find/skill.cpp"
|
||||
#include "find/stance.cpp"
|
||||
#include "find/spell.cpp"
|
||||
#include "find/special_ability.cpp"
|
||||
#include "find/task.cpp"
|
||||
@@ -33,6 +36,7 @@ void command_find(Client *c, const Seperator *sep)
|
||||
|
||||
std::vector<Cmd> commands = {
|
||||
Cmd{.cmd = "aa", .u = "aa [Search Criteria]", .fn = FindAA, .a = {"#findaa"}},
|
||||
Cmd{.cmd = "body_type", .u = "body_type [Search Criteria]", .fn = FindBodyType, .a = {"#findbodytype"}},
|
||||
Cmd{.cmd = "bug_category", .u = "bug_category [Search Criteria]", .fn = FindBugCategory, .a = {"#findbugcategory"}},
|
||||
Cmd{.cmd = "character", .u = "character [Search Criteria]", .fn = FindCharacter, .a = {"#findcharacter"}},
|
||||
Cmd{.cmd = "class", .u = "class [Search Criteria]", .fn = FindClass, .a = {"#findclass"}},
|
||||
@@ -50,10 +54,12 @@ void command_find(Client *c, const Seperator *sep)
|
||||
"#findnpctype"
|
||||
}
|
||||
},
|
||||
Cmd{.cmd = "object_type", .u = "object_type [Search Criteria]", .fn = FindObjectType, .a = {"#findobjecttype"}},
|
||||
Cmd{.cmd = "race", .u = "race [Search Criteria]", .fn = FindRace, .a = {"#findrace"}},
|
||||
Cmd{.cmd = "recipe", .u = "recipe [Search Criteria]", .fn = FindRecipe, .a = {"#findrecipe"}},
|
||||
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 = "stance", .u = "stance [Search Criteria]", .fn = FindStance, .a = {"#findstance"}},
|
||||
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 = "zone", .u = "zone [Search Criteria]", .fn = FindZone, .a = {"#fz", "#findzone"}},
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
#include "../../client.h"
|
||||
|
||||
void FindBodyType(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (sep->IsNumber(2)) {
|
||||
const uint8 body_type_id = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
|
||||
const std::string& body_type_name = BodyType::GetName(body_type_id);
|
||||
if (Strings::EqualFold(body_type_name, "UNKNOWN BODY TYPE")) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Body Type {} does not exist.",
|
||||
body_type_id
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Body Type {} | {}",
|
||||
body_type_id,
|
||||
body_type_name
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& search_criteria = Strings::ToLower(sep->argplus[2]);
|
||||
|
||||
uint32 found_count = 0;
|
||||
|
||||
for (const auto& e : body_type_names) {
|
||||
const std::string& body_type_name_lower = Strings::ToLower(e.second);
|
||||
if (!Strings::Contains(body_type_name_lower, search_criteria)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Body Type {} | {}",
|
||||
e.first,
|
||||
e.second
|
||||
).c_str()
|
||||
);
|
||||
|
||||
found_count++;
|
||||
}
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} Body Type{} found matching '{}'.",
|
||||
found_count,
|
||||
found_count != 1 ? "s" : "",
|
||||
sep->argplus[2]
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
#include "../../client.h"
|
||||
|
||||
void FindObjectType(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (sep->IsNumber(2)) {
|
||||
const uint32 object_type = Strings::ToUnsignedInt(sep->arg[2]);
|
||||
const std::string& object_type_name = ObjectType::GetName(object_type);
|
||||
if (Strings::EqualFold(object_type_name, "UNKNOWN OBJECT TYPE")) {
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Object Type {} does not exist.",
|
||||
object_type
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Object Type {} | {}",
|
||||
object_type,
|
||||
ObjectType::GetName(object_type)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string& search_criteria = Strings::ToLower(sep->argplus[2]);
|
||||
|
||||
uint32 found_count = 0;
|
||||
|
||||
for (const auto& e : object_types) {
|
||||
const std::string& object_type_name_lower = Strings::ToLower(e.second);
|
||||
if (!Strings::Contains(object_type_name_lower, search_criteria)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Object Type {} | {}",
|
||||
e.first,
|
||||
e.second
|
||||
).c_str()
|
||||
);
|
||||
|
||||
found_count++;
|
||||
}
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} Object Type{} found matching '{}'.",
|
||||
found_count,
|
||||
found_count != 1 ? "s" : "",
|
||||
sep->argplus[2]
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@ void command_goto(Client *c, const Seperator *sep)
|
||||
{
|
||||
const uint16 arguments = sep->argnum;
|
||||
|
||||
const bool goto_player = !sep->IsNumber(1) && sep->arg[1];
|
||||
const bool goto_player = arguments > 0 && !sep->IsNumber(1);
|
||||
const bool goto_position = sep->IsNumber(1) && sep->IsNumber(2) && sep->IsNumber(3);
|
||||
const bool goto_target = !arguments && c->GetTarget();
|
||||
|
||||
@@ -78,6 +78,14 @@ void command_goto(Client *c, const Seperator *sep)
|
||||
t->GetZ(),
|
||||
t->GetHeading()
|
||||
);
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Going to {} in the same zone.",
|
||||
c->GetTargetDescription(t)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -100,21 +100,25 @@ void command_npcedit(Client *c, const Seperator *sep)
|
||||
}
|
||||
} else if (!strcasecmp(sep->arg[1], "bodytype")) {
|
||||
if (sep->IsNumber(2)) {
|
||||
auto body_type_id = static_cast<uint8_t>(Strings::ToUnsignedInt(sep->arg[2]));
|
||||
auto body_type_name = EQ::constants::GetBodyTypeName(static_cast<bodyType>(body_type_id));
|
||||
const uint8 body_type_id = static_cast<uint8>(Strings::ToUnsignedInt(sep->arg[2]));
|
||||
const std::string& body_type_name = BodyType::GetName(body_type_id);
|
||||
if (Strings::EqualFold(body_type_name, "UNKNOWN BODY TYPE")) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Body Type {} does not exist.",
|
||||
body_type_id
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
n.bodytype = body_type_id;
|
||||
d = fmt::format(
|
||||
"{} is now using Body Type {}.",
|
||||
"{} is now using Body Type {} ({}).",
|
||||
npc_id_string,
|
||||
(
|
||||
!body_type_name.empty() ?
|
||||
fmt::format(
|
||||
"{} ({})",
|
||||
body_type_name,
|
||||
body_type_id
|
||||
) :
|
||||
std::to_string(body_type_id)
|
||||
)
|
||||
body_type_name,
|
||||
body_type_id
|
||||
);
|
||||
} else {
|
||||
c->Message(Chat::White, "Usage: #npcedit bodytype [Body Type ID] - Sets an NPC's Bodytype");
|
||||
|
||||
@@ -163,7 +163,7 @@ void command_parcels(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
CharacterParcelsRepository::CharacterParcels parcel_out;
|
||||
auto parcel_out = CharacterParcelsRepository::NewEntity();
|
||||
parcel_out.from_name = c->GetName();
|
||||
parcel_out.note = note;
|
||||
parcel_out.sent_date = time(nullptr);
|
||||
@@ -241,7 +241,7 @@ void command_parcels(Client *c, const Seperator *sep)
|
||||
? inst->GetItem()->MaxCharges : (int16) quantity;
|
||||
}
|
||||
|
||||
CharacterParcelsRepository::CharacterParcels parcel_out;
|
||||
auto parcel_out = CharacterParcelsRepository::NewEntity();
|
||||
parcel_out.from_name = c->GetName();
|
||||
parcel_out.note = note.empty() ? "" : note;
|
||||
parcel_out.sent_date = time(nullptr);
|
||||
|
||||
@@ -25,6 +25,13 @@ void SetLevel(Client *c, const Seperator *sep)
|
||||
t->SetLevel(level, true);
|
||||
|
||||
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();
|
||||
|
||||
if (RuleB(Bots, Enabled) && RuleB(Bots, BotLevelsWithOwner)) {
|
||||
|
||||
@@ -160,23 +160,22 @@ void ShowInventory(Client *c, const Seperator *sep)
|
||||
|
||||
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(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"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(),
|
||||
item_data->ID,
|
||||
c->GetInv().GetItem(((scope_bit &peekWorld) ? (EQ::invslot::WORLD_BEGIN + index_main) : index_main))->GetSerialNumber(),
|
||||
(
|
||||
inst_main->IsStackable() && inst_main->GetCharges() > 0 ?
|
||||
fmt::format(
|
||||
" (Stack of {})",
|
||||
inst_main->GetCharges()
|
||||
) :
|
||||
""
|
||||
)
|
||||
inst_main->GetSerialNumber(),
|
||||
inst_main->IsStackable() && inst_main->GetCharges() > 0 ?
|
||||
fmt::format(
|
||||
" (Stack of {})",
|
||||
inst_main->GetCharges()
|
||||
) :
|
||||
""
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
@@ -239,7 +238,7 @@ void ShowInventory(Client *c, const Seperator *sep)
|
||||
sub_index,
|
||||
linker.GenerateLink(),
|
||||
item_data->ID,
|
||||
c->GetInv().GetItem(EQ::InventoryProfile::CalcSlotId(index_main, sub_index))->GetSerialNumber(),
|
||||
inst_sub->GetSerialNumber(),
|
||||
(
|
||||
inst_sub->IsStackable() && inst_sub->GetCharges() > 0 ?
|
||||
fmt::format(
|
||||
|
||||
@@ -54,8 +54,8 @@ void ShowRecipe(Client *c, const Seperator *sep)
|
||||
e.iscontainer > 0 ? " (Container)" : "",
|
||||
(
|
||||
e.item_id > 1000 ?
|
||||
database.CreateItemLink(e.item_id) :
|
||||
EQ::constants::GetObjectTypeName(e.item_id)
|
||||
database.CreateItemLink(static_cast<uint32>(e.item_id)) :
|
||||
ObjectType::GetName(static_cast<uint32>(e.item_id))
|
||||
),
|
||||
(
|
||||
can_summon_items && e.item_id > 1000 ?
|
||||
|
||||
@@ -174,7 +174,7 @@ void ShowWho(Client *c, const Seperator *sep)
|
||||
account_status ?
|
||||
fmt::format(
|
||||
"* {} * ",
|
||||
EQ::constants::GetAccountStatusName(account_status)
|
||||
AccountStatus::GetName(account_status)
|
||||
) :
|
||||
""
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user