mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-29 23:15:45 +00:00
Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b044d8533e | |||
| d810cb02c3 | |||
| 992a5cc132 | |||
| c50fda0f73 | |||
| 1b15f16e3e | |||
| 983cc1e82a | |||
| fc79614fac | |||
| d767217461 | |||
| 1310c5d528 | |||
| b253fce0d5 | |||
| 421857026d | |||
| 68f40c9255 | |||
| 0bceee5622 | |||
| cd03152550 | |||
| 316fa54bd8 | |||
| 49957e3269 | |||
| 9638d9af3a | |||
| 87c207e862 | |||
| 2df5f3f55a | |||
| e803d3e1e1 | |||
| fccb205a1d | |||
| f70078d62a | |||
| 34ae3094d6 | |||
| c56742a2a8 | |||
| 3e34447172 | |||
| ca25122bfa | |||
| 13a7532ef8 | |||
| e1344039ff | |||
| 98b137154a | |||
| fc9ef2fb7b | |||
| 6dc661032f | |||
| 2586527157 | |||
| 3a51f04291 | |||
| 66af3d2f63 | |||
| 6bcd8fea18 | |||
| 0d1cbecb55 | |||
| e33e076b2a | |||
| e26d17182e | |||
| 7e40c5bac2 | |||
| ca69cc67e8 | |||
| 099c6d657b | |||
| c0a8fd097e | |||
| a80ab75260 | |||
| c87aadbf0c |
+107
-13
@@ -1,3 +1,97 @@
|
||||
## [22.51.1] 5/27/2024
|
||||
|
||||
### Fixes
|
||||
|
||||
* Adjust return for perl release check @Akkadius 2024-05-26
|
||||
* Corrected issue with bazaar purchase via parcels where an incorrect quantity would be calculated. ([#4352](https://github.com/EQEmu/Server/pull/4352)) @neckkola 2024-05-27
|
||||
|
||||
### Performance
|
||||
|
||||
* Improve SkillCaps::GetTrainLevel() Efficiency ([#4350](https://github.com/EQEmu/Server/pull/4350)) @Kinglykrab 2024-05-26
|
||||
|
||||
### Rules
|
||||
|
||||
* Legacy Compute Defense against modern agi based defense. ([#4349](https://github.com/EQEmu/Server/pull/4349)) @fryguy503 2024-05-27
|
||||
|
||||
## [22.51.0] 5/26/2024
|
||||
|
||||
### Commands
|
||||
|
||||
* #npcspawn Changes ([#4311](https://github.com/EQEmu/Server/pull/4311)) @twincannon 2024-05-16
|
||||
* Cleanup #resetaa Command ([#4310](https://github.com/EQEmu/Server/pull/4310)) @Kinglykrab 2024-05-22
|
||||
|
||||
### Crash
|
||||
|
||||
* Add validation to RemoveXTarget ([#4324](https://github.com/EQEmu/Server/pull/4324)) @Akkadius 2024-05-25
|
||||
* Fix Zone deconstructor crashes ([#4325](https://github.com/EQEmu/Server/pull/4325)) @Akkadius 2024-05-25
|
||||
* Fix crash issue when dividing by zero in CalcHPRegen ([#4320](https://github.com/EQEmu/Server/pull/4320)) @Akkadius 2024-05-25
|
||||
* Fix crash when map name is null ([#4322](https://github.com/EQEmu/Server/pull/4322)) @Akkadius 2024-05-25
|
||||
* Fix player event crash in ITEM_DESTROY ([#4326](https://github.com/EQEmu/Server/pull/4326)) @Akkadius 2024-05-25
|
||||
* Fix player events reload when out of bounds ([#4321](https://github.com/EQEmu/Server/pull/4321)) @Akkadius 2024-05-25
|
||||
* Fix rarer crash in EntityList::MobProcess ([#4319](https://github.com/EQEmu/Server/pull/4319)) @Akkadius 2024-05-25
|
||||
|
||||
### Feature
|
||||
|
||||
* Add RoF2 Bazaar Support ([#4315](https://github.com/EQEmu/Server/pull/4315)) @neckkola 2024-05-26
|
||||
* Add SE_IncreaseArchery and rules to tune archery ([#4335](https://github.com/EQEmu/Server/pull/4335)) @fryguy503 2024-05-26
|
||||
* Add parcel container support ([#4305](https://github.com/EQEmu/Server/pull/4305)) @neckkola 2024-05-17
|
||||
|
||||
### Fixes
|
||||
|
||||
* Accuracy, Avoidance and Atk adjustments ([#4336](https://github.com/EQEmu/Server/pull/4336)) @fryguy503 2024-05-26
|
||||
* Fix Crash with null Argument in #modifynpcstat ([#4318](https://github.com/EQEmu/Server/pull/4318)) @Kinglykrab 2024-05-24
|
||||
* Fix RemoveAlternateCurrencyValue not updating Client ([#4317](https://github.com/EQEmu/Server/pull/4317)) @Kinglykrab 2024-05-23
|
||||
* Fix Using Bind Wound Above 70% Health ([#4340](https://github.com/EQEmu/Server/pull/4340)) @Kinglykrab 2024-05-26
|
||||
* Fix issue with #hotfix ([#4316](https://github.com/EQEmu/Server/pull/4316)) @Kinglykrab 2024-05-22
|
||||
* Fix issue with #suspend ([#4314](https://github.com/EQEmu/Server/pull/4314)) @Kinglykrab 2024-05-23
|
||||
* Fix issue with KeepOneRecordPerCompletedTask ([#4313](https://github.com/EQEmu/Server/pull/4313)) @Kinglykrab 2024-05-23
|
||||
* Fix mistaken removed RULE_CATEGORY_END() ([#4341](https://github.com/EQEmu/Server/pull/4341)) @fryguy503 2024-05-26
|
||||
* Missed a mob offense section for PR #4328 ([#4331](https://github.com/EQEmu/Server/pull/4331)) @fryguy503 2024-05-26
|
||||
* Raid Targets should not be Blindable as this will break all spell casting AI. ([#4334](https://github.com/EQEmu/Server/pull/4334)) @fryguy503 2024-05-26
|
||||
* When Mounts are allowed to zone, block them from zoning to disallowed zones. ([#4330](https://github.com/EQEmu/Server/pull/4330)) @fryguy503 2024-05-25
|
||||
* When refreshing buffs, attempt to use the same buffslot if the buff still exists. ([#4338](https://github.com/EQEmu/Server/pull/4338)) @fryguy503 2024-05-26
|
||||
|
||||
### Lua Mod
|
||||
|
||||
* Fix issue with SetAAEXP and SetEXP firing when uninitialized ([#4345](https://github.com/EQEmu/Server/pull/4345)) @Akkadius 2024-05-26
|
||||
|
||||
### Merchants
|
||||
|
||||
* Add New Classic Greed/Faction/Charisma Prices Rule ([#4301](https://github.com/EQEmu/Server/pull/4301)) @noudess 2024-05-17
|
||||
|
||||
### Mobs
|
||||
|
||||
* Remove entity type checks from ScanCloseMobs ([#4323](https://github.com/EQEmu/Server/pull/4323)) @Akkadius 2024-05-25
|
||||
|
||||
### NPC Spells
|
||||
|
||||
* Fix an issue where procs wouldn't fire if no spell entries in list ([#4344](https://github.com/EQEmu/Server/pull/4344)) @Akkadius 2024-05-26
|
||||
|
||||
### Perl
|
||||
|
||||
* Linux /opt/eqemu-perl checks when using release binaries ([#4346](https://github.com/EQEmu/Server/pull/4346)) @Akkadius 2024-05-26
|
||||
|
||||
### Quest API
|
||||
|
||||
* Add Zone Uptime Exports to Perl/Lua ([#4339](https://github.com/EQEmu/Server/pull/4339)) @Kinglykrab 2024-05-26
|
||||
|
||||
### Rules
|
||||
|
||||
* Added MeleeMitigation Level Difference Roll Adjusted for level diffs ([#4332](https://github.com/EQEmu/Server/pull/4332)) @fryguy503 2024-05-26
|
||||
* Allow maximum per kill AA amount ([#4329](https://github.com/EQEmu/Server/pull/4329)) @fryguy503 2024-05-25
|
||||
* Allow servers to adjust the filtering threshold for heals from damage (e.g. Mark of Kings). ([#4327](https://github.com/EQEmu/Server/pull/4327)) @fryguy503 2024-05-25
|
||||
* Backstab Haste Correction ([#4337](https://github.com/EQEmu/Server/pull/4337)) @fryguy503 2024-05-26
|
||||
* Mob Offensive and Weapon Skill static tables ([#4328](https://github.com/EQEmu/Server/pull/4328)) @fryguy503 2024-05-25
|
||||
* Remove hard coded initial aggro in favor or an adjustable Rule ([#4333](https://github.com/EQEmu/Server/pull/4333)) @fryguy503 2024-05-26
|
||||
|
||||
### Scripts
|
||||
|
||||
* Fix zone data load ordering issue ([#4343](https://github.com/EQEmu/Server/pull/4343)) @Akkadius 2024-05-26
|
||||
|
||||
### Spells
|
||||
|
||||
* Add content filtering to NPC spells ([#4309](https://github.com/EQEmu/Server/pull/4309)) @Akkadius 2024-05-17
|
||||
|
||||
## [22.50.1] 5/12/2024
|
||||
|
||||
### Fixes
|
||||
@@ -1289,7 +1383,7 @@
|
||||
|
||||
### EQTime
|
||||
|
||||
Hotfix for world not spamming save messages by setting to detail level logging @Akkadius 2023-11-20
|
||||
Hotfix for world not spamming save messages by setting to detail level logging @Akkadius 2023-11-20
|
||||
|
||||
## [22.34.0] - 11/19/2023
|
||||
|
||||
@@ -2371,7 +2465,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
|
||||
* Telnet encoding fix ([#3269](https://github.com/EQEmu/Server/pull/3269)) @Akkadius 2023-04-05
|
||||
|
||||
## [22.9.1] - 04/03/2023
|
||||
## [22.9.1] - 04/03/2023
|
||||
|
||||
### Code
|
||||
|
||||
@@ -2416,7 +2510,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
|
||||
* Change to use Pass by reference where valid. ([#3163](https://github.com/EQEmu/Server/pull/3163)) @Aeadoin 2023-04-02
|
||||
|
||||
## [22.9.0] - 04/01/2023
|
||||
## [22.9.0] - 04/01/2023
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -2440,7 +2534,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
|
||||
* Add missing Luabind definitions to lua_general.cpp ([#3167](https://github.com/EQEmu/Server/pull/3167)) @Kinglykrab 2023-04-01
|
||||
|
||||
## [22.8.2] - 03/30/2023
|
||||
## [22.8.2] - 03/30/2023
|
||||
|
||||
### Code
|
||||
|
||||
@@ -2464,13 +2558,13 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
|
||||
* Remove Guild Bank Zone ID Rule ([#3156](https://github.com/EQEmu/Server/pull/3156)) @Kinglykrab 2023-03-29
|
||||
|
||||
## [22.8.1] - 03/27/2023
|
||||
## [22.8.1] - 03/27/2023
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix for NPCs having spells interrupted. ([#3150](https://github.com/EQEmu/Server/pull/3150)) @Aeadoin 2023-03-27
|
||||
|
||||
## [22.8.0] - 03/25/2023
|
||||
## [22.8.0] - 03/25/2023
|
||||
|
||||
### Code
|
||||
|
||||
@@ -2490,7 +2584,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
* Fix for Items looted from corpses. ([#3147](https://github.com/EQEmu/Server/pull/3147)) @Aeadoin 2023-03-26
|
||||
* Fix for SQL Query in npc_scale_global_base ([#3144](https://github.com/EQEmu/Server/pull/3144)) @Aeadoin 2023-03-26
|
||||
|
||||
## [22.7.0] - 03/24/2023
|
||||
## [22.7.0] - 03/24/2023
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -2647,7 +2741,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
* Add exception handling to converters themselves ([#3029](https://github.com/EQEmu/Server/pull/3029)) @Akkadius 2023-03-05
|
||||
* Add more number formatters ([#2873](https://github.com/EQEmu/Server/pull/2873)) @Kinglykrab 2023-03-04
|
||||
|
||||
## [22.4.5] - 03/03/2023
|
||||
## [22.4.5] - 03/03/2023
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -2689,7 +2783,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
* Add IsFindable() and IsTrackable() to Perl/Lua ([#2996](https://github.com/EQEmu/Server/pull/2996)) @Kinglykrab 2023-03-01
|
||||
* Add IsUnderwaterOnly() to Perl/Lua ([#2995](https://github.com/EQEmu/Server/pull/2995)) @Kinglykrab 2023-03-01
|
||||
|
||||
## [22.4.4] - 02/24/2023
|
||||
## [22.4.4] - 02/24/2023
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -2736,7 +2830,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
|
||||
* Fix for Lore Conflict ([#2977](https://github.com/EQEmu/Server/pull/2977)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-24
|
||||
|
||||
## [22.4.3] - 02/21/2023
|
||||
## [22.4.3] - 02/21/2023
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -2783,7 +2877,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
|
||||
* Add date to optional Drakkin Guktan Faction Update ([#2965](https://github.com/EQEmu/Server/pull/2965)) ([joligario](https://github.com/joligario)) 2023-02-19
|
||||
|
||||
## [22.4.2] - 02/18/2023
|
||||
## [22.4.2] - 02/18/2023
|
||||
|
||||
### Content
|
||||
|
||||
@@ -2805,7 +2899,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
|
||||
* Fix regression caused by #2932 ([#2956](https://github.com/EQEmu/Server/pull/2956)) ([Aeadoin](https://github.com/Aeadoin)) 2023-02-18
|
||||
|
||||
## [22.4.1] - 02/17/2023
|
||||
## [22.4.1] - 02/17/2023
|
||||
|
||||
### Bots
|
||||
|
||||
@@ -2826,7 +2920,7 @@ Revert Perl regression in #3648 causing scripts to not reliably initialize on zo
|
||||
* Fix rare out of bound issue when loading event types ([#2946](https://github.com/EQEmu/Server/pull/2946)) ([Akkadius](https://github.com/Akkadius)) 2023-02-17
|
||||
* Turn off KILLED_NPC (trash) off by default ([#2948](https://github.com/EQEmu/Server/pull/2948)) ([Akkadius](https://github.com/Akkadius)) 2023-02-17
|
||||
|
||||
## [22.4.0] - 02/17/2023
|
||||
## [22.4.0] - 02/17/2023
|
||||
|
||||
### Bots
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ CMAKE_MINIMUM_REQUIRED(VERSION 3.12)
|
||||
|
||||
SET(common_sources
|
||||
base_packet.cpp
|
||||
bazaar.cpp
|
||||
classes.cpp
|
||||
cli/eqemu_command_handler.cpp
|
||||
compression.cpp
|
||||
@@ -178,6 +179,7 @@ SET(repositories
|
||||
repositories/base/base_character_material_repository.h
|
||||
repositories/base/base_character_memmed_spells_repository.h
|
||||
repositories/base/base_character_parcels_repository.h
|
||||
repositories/base/base_character_parcels_containers_repository.h
|
||||
repositories/base/base_character_peqzone_flags_repository.h
|
||||
repositories/base/base_character_pet_buffs_repository.h
|
||||
repositories/base/base_character_pet_info_repository.h
|
||||
@@ -359,6 +361,7 @@ SET(repositories
|
||||
repositories/character_material_repository.h
|
||||
repositories/character_memmed_spells_repository.h
|
||||
repositories/character_parcels_repository.h
|
||||
repositories/character_parcels_containers_repository.h
|
||||
repositories/character_peqzone_flags_repository.h
|
||||
repositories/character_pet_buffs_repository.h
|
||||
repositories/character_pet_info_repository.h
|
||||
@@ -499,6 +502,7 @@ SET(repositories
|
||||
|
||||
SET(common_headers
|
||||
additive_lagged_fibonacci_engine.h
|
||||
bazaar.h
|
||||
base_packet.h
|
||||
bodytypes.h
|
||||
classes.h
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
#include "bazaar.h"
|
||||
|
||||
#include "../../common/item_instance.h"
|
||||
#include "repositories/trader_repository.h"
|
||||
#include <memory>
|
||||
|
||||
std::vector<BazaarSearchResultsFromDB_Struct>
|
||||
Bazaar::GetSearchResults(
|
||||
SharedDatabase &db,
|
||||
BazaarSearchCriteria_Struct search,
|
||||
uint32 char_zone_id
|
||||
)
|
||||
{
|
||||
LogTrading(
|
||||
"Searching for items with search criteria - item_name [{}] min_cost [{}] max_cost [{}] min_level [{}] "
|
||||
"max_level [{}] max_results [{}] prestige [{}] augment [{}] trader_entity_id [{}] trader_id [{}] "
|
||||
"search_scope [{}] char_zone_id [{}]",
|
||||
search.item_name,
|
||||
search.min_cost,
|
||||
search.max_cost,
|
||||
search.min_level,
|
||||
search.max_level,
|
||||
search.max_results,
|
||||
search.prestige,
|
||||
search.augment,
|
||||
search.trader_entity_id,
|
||||
search.trader_id,
|
||||
search.search_scope,
|
||||
char_zone_id
|
||||
);
|
||||
|
||||
std::string search_criteria_trader("TRUE ");
|
||||
|
||||
if (search.search_scope == NonRoFBazaarSearchScope) {
|
||||
search_criteria_trader.append(
|
||||
fmt::format(
|
||||
" AND trader.char_entity_id = {} AND trader.char_zone_id = {}",
|
||||
search.trader_entity_id,
|
||||
Zones::BAZAAR
|
||||
)
|
||||
);
|
||||
}
|
||||
else if (search.search_scope == Local_Scope) {
|
||||
search_criteria_trader.append(fmt::format(" AND trader.char_zone_id = {}", char_zone_id));
|
||||
}
|
||||
else if (search.trader_id > 0) {
|
||||
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));
|
||||
}
|
||||
if (search.max_cost != 0) {
|
||||
search_criteria_trader.append(fmt::format(" AND trader.item_cost <= {}", (uint64) search.max_cost * 1000));
|
||||
}
|
||||
|
||||
// not yet implemented
|
||||
// if (search.prestige != 0) {
|
||||
// 0xffffffff prestige only, 0xfffffffe non-prestige, 0 all
|
||||
// search_criteria.append(fmt::format(" AND items.type = {} ", search.prestige));
|
||||
// }
|
||||
|
||||
std::string query = fmt::format(
|
||||
"SELECT COUNT(item_id), trader.char_id, trader.item_id, trader.item_sn, trader.item_charges, trader.item_cost, "
|
||||
"trader.slot_id, SUM(trader.item_charges), trader.char_zone_id, trader.char_entity_id, character_data.name, "
|
||||
"aug_slot_1, aug_slot_2, aug_slot_3, aug_slot_4, aug_slot_5, aug_slot_6 "
|
||||
"FROM trader, character_data "
|
||||
"WHERE {} AND trader.char_id = character_data.id "
|
||||
"GROUP BY trader.item_sn, trader.item_charges, trader.char_id",
|
||||
search_criteria_trader.c_str()
|
||||
);
|
||||
|
||||
std::vector<BazaarSearchResultsFromDB_Struct> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(query);
|
||||
|
||||
if (!results.Success()) {
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
struct ItemSearchType {
|
||||
EQ::item::ItemType type;
|
||||
bool condition;
|
||||
};
|
||||
|
||||
struct AddititiveSearchCriteria {
|
||||
bool should_check;
|
||||
bool condition;
|
||||
};
|
||||
|
||||
for (auto row: results) {
|
||||
BazaarSearchResultsFromDB_Struct r{};
|
||||
|
||||
r.item_id = Strings::ToInt(row[2]);
|
||||
r.charges = Strings::ToInt(row[4]);
|
||||
|
||||
auto item = db.GetItem(r.item_id);
|
||||
if (!item) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32 aug_slot_1 = Strings::ToUnsignedInt(row[11]);
|
||||
uint32 aug_slot_2 = Strings::ToUnsignedInt(row[12]);
|
||||
uint32 aug_slot_3 = Strings::ToUnsignedInt(row[13]);
|
||||
uint32 aug_slot_4 = Strings::ToUnsignedInt(row[14]);
|
||||
uint32 aug_slot_5 = Strings::ToUnsignedInt(row[15]);
|
||||
uint32 aug_slot_6 = Strings::ToUnsignedInt(row[16]);
|
||||
|
||||
std::unique_ptr<EQ::ItemInstance> inst(
|
||||
db.CreateItem(
|
||||
item,
|
||||
r.charges,
|
||||
aug_slot_1,
|
||||
aug_slot_2,
|
||||
aug_slot_3,
|
||||
aug_slot_4,
|
||||
aug_slot_5,
|
||||
aug_slot_6
|
||||
)
|
||||
);
|
||||
|
||||
if (!inst->GetItem()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
r.count = Strings::ToInt(row[0]);
|
||||
r.trader_id = Strings::ToInt(row[1]);
|
||||
r.serial_number = Strings::ToInt(row[3]);
|
||||
r.cost = Strings::ToInt(row[5]);
|
||||
r.slot_id = Strings::ToInt(row[6]);
|
||||
r.sum_charges = Strings::ToInt(row[7]);
|
||||
r.stackable = item->Stackable;
|
||||
r.icon_id = item->Icon;
|
||||
r.trader_zone_id = Strings::ToInt(row[8]);
|
||||
r.trader_entity_id = Strings::ToInt(row[9]);
|
||||
r.serial_number_RoF = fmt::format("{:016}\0", Strings::ToInt(row[3]));
|
||||
r.item_name = fmt::format("{:.63}\0", item->Name);
|
||||
r.trader_name = fmt::format("{:.63}\0", std::string(row[10]).c_str());
|
||||
|
||||
LogTradingDetail(
|
||||
"Searching against item [{}] ({}) for trader [{}]",
|
||||
item->Name,
|
||||
item->ID,
|
||||
r.trader_name
|
||||
);
|
||||
|
||||
// item stat searches
|
||||
std::map<uint32, uint32> item_stat_searches = {
|
||||
|
||||
{STAT_AC, inst->GetItemArmorClass(true)},
|
||||
{STAT_AGI, static_cast<uint32>(inst->GetItemAgi(true))},
|
||||
{STAT_CHA, static_cast<uint32>(inst->GetItemCha(true))},
|
||||
{STAT_DEX, static_cast<uint32>(inst->GetItemDex(true))},
|
||||
{STAT_INT, static_cast<uint32>(inst->GetItemInt(true))},
|
||||
{STAT_STA, static_cast<uint32>(inst->GetItemSta(true))},
|
||||
{STAT_STR, static_cast<uint32>(inst->GetItemStr(true))},
|
||||
{STAT_WIS, static_cast<uint32>(inst->GetItemWis(true))},
|
||||
{STAT_COLD, static_cast<uint32>(inst->GetItemCR(true))},
|
||||
{STAT_DISEASE, static_cast<uint32>(inst->GetItemDR(true))},
|
||||
{STAT_FIRE, static_cast<uint32>(inst->GetItemFR(true))},
|
||||
{STAT_MAGIC, static_cast<uint32>(inst->GetItemMR(true))},
|
||||
{STAT_POISON, static_cast<uint32>(inst->GetItemPR(true))},
|
||||
{STAT_HP, static_cast<uint32>(inst->GetItemHP(true))},
|
||||
{STAT_MANA, static_cast<uint32>(inst->GetItemMana(true))},
|
||||
{STAT_ENDURANCE, static_cast<uint32>(inst->GetItemEndur(true))},
|
||||
{STAT_ATTACK, static_cast<uint32>(inst->GetItemAttack(true))},
|
||||
{STAT_HP_REGEN, static_cast<uint32>(inst->GetItemRegen(true))},
|
||||
{STAT_MANA_REGEN, static_cast<uint32>(inst->GetItemManaRegen(true))},
|
||||
{STAT_HASTE, static_cast<uint32>(inst->GetItemHaste(true))},
|
||||
{STAT_DAMAGE_SHIELD, static_cast<uint32>(inst->GetItemDamageShield(true))},
|
||||
{STAT_DS_MITIGATION, static_cast<uint32>(inst->GetItemDSMitigation(true))},
|
||||
{STAT_HEAL_AMOUNT, static_cast<uint32>(inst->GetItemHealAmt(true))},
|
||||
{STAT_SPELL_DAMAGE, static_cast<uint32>(inst->GetItemSpellDamage(true))},
|
||||
{STAT_CLAIRVOYANCE, static_cast<uint32>(inst->GetItemClairvoyance(true))},
|
||||
{STAT_HEROIC_AGILITY, static_cast<uint32>(inst->GetItemHeroicAgi(true))},
|
||||
{STAT_HEROIC_CHARISMA, static_cast<uint32>(inst->GetItemHeroicCha(true))},
|
||||
{STAT_HEROIC_DEXTERITY, static_cast<uint32>(inst->GetItemHeroicDex(true))},
|
||||
{STAT_HEROIC_INTELLIGENCE, static_cast<uint32>(inst->GetItemHeroicInt(true))},
|
||||
{STAT_HEROIC_STAMINA, static_cast<uint32>(inst->GetItemHeroicSta(true))},
|
||||
{STAT_HEROIC_STRENGTH, static_cast<uint32>(inst->GetItemHeroicStr(true))},
|
||||
{STAT_HEROIC_WISDOM, static_cast<uint32>(inst->GetItemHeroicWis(true))},
|
||||
{STAT_BASH, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillBash, true))},
|
||||
{STAT_BACKSTAB, static_cast<uint32>(inst->GetItemBackstabDamage(true))},
|
||||
{STAT_DRAGON_PUNCH, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillDragonPunch, true))},
|
||||
{STAT_EAGLE_STRIKE, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillEagleStrike, true))},
|
||||
{STAT_FLYING_KICK, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillFlyingKick, true))},
|
||||
{STAT_KICK, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillKick, true))},
|
||||
{STAT_ROUND_KICK, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillRoundKick, true))},
|
||||
{STAT_TIGER_CLAW, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillTigerClaw, true))},
|
||||
{STAT_FRENZY, static_cast<uint32>(inst->GetItemSkillsStat(EQ::skills::SkillFrenzy, true))},
|
||||
};
|
||||
|
||||
r.item_stat = item_stat_searches.contains(search.item_stat) ? item_stat_searches[search.item_stat] : 0;
|
||||
if (item_stat_searches.contains(search.item_stat) && item_stat_searches[search.item_stat] <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
static std::map<uint8, uint32> item_slot_searches = {
|
||||
{EQ::invslot::slotCharm, 1},
|
||||
{EQ::invslot::slotEar1, 2},
|
||||
{EQ::invslot::slotHead, 4},
|
||||
{EQ::invslot::slotFace, 8},
|
||||
{EQ::invslot::slotEar2, 16},
|
||||
{EQ::invslot::slotNeck, 32},
|
||||
{EQ::invslot::slotShoulders, 64},
|
||||
{EQ::invslot::slotArms, 128},
|
||||
{EQ::invslot::slotBack, 256},
|
||||
{EQ::invslot::slotWrist1, 512},
|
||||
{EQ::invslot::slotWrist2, 1024},
|
||||
{EQ::invslot::slotRange, 2048},
|
||||
{EQ::invslot::slotHands, 4096},
|
||||
{EQ::invslot::slotPrimary, 8192},
|
||||
{EQ::invslot::slotSecondary, 16384},
|
||||
{EQ::invslot::slotFinger1, 32768},
|
||||
{EQ::invslot::slotFinger2, 65536},
|
||||
{EQ::invslot::slotChest, 131072},
|
||||
{EQ::invslot::slotLegs, 262144},
|
||||
{EQ::invslot::slotFeet, 524288},
|
||||
{EQ::invslot::slotWaist, 1048576},
|
||||
{EQ::invslot::slotPowerSource, 2097152},
|
||||
{EQ::invslot::slotAmmo, 4194304},
|
||||
};
|
||||
|
||||
auto GetEquipmentSlotBit = [&](uint32 slot) -> uint32 {
|
||||
return item_slot_searches.contains(slot) ? item_slot_searches[slot] : 0;
|
||||
};
|
||||
|
||||
auto FindItemAugSlot = [&]() -> bool {
|
||||
for (auto const &s: inst->GetItem()->AugSlotType) {
|
||||
return s == search.augment;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// item type searches
|
||||
std::vector<ItemSearchType> item_search_types = {
|
||||
{EQ::item::ItemType::ItemTypeAll, true},
|
||||
{EQ::item::ItemType::ItemTypeBook, item->ItemClass == EQ::item::ItemType::ItemTypeBook},
|
||||
{EQ::item::ItemType::ItemTypeContainer, item->ItemClass == EQ::item::ItemType::ItemTypeContainer},
|
||||
{EQ::item::ItemType::ItemTypeAllEffects, item->Scroll.Effect > 0 && item->Scroll.Effect < 65000},
|
||||
{EQ::item::ItemType::ItemTypeUnknown9, item->Worn.Effect == 998},
|
||||
{EQ::item::ItemType::ItemTypeUnknown10, item->Worn.Effect >= 1298 && item->Worn.Effect <= 1307},
|
||||
{EQ::item::ItemType::ItemTypeFocusEffect, item->Focus.Effect > 0},
|
||||
{EQ::item::ItemType::ItemTypeArmor, item->ItemType == EQ::item::ItemType::ItemTypeArmor},
|
||||
{EQ::item::ItemType::ItemType1HBlunt, item->ItemType == EQ::item::ItemType::ItemType1HBlunt},
|
||||
{EQ::item::ItemType::ItemType1HPiercing, item->ItemType == EQ::item::ItemType::ItemType1HPiercing},
|
||||
{EQ::item::ItemType::ItemType1HSlash, item->ItemType == EQ::item::ItemType::ItemType1HSlash},
|
||||
{EQ::item::ItemType::ItemType2HBlunt, item->ItemType == EQ::item::ItemType::ItemType2HBlunt},
|
||||
{EQ::item::ItemType::ItemType2HSlash, item->ItemType == EQ::item::ItemType::ItemType2HSlash},
|
||||
{EQ::item::ItemType::ItemTypeBow, item->ItemType == EQ::item::ItemType::ItemTypeBow},
|
||||
{EQ::item::ItemType::ItemTypeShield, item->ItemType == EQ::item::ItemType::ItemTypeShield},
|
||||
{EQ::item::ItemType::ItemTypeMisc, item->ItemType == EQ::item::ItemType::ItemTypeMisc},
|
||||
{EQ::item::ItemType::ItemTypeFood, item->ItemType == EQ::item::ItemType::ItemTypeFood},
|
||||
{EQ::item::ItemType::ItemTypeDrink, item->ItemType == EQ::item::ItemType::ItemTypeDrink},
|
||||
{EQ::item::ItemType::ItemTypeLight, item->ItemType == EQ::item::ItemType::ItemTypeLight},
|
||||
{EQ::item::ItemType::ItemTypeCombinable, item->ItemType == EQ::item::ItemType::ItemTypeCombinable},
|
||||
{EQ::item::ItemType::ItemTypeBandage, item->ItemType == EQ::item::ItemType::ItemTypeBandage},
|
||||
{EQ::item::ItemType::ItemTypeSmallThrowing, item->ItemType == EQ::item::ItemType::ItemTypeSmallThrowing ||
|
||||
item->ItemType == EQ::item::ItemType::ItemTypeLargeThrowing},
|
||||
{EQ::item::ItemType::ItemTypeSpell, item->ItemType == EQ::item::ItemType::ItemTypeSpell},
|
||||
{EQ::item::ItemType::ItemTypePotion, item->ItemType == EQ::item::ItemType::ItemTypePotion},
|
||||
{EQ::item::ItemType::ItemTypeBrassInstrument, item->ItemType == EQ::item::ItemType::ItemTypeBrassInstrument},
|
||||
{EQ::item::ItemType::ItemTypeWindInstrument, item->ItemType == EQ::item::ItemType::ItemTypeWindInstrument},
|
||||
{EQ::item::ItemType::ItemTypeStringedInstrument, item->ItemType == EQ::item::ItemType::ItemTypeStringedInstrument},
|
||||
{EQ::item::ItemType::ItemTypePercussionInstrument, item->ItemType == EQ::item::ItemType::ItemTypePercussionInstrument},
|
||||
{EQ::item::ItemType::ItemTypeArrow, item->ItemType == EQ::item::ItemType::ItemTypeArrow},
|
||||
{EQ::item::ItemType::ItemTypeJewelry, item->ItemType == EQ::item::ItemType::ItemTypeJewelry},
|
||||
{EQ::item::ItemType::ItemTypeNote, item->ItemType == EQ::item::ItemType::ItemTypeNote},
|
||||
{EQ::item::ItemType::ItemTypeKey, item->ItemType == EQ::item::ItemType::ItemTypeKey},
|
||||
{EQ::item::ItemType::ItemType2HPiercing, item->ItemType == EQ::item::ItemType::ItemType2HPiercing},
|
||||
{EQ::item::ItemType::ItemTypeAlcohol, item->ItemType == EQ::item::ItemType::ItemTypeAlcohol},
|
||||
{EQ::item::ItemType::ItemTypeMartial, item->ItemType == EQ::item::ItemType::ItemTypeMartial},
|
||||
{EQ::item::ItemType::ItemTypeAugmentation, item->ItemType == EQ::item::ItemType::ItemTypeAugmentation},
|
||||
{EQ::item::ItemType::ItemTypeAlternateAbility, item->ItemType == EQ::item::ItemType::ItemTypeAlternateAbility},
|
||||
{EQ::item::ItemType::ItemTypeCount, item->ItemType == EQ::item::ItemType::ItemTypeCount},
|
||||
{EQ::item::ItemType::ItemTypeCollectible, item->ItemType == EQ::item::ItemType::ItemTypeCollectible}
|
||||
};
|
||||
|
||||
bool met_filter = false;
|
||||
bool has_filter = false;
|
||||
|
||||
for (auto &i: item_search_types) {
|
||||
if (i.type == search.type) {
|
||||
has_filter = true;
|
||||
if (i.condition) {
|
||||
LogTradingDetail("Item [{}] met search criteria for type [{}]", item->Name, uint8(i.type));
|
||||
met_filter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (has_filter && !met_filter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Add catch-all item type filter for specific item types
|
||||
|
||||
// item additive searches
|
||||
std::vector<AddititiveSearchCriteria> item_additive_searches = {
|
||||
{
|
||||
.should_check = search.min_level != 1 && inst->GetItemRequiredLevel(true) > 0,
|
||||
.condition = inst->GetItemRequiredLevel(true) >= search.min_level
|
||||
},
|
||||
{
|
||||
.should_check = search.max_level != 1 && inst->GetItemRequiredLevel(true) > 0,
|
||||
.condition = inst->GetItemRequiredLevel(true) <= search.max_level
|
||||
},
|
||||
{
|
||||
.should_check = !std::string(search.item_name).empty(),
|
||||
.condition = Strings::ContainsLower(item->Name, search.item_name)
|
||||
},
|
||||
{
|
||||
.should_check = search._class != 0xFFFFFFFF,
|
||||
.condition = static_cast<bool>(item->Classes & GetPlayerClassBit(search._class))
|
||||
},
|
||||
{
|
||||
.should_check = search.race != 0xFFFFFFFF,
|
||||
.condition = static_cast<bool>(item->Races & GetPlayerRaceBit(search.race))
|
||||
},
|
||||
{
|
||||
.should_check = search.augment != 0,
|
||||
.condition = FindItemAugSlot()
|
||||
},
|
||||
{
|
||||
.should_check = search.slot != 0xFFFFFFFF,
|
||||
.condition = static_cast<bool>(item->Slots & GetEquipmentSlotBit(search.slot))
|
||||
},
|
||||
};
|
||||
|
||||
bool should_add = true;
|
||||
|
||||
for (auto &i: item_additive_searches) {
|
||||
LogTradingDetail(
|
||||
"Checking item [{}] for search criteria - should_check [{}] condition [{}]",
|
||||
item->Name,
|
||||
i.should_check,
|
||||
i.condition
|
||||
);
|
||||
if (i.should_check && !i.condition) {
|
||||
should_add = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (!should_add) {
|
||||
continue;
|
||||
}
|
||||
|
||||
LogTradingDetail("Found item [{}] meeting search criteria.", r.item_name);
|
||||
all_entries.push_back(r);
|
||||
}
|
||||
|
||||
if (all_entries.size() > search.max_results) {
|
||||
all_entries.resize(search.max_results);
|
||||
}
|
||||
|
||||
LogTrading("Returning [{}] items from search results", all_entries.size());
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef EQEMU_BAZAAR_H
|
||||
#define EQEMU_BAZAAR_H
|
||||
|
||||
#include <vector>
|
||||
#include "shareddb.h"
|
||||
|
||||
class Bazaar {
|
||||
public:
|
||||
static std::vector<BazaarSearchResultsFromDB_Struct>
|
||||
GetSearchResults(SharedDatabase &db, BazaarSearchCriteria_Struct search, unsigned int char_zone_id);
|
||||
};
|
||||
|
||||
|
||||
#endif //EQEMU_BAZAAR_H
|
||||
+13
-3
@@ -206,9 +206,19 @@ void Database::LoginIP(uint32 account_id, const std::string& login_ip)
|
||||
QueryDatabase(query);
|
||||
}
|
||||
|
||||
int16 Database::CheckStatus(uint32 account_id)
|
||||
int16 Database::GetAccountStatus(uint32 account_id)
|
||||
{
|
||||
return AccountRepository::GetAccountStatus(*this, account_id);
|
||||
auto e = AccountRepository::FindOne(*this, account_id);
|
||||
|
||||
if (e.suspendeduntil > 0 && e.suspendeduntil < std::time(nullptr)) {
|
||||
e.status = 0;
|
||||
e.suspendeduntil = 0;
|
||||
e.suspend_reason = "";
|
||||
|
||||
AccountRepository::UpdateOne(*this, e);
|
||||
}
|
||||
|
||||
return e.status;
|
||||
}
|
||||
|
||||
uint32 Database::CreateAccount(
|
||||
@@ -741,7 +751,7 @@ bool Database::SetVariable(const std::string& name, const std::string& value)
|
||||
auto l = VariablesRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`name` = '{}'",
|
||||
"`varname` = '{}'",
|
||||
Strings::Escape(name)
|
||||
)
|
||||
);
|
||||
|
||||
+1
-1
@@ -170,7 +170,7 @@ public:
|
||||
bool SetAccountStatus(const std::string& account_name, int16 status);
|
||||
bool SetLocalPassword(uint32 account_id, const std::string& password);
|
||||
bool UpdateLiveChar(const std::string& name, uint32 account_id);
|
||||
int16 CheckStatus(uint32 account_id);
|
||||
int16 GetAccountStatus(uint32 account_id);
|
||||
void SetAccountCRCField(uint32 account_id, const std::string& field_name, uint64 checksum);
|
||||
uint32 CheckLogin(const std::string& name, const std::string& password, const std::string& loginserver, int16* status = 0);
|
||||
uint32 CreateAccount(
|
||||
|
||||
@@ -5578,6 +5578,88 @@ ADD COLUMN `extra_haste` int(11) NOT NULL DEFAULT 0 AFTER `wis`;
|
||||
.sql = R"(
|
||||
ALTER TABLE `guild_bank`
|
||||
CHANGE COLUMN `qty` `qty` INT(10) NOT NULL DEFAULT '0' AFTER `itemid`;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9277,
|
||||
.description = "2024_05_09_parcel_enable_containers.sql",
|
||||
.check = "SHOW TABLES LIKE 'character_parcels_containers'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
CREATE TABLE `character_parcels_containers` (
|
||||
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
`parcels_id` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`slot_id` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`item_id` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`aug_slot_1` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`aug_slot_2` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`aug_slot_3` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`aug_slot_4` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`aug_slot_5` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`aug_slot_6` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
`quantity` INT(10) UNSIGNED NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
INDEX `fk_character_parcels_id` (`parcels_id`) USING BTREE,
|
||||
CONSTRAINT `fk_character_parcels_id` FOREIGN KEY (`parcels_id`) REFERENCES `character_parcels` (`id`) ON UPDATE NO ACTION ON DELETE CASCADE
|
||||
)
|
||||
COLLATE='latin1_swedish_ci'
|
||||
ENGINE=InnoDB
|
||||
AUTO_INCREMENT=1
|
||||
;
|
||||
)"
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9278,
|
||||
.description = "2024_05_06_npc_greed.sql",
|
||||
.check = "SHOW COLUMNS FROM `npc_types` LIKE 'greed'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `npc_types`
|
||||
ADD COLUMN `greed` tinyint(8) UNSIGNED NOT NULL DEFAULT 0 AFTER `merchant_id`;
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9279,
|
||||
.description = "2024_05_13_content_flagging_npc_spells_entries.sql",
|
||||
.check = "SHOW COLUMNS FROM `npc_spells_entries` LIKE 'content_flags'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `npc_spells_entries` ADD `min_expansion` tinyint(4) NOT NULL DEFAULT -1;
|
||||
ALTER TABLE `npc_spells_entries` ADD `max_expansion` tinyint(4) NOT NULL DEFAULT -1;
|
||||
ALTER TABLE `npc_spells_entries` ADD `content_flags` varchar(100) NULL;
|
||||
ALTER TABLE `npc_spells_entries` ADD `content_flags_disabled` varchar(100) NULL;
|
||||
)",
|
||||
.content_schema_update = true
|
||||
},
|
||||
ManifestEntry{
|
||||
.version = 9280,
|
||||
.description = "2024_05_11_update_trader_support.sql",
|
||||
.check = "SHOW COLUMNS FROM `trader` LIKE 'aug_slot_1'",
|
||||
.condition = "empty",
|
||||
.match = "",
|
||||
.sql = R"(
|
||||
ALTER TABLE `trader`
|
||||
ADD COLUMN `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT FIRST,
|
||||
CHANGE COLUMN `char_id` `char_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `id`,
|
||||
CHANGE COLUMN `item_id` `item_id` INT(11) UNSIGNED NOT NULL DEFAULT '0' AFTER `char_id`,
|
||||
ADD COLUMN `aug_slot_1` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `item_id`,
|
||||
ADD COLUMN `aug_slot_2` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `aug_slot_1`,
|
||||
ADD COLUMN `aug_slot_3` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `aug_slot_2`,
|
||||
ADD COLUMN `aug_slot_4` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `aug_slot_3`,
|
||||
ADD COLUMN `aug_slot_5` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `aug_slot_4`,
|
||||
ADD COLUMN `aug_slot_6` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `aug_slot_5`,
|
||||
CHANGE COLUMN `serialnumber` `item_sn` INT(10) UNSIGNED NOT NULL DEFAULT '0' AFTER `aug_slot_6`,
|
||||
CHANGE COLUMN `charges` `item_charges` INT(11) NOT NULL DEFAULT '0' AFTER `item_sn`,
|
||||
ADD COLUMN `char_entity_id` INT(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `slot_id`,
|
||||
ADD COLUMN `char_zone_id` INT(11) UNSIGNED NOT NULL DEFAULT 0 AFTER `char_entity_id`,
|
||||
ADD COLUMN `active_transaction` TINYINT(3) UNSIGNED NOT NULL DEFAULT 0 AFTER `char_zone_id`,
|
||||
DROP PRIMARY KEY,
|
||||
ADD PRIMARY KEY (`id`),
|
||||
ADD INDEX `charid_slotid` (`char_id`, `slot_id`);
|
||||
)"
|
||||
}
|
||||
// -- template; copy/paste this when you need to create a new entry
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace DatabaseSchema {
|
||||
{"character_material", "id"},
|
||||
{"character_memmed_spells", "id"},
|
||||
{"character_parcels", "char_id"},
|
||||
{"character_parcels_containers", "id"},
|
||||
{"character_pet_buffs", "char_id"},
|
||||
{"character_pet_info", "char_id"},
|
||||
{"character_pet_inventory", "char_id"},
|
||||
@@ -130,6 +131,7 @@ namespace DatabaseSchema {
|
||||
"character_material",
|
||||
"character_memmed_spells",
|
||||
"character_parcels",
|
||||
"character_parcels_containers",
|
||||
"character_pet_buffs",
|
||||
"character_pet_info",
|
||||
"character_pet_inventory",
|
||||
|
||||
@@ -557,6 +557,7 @@ N(OP_TradeBusy),
|
||||
N(OP_TradeCoins),
|
||||
N(OP_TradeMoneyUpdate),
|
||||
N(OP_Trader),
|
||||
N(OP_TraderBulkSend),
|
||||
N(OP_TraderBuy),
|
||||
N(OP_TraderDelItem),
|
||||
N(OP_TradeRequest),
|
||||
|
||||
+41
-21
@@ -772,27 +772,47 @@ typedef enum {
|
||||
FilterShowSelfOnly
|
||||
} eqFilterMode;
|
||||
|
||||
#define STAT_STR 0
|
||||
#define STAT_STA 1
|
||||
#define STAT_AGI 2
|
||||
#define STAT_DEX 3
|
||||
#define STAT_INT 4
|
||||
#define STAT_WIS 5
|
||||
#define STAT_CHA 6
|
||||
#define STAT_MAGIC 7
|
||||
#define STAT_COLD 8
|
||||
#define STAT_FIRE 9
|
||||
#define STAT_POISON 10
|
||||
#define STAT_DISEASE 11
|
||||
#define STAT_MANA 12
|
||||
#define STAT_HP 13
|
||||
#define STAT_AC 14
|
||||
#define STAT_ENDURANCE 15
|
||||
#define STAT_ATTACK 16
|
||||
#define STAT_HP_REGEN 17
|
||||
#define STAT_MANA_REGEN 18
|
||||
#define STAT_HASTE 19
|
||||
#define STAT_DAMAGE_SHIELD 20
|
||||
#define STAT_STR 0
|
||||
#define STAT_STA 1
|
||||
#define STAT_AGI 2
|
||||
#define STAT_DEX 3
|
||||
#define STAT_INT 4
|
||||
#define STAT_WIS 5
|
||||
#define STAT_CHA 6
|
||||
#define STAT_MAGIC 7
|
||||
#define STAT_COLD 8
|
||||
#define STAT_FIRE 9
|
||||
#define STAT_POISON 10
|
||||
#define STAT_DISEASE 11
|
||||
#define STAT_MANA 12
|
||||
#define STAT_HP 13
|
||||
#define STAT_AC 14
|
||||
#define STAT_ENDURANCE 15
|
||||
#define STAT_ATTACK 16
|
||||
#define STAT_HP_REGEN 17
|
||||
#define STAT_MANA_REGEN 18
|
||||
#define STAT_HASTE 19
|
||||
#define STAT_DAMAGE_SHIELD 20
|
||||
#define STAT_DS_MITIGATION 22
|
||||
#define STAT_HEAL_AMOUNT 23
|
||||
#define STAT_SPELL_DAMAGE 24
|
||||
#define STAT_CLAIRVOYANCE 25
|
||||
#define STAT_HEROIC_AGILITY 26
|
||||
#define STAT_HEROIC_CHARISMA 27
|
||||
#define STAT_HEROIC_DEXTERITY 28
|
||||
#define STAT_HEROIC_INTELLIGENCE 29
|
||||
#define STAT_HEROIC_STAMINA 30
|
||||
#define STAT_HEROIC_STRENGTH 31
|
||||
#define STAT_HEROIC_WISDOM 32
|
||||
#define STAT_BASH 33
|
||||
#define STAT_BACKSTAB 34
|
||||
#define STAT_DRAGON_PUNCH 35
|
||||
#define STAT_EAGLE_STRIKE 36
|
||||
#define STAT_FLYING_KICK 37
|
||||
#define STAT_KICK 38
|
||||
#define STAT_ROUND_KICK 39
|
||||
#define STAT_TIGER_CLAW 40
|
||||
#define STAT_FRENZY 41
|
||||
|
||||
/*
|
||||
** Recast timer types. Used as an off set to charProfileStruct timers.
|
||||
|
||||
+197
-72
@@ -31,7 +31,6 @@
|
||||
#include "../cereal/include/cereal/types/string.hpp"
|
||||
#include "../cereal/include/cereal/types/vector.hpp"
|
||||
|
||||
|
||||
static const uint32 BUFF_COUNT = 42;
|
||||
static const uint32 PET_BUFF_COUNT = 30;
|
||||
static const uint32 MAX_MERC = 100;
|
||||
@@ -323,6 +322,7 @@ union
|
||||
bool targetable_with_hotkey;
|
||||
bool show_name;
|
||||
bool guild_show;
|
||||
bool trader;
|
||||
};
|
||||
|
||||
struct PlayerState_Struct {
|
||||
@@ -1119,7 +1119,7 @@ struct PlayerProfile_Struct
|
||||
/*19558*/ uint8 guildAutoconsent; // 0=off, 1=on
|
||||
/*19559*/ uint8 unknown19595[5]; // ***Placeholder (6/29/2005)
|
||||
/*19564*/ uint32 RestTimer;
|
||||
/*19568*/
|
||||
/*19568*/ uint32 char_id; // Found as part of bazaar revamp (5/15/2024)
|
||||
|
||||
// All player profile packets are translated and this overhead is ignored in out-bound packets
|
||||
PlayerProfile_Struct() : m_player_profile_version(EQ::versions::MobVersion::Unknown) { }
|
||||
@@ -3002,26 +3002,26 @@ struct EnvDamage2_Struct {
|
||||
//
|
||||
|
||||
enum {
|
||||
BazaarTrader_StartTraderMode = 1,
|
||||
BazaarTrader_EndTraderMode = 2,
|
||||
BazaarTrader_UpdatePrice = 3,
|
||||
BazaarTrader_EndTransaction = 4,
|
||||
BazaarSearchResults = 7,
|
||||
BazaarWelcome = 9,
|
||||
BazaarBuyItem = 10,
|
||||
BazaarTrader_ShowItems = 11,
|
||||
BazaarSearchDone = 12,
|
||||
BazaarTrader_StartTraderMode = 1,
|
||||
BazaarTrader_EndTraderMode = 2,
|
||||
BazaarTrader_UpdatePrice = 3,
|
||||
BazaarTrader_EndTransaction = 4,
|
||||
BazaarSearchResults = 7,
|
||||
BazaarWelcome = 9,
|
||||
BazaarBuyItem = 10,
|
||||
BazaarTrader_ShowItems = 11,
|
||||
BazaarSearchDone = 12,
|
||||
BazaarTrader_CustomerBrowsing = 13,
|
||||
BazaarInspectItem = 18,
|
||||
BazaarSearchDone2 = 19,
|
||||
BazaarInspectItem = 18,
|
||||
BazaarSearchDone2 = 19,
|
||||
BazaarTrader_StartTraderMode2 = 22
|
||||
};
|
||||
|
||||
enum {
|
||||
BazaarPriceChange_Fail = 0,
|
||||
BazaarPriceChange_Fail = 0,
|
||||
BazaarPriceChange_UpdatePrice = 1,
|
||||
BazaarPriceChange_RemoveItem = 2,
|
||||
BazaarPriceChange_AddItem = 3
|
||||
BazaarPriceChange_RemoveItem = 2,
|
||||
BazaarPriceChange_AddItem = 3
|
||||
};
|
||||
|
||||
struct BazaarWindowStart_Struct {
|
||||
@@ -3032,31 +3032,41 @@ struct BazaarWindowStart_Struct {
|
||||
|
||||
|
||||
struct BazaarWelcome_Struct {
|
||||
BazaarWindowStart_Struct Beginning;
|
||||
uint32 Traders;
|
||||
uint32 Items;
|
||||
uint32 Unknown012;
|
||||
uint32 Unknown016;
|
||||
uint32 action;
|
||||
uint32 traders;
|
||||
uint32 items;
|
||||
uint32 unknown_012;
|
||||
uint32 unknown_016;
|
||||
};
|
||||
|
||||
struct BazaarSearch_Struct {
|
||||
BazaarWindowStart_Struct Beginning;
|
||||
uint32 TraderID;
|
||||
uint32 Class_;
|
||||
uint32 Race;
|
||||
uint32 ItemStat;
|
||||
uint32 Slot;
|
||||
uint32 Type;
|
||||
char Name[64];
|
||||
uint32 MinPrice;
|
||||
uint32 MaxPrice;
|
||||
uint32 Minlevel;
|
||||
uint32 MaxLlevel;
|
||||
struct BazaarSearchCriteria_Struct {
|
||||
/*000*/ uint32 action{0};
|
||||
/*004*/ uint32 search_scope{0}; // 1 all traders 0 local traders
|
||||
/*008*/ uint32 unknown_008{0};
|
||||
/*012*/ uint32 unknown_012{0};
|
||||
/*016*/ uint32 trader_id{0};
|
||||
/*020*/ uint32 _class{0};
|
||||
/*024*/ uint32 race{0};
|
||||
/*028*/ uint32 item_stat{0};
|
||||
/*032*/ uint32 slot{0};
|
||||
/*036*/ uint32 type{0};
|
||||
/*040*/ char item_name[64]{""};
|
||||
/*104*/ uint32 min_cost{0};
|
||||
/*108*/ uint32 max_cost{0};
|
||||
/*112*/ uint32 min_level{1};
|
||||
/*116*/ uint32 max_level{0};
|
||||
/*120*/ uint32 max_results{0};
|
||||
/*124*/ uint32 prestige{0};
|
||||
/*128*/ uint32 augment{0};
|
||||
/*132*/ uint32 trader_entity_id{0};
|
||||
};
|
||||
struct BazaarInspect_Struct{
|
||||
uint32 ItemID;
|
||||
uint32 Unknown004;
|
||||
char Name[64];
|
||||
|
||||
struct BazaarInspect_Struct {
|
||||
uint32 action;
|
||||
char player_name[64];
|
||||
uint32 serial_number;
|
||||
uint32 item_id;
|
||||
uint32 trader_id;
|
||||
};
|
||||
|
||||
struct NewBazaarInspect_Struct {
|
||||
@@ -3076,6 +3086,14 @@ struct BazaarReturnDone_Struct{
|
||||
uint32 Unknown012;
|
||||
uint32 Unknown016;
|
||||
};
|
||||
|
||||
struct BazaarDeliveryCost_Struct {
|
||||
uint32 action;
|
||||
uint16 voucher_delivery_cost;
|
||||
float parcel_deliver_cost; //percentage of item cost
|
||||
uint32 unknown_010;
|
||||
};
|
||||
|
||||
struct BazaarSearchResults_Struct {
|
||||
/*000*/ BazaarWindowStart_Struct Beginning;
|
||||
/*004*/ uint32 NumItems;
|
||||
@@ -3088,8 +3106,10 @@ struct BazaarSearchResults_Struct {
|
||||
// New fields for SoD+, stripped off when encoding for older clients.
|
||||
char SellerName[64];
|
||||
uint32 ItemID;
|
||||
uint32 ItemID2;
|
||||
};
|
||||
|
||||
|
||||
// Barter/Buyer
|
||||
//
|
||||
//
|
||||
@@ -3389,32 +3409,31 @@ struct WhoAllReturnStruct {
|
||||
};
|
||||
|
||||
struct Trader_Struct {
|
||||
/*000*/ uint32 Code;
|
||||
/*004*/ uint32 Unknown004;
|
||||
/*008*/ uint64 Items[80];
|
||||
/*648*/ uint32 ItemCost[80];
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 unknown_004;
|
||||
/*008*/ uint64 items[EQ::invtype::BAZAAR_SIZE];
|
||||
/*648*/ uint32 item_cost[EQ::invtype::BAZAAR_SIZE];
|
||||
};
|
||||
|
||||
struct ClickTrader_Struct {
|
||||
/*000*/ uint32 Code;
|
||||
/*004*/ uint32 Unknown004;
|
||||
/*008*/ int64 SerialNumber[80];
|
||||
/*648*/ uint32 ItemCost[80];
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 unknown_004;
|
||||
/*008*/ int64 serial_number[EQ::invtype::BAZAAR_SIZE] {};
|
||||
/*648*/ uint32 item_cost[EQ::invtype::BAZAAR_SIZE] {};
|
||||
};
|
||||
|
||||
struct GetItems_Struct{
|
||||
uint32 Items[80];
|
||||
int32 SerialNumber[80];
|
||||
int32 Charges[80];
|
||||
uint32 items[EQ::invtype::BAZAAR_SIZE];
|
||||
int32 serial_number[EQ::invtype::BAZAAR_SIZE];
|
||||
int32 charges[EQ::invtype::BAZAAR_SIZE];
|
||||
};
|
||||
|
||||
struct BecomeTrader_Struct
|
||||
{
|
||||
/*000*/ uint32 ID;
|
||||
/*004*/ uint32 Code;
|
||||
/*008*/ char Name[64];
|
||||
/*072*/ uint32 Unknown072; // Observed 0x33,0x91 etc on zone-in, 0x00 when sent for a new trader after zone-in
|
||||
/*076*/
|
||||
struct BecomeTrader_Struct {
|
||||
uint32 action;
|
||||
uint32 zone_id;
|
||||
uint32 trader_id;
|
||||
uint32 entity_id;
|
||||
char trader_name[64];
|
||||
};
|
||||
|
||||
struct TraderStatus_Struct{
|
||||
@@ -3424,20 +3443,30 @@ struct TraderStatus_Struct{
|
||||
};
|
||||
|
||||
struct Trader_ShowItems_Struct{
|
||||
/*000*/ uint32 Code;
|
||||
/*004*/ uint32 TraderID;
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 entity_id;
|
||||
/*008*/ uint32 Unknown08[3];
|
||||
/*020*/
|
||||
};
|
||||
|
||||
struct TraderBuy_Struct{
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ uint32 TraderID;
|
||||
/*008*/ uint32 ItemID;
|
||||
/*012*/ uint32 AlreadySold;
|
||||
/*016*/ uint32 Price;
|
||||
/*020*/ uint32 Quantity;
|
||||
/*024*/ char ItemName[64];
|
||||
struct TraderBuy_Struct {
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 method;
|
||||
/*008*/ uint32 sub_action;
|
||||
/*012*/ uint32 unknown_012;
|
||||
/*016*/ uint32 trader_id;
|
||||
/*020*/ char buyer_name[64];
|
||||
/*084*/ char seller_name[64];
|
||||
/*148*/ char unknown_148[32];
|
||||
/*180*/ char item_name[64];
|
||||
/*244*/ char serial_number[17];
|
||||
/*261*/ char unknown_261[3];
|
||||
/*264*/ uint32 item_id;
|
||||
/*268*/ uint32 price;
|
||||
/*272*/ uint32 already_sold;
|
||||
/*276*/ uint32 unknown_276;
|
||||
/*280*/ uint32 quantity;
|
||||
/*284*/
|
||||
};
|
||||
|
||||
struct TraderItemUpdate_Struct{
|
||||
@@ -3465,15 +3494,15 @@ struct MoneyUpdate_Struct{
|
||||
};
|
||||
|
||||
struct TraderDelItem_Struct{
|
||||
uint32 Unknown000;
|
||||
uint32 TraderID;
|
||||
uint32 ItemID;
|
||||
uint32 Unknown012;
|
||||
uint32 unknown_000;
|
||||
uint32 trader_id;
|
||||
uint32 item_id;
|
||||
uint32 unknown_012;
|
||||
};
|
||||
|
||||
struct TraderClick_Struct{
|
||||
/*000*/ uint32 TraderID;
|
||||
/*004*/ uint32 Code;
|
||||
/*000*/ uint32 Code;
|
||||
/*004*/ uint32 TraderID;
|
||||
/*008*/ uint32 Unknown008;
|
||||
/*012*/ uint32 Approval;
|
||||
/*016*/
|
||||
@@ -5991,6 +6020,102 @@ struct UnderWorld {
|
||||
/* 16 */
|
||||
};
|
||||
|
||||
enum BazaarTraderBarterActions {
|
||||
TraderOff = 0,
|
||||
TraderOn = 1,
|
||||
PriceUpdate = 3,
|
||||
EndTransaction = 4,
|
||||
BazaarSearch = 7,
|
||||
WelcomeMessage = 9,
|
||||
BuyTraderItem = 10,
|
||||
ListTraderItems = 11,
|
||||
CustomerBrowsing = 13,
|
||||
BazaarInspect = 18,
|
||||
ItemMove = 19,
|
||||
TraderAck2 = 22,
|
||||
AddTraderToBazaarWindow = 24,
|
||||
RemoveTraderFromBazaarWindow = 25,
|
||||
ClickTrader = 28,
|
||||
DeliveryCostUpdate = 29
|
||||
};
|
||||
|
||||
enum BazaarPurchaseActions {
|
||||
ByVendor = 0,
|
||||
ByParcel = 1,
|
||||
ByDirectToInventory = 2
|
||||
};
|
||||
|
||||
enum BazaarPurchaseSubActions {
|
||||
Success = 0,
|
||||
Failed = 1,
|
||||
DataOutDated = 3,
|
||||
TooManyParcels = 5,
|
||||
TransactionInProgress = 6,
|
||||
InsufficientFunds = 7
|
||||
};
|
||||
|
||||
enum BazaarSearchScopes {
|
||||
Local_Scope = 0,
|
||||
AllTraders_Scope = 1,
|
||||
NonRoFBazaarSearchScope = 99
|
||||
};
|
||||
|
||||
struct BazaarSearchResultsFromDB_Struct {
|
||||
uint32 count;
|
||||
uint32 trader_id;
|
||||
uint32 item_id;
|
||||
uint32 serial_number;
|
||||
uint32 charges;
|
||||
uint32 cost;
|
||||
uint32 slot_id;
|
||||
uint32 icon_id;
|
||||
uint32 sum_charges;
|
||||
uint32 trader_zone_id;
|
||||
uint32 trader_entity_id;
|
||||
uint32 item_stat;
|
||||
bool stackable;
|
||||
std::string item_name;
|
||||
std::string serial_number_RoF;
|
||||
std::string trader_name;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(count),
|
||||
CEREAL_NVP(trader_id),
|
||||
CEREAL_NVP(item_id),
|
||||
CEREAL_NVP(serial_number),
|
||||
CEREAL_NVP(charges),
|
||||
CEREAL_NVP(cost),
|
||||
CEREAL_NVP(slot_id),
|
||||
CEREAL_NVP(icon_id),
|
||||
CEREAL_NVP(sum_charges),
|
||||
CEREAL_NVP(trader_zone_id),
|
||||
CEREAL_NVP(trader_entity_id),
|
||||
CEREAL_NVP(item_stat),
|
||||
CEREAL_NVP(stackable),
|
||||
CEREAL_NVP(item_name),
|
||||
CEREAL_NVP(serial_number_RoF),
|
||||
CEREAL_NVP(trader_name)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
struct BazaarSearchMessaging_Struct {
|
||||
uint32 action;
|
||||
char payload[];
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive &archive)
|
||||
{
|
||||
archive(
|
||||
CEREAL_NVP(action),
|
||||
CEREAL_NVP(payload)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// Restore structure packing to default
|
||||
#pragma pack()
|
||||
|
||||
|
||||
@@ -642,6 +642,10 @@ void PlayerEventLogs::ProcessRetentionTruncation()
|
||||
void PlayerEventLogs::ReloadSettings()
|
||||
{
|
||||
for (auto &e: PlayerEventLogSettingsRepository::All(*m_database)) {
|
||||
if (e.id >= PlayerEvent::MAX || e.id < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
m_settings[e.id] = e;
|
||||
}
|
||||
}
|
||||
|
||||
+69
-66
@@ -58,72 +58,75 @@ namespace EQ
|
||||
};
|
||||
|
||||
enum ItemType : uint8 {
|
||||
/*9138*/ ItemType1HSlash = 0,
|
||||
/*9141*/ ItemType2HSlash,
|
||||
/*9140*/ ItemType1HPiercing,
|
||||
/*9139*/ ItemType1HBlunt,
|
||||
/*9142*/ ItemType2HBlunt,
|
||||
/*5504*/ ItemTypeBow, // 5
|
||||
/*----*/ ItemTypeUnknown1,
|
||||
/*----*/ ItemTypeLargeThrowing,
|
||||
/*5505*/ ItemTypeShield,
|
||||
/*5506*/ ItemTypeScroll,
|
||||
/*5507*/ ItemTypeArmor, // 10
|
||||
/*5508*/ ItemTypeMisc, // a lot of random crap has this item use.
|
||||
/*7564*/ ItemTypeLockPick,
|
||||
/*----*/ ItemTypeUnknown2,
|
||||
/*5509*/ ItemTypeFood,
|
||||
/*5510*/ ItemTypeDrink, // 15
|
||||
/*5511*/ ItemTypeLight,
|
||||
/*5512*/ ItemTypeCombinable, // not all stackable items are this use...
|
||||
/*5513*/ ItemTypeBandage,
|
||||
/*----*/ ItemTypeSmallThrowing,
|
||||
/*----*/ ItemTypeSpell, // 20 // spells and tomes
|
||||
/*5514*/ ItemTypePotion,
|
||||
/*----*/ ItemTypeUnknown3,
|
||||
/*0406*/ ItemTypeWindInstrument,
|
||||
/*0407*/ ItemTypeStringedInstrument,
|
||||
/*0408*/ ItemTypeBrassInstrument, // 25
|
||||
/*0405*/ ItemTypePercussionInstrument,
|
||||
/*5515*/ ItemTypeArrow,
|
||||
/*----*/ ItemTypeUnknown4,
|
||||
/*5521*/ ItemTypeJewelry,
|
||||
/*----*/ ItemTypeSkull, // 30
|
||||
/*5516*/ ItemTypeBook, // skill-up tomes/books? (would probably need a pp flag if true...)
|
||||
/*5517*/ ItemTypeNote,
|
||||
/*5518*/ ItemTypeKey,
|
||||
/*----*/ ItemTypeCoin,
|
||||
/*5520*/ ItemType2HPiercing, // 35
|
||||
/*----*/ ItemTypeFishingPole,
|
||||
/*----*/ ItemTypeFishingBait,
|
||||
/*5519*/ ItemTypeAlcohol,
|
||||
/*----*/ ItemTypeKey2, // keys and satchels?? (questable keys?)
|
||||
/*----*/ ItemTypeCompass, // 40
|
||||
/*----*/ ItemTypeUnknown5,
|
||||
/*----*/ ItemTypePoison, // might be wrong, but includes poisons
|
||||
/*----*/ ItemTypeUnknown6,
|
||||
/*----*/ ItemTypeUnknown7,
|
||||
/*5522*/ ItemTypeMartial, // 45
|
||||
/*----*/ ItemTypeUnknown8,
|
||||
/*----*/ ItemTypeUnknown9,
|
||||
/*----*/ ItemTypeUnknown10,
|
||||
/*----*/ ItemTypeUnknown11,
|
||||
/*----*/ ItemTypeSinging, // 50
|
||||
/*5750*/ ItemTypeAllInstrumentTypes,
|
||||
/*5776*/ ItemTypeCharm,
|
||||
/*----*/ ItemTypeDye,
|
||||
/*----*/ ItemTypeAugmentation,
|
||||
/*----*/ ItemTypeAugmentationSolvent, // 55
|
||||
/*----*/ ItemTypeAugmentationDistiller,
|
||||
/*----*/ ItemTypeUnknown12,
|
||||
/*----*/ ItemTypeFellowshipKit,
|
||||
/*----*/ ItemTypeUnknown13,
|
||||
/*----*/ ItemTypeRecipe, // 60
|
||||
/*----*/ ItemTypeAdvancedRecipe,
|
||||
/*----*/ ItemTypeJournal, // only one(1) database entry
|
||||
/*----*/ ItemTypeAltCurrency, // alt-currency (as opposed to coinage)
|
||||
/*5881*/ ItemTypePerfectedAugmentationDistiller,
|
||||
/*----*/ ItemTypeCount
|
||||
/*9138*/ ItemType1HSlash = 0,
|
||||
/*9141*/ ItemType2HSlash,
|
||||
/*9140*/ ItemType1HPiercing,
|
||||
/*9139*/ ItemType1HBlunt,
|
||||
/*9142*/ ItemType2HBlunt,
|
||||
/*5504*/ ItemTypeBow, // 5
|
||||
/*----*/ ItemTypeUnknown1,
|
||||
/*----*/ ItemTypeLargeThrowing,
|
||||
/*5505*/ ItemTypeShield,
|
||||
/*5506*/ ItemTypeScroll,
|
||||
/*5507*/ ItemTypeArmor, // 10
|
||||
/*5508*/ ItemTypeMisc, // a lot of random crap has this item use.
|
||||
/*7564*/ ItemTypeLockPick,
|
||||
/*----*/ ItemTypeUnknown2,
|
||||
/*5509*/ ItemTypeFood,
|
||||
/*5510*/ ItemTypeDrink, // 15
|
||||
/*5511*/ ItemTypeLight,
|
||||
/*5512*/ ItemTypeCombinable, // not all stackable items are this use...
|
||||
/*5513*/ ItemTypeBandage,
|
||||
/*----*/ ItemTypeSmallThrowing,
|
||||
/*----*/ ItemTypeSpell, // 20 // spells and tomes
|
||||
/*5514*/ ItemTypePotion,
|
||||
/*----*/ ItemTypeUnknown3,
|
||||
/*0406*/ ItemTypeWindInstrument,
|
||||
/*0407*/ ItemTypeStringedInstrument,
|
||||
/*0408*/ ItemTypeBrassInstrument, // 25
|
||||
/*0405*/ ItemTypePercussionInstrument,
|
||||
/*5515*/ ItemTypeArrow,
|
||||
/*----*/ ItemTypeUnknown4,
|
||||
/*5521*/ ItemTypeJewelry,
|
||||
/*----*/ ItemTypeSkull, // 30
|
||||
/*5516*/ ItemTypeBook, // skill-up tomes/books? (would probably need a pp flag if true...)
|
||||
/*5517*/ ItemTypeNote,
|
||||
/*5518*/ ItemTypeKey,
|
||||
/*----*/ ItemTypeCoin,
|
||||
/*5520*/ ItemType2HPiercing, // 35
|
||||
/*----*/ ItemTypeFishingPole,
|
||||
/*----*/ ItemTypeFishingBait,
|
||||
/*5519*/ ItemTypeAlcohol,
|
||||
/*----*/ ItemTypeKey2, // keys and satchels?? (questable keys?)
|
||||
/*----*/ ItemTypeCompass, // 40
|
||||
/*----*/ ItemTypeUnknown5,
|
||||
/*----*/ ItemTypePoison, // might be wrong, but includes poisons
|
||||
/*----*/ ItemTypeUnknown6,
|
||||
/*----*/ ItemTypeUnknown7,
|
||||
/*5522*/ ItemTypeMartial, // 45
|
||||
/*----*/ ItemTypeAllEffects,
|
||||
/*----*/ ItemTypeUnknown9,
|
||||
/*----*/ ItemTypeUnknown10,
|
||||
/*----*/ ItemTypeFocusEffect,
|
||||
/*----*/ ItemTypeSinging, // 50
|
||||
/*5750*/ ItemTypeAllInstrumentTypes,
|
||||
/*5776*/ ItemTypeCharm,
|
||||
/*----*/ ItemTypeDye,
|
||||
/*----*/ ItemTypeAugmentation,
|
||||
/*----*/ ItemTypeAugmentationSolvent, // 55
|
||||
/*----*/ ItemTypeAugmentationDistiller,
|
||||
/*----*/ ItemTypeAlternateAbility,
|
||||
/*----*/ ItemTypeFellowshipKit,
|
||||
/*----*/ ItemTypeUnknown13,
|
||||
/*----*/ ItemTypeRecipe, // 60
|
||||
/*----*/ ItemTypeAdvancedRecipe,
|
||||
/*----*/ ItemTypeJournal, // only one(1) database entry
|
||||
/*----*/ ItemTypeAltCurrency, // alt-currency (as opposed to coinage)
|
||||
/*5881*/ ItemTypePerfectedAugmentationDistiller,
|
||||
/*----*/ ItemTypeCount,
|
||||
/*----*/ ItemTypeCollectible,
|
||||
/*----*/ ItemTypeContainer,
|
||||
/*----*/ ItemTypeAll = 0xFF
|
||||
|
||||
/*
|
||||
Unknowns:
|
||||
|
||||
@@ -1812,6 +1812,142 @@ std::vector<uint32> EQ::ItemInstance::GetAugmentIDs() const
|
||||
return augments;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemRegen(bool augments) const
|
||||
{
|
||||
int stat = 0;
|
||||
const auto item = GetItem();
|
||||
if (item) {
|
||||
stat = item->Regen;
|
||||
if (augments) {
|
||||
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetAugment(i)) {
|
||||
stat += GetAugment(i)->GetItemRegen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemManaRegen(bool augments) const
|
||||
{
|
||||
int stat = 0;
|
||||
const auto item = GetItem();
|
||||
if (item) {
|
||||
stat = item->ManaRegen;
|
||||
if (augments) {
|
||||
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetAugment(i)) {
|
||||
stat += GetAugment(i)->GetItemManaRegen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemDamageShield(bool augments) const
|
||||
{
|
||||
int stat = 0;
|
||||
const auto item = GetItem();
|
||||
if (item) {
|
||||
stat = item->DamageShield;
|
||||
if (augments) {
|
||||
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetAugment(i)) {
|
||||
stat += GetAugment(i)->GetItemDamageShield();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemDSMitigation(bool augments) const
|
||||
{
|
||||
int stat = 0;
|
||||
const auto item = GetItem();
|
||||
if (item) {
|
||||
stat = item->DSMitigation;
|
||||
if (augments) {
|
||||
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetAugment(i)) {
|
||||
stat += GetAugment(i)->GetItemDSMitigation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemHealAmt(bool augments) const
|
||||
{
|
||||
int stat = 0;
|
||||
const auto item = GetItem();
|
||||
if (item) {
|
||||
stat = item->HealAmt;
|
||||
if (augments) {
|
||||
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetAugment(i)) {
|
||||
stat += GetAugment(i)->GetItemHealAmt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemSpellDamage(bool augments) const
|
||||
{
|
||||
int stat = 0;
|
||||
const auto item = GetItem();
|
||||
if (item) {
|
||||
stat = item->SpellDmg;
|
||||
if (augments) {
|
||||
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetAugment(i)) {
|
||||
stat += GetAugment(i)->GetItemSpellDamage();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemClairvoyance(bool augments) const
|
||||
{
|
||||
int stat = 0;
|
||||
const auto item = GetItem();
|
||||
if (item) {
|
||||
stat = item->Clairvoyance;
|
||||
if (augments) {
|
||||
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetAugment(i)) {
|
||||
stat += GetAugment(i)->GetItemClairvoyance();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
int EQ::ItemInstance::GetItemSkillsStat(EQ::skills::SkillType skill, bool augments) const
|
||||
{
|
||||
int stat = 0;
|
||||
const auto item = GetItem();
|
||||
if (item) {
|
||||
stat = item->ExtraDmgSkill == skill ? item->ExtraDmgAmt : 0;
|
||||
if (augments) {
|
||||
for (int i = invaug::SOCKET_BEGIN; i <= invaug::SOCKET_END; ++i) {
|
||||
if (GetAugment(i)) {
|
||||
stat += GetAugment(i)->GetItemSkillsStat(skill);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stat;
|
||||
}
|
||||
|
||||
//
|
||||
// class EvolveInfo
|
||||
//
|
||||
|
||||
@@ -270,6 +270,7 @@ namespace EQ
|
||||
int GetItemMagical(bool augments = false) const;
|
||||
int GetItemHP(bool augments = false) const;
|
||||
int GetItemMana(bool augments = false) const;
|
||||
int GetItemManaRegen(bool augments = false) const;
|
||||
int GetItemEndur(bool augments = false) const;
|
||||
int GetItemAttack(bool augments = false) const;
|
||||
int GetItemStr(bool augments = false) const;
|
||||
@@ -299,6 +300,13 @@ namespace EQ
|
||||
int GetItemHeroicDR(bool augments = false) const;
|
||||
int GetItemHeroicCorrup(bool augments = false) const;
|
||||
int GetItemHaste(bool augments = false) const;
|
||||
int GetItemRegen(bool augments = false) const;
|
||||
int GetItemDamageShield(bool augments = false) const;
|
||||
int GetItemDSMitigation(bool augments = false) const;
|
||||
int GetItemHealAmt(bool augments = false) const;
|
||||
int GetItemSpellDamage(bool augments = false) const;
|
||||
int GetItemClairvoyance(bool augments = false) const;
|
||||
int GetItemSkillsStat(EQ::skills::SkillType skill, bool augments = false) const;
|
||||
uint32 GetItemGuildFavor() const;
|
||||
std::vector<uint32> GetAugmentIDs() const;
|
||||
|
||||
|
||||
+24
-26
@@ -3494,14 +3494,13 @@ namespace RoF
|
||||
ENCODE_LENGTH_EXACT(ClickTrader_Struct);
|
||||
SETUP_DIRECT_ENCODE(ClickTrader_Struct, structs::ClickTrader_Struct);
|
||||
|
||||
eq->Code = emu->Code;
|
||||
// Live actually has 200 items now, but 80 is the most our internal struct supports
|
||||
for (uint32 i = 0; i < 200; i++)
|
||||
eq->Code = emu->action;
|
||||
for (uint32 i = 0; i < RoF::invtype::BAZAAR_SIZE; i++)
|
||||
{
|
||||
strncpy(eq->items[i].SerialNumber, "0000000000000000", sizeof(eq->items[i].SerialNumber));
|
||||
eq->items[i].Unknown18 = 0;
|
||||
if (i < 80) {
|
||||
eq->ItemCost[i] = emu->ItemCost[i];
|
||||
eq->ItemCost[i] = emu->item_cost[i];
|
||||
}
|
||||
else {
|
||||
eq->ItemCost[i] = 0;
|
||||
@@ -3515,9 +3514,9 @@ namespace RoF
|
||||
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
|
||||
eq->Code = emu->Code;
|
||||
eq->Code = emu->action;
|
||||
strncpy(eq->SerialNumber, "0000000000000000", sizeof(eq->SerialNumber));
|
||||
eq->TraderID = emu->TraderID;
|
||||
eq->TraderID = emu->entity_id;
|
||||
eq->Stacksize = 0;
|
||||
eq->Price = 0;
|
||||
|
||||
@@ -3543,13 +3542,13 @@ namespace RoF
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
|
||||
OUT(Action);
|
||||
OUT(Price);
|
||||
OUT(TraderID);
|
||||
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
||||
OUT(ItemID);
|
||||
OUT(Quantity);
|
||||
OUT(AlreadySold);
|
||||
OUT(action);
|
||||
OUT(price);
|
||||
OUT(trader_id);
|
||||
memcpy(eq->item_name, emu->item_name, sizeof(eq->item_name));
|
||||
OUT(item_id);
|
||||
OUT(quantity);
|
||||
OUT(already_sold);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@@ -5041,12 +5040,11 @@ namespace RoF
|
||||
SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::ClickTrader_Struct);
|
||||
MEMSET_IN(ClickTrader_Struct);
|
||||
|
||||
emu->Code = eq->Code;
|
||||
// Live actually has 200 items now, but 80 is the most our internal struct supports
|
||||
for (uint32 i = 0; i < 80; i++)
|
||||
emu->action = eq->Code;
|
||||
for (uint32 i = 0; i < RoF::invtype::BAZAAR_SIZE; i++)
|
||||
{
|
||||
emu->SerialNumber[i] = 0; // eq->SerialNumber[i];
|
||||
emu->ItemCost[i] = eq->ItemCost[i];
|
||||
emu->serial_number[i] = 0; // eq->SerialNumber[i];
|
||||
emu->item_cost[i] = eq->ItemCost[i];
|
||||
}
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
@@ -5057,8 +5055,8 @@ namespace RoF
|
||||
SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
MEMSET_IN(Trader_ShowItems_Struct);
|
||||
|
||||
emu->Code = eq->Code;
|
||||
emu->TraderID = eq->TraderID;
|
||||
emu->action = eq->Code;
|
||||
emu->entity_id = eq->TraderID;
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
@@ -5080,12 +5078,12 @@ namespace RoF
|
||||
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
MEMSET_IN(TraderBuy_Struct);
|
||||
|
||||
IN(Action);
|
||||
IN(Price);
|
||||
IN(TraderID);
|
||||
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
|
||||
IN(ItemID);
|
||||
IN(Quantity);
|
||||
IN(action);
|
||||
IN(price);
|
||||
IN(trader_id);
|
||||
memcpy(emu->item_name, eq->item_name, sizeof(emu->item_name));
|
||||
IN(item_id);
|
||||
IN(quantity);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
+558
-246
@@ -398,51 +398,182 @@ namespace RoF2
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
char *Buffer = (char *)in->pBuffer;
|
||||
uint32 action = *(uint32 *) in->pBuffer;
|
||||
|
||||
uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
|
||||
switch (action) {
|
||||
case BazaarSearch: {
|
||||
LogTrading("(RoF2) BazaarSearch action <green>[{}]", action);
|
||||
std::vector<BazaarSearchResultsFromDB_Struct> results{};
|
||||
auto bsms = (BazaarSearchMessaging_Struct *) in->pBuffer;
|
||||
EQ::Util::MemoryStreamReader ss(
|
||||
reinterpret_cast<char *>(bsms->payload),
|
||||
in->size - sizeof(BazaarSearchMessaging_Struct)
|
||||
);
|
||||
cereal::BinaryInputArchive ar(ss);
|
||||
ar(results);
|
||||
|
||||
if (SubAction != BazaarSearchResults)
|
||||
{
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
return;
|
||||
auto name_size = 0;
|
||||
for (auto const &i: results) {
|
||||
name_size += i.item_name.length() + 1;
|
||||
}
|
||||
|
||||
auto p_size = 41 * results.size() + name_size + 14;
|
||||
auto buffer = std::make_unique<char[]>(p_size);
|
||||
auto bufptr = buffer.get();
|
||||
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 0);
|
||||
VARSTRUCT_ENCODE_TYPE(uint16, bufptr, results[0].trader_zone_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, results[0].trader_id);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, results.size());
|
||||
|
||||
for (auto i: results) {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.trader_id); //trader ID
|
||||
VARSTRUCT_ENCODE_STRING(bufptr, i.serial_number_RoF.c_str()); //serial
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.cost); //cost
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.stackable ? i.charges : i.count); //quantity
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.item_id); //ID
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.icon_id); //icon
|
||||
VARSTRUCT_ENCODE_STRING(bufptr, i.item_name.c_str()); //name
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, i.item_stat); //itemstat
|
||||
}
|
||||
|
||||
safe_delete(in->pBuffer);
|
||||
in->size = p_size;
|
||||
in->pBuffer = (uchar *) buffer.get();
|
||||
dest->QueuePacket(in);
|
||||
|
||||
break;
|
||||
}
|
||||
case BazaarInspect: {
|
||||
LogTrading("(RoF2) BazaarInspect action <green>[{}]", action);
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
break;
|
||||
}
|
||||
case WelcomeMessage: {
|
||||
auto buffer = std::make_unique<char[]>(sizeof(structs::BazaarWelcome_Struct));
|
||||
auto emu = (BazaarWelcome_Struct *) in->pBuffer;
|
||||
auto eq = (structs::BazaarWelcome_Struct *) buffer.get();
|
||||
|
||||
eq->action = structs::RoF2BazaarTraderBuyerActions::WelcomeMessage;
|
||||
eq->num_of_traders = emu->traders;
|
||||
eq->num_of_items = emu->items;
|
||||
|
||||
safe_delete(in->pBuffer);
|
||||
in->SetOpcode(OP_TraderShop);
|
||||
in->size = sizeof(structs::BazaarWelcome_Struct);
|
||||
in->pBuffer = (uchar *) buffer.get();
|
||||
|
||||
LogTrading("(RoF2) WelcomeMessage action <green>[{}]", action);
|
||||
dest->QueuePacket(in);
|
||||
|
||||
break;
|
||||
}
|
||||
case DeliveryCostUpdate: {
|
||||
auto data = (BazaarDeliveryCost_Struct *) in->pBuffer;
|
||||
LogTrading("(RoF2) Delivery costs updated: vouchers <green>[{}] parcel percentage <green>[{}]",
|
||||
data->voucher_delivery_cost,
|
||||
data->parcel_deliver_cost
|
||||
);
|
||||
data->action = 0;
|
||||
dest->FastQueuePacket(&in);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading("(RoF2) Unhandled action <red>[{}]", action);
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
ENCODE(OP_BecomeTrader)
|
||||
{
|
||||
EQApplicationPacket *inapp = *p;
|
||||
*p = nullptr;
|
||||
|
||||
BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer;
|
||||
unsigned char *__emu_buffer = inapp->pBuffer;
|
||||
auto in = (BecomeTrader_Struct *) __emu_buffer;
|
||||
|
||||
int EntryCount = in->size / sizeof(BazaarSearchResults_Struct);
|
||||
switch (in->action) {
|
||||
case TraderOff: {
|
||||
auto emu = (BecomeTrader_Struct *) __emu_buffer;
|
||||
|
||||
if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0)
|
||||
{
|
||||
LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct));
|
||||
delete in;
|
||||
return;
|
||||
auto outapp = new EQApplicationPacket(OP_BecomeTrader, sizeof(structs::BecomeTrader_Struct));
|
||||
auto eq = (structs::BecomeTrader_Struct *) outapp->pBuffer;
|
||||
|
||||
eq->action = TraderOff;
|
||||
eq->entity_id = emu->entity_id;
|
||||
|
||||
LogTrading(
|
||||
"(RoF2) TraderOff action <green>[{}] for entity_id <green>[{}]",
|
||||
eq->action,
|
||||
eq->entity_id
|
||||
);
|
||||
dest->FastQueuePacket(&outapp);
|
||||
break;
|
||||
}
|
||||
case TraderOn: {
|
||||
auto emu = (BecomeTrader_Struct *) __emu_buffer;
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_BecomeTrader, sizeof(structs::BecomeTrader_Struct));
|
||||
auto eq = (structs::BecomeTrader_Struct *) outapp->pBuffer;
|
||||
|
||||
eq->action = TraderOn;
|
||||
eq->entity_id = emu->entity_id;
|
||||
|
||||
LogTrading(
|
||||
"(RoF2) TraderOn action <green>[{}] for entity_id <green>[{}]",
|
||||
eq->action,
|
||||
eq->entity_id
|
||||
);
|
||||
dest->FastQueuePacket(&outapp);
|
||||
break;
|
||||
}
|
||||
case AddTraderToBazaarWindow: {
|
||||
auto emu = (BecomeTrader_Struct *) __emu_buffer;
|
||||
auto outapp = new EQApplicationPacket(OP_TraderShop, sizeof(BecomeTrader_Struct));
|
||||
auto eq = (BecomeTrader_Struct *) outapp->pBuffer;
|
||||
|
||||
eq->action = emu->action;
|
||||
eq->entity_id = emu->entity_id;
|
||||
eq->trader_id = emu->trader_id;
|
||||
eq->zone_id = emu->zone_id;
|
||||
strn0cpy(eq->trader_name, emu->trader_name, sizeof(eq->trader_name));
|
||||
|
||||
LogTrading(
|
||||
"(RoF2) AddTraderToBazaarWindow action <green>[{}] trader_id <green>[{}] entity_id <green>[{}] zone_id <green>[{}]",
|
||||
eq->action,
|
||||
eq->entity_id,
|
||||
eq->trader_id,
|
||||
eq->zone_id
|
||||
);
|
||||
dest->FastQueuePacket(&outapp);
|
||||
break;
|
||||
}
|
||||
case RemoveTraderFromBazaarWindow: {
|
||||
auto emu = (BecomeTrader_Struct *) __emu_buffer;
|
||||
auto outapp = new EQApplicationPacket(OP_TraderShop, sizeof(structs::BazaarWindowRemoveTrader_Struct));
|
||||
auto eq = (structs::BazaarWindowRemoveTrader_Struct *) outapp->pBuffer;
|
||||
|
||||
eq->action = emu->action;
|
||||
eq->trader_id = emu->trader_id;
|
||||
|
||||
LogTrading(
|
||||
"(RoF2) RemoveTraderFromBazaarWindow action <green>[{}] for entity_id <green>[{}]",
|
||||
eq->action,
|
||||
eq->trader_id
|
||||
);
|
||||
dest->FastQueuePacket(&outapp);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading(
|
||||
"(RoF2) Unhandled action <red>[{}]",
|
||||
in->action
|
||||
);
|
||||
dest->QueuePacket(inapp);
|
||||
}
|
||||
}
|
||||
|
||||
in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct);
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
|
||||
memset(in->pBuffer, 0, in->size);
|
||||
|
||||
structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer;
|
||||
|
||||
for (int i = 0; i < EntryCount; ++i, ++emu, ++eq)
|
||||
{
|
||||
OUT(Beginning.Action);
|
||||
OUT(SellerID);
|
||||
memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName));
|
||||
OUT(NumItems);
|
||||
OUT(ItemID);
|
||||
OUT(SerialNumber);
|
||||
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
||||
OUT(Cost);
|
||||
OUT(ItemStat);
|
||||
}
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
safe_delete(inapp);
|
||||
}
|
||||
|
||||
ENCODE(OP_BeginCast)
|
||||
@@ -2591,7 +2722,7 @@ namespace RoF2
|
||||
outapp->WriteUInt8(0); // Unknown
|
||||
}
|
||||
|
||||
outapp->WriteUInt32(0); // Unknown
|
||||
outapp->WriteUInt32(emu->char_id); // character_id
|
||||
|
||||
outapp->WriteUInt8(emu->leadAAActive);
|
||||
|
||||
@@ -3586,54 +3717,146 @@ namespace RoF2
|
||||
|
||||
ENCODE(OP_Trader)
|
||||
{
|
||||
if ((*p)->size == sizeof(ClickTrader_Struct))
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(ClickTrader_Struct);
|
||||
SETUP_DIRECT_ENCODE(ClickTrader_Struct, structs::ClickTrader_Struct);
|
||||
uint32 action = *(uint32 *) (*p)->pBuffer;
|
||||
|
||||
eq->Code = emu->Code;
|
||||
// Live actually has 200 items now, but 80 is the most our internal struct supports
|
||||
for (uint32 i = 0; i < 200; i++)
|
||||
{
|
||||
eq->items[i].Unknown18 = 0;
|
||||
if (i < 80) {
|
||||
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016" PRId64, emu->SerialNumber[i]);
|
||||
eq->ItemCost[i] = emu->ItemCost[i];
|
||||
}
|
||||
else {
|
||||
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", 0);
|
||||
eq->ItemCost[i] = 0;
|
||||
}
|
||||
switch (action) {
|
||||
case TraderOn: {
|
||||
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
|
||||
eq->action = structs::RoF2BazaarTraderBuyerActions::BeginTraderMode;
|
||||
OUT(entity_id);
|
||||
|
||||
LogTrading("(RoF2) TraderOn action <green>[{}] entity_id <green>[{}]", action, eq->entity_id);
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case TraderOff: {
|
||||
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
else if ((*p)->size == sizeof(Trader_ShowItems_Struct))
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
eq->action = structs::RoF2BazaarTraderBuyerActions::EndTraderMode;
|
||||
OUT(entity_id);
|
||||
|
||||
eq->Code = emu->Code;
|
||||
//strncpy(eq->SerialNumber, "0000000000000000", sizeof(eq->SerialNumber));
|
||||
//snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", 0);
|
||||
eq->TraderID = emu->TraderID;
|
||||
//eq->Stacksize = 0;
|
||||
//eq->Price = 0;
|
||||
LogTrading("(RoF2) TraderOff action <green>[{}] entity_id <green>[{}]", action, eq->entity_id);
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case ListTraderItems: {
|
||||
ENCODE_LENGTH_EXACT(Trader_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_Struct, structs::ClickTrader_Struct);
|
||||
LogTrading("(RoF2) action <green>[{}]", action);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
else if ((*p)->size == sizeof(TraderStatus_Struct))
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(TraderStatus_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderStatus_Struct, structs::TraderStatus_Struct);
|
||||
eq->action = structs::RoF2BazaarTraderBuyerActions::ListTraderItems;
|
||||
std::transform(
|
||||
std::begin(emu->items),
|
||||
std::end(emu->items),
|
||||
std::begin(eq->items),
|
||||
[&](const uint32 x) {
|
||||
return x;
|
||||
}
|
||||
);
|
||||
std::copy_n(
|
||||
std::begin(emu->item_cost),
|
||||
EQ::invtype::BAZAAR_SIZE,
|
||||
std::begin(eq->item_cost)
|
||||
);
|
||||
|
||||
eq->Code = emu->Code;
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case TraderAck2: {
|
||||
LogTrading("(RoF2) TraderAck2 action");
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
else if ((*p)->size == sizeof(TraderBuy_Struct))
|
||||
{
|
||||
ENCODE_FORWARD(OP_TraderBuy);
|
||||
dest->FastQueuePacket(&in);
|
||||
break;
|
||||
}
|
||||
case PriceUpdate: {
|
||||
SETUP_DIRECT_ENCODE(TraderPriceUpdate_Struct, structs::TraderPriceUpdate_Struct);
|
||||
switch (emu->SubAction) {
|
||||
case BazaarPriceChange_AddItem: {
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(
|
||||
OP_Trader,
|
||||
sizeof(structs::TraderStatus_Struct)
|
||||
);
|
||||
|
||||
auto data = (structs::TraderStatus_Struct *) outapp->pBuffer;
|
||||
data->action = emu->Action;
|
||||
data->sub_action = BazaarPriceChange_AddItem;
|
||||
LogTrading(
|
||||
"(RoF2) PriceUpdate action <green>[{}] AddItem subaction <yellow>[{}]",
|
||||
data->action,
|
||||
data->sub_action
|
||||
);
|
||||
|
||||
dest->QueuePacket(outapp.get());
|
||||
break;
|
||||
}
|
||||
case BazaarPriceChange_RemoveItem: {
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(
|
||||
OP_Trader,
|
||||
sizeof(structs::TraderStatus_Struct)
|
||||
);
|
||||
|
||||
auto data = (structs::TraderStatus_Struct *) outapp->pBuffer;
|
||||
data->action = emu->Action;
|
||||
data->sub_action = BazaarPriceChange_RemoveItem;
|
||||
LogTrading(
|
||||
"(RoF2) PriceUpdate action <green>[{}] RemoveItem subaction <yellow>[{}]",
|
||||
data->action,
|
||||
data->sub_action
|
||||
);
|
||||
|
||||
dest->QueuePacket(outapp.get());
|
||||
break;
|
||||
}
|
||||
case BazaarPriceChange_UpdatePrice: {
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(
|
||||
OP_Trader,
|
||||
sizeof(structs::TraderStatus_Struct)
|
||||
);
|
||||
|
||||
auto data = (structs::TraderStatus_Struct *) outapp->pBuffer;
|
||||
data->action = emu->Action;
|
||||
data->sub_action = BazaarPriceChange_UpdatePrice;
|
||||
LogTrading(
|
||||
"(RoF2) PriceUpdate action <green>[{}] UpdatePrice subaction <yellow>[{}]",
|
||||
data->action,
|
||||
data->sub_action
|
||||
);
|
||||
|
||||
dest->QueuePacket(outapp.get());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case BuyTraderItem: {
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
auto eq = (structs::TraderBuy_Struct *) in->pBuffer;
|
||||
LogTrading(
|
||||
"(RoF2) BuyTraderItem action <green>[{}] item_id <green>[{}] item_sn <green>[{}] buyer <green>[{}]",
|
||||
action,
|
||||
eq->item_id,
|
||||
eq->serial_number,
|
||||
eq->buyer_name
|
||||
);
|
||||
dest->FastQueuePacket(&in);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading("(RoF2) action <red>[{}]", action);
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
dest->FastQueuePacket(&in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3641,14 +3864,26 @@ namespace RoF2
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
|
||||
OUT(Action);
|
||||
OUT(Price);
|
||||
OUT(TraderID);
|
||||
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
||||
OUT(ItemID);
|
||||
OUT(Quantity);
|
||||
OUT(AlreadySold);
|
||||
LogTrading(
|
||||
"(RoF2) item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
emu->item_id,
|
||||
emu->price,
|
||||
emu->quantity,
|
||||
emu->trader_id
|
||||
);
|
||||
__packet->SetOpcode(OP_TraderShop);
|
||||
OUT(action);
|
||||
OUT(method);
|
||||
OUT(sub_action);
|
||||
OUT(trader_id);
|
||||
OUT(item_id);
|
||||
OUT(price);
|
||||
OUT(already_sold);
|
||||
OUT(quantity);
|
||||
OUT_str(buyer_name);
|
||||
OUT_str(seller_name);
|
||||
OUT_str(item_name);
|
||||
OUT_str(serial_number);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@@ -3657,71 +3892,74 @@ namespace RoF2
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(TraderDelItem_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderDelItem_Struct, structs::TraderDelItem_Struct);
|
||||
LogTrading(
|
||||
"(RoF2) trader_id <green>[{}] item_id <green>[{}]",
|
||||
emu->trader_id,
|
||||
emu->item_id
|
||||
);
|
||||
|
||||
OUT(TraderID);
|
||||
snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID);
|
||||
LogTrading("ENCODE(OP_TraderDelItem): TraderID [{}], SerialNumber: [{}]", emu->TraderID, emu->ItemID);
|
||||
eq->TraderID = emu->trader_id;
|
||||
auto serial = fmt::format("{:016}\n", emu->item_id);
|
||||
strn0cpy(eq->SerialNumber, serial.c_str(), sizeof(eq->SerialNumber));
|
||||
LogTrading("(RoF2) TraderID <green>[{}], SerialNumber: <green>[{}]", emu->trader_id, emu->item_id);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_TraderShop)
|
||||
{
|
||||
uint32 psize = (*p)->size;
|
||||
if (psize == sizeof(TraderClick_Struct))
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(TraderClick_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderClick_Struct, structs::TraderClick_Struct);
|
||||
auto action = *(uint32 *) (*p)->pBuffer;
|
||||
|
||||
eq->Code = 28; // Seen on Live
|
||||
OUT(TraderID);
|
||||
OUT(Approval);
|
||||
switch (action) {
|
||||
case ClickTrader: {
|
||||
ENCODE_LENGTH_EXACT(TraderClick_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderClick_Struct, structs::TraderClick_Struct);
|
||||
LogTrading(
|
||||
"(RoF2) ClickTrader action <green>[{}] trader_id <green>[{}]",
|
||||
action,
|
||||
emu->TraderID
|
||||
);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
else if (psize == sizeof(BazaarWelcome_Struct))
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(BazaarWelcome_Struct);
|
||||
SETUP_DIRECT_ENCODE(BazaarWelcome_Struct, structs::BazaarWelcome_Struct);
|
||||
eq->action = structs::RoF2BazaarTraderBuyerActions::ClickTrader; // Seen on Live
|
||||
eq->trader_id = emu->TraderID;
|
||||
eq->unknown_008 = emu->Approval;
|
||||
|
||||
eq->Code = emu->Beginning.Action;
|
||||
eq->EntityID = emu->Unknown012;
|
||||
OUT(Traders);
|
||||
OUT(Items);
|
||||
eq->Traders2 = emu->Traders;
|
||||
eq->Items2 = emu->Items;
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BazaarTraderBuyerActions::BuyTraderItem: {
|
||||
ENCODE_LENGTH_EXACT(structs::TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"(RoF2) item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
eq->item_id,
|
||||
eq->price,
|
||||
eq->quantity,
|
||||
eq->trader_id
|
||||
);
|
||||
|
||||
LogTrading("ENCODE(OP_TraderShop): BazaarWelcome_Struct Code [{}], Traders [{}], Items [{}]",
|
||||
eq->Code, eq->Traders, eq->Items);
|
||||
OUT(action);
|
||||
OUT(method);
|
||||
OUT(trader_id);
|
||||
OUT(item_id);
|
||||
OUT(price);
|
||||
OUT(already_sold);
|
||||
OUT(quantity);
|
||||
OUT_str(buyer_name);
|
||||
OUT_str(seller_name);
|
||||
OUT_str(item_name);
|
||||
OUT_str(serial_number);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
else if (psize == sizeof(TraderBuy_Struct))
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading("(RoF2) Unhandled action <red>[{}]", action);
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
OUT(Action);
|
||||
OUT(TraderID);
|
||||
|
||||
//memcpy(eq->BuyerName, emu->BuyerName, sizeof(eq->BuyerName));
|
||||
//memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName));
|
||||
|
||||
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
||||
OUT(ItemID);
|
||||
OUT(AlreadySold);
|
||||
OUT(Price);
|
||||
OUT(Quantity);
|
||||
snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID);
|
||||
|
||||
LogTrading("ENCODE(OP_TraderShop): Buy Action [{}], Price [{}], Trader [{}], ItemID [{}], Quantity [{}], ItemName, [{}]",
|
||||
eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, emu->ItemName);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
else
|
||||
{
|
||||
LogTrading("ENCODE(OP_TraderShop): Encode Size Unknown ([{}])", psize);
|
||||
dest->FastQueuePacket(&in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4067,20 +4305,20 @@ namespace RoF2
|
||||
|
||||
structs::Spawn_Struct_Bitfields *Bitfields = (structs::Spawn_Struct_Bitfields*)Buffer;
|
||||
|
||||
Bitfields->gender = emu->gender;
|
||||
Bitfields->ispet = emu->is_pet;
|
||||
Bitfields->afk = emu->afk;
|
||||
Bitfields->anon = emu->anon;
|
||||
Bitfields->gm = emu->gm;
|
||||
Bitfields->sneak = 0;
|
||||
Bitfields->lfg = emu->lfg;
|
||||
Bitfields->invis = emu->invis;
|
||||
Bitfields->linkdead = 0;
|
||||
Bitfields->showhelm = emu->showhelm;
|
||||
Bitfields->trader = 0;
|
||||
Bitfields->targetable = 1;
|
||||
Bitfields->gender = emu->gender;
|
||||
Bitfields->ispet = emu->is_pet;
|
||||
Bitfields->afk = emu->afk;
|
||||
Bitfields->anon = emu->anon;
|
||||
Bitfields->gm = emu->gm;
|
||||
Bitfields->sneak = 0;
|
||||
Bitfields->lfg = emu->lfg;
|
||||
Bitfields->invis = emu->invis;
|
||||
Bitfields->linkdead = 0;
|
||||
Bitfields->showhelm = emu->showhelm;
|
||||
Bitfields->trader = emu->trader ? 1 : 0;
|
||||
Bitfields->targetable = 1;
|
||||
Bitfields->targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0;
|
||||
Bitfields->showname = ShowName;
|
||||
Bitfields->showname = ShowName;
|
||||
|
||||
if (emu->DestructibleObject)
|
||||
{
|
||||
@@ -4431,6 +4669,7 @@ namespace RoF2
|
||||
char *Buffer = (char *)__packet->pBuffer;
|
||||
|
||||
uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
|
||||
LogTrading("(RoF2) action <green>[{}]", SubAction);
|
||||
|
||||
if ((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct)))
|
||||
return;
|
||||
@@ -4440,6 +4679,7 @@ namespace RoF2
|
||||
IN(Beginning.Action);
|
||||
memcpy(emu->Name, eq->Name, sizeof(emu->Name));
|
||||
IN(SerialNumber);
|
||||
LogTrading("(RoF2) action <green>[{}] serial_number <green>[{}]", eq->Beginning.Action, eq->SerialNumber);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
@@ -5331,40 +5571,61 @@ namespace RoF2
|
||||
|
||||
DECODE(OP_Trader)
|
||||
{
|
||||
uint32 psize = __packet->size;
|
||||
if (psize == sizeof(structs::ClickTrader_Struct))
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::ClickTrader_Struct);
|
||||
SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::ClickTrader_Struct);
|
||||
auto action = *(uint32 *)__packet->pBuffer;
|
||||
|
||||
emu->Code = eq->Code;
|
||||
// Live actually has 200 items now, but 80 is the most our internal struct supports
|
||||
for (uint32 i = 0; i < 80; i++)
|
||||
{
|
||||
emu->SerialNumber[i] = 0; // eq->SerialNumber[i];
|
||||
emu->ItemCost[i] = eq->ItemCost[i];
|
||||
switch (action) {
|
||||
case structs::RoF2BazaarTraderBuyerActions::BeginTraderMode: {
|
||||
DECODE_LENGTH_EXACT(structs::BeginTrader_Struct);
|
||||
SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::BeginTrader_Struct);
|
||||
LogTrading("(RoF2) BeginTraderMode action <green>[{}]", action);
|
||||
|
||||
emu->action = TraderOn;
|
||||
std::copy_n(eq->item_cost, RoF2::invtype::BAZAAR_SIZE, emu->item_cost);
|
||||
std::transform(
|
||||
std::begin(eq->items),
|
||||
std::end(eq->items),
|
||||
std::begin(emu->serial_number),
|
||||
[&](const structs::TraderItemSerial_Struct x) {
|
||||
return Strings::ToUnsignedBigInt(x.serial_number,0);
|
||||
}
|
||||
);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BazaarTraderBuyerActions::EndTraderMode: {
|
||||
DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
LogTrading("(RoF2) EndTraderMode action <green>[{}]", action);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
else if (psize == sizeof(structs::Trader_ShowItems_Struct))
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
emu->action = TraderOff;
|
||||
emu->entity_id = eq->entity_id;
|
||||
|
||||
emu->Code = eq->Code;
|
||||
emu->TraderID = eq->TraderID;
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BazaarTraderBuyerActions::ListTraderItems: {
|
||||
LogTrading("(RoF2) ListTraderItems action <green>[{}]", action);
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BazaarTraderBuyerActions::PriceUpdate: {
|
||||
DECODE_LENGTH_EXACT(structs::TraderPriceUpdate_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderPriceUpdate_Struct, structs::TraderPriceUpdate_Struct);
|
||||
LogTrading("(RoF2) PriceUpdate action <green>[{}]", action);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
else if (psize == sizeof(structs::TraderStatus_Struct))
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::TraderStatus_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderStatus_Struct, structs::TraderStatus_Struct);
|
||||
emu->Action = PriceUpdate;
|
||||
emu->SerialNumber = Strings::ToUnsignedBigInt(eq->serial_number, 0);
|
||||
if (emu->SerialNumber == 0) {
|
||||
LogTrading("(RoF2) Price change with invalid serial number <red>[{}]", eq->serial_number);
|
||||
}
|
||||
emu->NewPrice = eq->new_price;
|
||||
|
||||
emu->Code = eq->Code; // 11 = Start Trader, 2 = End Trader, 22 = ? - Guessing
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading("(RoF2) Unhandled action <red>[{}]", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5372,73 +5633,136 @@ namespace RoF2
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"(RoF2) item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
eq->item_id,
|
||||
eq->price,
|
||||
eq->quantity,
|
||||
eq->trader_id
|
||||
);
|
||||
|
||||
IN(Action);
|
||||
IN(Price);
|
||||
IN(TraderID);
|
||||
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
|
||||
IN(ItemID);
|
||||
IN(Quantity);
|
||||
IN(action);
|
||||
IN(price);
|
||||
IN(trader_id);
|
||||
memcpy(emu->item_name, eq->item_name, sizeof(emu->item_name));
|
||||
IN(item_id);
|
||||
IN(quantity);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_TraderShop)
|
||||
{
|
||||
uint32 psize = __packet->size;
|
||||
if (psize == sizeof(structs::TraderClick_Struct))
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::TraderClick_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderClick_Struct, structs::TraderClick_Struct);
|
||||
uint32 action = *(uint32 *)__packet->pBuffer;
|
||||
|
||||
IN(Code);
|
||||
IN(TraderID);
|
||||
IN(Approval);
|
||||
LogTrading("DECODE(OP_TraderShop): TraderClick_Struct Code [{}], TraderID [{}], Approval [{}]",
|
||||
eq->Code, eq->TraderID, eq->Approval);
|
||||
switch (action) {
|
||||
case structs::RoF2BazaarTraderBuyerActions::BazaarSearch: {
|
||||
DECODE_LENGTH_EXACT(structs::BazaarSearch_Struct);
|
||||
SETUP_DIRECT_DECODE(BazaarSearchCriteria_Struct, structs::BazaarSearch_Struct);
|
||||
LogTrading(
|
||||
"(RoF2) BazaarSearch action <green>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
else if (psize == sizeof(structs::BazaarWelcome_Struct))
|
||||
{
|
||||
// Don't think this size gets used in RoF+ - Leaving for now...
|
||||
DECODE_LENGTH_EXACT(structs::BazaarWelcome_Struct);
|
||||
SETUP_DIRECT_DECODE(BazaarWelcome_Struct, structs::BazaarWelcome_Struct);
|
||||
__packet->SetOpcode(OP_BazaarSearch);
|
||||
emu->action = BazaarSearch;
|
||||
emu->type = eq->type == UINT32_MAX ? UINT8_MAX : eq->type;
|
||||
IN(item_stat);
|
||||
IN(max_cost);
|
||||
IN(min_cost);
|
||||
IN(max_level);
|
||||
IN(min_level);
|
||||
IN(race);
|
||||
IN(slot);
|
||||
IN(trader_id);
|
||||
IN(_class);
|
||||
IN(prestige);
|
||||
IN(search_scope);
|
||||
IN(max_results);
|
||||
IN(augment);
|
||||
IN_str(item_name);
|
||||
|
||||
emu->Beginning.Action = eq->Code;
|
||||
IN(Traders);
|
||||
IN(Items);
|
||||
LogTrading("DECODE(OP_TraderShop): BazaarWelcome_Struct Code [{}], Traders [{}], Items [{}]",
|
||||
eq->Code, eq->Traders, eq->Items);
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BazaarTraderBuyerActions::ClickTrader: {
|
||||
DECODE_LENGTH_EXACT(structs::TraderClick_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderClick_Struct, structs::TraderClick_Struct);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
else if (psize == sizeof(structs::TraderBuy_Struct))
|
||||
{
|
||||
emu->Code = ClickTrader;
|
||||
emu->TraderID = eq->trader_id;
|
||||
emu->Approval = eq->unknown_008;
|
||||
LogTrading("(RoF2) ClickTrader action <green>[{}], trader_id <green>[{}], approval <green>[{}]",
|
||||
eq->action,
|
||||
eq->trader_id,
|
||||
eq->unknown_008
|
||||
);
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BazaarTraderBuyerActions::BazaarInspect: {
|
||||
DECODE_LENGTH_EXACT(structs::BazaarInspect_Struct);
|
||||
SETUP_DIRECT_DECODE(BazaarInspect_Struct, structs::BazaarInspect_Struct);
|
||||
|
||||
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
__packet->SetOpcode(OP_BazaarSearch);
|
||||
IN(item_id);
|
||||
IN(trader_id);
|
||||
emu->action = BazaarInspect;
|
||||
emu->serial_number = Strings::ToUnsignedInt(eq->serial_number, 0);
|
||||
if (emu->serial_number == 0) {
|
||||
LogTrading(
|
||||
"(RoF2) trader_id = <green>[{}] requested a BazaarInspect with an invalid serial number of <red>[{}]",
|
||||
eq->trader_id,
|
||||
eq->serial_number
|
||||
);
|
||||
FINISH_DIRECT_DECODE();
|
||||
return;
|
||||
}
|
||||
|
||||
IN(Action);
|
||||
IN(Price);
|
||||
IN(TraderID);
|
||||
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
|
||||
IN(ItemID);
|
||||
IN(Quantity);
|
||||
LogTrading("DECODE(OP_TraderShop): TraderBuy_Struct (Unknowns) Unknown004 [{}], Unknown008 [{}], Unknown012 [{}], Unknown076 [{}], Unknown276 [{}]",
|
||||
eq->Unknown004, eq->Unknown008, eq->Unknown012, eq->Unknown076, eq->Unknown276);
|
||||
LogTrading("DECODE(OP_TraderShop): TraderBuy_Struct Buy Action [{}], Price [{}], Trader [{}], ItemID [{}], Quantity [{}], ItemName, [{}]",
|
||||
eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, eq->ItemName);
|
||||
LogTrading("(RoF2) BazaarInspect action <green>[{}] item_id <green>[{}] serial_number <green>[{}]",
|
||||
action,
|
||||
eq->item_id,
|
||||
eq->serial_number
|
||||
);
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BazaarTraderBuyerActions::WelcomeMessage: {
|
||||
__packet->SetOpcode(OP_BazaarSearch);
|
||||
LogTrading("(RoF2) WelcomeMessage action <green>[{}]", action);
|
||||
break;
|
||||
}
|
||||
case structs::RoF2BazaarTraderBuyerActions::BuyTraderItem: {
|
||||
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"(RoF2) item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
eq->item_id,
|
||||
eq->price,
|
||||
eq->quantity,
|
||||
eq->trader_id
|
||||
);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
else if (psize == 4)
|
||||
{
|
||||
LogTrading("DECODE(OP_TraderShop): Forwarding packet as-is with size 4");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogTrading("DECODE(OP_TraderShop): Decode Size Unknown ([{}])", psize);
|
||||
__packet->SetOpcode(OP_TraderBuy);
|
||||
IN(action);
|
||||
IN(method);
|
||||
IN(trader_id);
|
||||
IN(item_id);
|
||||
IN(price);
|
||||
IN(already_sold);
|
||||
IN(quantity);
|
||||
IN_str(buyer_name);
|
||||
IN_str(seller_name);
|
||||
IN_str(item_name);
|
||||
IN_str(serial_number);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading("(RoF2) Unhandled action <red>[{}]", action);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5541,19 +5865,7 @@ namespace RoF2
|
||||
RoF2::structs::ItemSerializationHeader hdr;
|
||||
|
||||
//sprintf(hdr.unknown000, "06e0002Y1W00");
|
||||
|
||||
snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%016d", item->ID);
|
||||
if (packet_type == ItemPacketParcel) {
|
||||
strn0cpy(
|
||||
hdr.unknown000,
|
||||
fmt::format(
|
||||
"{:03}PAR{:010}\0",
|
||||
inst->GetMerchantSlot(),
|
||||
item->ID
|
||||
).c_str(),
|
||||
sizeof(hdr.unknown000)
|
||||
);
|
||||
}
|
||||
strn0cpy(hdr.unknown000, fmt::format("{:016}\0", inst->GetSerialNumber()).c_str(),sizeof(hdr.unknown000));
|
||||
|
||||
hdr.stacksize =
|
||||
item->ID == PARCEL_MONEY_ITEM_ID ? inst->GetPrice() : (inst->IsStackable() ? ((inst->GetCharges() > 1000)
|
||||
|
||||
@@ -43,6 +43,7 @@ E(OP_ApplyPoison)
|
||||
E(OP_AugmentInfo)
|
||||
E(OP_Barter)
|
||||
E(OP_BazaarSearch)
|
||||
E(OP_BecomeTrader)
|
||||
E(OP_BeginCast)
|
||||
E(OP_BlockedBuffs)
|
||||
E(OP_Buff)
|
||||
|
||||
+142
-112
@@ -3106,28 +3106,43 @@ 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
|
||||
};
|
||||
|
||||
enum {
|
||||
BazaarTrader_StartTraderMode = 1,
|
||||
BazaarTrader_EndTraderMode = 2,
|
||||
BazaarTrader_UpdatePrice = 3,
|
||||
BazaarTrader_EndTransaction = 4,
|
||||
BazaarSearchResults = 7,
|
||||
BazaarWelcome = 9,
|
||||
BazaarBuyItem = 10,
|
||||
BazaarTrader_ShowItems = 11,
|
||||
BazaarSearchDone = 12,
|
||||
BazaarTrader_StartTraderMode = 1,
|
||||
BazaarTrader_EndTraderMode = 2,
|
||||
BazaarTrader_UpdatePrice = 3,
|
||||
BazaarTrader_EndTransaction = 4,
|
||||
BazaarSearchResults = 7,
|
||||
BazaarWelcome = 9,
|
||||
BazaarBuyItem = 10,
|
||||
BazaarTrader_ShowItems = 11,
|
||||
BazaarSearchDone = 12,
|
||||
BazaarTrader_CustomerBrowsing = 13,
|
||||
BazaarInspectItem = 18,
|
||||
BazaarSearchDone2 = 19,
|
||||
BazaarInspectItem = 18,
|
||||
BazaarSearchDone2 = 19,
|
||||
BazaarTrader_StartTraderMode2 = 22
|
||||
};
|
||||
|
||||
enum {
|
||||
BazaarPriceChange_Fail = 0,
|
||||
BazaarPriceChange_Fail = 0,
|
||||
BazaarPriceChange_UpdatePrice = 1,
|
||||
BazaarPriceChange_RemoveItem = 2,
|
||||
BazaarPriceChange_AddItem = 3
|
||||
BazaarPriceChange_RemoveItem = 2,
|
||||
BazaarPriceChange_AddItem = 3
|
||||
};
|
||||
|
||||
struct BazaarWindowStart_Struct {
|
||||
@@ -3136,34 +3151,51 @@ struct BazaarWindowStart_Struct {
|
||||
uint16 Unknown002;
|
||||
};
|
||||
|
||||
struct BazaarDeliveryCost_Struct {
|
||||
uint32 action;
|
||||
uint32 voucher_delivery_cost;
|
||||
float parcel_deliver_cost; //percentage of item cost
|
||||
uint32 unknown_010;
|
||||
};
|
||||
|
||||
struct BazaarWelcome_Struct {
|
||||
uint32 Code;
|
||||
uint32 EntityID;
|
||||
uint32 Traders;
|
||||
uint32 Items;
|
||||
uint32 Traders2;
|
||||
uint32 Items2;
|
||||
uint32 action;
|
||||
uint32 unknown_004;
|
||||
uint32 num_of_traders;
|
||||
uint32 num_of_items;
|
||||
};
|
||||
|
||||
struct BazaarSearch_Struct {
|
||||
BazaarWindowStart_Struct Beginning;
|
||||
uint32 TraderID;
|
||||
uint32 Class_;
|
||||
uint32 Race;
|
||||
uint32 ItemStat;
|
||||
uint32 Slot;
|
||||
uint32 Type;
|
||||
char Name[64];
|
||||
uint32 MinPrice;
|
||||
uint32 MaxPrice;
|
||||
uint32 Minlevel;
|
||||
uint32 MaxLlevel;
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint8 search_scope;//1 all traders 0 local traders
|
||||
/*005*/ uint8 unknown_005{0};
|
||||
/*006*/ uint16 unknown_006{0};
|
||||
/*008*/ uint32 unknown_008{0};
|
||||
/*012*/ uint32 unknown_012{0};
|
||||
/*016*/ uint32 trader_id;
|
||||
/*020*/ uint32 _class;
|
||||
/*024*/ uint32 race;
|
||||
/*028*/ uint32 item_stat;
|
||||
/*032*/ uint32 slot;
|
||||
/*036*/ uint32 type;
|
||||
/*040*/ char item_name[64];
|
||||
/*104*/ uint32 min_cost;
|
||||
/*108*/ uint32 max_cost;
|
||||
/*112*/ uint32 min_level;
|
||||
/*116*/ uint32 max_level;
|
||||
/*120*/ uint32 max_results;
|
||||
/*124*/ uint32 prestige;
|
||||
/*128*/ uint32 augment;
|
||||
};
|
||||
struct BazaarInspect_Struct{
|
||||
uint32 ItemID;
|
||||
uint32 Unknown004;
|
||||
char Name[64];
|
||||
|
||||
struct BazaarInspect_Struct {
|
||||
uint32 action;
|
||||
uint32 unknown_004;
|
||||
uint32 trader_id;
|
||||
char serial_number[17];
|
||||
char unknown_029[3];
|
||||
uint32 item_id;
|
||||
uint32 unknown_036;
|
||||
};
|
||||
|
||||
struct NewBazaarInspect_Struct {
|
||||
@@ -3184,18 +3216,23 @@ struct BazaarReturnDone_Struct{
|
||||
uint32 Unknown016;
|
||||
};
|
||||
|
||||
struct BazaarSearchResults_Struct {
|
||||
/*000*/ BazaarWindowStart_Struct Beginning;
|
||||
/*004*/ uint32 SellerID;
|
||||
/*008*/ char SellerName[64];
|
||||
/*072*/ uint32 NumItems;
|
||||
/*076*/ uint32 ItemID;
|
||||
/*080*/ uint32 SerialNumber;
|
||||
/*084*/ uint32 Unknown084;
|
||||
/*088*/ char ItemName[64];
|
||||
/*152*/ uint32 Cost;
|
||||
/*156*/ uint32 ItemStat;
|
||||
/*160*/
|
||||
struct BazaarSearchDetails_Struct { //24+name+17
|
||||
/*014*/ uint32 trader_id;
|
||||
/*018*/ char serial_num[17];
|
||||
/*022*/ uint32 cost;
|
||||
/*026*/ uint32 quanity;
|
||||
/*030*/ uint32 id;
|
||||
/*034*/ uint32 icon;
|
||||
/*038*/ char name[1];
|
||||
/*039*/ uint32 stat;
|
||||
};
|
||||
|
||||
struct BazaarSearchResults_Struct { //14
|
||||
/*000*/ uint32 unknown000;
|
||||
/*004*/ uint16 zone_id;
|
||||
/*006*/ uint32 entity_id;
|
||||
/*010*/ uint32 num_items;
|
||||
/*014*/ BazaarSearchDetails_Struct items[];
|
||||
};
|
||||
|
||||
struct ServerSideFilters_Struct {
|
||||
@@ -3398,21 +3435,26 @@ struct WhoAllPlayerPart4 {
|
||||
};
|
||||
|
||||
struct TraderItemSerial_Struct {
|
||||
char SerialNumber[17];
|
||||
uint8 Unknown18;
|
||||
char serial_number[17];
|
||||
uint8 unknown_018;
|
||||
|
||||
void operator=(uint32 a) {
|
||||
auto _tmp = fmt::format("{:016}", a);
|
||||
strn0cpy(this->serial_number, _tmp.c_str(), sizeof(this->serial_number));
|
||||
}
|
||||
};
|
||||
|
||||
struct Trader_Struct {
|
||||
/*0000*/ uint32 Code;
|
||||
/*0004*/ TraderItemSerial_Struct items[200];
|
||||
/*3604*/ uint32 ItemCost[200];
|
||||
struct BeginTrader_Struct {
|
||||
/*0000*/ uint32 action;
|
||||
/*0004*/ TraderItemSerial_Struct items[200];
|
||||
/*3604*/ uint32 item_cost[200];
|
||||
/*4404*/
|
||||
};
|
||||
|
||||
struct ClickTrader_Struct {
|
||||
/*0000*/ uint32 Code;
|
||||
/*0004*/ TraderItemSerial_Struct items[200];
|
||||
/*3604*/ uint32 ItemCost[200];
|
||||
/*0000*/ uint32 action;
|
||||
/*0004*/ TraderItemSerial_Struct items[200];
|
||||
/*3604*/ uint32 item_cost[200];
|
||||
/*4404*/
|
||||
};
|
||||
|
||||
@@ -3421,67 +3463,55 @@ struct GetItems_Struct {
|
||||
};
|
||||
|
||||
struct BecomeTrader_Struct {
|
||||
uint32 id;
|
||||
uint32 code;
|
||||
uint32 entity_id;
|
||||
uint32 action;
|
||||
};
|
||||
|
||||
struct BazaarWindowRemoveTrader_Struct {
|
||||
uint32 action;
|
||||
uint32 trader_id;
|
||||
};
|
||||
|
||||
struct TraderPriceUpdate_Struct {
|
||||
uint32 action;
|
||||
char serial_number[17];
|
||||
char unknown_021[3];
|
||||
uint32 unknown_024;
|
||||
uint32 new_price;
|
||||
};
|
||||
|
||||
struct Trader_ShowItems_Struct {
|
||||
/*000*/ uint32 Code;
|
||||
/*004*/ uint16 TraderID;
|
||||
/*008*/ uint32 Unknown08;
|
||||
/*012*/
|
||||
};
|
||||
|
||||
struct Trader_ShowItems_Struct_WIP {
|
||||
/*000*/ uint32 Code;
|
||||
/*004*/ char SerialNumber[17];
|
||||
/*021*/ uint8 Unknown21;
|
||||
/*022*/ uint16 TraderID;
|
||||
/*026*/ uint32 Stacksize;
|
||||
/*030*/ uint32 Price;
|
||||
/*032*/
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 entity_id;
|
||||
/*008*/ uint32 unknown_008;
|
||||
/*012*/
|
||||
};
|
||||
|
||||
struct TraderStatus_Struct {
|
||||
/*000*/ uint32 Code;
|
||||
/*004*/ uint32 Uknown04;
|
||||
/*008*/ uint32 Uknown08;
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 sub_action;
|
||||
/*008*/ uint32 uknown_008;
|
||||
/*012*/
|
||||
};
|
||||
|
||||
struct TraderBuy_Struct {
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ uint32 Unknown004;
|
||||
/*008*/ uint32 Unknown008;
|
||||
/*012*/ uint32 Unknown012;
|
||||
/*016*/ uint32 TraderID;
|
||||
/*020*/ char BuyerName[64];
|
||||
/*084*/ char SellerName[64];
|
||||
/*148*/ char Unknown148[32];
|
||||
/*180*/ char ItemName[64];
|
||||
/*244*/ char SerialNumber[16];
|
||||
/*260*/ uint32 Unknown076;
|
||||
/*264*/ uint32 ItemID;
|
||||
/*268*/ uint32 Price;
|
||||
/*272*/ uint32 AlreadySold;
|
||||
/*276*/ uint32 Unknown276;
|
||||
/*280*/ uint32 Quantity;
|
||||
/*284*/
|
||||
};
|
||||
|
||||
struct TraderBuy_Struct_OLD {
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ uint32 Unknown004;
|
||||
/*008*/ uint32 Price;
|
||||
/*012*/ uint32 Unknown008; // Probably high order bits of a 64 bit price.
|
||||
/*016*/ uint32 TraderID;
|
||||
/*020*/ char ItemName[64];
|
||||
/*084*/ uint32 Unknown076;
|
||||
/*088*/ uint32 ItemID;
|
||||
/*092*/ uint32 AlreadySold;
|
||||
/*096*/ uint32 Quantity;
|
||||
/*100*/ uint32 Unknown092;
|
||||
/*104*/
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 method;
|
||||
/*008*/ uint32 sub_action;
|
||||
/*012*/ uint32 unknown_012;
|
||||
/*016*/ uint32 trader_id;
|
||||
/*020*/ char buyer_name[64];
|
||||
/*084*/ char seller_name[64];
|
||||
/*148*/ char unknown_148[32];
|
||||
/*180*/ char item_name[64];
|
||||
/*244*/ char serial_number[17];
|
||||
/*261*/ char unknown_261[3];
|
||||
/*264*/ uint32 item_id;
|
||||
/*268*/ uint32 price;
|
||||
/*272*/ uint32 already_sold;
|
||||
/*276*/ uint32 unknown_276;
|
||||
/*280*/ uint32 quantity;
|
||||
/*284*/
|
||||
};
|
||||
|
||||
struct TraderItemUpdate_Struct{
|
||||
@@ -3502,15 +3532,15 @@ struct MoneyUpdate_Struct{
|
||||
struct TraderDelItem_Struct{
|
||||
/*000*/ uint32 Unknown000;
|
||||
/*004*/ uint32 TraderID;
|
||||
/*008*/ char SerialNumber[16];
|
||||
/*008*/ char SerialNumber[17];
|
||||
/*024*/ uint32 Unknown012;
|
||||
/*028*/
|
||||
};
|
||||
|
||||
struct TraderClick_Struct{
|
||||
/*000*/ uint32 Code;
|
||||
/*004*/ uint32 TraderID;
|
||||
/*008*/ uint32 Approval;
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 trader_id;
|
||||
/*008*/ uint32 unknown_008;
|
||||
/*012*/
|
||||
};
|
||||
|
||||
|
||||
@@ -3376,17 +3376,17 @@ struct TraderStatus_Struct {
|
||||
};
|
||||
|
||||
struct TraderBuy_Struct {
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ uint32 Unknown004;
|
||||
/*008*/ uint32 Price;
|
||||
/*012*/ uint32 Unknown008; // Probably high order bits of a 64 bit price.
|
||||
/*016*/ uint32 TraderID;
|
||||
/*020*/ char ItemName[64];
|
||||
/*084*/ uint32 Unknown076;
|
||||
/*088*/ uint32 ItemID;
|
||||
/*092*/ uint32 AlreadySold;
|
||||
/*096*/ uint32 Quantity;
|
||||
/*100*/ uint32 Unknown092;
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 unknown_004;
|
||||
/*008*/ uint32 price;
|
||||
/*012*/ uint32 unknown_008; // Probably high order bits of a 64 bit price.
|
||||
/*016*/ uint32 trader_id;
|
||||
/*020*/ char item_name[64];
|
||||
/*084*/ uint32 unknown_076;
|
||||
/*088*/ uint32 item_id;
|
||||
/*092*/ uint32 already_sold;
|
||||
/*096*/ uint32 quantity;
|
||||
/*100*/ uint32 unknown_092;
|
||||
/*104*/
|
||||
};
|
||||
|
||||
|
||||
+13
-13
@@ -2286,13 +2286,13 @@ namespace SoD
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
|
||||
OUT(Action);
|
||||
OUT(Price);
|
||||
OUT(TraderID);
|
||||
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
||||
OUT(ItemID);
|
||||
OUT(Quantity);
|
||||
OUT(AlreadySold);
|
||||
OUT(action);
|
||||
OUT(price);
|
||||
OUT(trader_id);
|
||||
memcpy(eq->item_name, emu->item_name, sizeof(eq->item_name));
|
||||
OUT(item_id);
|
||||
OUT(quantity);
|
||||
OUT(already_sold);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@@ -3517,12 +3517,12 @@ namespace SoD
|
||||
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
MEMSET_IN(TraderBuy_Struct);
|
||||
|
||||
IN(Action);
|
||||
IN(Price);
|
||||
IN(TraderID);
|
||||
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
|
||||
IN(ItemID);
|
||||
IN(Quantity);
|
||||
IN(action);
|
||||
IN(price);
|
||||
IN(trader_id);
|
||||
memcpy(emu->item_name, eq->item_name, sizeof(emu->item_name));
|
||||
IN(item_id);
|
||||
IN(quantity);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
@@ -2879,20 +2879,21 @@ struct Trader_ShowItems_Struct{
|
||||
};
|
||||
|
||||
struct TraderBuy_Struct {
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ uint32 Unknown004;
|
||||
/*008*/ uint32 Price;
|
||||
/*012*/ uint32 Unknown008; // Probably high order bits of a 64 bit price.
|
||||
/*016*/ uint32 TraderID;
|
||||
/*020*/ char ItemName[64];
|
||||
/*084*/ uint32 Unknown076;
|
||||
/*088*/ uint32 ItemID;
|
||||
/*092*/ uint32 AlreadySold;
|
||||
/*096*/ uint32 Quantity;
|
||||
/*100*/ uint32 Unknown092;
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 unknown_004;
|
||||
/*008*/ uint32 price;
|
||||
/*012*/ uint32 unknown_008; // Probably high order bits of a 64 bit price.
|
||||
/*016*/ uint32 trader_id;
|
||||
/*020*/ char item_name[64];
|
||||
/*084*/ uint32 unknown_076;
|
||||
/*088*/ uint32 item_id;
|
||||
/*092*/ uint32 already_sold;
|
||||
/*096*/ uint32 quantity;
|
||||
/*100*/ uint32 unknown_092;
|
||||
/*104*/
|
||||
};
|
||||
|
||||
|
||||
struct TraderItemUpdate_Struct{
|
||||
uint32 unknown0;
|
||||
uint32 traderid;
|
||||
|
||||
+15
-15
@@ -270,8 +270,8 @@ namespace SoF
|
||||
ENCODE_LENGTH_EXACT(BecomeTrader_Struct);
|
||||
SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct);
|
||||
|
||||
OUT(ID);
|
||||
OUT(Code);
|
||||
OUT(trader_id);
|
||||
OUT(action);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@@ -1902,13 +1902,13 @@ namespace SoF
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
|
||||
OUT(Action);
|
||||
OUT(Price);
|
||||
OUT(TraderID);
|
||||
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
||||
OUT(ItemID);
|
||||
OUT(Quantity);
|
||||
OUT(AlreadySold);
|
||||
OUT(action);
|
||||
OUT(price);
|
||||
OUT(trader_id);
|
||||
memcpy(eq->item_name, emu->item_name, sizeof(eq->item_name));
|
||||
OUT(item_id);
|
||||
OUT(quantity);
|
||||
OUT(already_sold);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
@@ -2908,12 +2908,12 @@ namespace SoF
|
||||
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
MEMSET_IN(TraderBuy_Struct);
|
||||
|
||||
IN(Action);
|
||||
IN(Price);
|
||||
IN(TraderID);
|
||||
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
|
||||
IN(ItemID);
|
||||
IN(Quantity);
|
||||
IN(action);
|
||||
IN(price);
|
||||
IN(trader_id);
|
||||
memcpy(emu->item_name, eq->item_name, sizeof(emu->item_name));
|
||||
IN(item_id);
|
||||
IN(quantity);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
@@ -2771,8 +2771,8 @@ struct GetItems_Struct{
|
||||
};
|
||||
|
||||
struct BecomeTrader_Struct{
|
||||
uint32 ID;
|
||||
uint32 Code;
|
||||
uint32 trader_id;
|
||||
uint32 action;
|
||||
};
|
||||
|
||||
struct Trader_ShowItems_Struct{
|
||||
@@ -2782,15 +2782,15 @@ struct Trader_ShowItems_Struct{
|
||||
};
|
||||
|
||||
struct TraderBuy_Struct {
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ uint32 Price;
|
||||
/*008*/ uint32 TraderID;
|
||||
/*012*/ char ItemName[64];
|
||||
/*076*/ uint32 Unknown076;
|
||||
/*080*/ uint32 ItemID;
|
||||
/*084*/ uint32 AlreadySold;
|
||||
/*088*/ uint32 Quantity;
|
||||
/*092*/ uint32 Unknown092;
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 price;
|
||||
/*008*/ uint32 trader_id;
|
||||
/*012*/ char item_name[64];
|
||||
/*076*/ uint32 unknown_076;
|
||||
/*080*/ uint32 item_id;
|
||||
/*084*/ uint32 already_sold;
|
||||
/*088*/ uint32 quantity;
|
||||
/*092*/ uint32 unknown_092;
|
||||
};
|
||||
|
||||
struct TraderItemUpdate_Struct{
|
||||
|
||||
@@ -60,7 +60,7 @@
|
||||
eq_struct *eq = (eq_struct *) __packet->pBuffer; \
|
||||
|
||||
#define ALLOC_LEN_ENCODE(len) \
|
||||
__packet->pBuffer = new unsigned char[len]; \
|
||||
__packet->pBuffer = new unsigned char[len] {}; \
|
||||
__packet->size = len; \
|
||||
memset(__packet->pBuffer, 0, len); \
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
#define SETUP_DIRECT_DECODE(emu_struct, eq_struct) \
|
||||
unsigned char *__eq_buffer = __packet->pBuffer; \
|
||||
__packet->size = sizeof(emu_struct); \
|
||||
__packet->pBuffer = new unsigned char[__packet->size]; \
|
||||
__packet->pBuffer = new unsigned char[__packet->size] {}; \
|
||||
emu_struct *emu = (emu_struct *) __packet->pBuffer; \
|
||||
eq_struct *eq = (eq_struct *) __eq_buffer;
|
||||
|
||||
@@ -133,7 +133,7 @@
|
||||
eq_struct* in = (eq_struct*)__packet->pBuffer; \
|
||||
auto size = strlen(in->var_field); \
|
||||
__packet->size = sizeof(emu_struct) + size + 1; \
|
||||
__packet->pBuffer = new unsigned char[__packet->size]; \
|
||||
__packet->pBuffer = new unsigned char[__packet->size] {}; \
|
||||
emu_struct *emu = (emu_struct *) __packet->pBuffer; \
|
||||
eq_struct *eq = (eq_struct *) __eq_buffer;
|
||||
|
||||
|
||||
+397
-68
@@ -32,6 +32,7 @@
|
||||
#include "../strings.h"
|
||||
#include "../item_instance.h"
|
||||
#include "titanium_structs.h"
|
||||
#include "../rulesys.h"
|
||||
#include "../path_manager.h"
|
||||
#include "../raid.h"
|
||||
#include "../guilds.h"
|
||||
@@ -197,64 +198,135 @@ namespace Titanium
|
||||
|
||||
ENCODE(OP_BazaarSearch)
|
||||
{
|
||||
if (((*p)->size == sizeof(BazaarReturnDone_Struct)) || ((*p)->size == sizeof(BazaarWelcome_Struct))) {
|
||||
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
return;
|
||||
}
|
||||
|
||||
//consume the packet
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
//store away the emu struct
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer;
|
||||
uint32 action = *(uint32 *)in->pBuffer;
|
||||
|
||||
//determine and verify length
|
||||
int entrycount = in->size / sizeof(BazaarSearchResults_Struct);
|
||||
if (entrycount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0) {
|
||||
Log(Logs::General, Logs::Netcode, "[STRUCTS] Wrong size on outbound %s: Got %d, expected multiple of %d",
|
||||
opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct));
|
||||
delete in;
|
||||
return;
|
||||
switch (action) {
|
||||
case BazaarSearch: {
|
||||
LogTrading(
|
||||
"Encode OP_BazaarSearch(Ti) BazaarSearch action <green>[{}]",
|
||||
action
|
||||
);
|
||||
std::vector<BazaarSearchResultsFromDB_Struct> results {};
|
||||
auto bsms = (BazaarSearchMessaging_Struct *)in->pBuffer;
|
||||
EQ::Util::MemoryStreamReader ss(
|
||||
reinterpret_cast<char *>(bsms->payload),
|
||||
in->size - sizeof(BazaarSearchMessaging_Struct)
|
||||
);
|
||||
cereal::BinaryInputArchive ar(ss);
|
||||
ar(results);
|
||||
|
||||
auto size = results.size() * sizeof(structs::BazaarSearchResults_Struct);
|
||||
auto buffer = new uchar[size];
|
||||
uchar *bufptr = buffer;
|
||||
memset(buffer, 0, size);
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, structs::TiBazaarTraderBuyerActions::BazaarSearch);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, row->trader_entity_id);
|
||||
bufptr += 4;
|
||||
VARSTRUCT_ENCODE_TYPE(int32, bufptr, row->item_id);
|
||||
VARSTRUCT_ENCODE_TYPE(int32, bufptr, row->serial_number);
|
||||
bufptr += 4;
|
||||
if (row->stackable) {
|
||||
strn0cpy(
|
||||
reinterpret_cast<char *>(bufptr),
|
||||
fmt::format("{}({})", row->item_name.c_str(), row->charges).c_str(),
|
||||
64
|
||||
);
|
||||
}
|
||||
else {
|
||||
strn0cpy(
|
||||
reinterpret_cast<char *>(bufptr),
|
||||
fmt::format("{}({})", row->item_name.c_str(), row->count).c_str(),
|
||||
64
|
||||
);
|
||||
}
|
||||
bufptr += 64;
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, row->cost);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, row->item_stat);
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_BazaarSearch, size);
|
||||
memcpy(outapp->pBuffer, buffer, size);
|
||||
dest->FastQueuePacket(&outapp);
|
||||
|
||||
safe_delete(outapp);
|
||||
safe_delete_array(buffer);
|
||||
safe_delete(in);
|
||||
break;
|
||||
}
|
||||
case BazaarInspect:
|
||||
case WelcomeMessage: {
|
||||
LogTrading(
|
||||
"Encode OP_BazaarSearch(Ti) BazaarInspect/WelcomeMessage action <green>[{}]",
|
||||
action
|
||||
);
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading(
|
||||
"Encode OP_BazaarSearch(Ti) unhandled action <red>[{}]",
|
||||
action
|
||||
);
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
}
|
||||
|
||||
//make the EQ struct.
|
||||
in->size = sizeof(structs::BazaarSearchResults_Struct)*entrycount;
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *) in->pBuffer;
|
||||
|
||||
//zero out the packet. We could avoid this memset by setting all fields (including unknowns) in the loop.
|
||||
memset(in->pBuffer, 0, in->size);
|
||||
|
||||
for (int i = 0; i < entrycount; i++, eq++, emu++) {
|
||||
eq->Beginning.Action = emu->Beginning.Action;
|
||||
eq->Beginning.Unknown001 = emu->Beginning.Unknown001;
|
||||
eq->Beginning.Unknown002 = emu->Beginning.Unknown002;
|
||||
eq->NumItems = emu->NumItems;
|
||||
eq->SerialNumber = emu->SerialNumber;
|
||||
eq->SellerID = emu->SellerID;
|
||||
eq->Cost = emu->Cost;
|
||||
eq->ItemStat = emu->ItemStat;
|
||||
strcpy(eq->ItemName, emu->ItemName);
|
||||
}
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
|
||||
ENCODE(OP_BecomeTrader)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(BecomeTrader_Struct);
|
||||
SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct);
|
||||
uint32 action = *(uint32 *)(*p)->pBuffer;
|
||||
|
||||
OUT(ID);
|
||||
OUT(Code);
|
||||
|
||||
FINISH_ENCODE();
|
||||
switch (action)
|
||||
{
|
||||
case TraderOff:
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(BecomeTrader_Struct);
|
||||
SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_BecomeTrader(Ti) TraderOff action <green>[{}] entity_id <green>[{}] trader_name "
|
||||
"<green>[{}]",
|
||||
emu->action,
|
||||
emu->entity_id,
|
||||
emu->trader_name
|
||||
);
|
||||
eq->action = structs::TiBazaarTraderBuyerActions::Zero;
|
||||
eq->entity_id = emu->entity_id;
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case TraderOn:
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(BecomeTrader_Struct);
|
||||
SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_BecomeTrader(Ti) TraderOn action <green>[{}] entity_id <green>[{}] trader_name "
|
||||
"<green>[{}]",
|
||||
emu->action,
|
||||
emu->entity_id,
|
||||
emu->trader_name
|
||||
);
|
||||
eq->action = structs::TiBazaarTraderBuyerActions::BeginTraderMode;
|
||||
eq->entity_id = emu->entity_id;
|
||||
strn0cpy(eq->trader_name, emu->trader_name, sizeof(eq->trader_name));
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LogTrading(
|
||||
"Encode OP_BecomeTrader(Ti) unhandled action <red>[{}] Sending packet as is.",
|
||||
action
|
||||
);
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ENCODE(OP_Buff)
|
||||
@@ -2010,32 +2082,170 @@ namespace Titanium
|
||||
|
||||
ENCODE(OP_Trader)
|
||||
{
|
||||
if ((*p)->size != sizeof(TraderBuy_Struct)) {
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
return;
|
||||
}
|
||||
auto action = *(uint32 *) (*p)->pBuffer;
|
||||
|
||||
ENCODE_FORWARD(OP_TraderBuy);
|
||||
switch (action) {
|
||||
case TraderOn: {
|
||||
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_Trader BeginTraderMode action <green>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
eq->action = structs::TiBazaarTraderBuyerActions::BeginTraderMode;
|
||||
OUT(entity_id);
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case TraderOff: {
|
||||
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_Trader EndTraderMode action <green>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
eq->action = structs::TiBazaarTraderBuyerActions::EndTraderMode;
|
||||
OUT(entity_id);
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case ListTraderItems: {
|
||||
ENCODE_LENGTH_EXACT(Trader_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_Struct, structs::Trader_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_Trader ListTraderItems action <green>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
eq->action = structs::TiBazaarTraderBuyerActions::ListTraderItems;
|
||||
std::copy_n(emu->items, UF::invtype::BAZAAR_SIZE, eq->item_id);
|
||||
std::copy_n(emu->item_cost, UF::invtype::BAZAAR_SIZE, eq->item_cost);
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case BuyTraderItem: {
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_Trader item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
eq->item_id,
|
||||
eq->price,
|
||||
eq->quantity,
|
||||
eq->trader_id
|
||||
);
|
||||
|
||||
eq->action = structs::TiBazaarTraderBuyerActions::BuyTraderItem;
|
||||
OUT(price);
|
||||
OUT(trader_id);
|
||||
OUT(item_id);
|
||||
OUT(already_sold);
|
||||
OUT(quantity);
|
||||
strn0cpy(eq->item_name, emu->item_name, sizeof(eq->item_name));
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case ItemMove: {
|
||||
LogTrading(
|
||||
"Encode OP_Trader ItemMove action <green>[{}]",
|
||||
action
|
||||
);
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
LogError("Unknown Encode OP_Trader action <red>{} received. Unhandled.", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ENCODE(OP_TraderBuy)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_TraderBuy item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
emu->item_id,
|
||||
emu->price,
|
||||
emu->quantity,
|
||||
emu->trader_id
|
||||
);
|
||||
|
||||
OUT(Action);
|
||||
OUT(Price);
|
||||
OUT(TraderID);
|
||||
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
||||
OUT(ItemID);
|
||||
OUT(Quantity);
|
||||
OUT(AlreadySold);
|
||||
OUT(action);
|
||||
OUT(price);
|
||||
OUT(trader_id);
|
||||
OUT(item_id);
|
||||
OUT(already_sold);
|
||||
OUT(quantity);
|
||||
OUT_str(item_name);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_TraderShop)
|
||||
{
|
||||
auto action = *(uint32 *)(*p)->pBuffer;
|
||||
|
||||
switch (action) {
|
||||
case ClickTrader: {
|
||||
ENCODE_LENGTH_EXACT(TraderClick_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderClick_Struct, structs::TraderClick_Struct);
|
||||
LogTrading(
|
||||
"ClickTrader action <green>[{}] trader_id <green>[{}]",
|
||||
action,
|
||||
emu->TraderID
|
||||
);
|
||||
|
||||
eq->action = 0;
|
||||
eq->trader_id = emu->TraderID;
|
||||
eq->approval = emu->Approval;
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case BuyTraderItem: {
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_TraderShop item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
eq->item_id,
|
||||
eq->price,
|
||||
eq->quantity,
|
||||
eq->trader_id
|
||||
);
|
||||
|
||||
eq->action = structs::TiBazaarTraderBuyerActions::BuyTraderItem;
|
||||
OUT(price);
|
||||
OUT(trader_id);
|
||||
OUT(item_id);
|
||||
OUT(already_sold);
|
||||
OUT(quantity);
|
||||
strn0cpy(eq->item_name, emu->item_name, sizeof(eq->item_name));
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
LogError("Unknown Encode OP_TraderShop action <red>[{}] received. Unhandled.", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ENCODE(OP_TributeItem)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(TributeItem_Struct);
|
||||
@@ -2286,6 +2496,53 @@ namespace Titanium
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_BazaarSearch)
|
||||
{
|
||||
uint32 action = *(uint32 *) __packet->pBuffer;
|
||||
|
||||
switch (action) {
|
||||
case structs::TiBazaarTraderBuyerActions::BazaarSearch: {
|
||||
DECODE_LENGTH_EXACT(structs::BazaarSearch_Struct);
|
||||
SETUP_DIRECT_DECODE(BazaarSearchCriteria_Struct, structs::BazaarSearch_Struct);
|
||||
|
||||
emu->action = eq->Beginning.Action;
|
||||
emu->item_stat = eq->ItemStat;
|
||||
emu->max_cost = eq->MaxPrice;
|
||||
emu->min_cost = eq->MinPrice;
|
||||
emu->max_level = eq->MaxLlevel;
|
||||
emu->min_level = eq->Minlevel;
|
||||
emu->race = eq->Race;
|
||||
emu->slot = eq->Slot;
|
||||
emu->type = eq->Type == UINT32_MAX ? UINT8_MAX : eq->Type;
|
||||
emu->trader_entity_id = eq->TraderID;
|
||||
emu->trader_id = 0;
|
||||
emu->_class = eq->Class_;
|
||||
emu->search_scope = eq->TraderID > 0 ? NonRoFBazaarSearchScope : Local_Scope;
|
||||
emu->max_results = RuleI(Bazaar, MaxSearchResults);
|
||||
strn0cpy(emu->item_name, eq->Name, sizeof(emu->item_name));
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::TiBazaarTraderBuyerActions::BazaarInspect: {
|
||||
SETUP_DIRECT_DECODE(BazaarInspect_Struct, structs::BazaarInspect_Struct);
|
||||
|
||||
IN(action);
|
||||
memcpy(emu->player_name, eq->player_name, sizeof(emu->player_name));
|
||||
IN(serial_number);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::TiBazaarTraderBuyerActions::WelcomeMessage: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading("(Ti) Unhandled action <red>[{}]", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DECODE(OP_Buff)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::SpellBuffPacket_Struct);
|
||||
@@ -2925,18 +3182,90 @@ namespace Titanium
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_Trader)
|
||||
{
|
||||
auto action = (uint32) __packet->pBuffer[0];
|
||||
|
||||
switch (action) {
|
||||
case structs::TiBazaarTraderBuyerActions::BeginTraderMode: {
|
||||
DECODE_LENGTH_EXACT(structs::BeginTrader_Struct);
|
||||
SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::BeginTrader_Struct);
|
||||
LogTrading(
|
||||
"Decode OP_Trader BeginTraderMode action <red>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
emu->action = TraderOn;
|
||||
emu->unknown_004 = 0;
|
||||
std::copy_n(eq->serial_number, Titanium::invtype::BAZAAR_SIZE, emu->serial_number);
|
||||
std::copy_n(eq->cost, Titanium::invtype::BAZAAR_SIZE, emu->item_cost);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::TiBazaarTraderBuyerActions::EndTraderMode: {
|
||||
DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
LogTrading(
|
||||
"Decode OP_Trader(Ti) EndTraderMode action <red>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
emu->action = TraderOff;
|
||||
IN(entity_id);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::TiBazaarTraderBuyerActions::PriceUpdate:
|
||||
case structs::TiBazaarTraderBuyerActions::ItemMove:
|
||||
case structs::TiBazaarTraderBuyerActions::EndTransaction:
|
||||
case structs::TiBazaarTraderBuyerActions::ListTraderItems: {
|
||||
LogTrading(
|
||||
"Decode OP_Trader(Ti) Price/ItemMove/EndTransaction/ListTraderItems action <red>[{}]",
|
||||
action
|
||||
);
|
||||
break;
|
||||
}
|
||||
case structs::TiBazaarTraderBuyerActions::ReconcileItems: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogError("Unhandled(Ti) action <red>[{}] received.", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DECODE(OP_TraderBuy)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
MEMSET_IN(TraderBuy_Struct);
|
||||
|
||||
IN(Action);
|
||||
IN(Price);
|
||||
IN(TraderID);
|
||||
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
|
||||
IN(ItemID);
|
||||
IN(Quantity);
|
||||
IN(action);
|
||||
IN(price);
|
||||
IN(trader_id);
|
||||
memcpy(emu->item_name, eq->item_name, sizeof(emu->item_name));
|
||||
IN(item_id);
|
||||
IN(quantity);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_TraderShop)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::TraderClick_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderClick_Struct, structs::TraderClick_Struct);
|
||||
LogTrading(
|
||||
"(Ti) action <green>[{}] trader_id <green>[{}] approval <green>[{}]",
|
||||
eq->action,
|
||||
eq->trader_id,
|
||||
eq->approval
|
||||
);
|
||||
|
||||
emu->Code = ClickTrader;
|
||||
emu->TraderID = eq->trader_id;
|
||||
emu->Approval = eq->approval;
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ E(OP_TaskDescription)
|
||||
E(OP_Track)
|
||||
E(OP_Trader)
|
||||
E(OP_TraderBuy)
|
||||
E(OP_TraderShop)
|
||||
E(OP_TributeItem)
|
||||
E(OP_VetRewardsAvaliable)
|
||||
E(OP_WearChange)
|
||||
@@ -92,6 +93,7 @@ E(OP_ZoneSpawns)
|
||||
D(OP_AdventureMerchantSell)
|
||||
D(OP_ApplyPoison)
|
||||
D(OP_AugmentItem)
|
||||
D(OP_BazaarSearch)
|
||||
D(OP_Buff)
|
||||
D(OP_Bug)
|
||||
D(OP_CastSpell)
|
||||
@@ -123,7 +125,9 @@ D(OP_ReadBook)
|
||||
D(OP_SetServerFilter)
|
||||
D(OP_ShopPlayerSell)
|
||||
D(OP_ShopRequest)
|
||||
D(OP_Trader)
|
||||
D(OP_TraderBuy)
|
||||
D(OP_TraderShop)
|
||||
D(OP_TradeSkillCombine)
|
||||
D(OP_TributeItem)
|
||||
D(OP_WearChange)
|
||||
|
||||
@@ -2273,24 +2273,29 @@ struct BazaarWelcome_Struct {
|
||||
};
|
||||
|
||||
struct BazaarSearch_Struct {
|
||||
BazaarWindowStart_Struct beginning;
|
||||
uint32 traderid;
|
||||
uint32 class_;
|
||||
uint32 race;
|
||||
uint32 stat;
|
||||
uint32 slot;
|
||||
uint32 type;
|
||||
char name[64];
|
||||
uint32 minprice;
|
||||
uint32 maxprice;
|
||||
uint32 minlevel;
|
||||
uint32 maxlevel;
|
||||
BazaarWindowStart_Struct Beginning;
|
||||
uint32 TraderID;
|
||||
uint32 Class_;
|
||||
uint32 Race;
|
||||
uint32 ItemStat;
|
||||
uint32 Slot;
|
||||
uint32 Type;
|
||||
char Name[64];
|
||||
uint32 MinPrice;
|
||||
uint32 MaxPrice;
|
||||
uint32 Minlevel;
|
||||
uint32 MaxLlevel;
|
||||
};
|
||||
struct BazaarInspect_Struct{
|
||||
|
||||
struct BazaarInspect_Struct {
|
||||
uint32 action;
|
||||
char player_name[64];
|
||||
uint32 unknown_068;
|
||||
uint32 serial_number;
|
||||
uint32 unknown_076;
|
||||
uint32 item_id;
|
||||
uint32 unknown;
|
||||
char name[64];
|
||||
};
|
||||
|
||||
struct BazaarReturnDone_Struct{
|
||||
uint32 type;
|
||||
uint32 traderid;
|
||||
@@ -2298,16 +2303,17 @@ struct BazaarReturnDone_Struct{
|
||||
uint32 unknown12;
|
||||
uint32 unknown16;
|
||||
};
|
||||
|
||||
struct BazaarSearchResults_Struct {
|
||||
BazaarWindowStart_Struct Beginning;
|
||||
uint32 SellerID;
|
||||
uint32 NumItems; // Don't know. Don't know the significance of this field.
|
||||
uint32 SerialNumber;
|
||||
uint32 Unknown016;
|
||||
uint32 Unknown020; // Something to do with stats as well
|
||||
char ItemName[64];
|
||||
uint32 Cost;
|
||||
uint32 ItemStat;
|
||||
uint32 entity_id;
|
||||
uint32 unknown_008;
|
||||
uint32 item_id;
|
||||
uint32 serial_number;
|
||||
uint32 unknown_020;
|
||||
char item_name[64];
|
||||
uint32 item_cost;
|
||||
uint32 item_stat;
|
||||
};
|
||||
|
||||
struct ServerSideFilters_Struct {
|
||||
@@ -2454,11 +2460,18 @@ struct WhoAllReturnStruct {
|
||||
struct WhoAllPlayer player[0];
|
||||
};
|
||||
|
||||
struct BeginTrader_Struct {
|
||||
uint32 action;
|
||||
uint32 unknown04;
|
||||
uint64 serial_number[80];
|
||||
uint32 cost[80];
|
||||
};
|
||||
|
||||
struct Trader_Struct {
|
||||
uint32 code;
|
||||
uint32 itemid[160];
|
||||
uint32 unknown;
|
||||
uint32 itemcost[80];
|
||||
uint32 action;
|
||||
uint32 unknown004;
|
||||
uint64 item_id[80];
|
||||
uint32 item_cost[80];
|
||||
};
|
||||
|
||||
struct ClickTrader_Struct {
|
||||
@@ -2471,27 +2484,28 @@ struct GetItems_Struct{
|
||||
uint32 items[80];
|
||||
};
|
||||
|
||||
struct BecomeTrader_Struct{
|
||||
uint32 ID;
|
||||
uint32 Code;
|
||||
struct BecomeTrader_Struct {
|
||||
uint32 entity_id;
|
||||
uint32 action;
|
||||
char trader_name[64];
|
||||
};
|
||||
|
||||
struct Trader_ShowItems_Struct{
|
||||
uint32 code;
|
||||
uint32 traderid;
|
||||
uint32 action;
|
||||
uint32 entity_id;
|
||||
uint32 unknown08[3];
|
||||
};
|
||||
|
||||
struct TraderBuy_Struct {
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ uint32 Price;
|
||||
/*008*/ uint32 TraderID;
|
||||
/*012*/ char ItemName[64];
|
||||
/*076*/ uint32 Unknown076;
|
||||
/*080*/ uint32 ItemID;
|
||||
/*084*/ uint32 AlreadySold;
|
||||
/*088*/ uint32 Quantity;
|
||||
/*092*/ uint32 Unknown092;
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 price;
|
||||
/*008*/ uint32 trader_id;
|
||||
/*012*/ char item_name[64];
|
||||
/*076*/ uint32 unknown_076;
|
||||
/*080*/ uint32 item_id;
|
||||
/*084*/ uint32 already_sold;
|
||||
/*088*/ uint32 quantity;
|
||||
/*092*/ uint32 unknown_092;
|
||||
};
|
||||
|
||||
|
||||
@@ -2517,8 +2531,9 @@ struct TraderDelItem_Struct{
|
||||
};
|
||||
|
||||
struct TraderClick_Struct{
|
||||
uint32 traderid;
|
||||
uint32 unknown4[2];
|
||||
uint32 trader_id;
|
||||
uint32 action;
|
||||
uint32 unknown_004;
|
||||
uint32 approval;
|
||||
};
|
||||
|
||||
@@ -3744,6 +3759,21 @@ struct GuildMemberRank_Struct {
|
||||
/*076*/ uint32 alt_banker; //Banker/Alt bit 00 - none 10 - Alt 11 - Alt and Banker 01 - Banker. Banker not functional for RoF2+
|
||||
};
|
||||
|
||||
enum TiBazaarTraderBuyerActions {
|
||||
Zero = 0,
|
||||
BeginTraderMode = 1,
|
||||
EndTraderMode = 2,
|
||||
PriceUpdate = 3,
|
||||
EndTransaction = 4,
|
||||
BazaarSearch = 7,
|
||||
WelcomeMessage = 9,
|
||||
BuyTraderItem = 10,
|
||||
ListTraderItems = 11,
|
||||
BazaarInspect = 18,
|
||||
ItemMove = 19,
|
||||
ReconcileItems = 20
|
||||
};
|
||||
|
||||
}; /*structs*/
|
||||
|
||||
}; /*Titanium*/
|
||||
|
||||
+401
-69
@@ -37,6 +37,8 @@
|
||||
#include "../races.h"
|
||||
#include "../raid.h"
|
||||
#include "../guilds.h"
|
||||
//#include "../repositories/trader_repository.h"
|
||||
#include "../cereal/include/cereal/types/vector.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@@ -307,50 +309,134 @@ namespace UF
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
char *Buffer = (char *)in->pBuffer;
|
||||
uint32 action = *(uint32 *)in->pBuffer;
|
||||
|
||||
uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
|
||||
switch (action) {
|
||||
case BazaarSearch: {
|
||||
LogTrading(
|
||||
"Encode OP_BazaarSearch(UF) BazaarSearch action <green>[{}]",
|
||||
action
|
||||
);
|
||||
std::vector<BazaarSearchResultsFromDB_Struct> results {};
|
||||
auto bsms = (BazaarSearchMessaging_Struct *)in->pBuffer;
|
||||
EQ::Util::MemoryStreamReader ss(
|
||||
reinterpret_cast<char *>(bsms->payload),
|
||||
in->size - sizeof(BazaarSearchMessaging_Struct)
|
||||
);
|
||||
cereal::BinaryInputArchive ar(ss);
|
||||
ar(results);
|
||||
|
||||
if (SubAction != BazaarSearchResults)
|
||||
{
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
return;
|
||||
auto size = results.size() * sizeof(BazaarSearchResults_Struct);
|
||||
auto buffer = new uchar[size];
|
||||
uchar *bufptr = buffer;
|
||||
memset(buffer, 0, size);
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, structs::UFBazaarTraderBuyerActions::BazaarSearch);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, row->trader_entity_id);
|
||||
strn0cpy(reinterpret_cast<char *>(bufptr), row->trader_name.c_str(), 64);
|
||||
bufptr += 64;
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, 1);
|
||||
VARSTRUCT_ENCODE_TYPE(int32, bufptr, row->item_id);
|
||||
VARSTRUCT_ENCODE_TYPE(int32, bufptr, row->serial_number);
|
||||
bufptr += 4;
|
||||
if (row->stackable) {
|
||||
strn0cpy(
|
||||
reinterpret_cast<char *>(bufptr),
|
||||
fmt::format("{}({})", row->item_name.c_str(), row->charges).c_str(),
|
||||
64
|
||||
);
|
||||
}
|
||||
else {
|
||||
strn0cpy(
|
||||
reinterpret_cast<char *>(bufptr),
|
||||
fmt::format("{}({})", row->item_name.c_str(), row->count).c_str(),
|
||||
64
|
||||
);
|
||||
}
|
||||
bufptr += 64;
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, row->cost);
|
||||
VARSTRUCT_ENCODE_TYPE(uint32, bufptr, row->item_stat);
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_BazaarSearch, size);
|
||||
memcpy(outapp->pBuffer, buffer, size);
|
||||
dest->FastQueuePacket(&outapp);
|
||||
|
||||
safe_delete(outapp);
|
||||
safe_delete_array(buffer);
|
||||
safe_delete(in);
|
||||
break;
|
||||
}
|
||||
case BazaarInspect:
|
||||
case WelcomeMessage: {
|
||||
LogTrading(
|
||||
"Encode OP_BazaarSearch(UF) BazaarInspect/WelcomeMessage action <green>[{}]",
|
||||
action
|
||||
);
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading(
|
||||
"Encode OP_BazaarSearch(UF) unhandled action <red>[{}]",
|
||||
action
|
||||
);
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char *__emu_buffer = in->pBuffer;
|
||||
ENCODE(OP_BecomeTrader)
|
||||
{
|
||||
uint32 action = *(uint32 *)(*p)->pBuffer;
|
||||
|
||||
BazaarSearchResults_Struct *emu = (BazaarSearchResults_Struct *)__emu_buffer;
|
||||
|
||||
int EntryCount = in->size / sizeof(BazaarSearchResults_Struct);
|
||||
|
||||
if (EntryCount == 0 || (in->size % sizeof(BazaarSearchResults_Struct)) != 0)
|
||||
switch (action)
|
||||
{
|
||||
LogNetcode("[STRUCTS] Wrong size on outbound [{}]: Got [{}], expected multiple of [{}]", opcodes->EmuToName(in->GetOpcode()), in->size, sizeof(BazaarSearchResults_Struct));
|
||||
delete in;
|
||||
return;
|
||||
case TraderOff:
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(BecomeTrader_Struct);
|
||||
SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_BecomeTrader(UF) TraderOff action <green>[{}] entity_id <green>[{}] trader_name "
|
||||
"<green>[{}]",
|
||||
emu->action,
|
||||
emu->entity_id,
|
||||
emu->trader_name
|
||||
);
|
||||
eq->action = structs::UFBazaarTraderBuyerActions::Zero;
|
||||
eq->entity_id = emu->entity_id;
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case TraderOn:
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(BecomeTrader_Struct);
|
||||
SETUP_DIRECT_ENCODE(BecomeTrader_Struct, structs::BecomeTrader_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_BecomeTrader(UF) TraderOn action <green>[{}] entity_id <green>[{}] trader_name "
|
||||
"<green>[{}]",
|
||||
emu->action,
|
||||
emu->entity_id,
|
||||
emu->trader_name
|
||||
);
|
||||
eq->action = structs::UFBazaarTraderBuyerActions::BeginTraderMode;
|
||||
eq->entity_id = emu->entity_id;
|
||||
strn0cpy(eq->trader_name, emu->trader_name, sizeof(eq->trader_name));
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
LogTrading(
|
||||
"Encode OP_BecomeTrader(UF) unhandled action <red>[{}] Sending packet as is.",
|
||||
action
|
||||
);
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
}
|
||||
|
||||
in->size = EntryCount * sizeof(structs::BazaarSearchResults_Struct);
|
||||
in->pBuffer = new unsigned char[in->size];
|
||||
memset(in->pBuffer, 0, in->size);
|
||||
|
||||
structs::BazaarSearchResults_Struct *eq = (structs::BazaarSearchResults_Struct *)in->pBuffer;
|
||||
|
||||
for (int i = 0; i < EntryCount; ++i, ++emu, ++eq)
|
||||
{
|
||||
OUT(Beginning.Action);
|
||||
OUT(SellerID);
|
||||
memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName));
|
||||
OUT(NumItems);
|
||||
OUT(ItemID);
|
||||
OUT(SerialNumber);
|
||||
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
||||
OUT(Cost);
|
||||
OUT(ItemStat);
|
||||
}
|
||||
|
||||
delete[] __emu_buffer;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
}
|
||||
|
||||
ENCODE(OP_Buff)
|
||||
@@ -2743,32 +2829,170 @@ namespace UF
|
||||
|
||||
ENCODE(OP_Trader)
|
||||
{
|
||||
if ((*p)->size != sizeof(TraderBuy_Struct)) {
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
return;
|
||||
}
|
||||
auto action = *(uint32 *) (*p)->pBuffer;
|
||||
|
||||
ENCODE_FORWARD(OP_TraderBuy);
|
||||
switch (action) {
|
||||
case TraderOn: {
|
||||
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_Trader BeginTraderMode action <green>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
eq->action = structs::UFBazaarTraderBuyerActions::BeginTraderMode;
|
||||
OUT(entity_id);
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case TraderOff: {
|
||||
ENCODE_LENGTH_EXACT(Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_Trader EndTraderMode action <green>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
eq->action = structs::UFBazaarTraderBuyerActions::EndTraderMode;
|
||||
OUT(entity_id);
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case ListTraderItems: {
|
||||
ENCODE_LENGTH_EXACT(Trader_Struct);
|
||||
SETUP_DIRECT_ENCODE(Trader_Struct, structs::Trader_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_Trader ListTraderItems action <green>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
eq->action = structs::UFBazaarTraderBuyerActions::ListTraderItems;
|
||||
std::copy_n(emu->items, UF::invtype::BAZAAR_SIZE, eq->item_id);
|
||||
std::copy_n(emu->item_cost, UF::invtype::BAZAAR_SIZE, eq->item_cost);
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case BuyTraderItem: {
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_Trader item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
eq->item_id,
|
||||
eq->price,
|
||||
eq->quantity,
|
||||
eq->trader_id
|
||||
);
|
||||
|
||||
eq->action = structs::UFBazaarTraderBuyerActions::BuyTraderItem;
|
||||
OUT(price);
|
||||
OUT(trader_id);
|
||||
OUT(item_id);
|
||||
OUT(already_sold);
|
||||
OUT(quantity);
|
||||
strn0cpy(eq->item_name, emu->item_name, sizeof(eq->item_name));
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case ItemMove: {
|
||||
LogTrading(
|
||||
"Encode OP_Trader ItemMove action <green>[{}]",
|
||||
action
|
||||
);
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
LogError("Unknown Encode OP_Trader action <red>{} received. Unhandled.", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ENCODE(OP_TraderBuy)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_TraderBuy item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
emu->item_id,
|
||||
emu->price,
|
||||
emu->quantity,
|
||||
emu->trader_id
|
||||
);
|
||||
|
||||
OUT(Action);
|
||||
OUT(Price);
|
||||
OUT(TraderID);
|
||||
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
|
||||
OUT(ItemID);
|
||||
OUT(Quantity);
|
||||
OUT(AlreadySold);
|
||||
OUT(action);
|
||||
OUT(price);
|
||||
OUT(trader_id);
|
||||
OUT(item_id);
|
||||
OUT(already_sold);
|
||||
OUT(quantity);
|
||||
OUT_str(item_name);
|
||||
|
||||
FINISH_ENCODE();
|
||||
}
|
||||
|
||||
ENCODE(OP_TraderShop)
|
||||
{
|
||||
auto action = *(uint32 *)(*p)->pBuffer;
|
||||
|
||||
switch (action) {
|
||||
case ClickTrader: {
|
||||
ENCODE_LENGTH_EXACT(TraderClick_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderClick_Struct, structs::TraderClick_Struct);
|
||||
LogTrading(
|
||||
"ClickTrader action <green>[{}] trader_id <green>[{}]",
|
||||
action,
|
||||
emu->TraderID
|
||||
);
|
||||
|
||||
eq->action = 0;
|
||||
eq->trader_id = emu->TraderID;
|
||||
eq->approval = emu->Approval;
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
case BuyTraderItem: {
|
||||
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
|
||||
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"Encode OP_TraderShop item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
eq->item_id,
|
||||
eq->price,
|
||||
eq->quantity,
|
||||
eq->trader_id
|
||||
);
|
||||
|
||||
eq->action = structs::UFBazaarTraderBuyerActions::BuyTraderItem;
|
||||
OUT(price);
|
||||
OUT(trader_id);
|
||||
OUT(item_id);
|
||||
OUT(already_sold);
|
||||
OUT(quantity);
|
||||
strn0cpy(eq->item_name, emu->item_name, sizeof(eq->item_name));
|
||||
|
||||
FINISH_ENCODE();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
EQApplicationPacket *in = *p;
|
||||
*p = nullptr;
|
||||
|
||||
dest->FastQueuePacket(&in, ack_req);
|
||||
LogError("Unknown Encode OP_TraderShop action <red>[{}] received. Unhandled.", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ENCODE(OP_TributeItem)
|
||||
{
|
||||
ENCODE_LENGTH_EXACT(TributeItem_Struct);
|
||||
@@ -3040,7 +3264,7 @@ namespace UF
|
||||
Bitfields->targetable = 1;
|
||||
Bitfields->targetable_with_hotkey = emu->targetable_with_hotkey ? 1 : 0;
|
||||
Bitfields->statue = 0;
|
||||
Bitfields->trader = 0;
|
||||
Bitfields->trader = emu->trader ? 1 : 0;
|
||||
Bitfields->buyer = 0;
|
||||
|
||||
Bitfields->showname = ShowName;
|
||||
@@ -3363,20 +3587,49 @@ namespace UF
|
||||
|
||||
DECODE(OP_BazaarSearch)
|
||||
{
|
||||
char *Buffer = (char *)__packet->pBuffer;
|
||||
uint32 action = *(uint32 *) __packet->pBuffer;
|
||||
|
||||
uint8 SubAction = VARSTRUCT_DECODE_TYPE(uint8, Buffer);
|
||||
switch (action) {
|
||||
case structs::UFBazaarTraderBuyerActions::BazaarSearch: {
|
||||
DECODE_LENGTH_EXACT(structs::BazaarSearch_Struct);
|
||||
SETUP_DIRECT_DECODE(BazaarSearchCriteria_Struct, structs::BazaarSearch_Struct);
|
||||
|
||||
if ((SubAction != BazaarInspectItem) || (__packet->size != sizeof(structs::NewBazaarInspect_Struct)))
|
||||
return;
|
||||
emu->action = eq->Beginning.Action;
|
||||
emu->item_stat = eq->ItemStat;
|
||||
emu->max_cost = eq->MaxPrice;
|
||||
emu->min_cost = eq->MinPrice;
|
||||
emu->max_level = eq->MaxLlevel;
|
||||
emu->min_level = eq->Minlevel;
|
||||
emu->race = eq->Race;
|
||||
emu->slot = eq->Slot;
|
||||
emu->type = eq->Type == UINT32_MAX ? UINT8_MAX : eq->Type;
|
||||
emu->trader_entity_id = eq->TraderID;
|
||||
emu->trader_id = 0;
|
||||
emu->_class = eq->Class_;
|
||||
emu->search_scope = eq->TraderID > 0 ? NonRoFBazaarSearchScope : Local_Scope;
|
||||
emu->max_results = RuleI(Bazaar, MaxSearchResults);
|
||||
strn0cpy(emu->item_name, eq->Name, sizeof(emu->item_name));
|
||||
|
||||
SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct);
|
||||
MEMSET_IN(structs::NewBazaarInspect_Struct);
|
||||
IN(Beginning.Action);
|
||||
memcpy(emu->Name, eq->Name, sizeof(emu->Name));
|
||||
IN(SerialNumber);
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::UFBazaarTraderBuyerActions::BazaarInspect: {
|
||||
SETUP_DIRECT_DECODE(BazaarInspect_Struct, structs::BazaarInspect_Struct);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
IN(action);
|
||||
memcpy(emu->player_name, eq->player_name, sizeof(emu->player_name));
|
||||
IN(serial_number);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::UFBazaarTraderBuyerActions::WelcomeMessage: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading("(UF) Unhandled action <red>[{}]", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DECODE(OP_BookButton)
|
||||
@@ -4059,18 +4312,97 @@ namespace UF
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_Trader)
|
||||
{
|
||||
auto action = (uint32) __packet->pBuffer[0];
|
||||
|
||||
switch (action) {
|
||||
case structs::UFBazaarTraderBuyerActions::BeginTraderMode: {
|
||||
DECODE_LENGTH_EXACT(structs::BeginTrader_Struct);
|
||||
SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::BeginTrader_Struct);
|
||||
LogTrading(
|
||||
"Decode OP_Trader BeginTraderMode action <red>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
emu->action = TraderOn;
|
||||
emu->unknown_004 = 0;
|
||||
std::copy_n(eq->serial_number, UF::invtype::BAZAAR_SIZE, emu->serial_number);
|
||||
std::copy_n(eq->cost, UF::invtype::BAZAAR_SIZE, emu->item_cost);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::UFBazaarTraderBuyerActions::EndTraderMode: {
|
||||
DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct);
|
||||
SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
|
||||
LogTrading(
|
||||
"Decode OP_Trader(UF) EndTraderMode action <red>[{}]",
|
||||
action
|
||||
);
|
||||
|
||||
emu->action = TraderOff;
|
||||
IN(entity_id);
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
break;
|
||||
}
|
||||
case structs::UFBazaarTraderBuyerActions::PriceUpdate:
|
||||
case structs::UFBazaarTraderBuyerActions::ItemMove:
|
||||
case structs::UFBazaarTraderBuyerActions::EndTransaction:
|
||||
case structs::UFBazaarTraderBuyerActions::ListTraderItems: {
|
||||
LogTrading(
|
||||
"Decode OP_Trader(UF) Price/ItemMove/EndTransaction/ListTraderItems action <red>[{}]",
|
||||
action
|
||||
);
|
||||
break;
|
||||
}
|
||||
case structs::UFBazaarTraderBuyerActions::ReconcileItems: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogError("Unhandled(UF) action <red>[{}] received.", action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DECODE(OP_TraderBuy)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
|
||||
MEMSET_IN(TraderBuy_Struct);
|
||||
LogTrading(
|
||||
"Decode OP_TraderBuy(UF) item_id <green>[{}] price <green>[{}] quantity <green>[{}] trader_id <green>[{}]",
|
||||
eq->item_id,
|
||||
eq->price,
|
||||
eq->quantity,
|
||||
eq->trader_id
|
||||
);
|
||||
|
||||
IN(Action);
|
||||
IN(Price);
|
||||
IN(TraderID);
|
||||
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
|
||||
IN(ItemID);
|
||||
IN(Quantity);
|
||||
emu->action = BuyTraderItem;
|
||||
IN(price);
|
||||
IN(trader_id);
|
||||
IN(item_id);
|
||||
IN(quantity);
|
||||
IN(already_sold);
|
||||
strn0cpy(emu->item_name, eq->item_name, sizeof(eq->item_name));
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
DECODE(OP_TraderShop)
|
||||
{
|
||||
DECODE_LENGTH_EXACT(structs::TraderClick_Struct);
|
||||
SETUP_DIRECT_DECODE(TraderClick_Struct, structs::TraderClick_Struct);
|
||||
LogTrading(
|
||||
"(UF) action <green>[{}] trader_id <green>[{}] approval <green>[{}]",
|
||||
eq->action,
|
||||
eq->trader_id,
|
||||
eq->approval
|
||||
);
|
||||
|
||||
emu->Code = ClickTrader;
|
||||
emu->TraderID = eq->trader_id;
|
||||
emu->Approval = eq->approval;
|
||||
|
||||
FINISH_DIRECT_DECODE();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ E(OP_ApplyPoison)
|
||||
E(OP_AugmentInfo)
|
||||
E(OP_Barter)
|
||||
E(OP_BazaarSearch)
|
||||
E(OP_BecomeTrader)
|
||||
E(OP_Buff)
|
||||
E(OP_BuffCreate)
|
||||
E(OP_CancelTrade)
|
||||
@@ -100,6 +101,7 @@ E(OP_TaskDescription)
|
||||
E(OP_Track)
|
||||
E(OP_Trader)
|
||||
E(OP_TraderBuy)
|
||||
E(OP_TraderShop)
|
||||
E(OP_TributeItem)
|
||||
E(OP_VetRewardsAvaliable)
|
||||
E(OP_WearChange)
|
||||
@@ -160,7 +162,9 @@ D(OP_SetServerFilter)
|
||||
D(OP_ShopPlayerBuy)
|
||||
D(OP_ShopPlayerSell)
|
||||
D(OP_ShopRequest)
|
||||
D(OP_Trader)
|
||||
D(OP_TraderBuy)
|
||||
D(OP_TraderShop)
|
||||
D(OP_TradeSkillCombine)
|
||||
D(OP_TributeItem)
|
||||
D(OP_WearChange)
|
||||
|
||||
+75
-39
@@ -2640,23 +2640,23 @@ struct EnvDamage2_Struct {
|
||||
//Bazaar Stuff
|
||||
|
||||
enum {
|
||||
BazaarTrader_StartTraderMode = 1,
|
||||
BazaarTrader_EndTraderMode = 2,
|
||||
BazaarTrader_UpdatePrice = 3,
|
||||
BazaarTrader_EndTransaction = 4,
|
||||
BazaarSearchResults = 7,
|
||||
BazaarWelcome = 9,
|
||||
BazaarBuyItem = 10,
|
||||
BazaarTrader_ShowItems = 11,
|
||||
BazaarSearchDone = 12,
|
||||
BazaarTrader_StartTraderMode = 1,
|
||||
BazaarTrader_EndTraderMode = 2,
|
||||
BazaarTrader_UpdatePrice = 3,
|
||||
BazaarTrader_EndTransaction = 4,
|
||||
BazaarSearchResults = 7,
|
||||
BazaarWelcome = 9,
|
||||
BazaarBuyItem = 10,
|
||||
BazaarTrader_ShowItems = 11,
|
||||
BazaarSearchDone = 12,
|
||||
BazaarTrader_CustomerBrowsing = 13
|
||||
};
|
||||
|
||||
enum {
|
||||
BazaarPriceChange_Fail = 0,
|
||||
BazaarPriceChange_Fail = 0,
|
||||
BazaarPriceChange_UpdatePrice = 1,
|
||||
BazaarPriceChange_RemoveItem = 2,
|
||||
BazaarPriceChange_AddItem = 3
|
||||
BazaarPriceChange_RemoveItem = 2,
|
||||
BazaarPriceChange_AddItem = 3
|
||||
};
|
||||
|
||||
struct BazaarWindowStart_Struct {
|
||||
@@ -2687,10 +2687,14 @@ struct BazaarSearch_Struct {
|
||||
uint32 Minlevel;
|
||||
uint32 MaxLlevel;
|
||||
};
|
||||
struct BazaarInspect_Struct{
|
||||
uint32 ItemID;
|
||||
uint32 Unknown004;
|
||||
char Name[64];
|
||||
|
||||
struct BazaarInspect_Struct {
|
||||
uint32 action;
|
||||
char player_name[64];
|
||||
uint32 unknown_068;
|
||||
uint32 serial_number;
|
||||
uint32 unknown_076;
|
||||
uint32 item_id;
|
||||
};
|
||||
|
||||
struct NewBazaarInspect_Struct {
|
||||
@@ -2929,10 +2933,17 @@ struct WhoAllPlayerPart4 {
|
||||
};
|
||||
|
||||
struct Trader_Struct {
|
||||
uint32 code;
|
||||
uint32 itemid[160];
|
||||
uint32 unknown;
|
||||
uint32 itemcost[80];
|
||||
uint32 action;
|
||||
uint32 unknown004;
|
||||
uint64 item_id[80];
|
||||
uint32 item_cost[80];
|
||||
};
|
||||
|
||||
struct BeginTrader_Struct {
|
||||
uint32 action;
|
||||
uint32 unknown04;
|
||||
uint64 serial_number[80];
|
||||
uint32 cost[80];
|
||||
};
|
||||
|
||||
struct ClickTrader_Struct {
|
||||
@@ -2945,30 +2956,30 @@ struct GetItems_Struct{
|
||||
uint32 items[80];
|
||||
};
|
||||
|
||||
struct BecomeTrader_Struct{
|
||||
uint32 id;
|
||||
uint32 code;
|
||||
struct BecomeTrader_Struct {
|
||||
uint32 entity_id;
|
||||
uint32 action;
|
||||
char trader_name[64];
|
||||
};
|
||||
|
||||
struct Trader_ShowItems_Struct{
|
||||
uint32 code;
|
||||
uint32 traderid;
|
||||
uint32 action;
|
||||
uint32 entity_id;
|
||||
uint32 unknown08[3];
|
||||
};
|
||||
|
||||
struct TraderBuy_Struct {
|
||||
/*000*/ uint32 Action;
|
||||
/*004*/ uint32 Unknown004;
|
||||
/*008*/ uint32 Price;
|
||||
/*012*/ uint32 Unknown008; // Probably high order bits of a 64 bit price.
|
||||
/*016*/ uint32 TraderID;
|
||||
/*020*/ char ItemName[64];
|
||||
/*084*/ uint32 Unknown076;
|
||||
/*088*/ uint32 ItemID;
|
||||
/*092*/ uint32 AlreadySold;
|
||||
/*096*/ uint32 Quantity;
|
||||
/*100*/ uint32 Unknown092;
|
||||
/*104*/
|
||||
uint32 action;
|
||||
uint32 unknown_004;
|
||||
uint32 price;
|
||||
uint32 unknown_008; // Probably high order bits of a 64 bit price.
|
||||
uint32 trader_id;
|
||||
char item_name[64];
|
||||
uint32 unknown_076;
|
||||
uint32 item_id;
|
||||
uint32 already_sold;
|
||||
uint32 quantity;
|
||||
uint32 unknown_092;
|
||||
};
|
||||
|
||||
struct TraderItemUpdate_Struct{
|
||||
@@ -3002,8 +3013,9 @@ struct TraderDelItem_Struct{
|
||||
};
|
||||
|
||||
struct TraderClick_Struct{
|
||||
uint32 traderid;
|
||||
uint32 unknown4[2];
|
||||
uint32 trader_id;
|
||||
uint32 action;
|
||||
uint32 unknown_004;
|
||||
uint32 approval;
|
||||
};
|
||||
|
||||
@@ -4674,6 +4686,30 @@ struct SayLinkBodyFrame_Struct {
|
||||
/*050*/
|
||||
};
|
||||
|
||||
struct TraderPriceUpdate_Struct {
|
||||
/*000*/ uint32 action;
|
||||
/*004*/ uint32 sub_action;
|
||||
/*008*/ int32 serial_number;
|
||||
/*012*/ uint32 unknown_012;
|
||||
/*016*/ uint32 new_price;
|
||||
/*020*/ uint32 unknown_016;
|
||||
};
|
||||
|
||||
enum UFBazaarTraderBuyerActions {
|
||||
Zero = 0,
|
||||
BeginTraderMode = 1,
|
||||
EndTraderMode = 2,
|
||||
PriceUpdate = 3,
|
||||
EndTransaction = 4,
|
||||
BazaarSearch = 7,
|
||||
WelcomeMessage = 9,
|
||||
BuyTraderItem = 10,
|
||||
ListTraderItems = 11,
|
||||
BazaarInspect = 18,
|
||||
ItemMove = 19,
|
||||
ReconcileItems = 20
|
||||
};
|
||||
|
||||
}; /*structs*/
|
||||
|
||||
}; /*UF*/
|
||||
|
||||
@@ -0,0 +1,499 @@
|
||||
/**
|
||||
* 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_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H
|
||||
#define EQEMU_BASE_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseCharacterParcelsContainersRepository {
|
||||
public:
|
||||
struct CharacterParcelsContainers {
|
||||
uint32_t id;
|
||||
uint32_t parcels_id;
|
||||
uint32_t slot_id;
|
||||
uint32_t item_id;
|
||||
uint32_t aug_slot_1;
|
||||
uint32_t aug_slot_2;
|
||||
uint32_t aug_slot_3;
|
||||
uint32_t aug_slot_4;
|
||||
uint32_t aug_slot_5;
|
||||
uint32_t aug_slot_6;
|
||||
uint32_t quantity;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"parcels_id",
|
||||
"slot_id",
|
||||
"item_id",
|
||||
"aug_slot_1",
|
||||
"aug_slot_2",
|
||||
"aug_slot_3",
|
||||
"aug_slot_4",
|
||||
"aug_slot_5",
|
||||
"aug_slot_6",
|
||||
"quantity",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"parcels_id",
|
||||
"slot_id",
|
||||
"item_id",
|
||||
"aug_slot_1",
|
||||
"aug_slot_2",
|
||||
"aug_slot_3",
|
||||
"aug_slot_4",
|
||||
"aug_slot_5",
|
||||
"aug_slot_6",
|
||||
"quantity",
|
||||
};
|
||||
}
|
||||
|
||||
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("character_parcels_containers");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static CharacterParcelsContainers NewEntity()
|
||||
{
|
||||
CharacterParcelsContainers e{};
|
||||
|
||||
e.id = 0;
|
||||
e.parcels_id = 0;
|
||||
e.slot_id = 0;
|
||||
e.item_id = 0;
|
||||
e.aug_slot_1 = 0;
|
||||
e.aug_slot_2 = 0;
|
||||
e.aug_slot_3 = 0;
|
||||
e.aug_slot_4 = 0;
|
||||
e.aug_slot_5 = 0;
|
||||
e.aug_slot_6 = 0;
|
||||
e.quantity = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static CharacterParcelsContainers GetCharacterParcelsContainers(
|
||||
const std::vector<CharacterParcelsContainers> &character_parcels_containerss,
|
||||
int character_parcels_containers_id
|
||||
)
|
||||
{
|
||||
for (auto &character_parcels_containers : character_parcels_containerss) {
|
||||
if (character_parcels_containers.id == character_parcels_containers_id) {
|
||||
return character_parcels_containers;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static CharacterParcelsContainers FindOne(
|
||||
Database& db,
|
||||
int character_parcels_containers_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE {} = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
PrimaryKey(),
|
||||
character_parcels_containers_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
CharacterParcelsContainers e{};
|
||||
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.parcels_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.slot_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.aug_slot_1 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.aug_slot_2 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.aug_slot_3 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.aug_slot_4 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.aug_slot_5 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.aug_slot_6 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int character_parcels_containers_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
character_parcels_containers_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const CharacterParcelsContainers &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.parcels_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.slot_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.item_id));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.aug_slot_1));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.aug_slot_2));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.aug_slot_3));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.aug_slot_4));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.aug_slot_5));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.aug_slot_6));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.quantity));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static CharacterParcelsContainers InsertOne(
|
||||
Database& db,
|
||||
CharacterParcelsContainers e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.parcels_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.aug_slot_1));
|
||||
v.push_back(std::to_string(e.aug_slot_2));
|
||||
v.push_back(std::to_string(e.aug_slot_3));
|
||||
v.push_back(std::to_string(e.aug_slot_4));
|
||||
v.push_back(std::to_string(e.aug_slot_5));
|
||||
v.push_back(std::to_string(e.aug_slot_6));
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
|
||||
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<CharacterParcelsContainers> &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.parcels_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.aug_slot_1));
|
||||
v.push_back(std::to_string(e.aug_slot_2));
|
||||
v.push_back(std::to_string(e.aug_slot_3));
|
||||
v.push_back(std::to_string(e.aug_slot_4));
|
||||
v.push_back(std::to_string(e.aug_slot_5));
|
||||
v.push_back(std::to_string(e.aug_slot_6));
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
|
||||
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<CharacterParcelsContainers> All(Database& db)
|
||||
{
|
||||
std::vector<CharacterParcelsContainers> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
CharacterParcelsContainers e{};
|
||||
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.parcels_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.slot_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.aug_slot_1 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.aug_slot_2 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.aug_slot_3 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.aug_slot_4 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.aug_slot_5 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.aug_slot_6 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<CharacterParcelsContainers> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<CharacterParcelsContainers> 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) {
|
||||
CharacterParcelsContainers e{};
|
||||
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.parcels_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.slot_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.aug_slot_1 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.aug_slot_2 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.aug_slot_3 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.aug_slot_4 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.aug_slot_5 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.aug_slot_6 = row[9] ? static_cast<uint32_t>(strtoul(row[9], nullptr, 10)) : 0;
|
||||
e.quantity = row[10] ? static_cast<uint32_t>(strtoul(row[10], nullptr, 10)) : 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 CharacterParcelsContainers &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.parcels_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.aug_slot_1));
|
||||
v.push_back(std::to_string(e.aug_slot_2));
|
||||
v.push_back(std::to_string(e.aug_slot_3));
|
||||
v.push_back(std::to_string(e.aug_slot_4));
|
||||
v.push_back(std::to_string(e.aug_slot_5));
|
||||
v.push_back(std::to_string(e.aug_slot_6));
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
|
||||
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<CharacterParcelsContainers> &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.parcels_id));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.aug_slot_1));
|
||||
v.push_back(std::to_string(e.aug_slot_2));
|
||||
v.push_back(std::to_string(e.aug_slot_3));
|
||||
v.push_back(std::to_string(e.aug_slot_4));
|
||||
v.push_back(std::to_string(e.aug_slot_5));
|
||||
v.push_back(std::to_string(e.aug_slot_6));
|
||||
v.push_back(std::to_string(e.quantity));
|
||||
|
||||
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_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H
|
||||
@@ -19,18 +19,22 @@
|
||||
class BaseNpcSpellsEntriesRepository {
|
||||
public:
|
||||
struct NpcSpellsEntries {
|
||||
uint32_t id;
|
||||
int32_t npc_spells_id;
|
||||
uint16_t spellid;
|
||||
uint32_t type;
|
||||
uint8_t minlevel;
|
||||
uint8_t maxlevel;
|
||||
int16_t manacost;
|
||||
int32_t recast_delay;
|
||||
int16_t priority;
|
||||
int32_t resist_adjust;
|
||||
int16_t min_hp;
|
||||
int16_t max_hp;
|
||||
uint32_t id;
|
||||
int32_t npc_spells_id;
|
||||
uint16_t spellid;
|
||||
uint32_t type;
|
||||
uint8_t minlevel;
|
||||
uint8_t maxlevel;
|
||||
int16_t manacost;
|
||||
int32_t recast_delay;
|
||||
int16_t priority;
|
||||
int32_t resist_adjust;
|
||||
int16_t min_hp;
|
||||
int16_t max_hp;
|
||||
int8_t min_expansion;
|
||||
int8_t max_expansion;
|
||||
std::string content_flags;
|
||||
std::string content_flags_disabled;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
@@ -53,6 +57,10 @@ public:
|
||||
"resist_adjust",
|
||||
"min_hp",
|
||||
"max_hp",
|
||||
"min_expansion",
|
||||
"max_expansion",
|
||||
"content_flags",
|
||||
"content_flags_disabled",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,6 +79,10 @@ public:
|
||||
"resist_adjust",
|
||||
"min_hp",
|
||||
"max_hp",
|
||||
"min_expansion",
|
||||
"max_expansion",
|
||||
"content_flags",
|
||||
"content_flags_disabled",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -111,18 +123,22 @@ public:
|
||||
{
|
||||
NpcSpellsEntries e{};
|
||||
|
||||
e.id = 0;
|
||||
e.npc_spells_id = 0;
|
||||
e.spellid = 0;
|
||||
e.type = 0;
|
||||
e.minlevel = 0;
|
||||
e.maxlevel = 255;
|
||||
e.manacost = -1;
|
||||
e.recast_delay = -1;
|
||||
e.priority = 0;
|
||||
e.resist_adjust = 0;
|
||||
e.min_hp = 0;
|
||||
e.max_hp = 0;
|
||||
e.id = 0;
|
||||
e.npc_spells_id = 0;
|
||||
e.spellid = 0;
|
||||
e.type = 0;
|
||||
e.minlevel = 0;
|
||||
e.maxlevel = 255;
|
||||
e.manacost = -1;
|
||||
e.recast_delay = -1;
|
||||
e.priority = 0;
|
||||
e.resist_adjust = 0;
|
||||
e.min_hp = 0;
|
||||
e.max_hp = 0;
|
||||
e.min_expansion = -1;
|
||||
e.max_expansion = -1;
|
||||
e.content_flags = "";
|
||||
e.content_flags_disabled = "";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -159,18 +175,22 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
NpcSpellsEntries e{};
|
||||
|
||||
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<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;
|
||||
e.manacost = row[6] ? static_cast<int16_t>(atoi(row[6])) : -1;
|
||||
e.recast_delay = row[7] ? static_cast<int32_t>(atoi(row[7])) : -1;
|
||||
e.priority = row[8] ? static_cast<int16_t>(atoi(row[8])) : 0;
|
||||
e.resist_adjust = row[9] ? static_cast<int32_t>(atoi(row[9])) : 0;
|
||||
e.min_hp = row[10] ? static_cast<int16_t>(atoi(row[10])) : 0;
|
||||
e.max_hp = row[11] ? static_cast<int16_t>(atoi(row[11])) : 0;
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.spellid = 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;
|
||||
e.manacost = row[6] ? static_cast<int16_t>(atoi(row[6])) : -1;
|
||||
e.recast_delay = row[7] ? static_cast<int32_t>(atoi(row[7])) : -1;
|
||||
e.priority = row[8] ? static_cast<int16_t>(atoi(row[8])) : 0;
|
||||
e.resist_adjust = row[9] ? static_cast<int32_t>(atoi(row[9])) : 0;
|
||||
e.min_hp = row[10] ? static_cast<int16_t>(atoi(row[10])) : 0;
|
||||
e.max_hp = row[11] ? static_cast<int16_t>(atoi(row[11])) : 0;
|
||||
e.min_expansion = row[12] ? static_cast<int8_t>(atoi(row[12])) : -1;
|
||||
e.max_expansion = row[13] ? static_cast<int8_t>(atoi(row[13])) : -1;
|
||||
e.content_flags = row[14] ? row[14] : "";
|
||||
e.content_flags_disabled = row[15] ? row[15] : "";
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -215,6 +235,10 @@ public:
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.resist_adjust));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.min_hp));
|
||||
v.push_back(columns[11] + " = " + std::to_string(e.max_hp));
|
||||
v.push_back(columns[12] + " = " + std::to_string(e.min_expansion));
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.max_expansion));
|
||||
v.push_back(columns[14] + " = '" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back(columns[15] + " = '" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -248,6 +272,10 @@ public:
|
||||
v.push_back(std::to_string(e.resist_adjust));
|
||||
v.push_back(std::to_string(e.min_hp));
|
||||
v.push_back(std::to_string(e.max_hp));
|
||||
v.push_back(std::to_string(e.min_expansion));
|
||||
v.push_back(std::to_string(e.max_expansion));
|
||||
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -289,6 +317,10 @@ public:
|
||||
v.push_back(std::to_string(e.resist_adjust));
|
||||
v.push_back(std::to_string(e.min_hp));
|
||||
v.push_back(std::to_string(e.max_hp));
|
||||
v.push_back(std::to_string(e.min_expansion));
|
||||
v.push_back(std::to_string(e.max_expansion));
|
||||
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -322,18 +354,22 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
NpcSpellsEntries e{};
|
||||
|
||||
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<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;
|
||||
e.manacost = row[6] ? static_cast<int16_t>(atoi(row[6])) : -1;
|
||||
e.recast_delay = row[7] ? static_cast<int32_t>(atoi(row[7])) : -1;
|
||||
e.priority = row[8] ? static_cast<int16_t>(atoi(row[8])) : 0;
|
||||
e.resist_adjust = row[9] ? static_cast<int32_t>(atoi(row[9])) : 0;
|
||||
e.min_hp = row[10] ? static_cast<int16_t>(atoi(row[10])) : 0;
|
||||
e.max_hp = row[11] ? static_cast<int16_t>(atoi(row[11])) : 0;
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.spellid = 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;
|
||||
e.manacost = row[6] ? static_cast<int16_t>(atoi(row[6])) : -1;
|
||||
e.recast_delay = row[7] ? static_cast<int32_t>(atoi(row[7])) : -1;
|
||||
e.priority = row[8] ? static_cast<int16_t>(atoi(row[8])) : 0;
|
||||
e.resist_adjust = row[9] ? static_cast<int32_t>(atoi(row[9])) : 0;
|
||||
e.min_hp = row[10] ? static_cast<int16_t>(atoi(row[10])) : 0;
|
||||
e.max_hp = row[11] ? static_cast<int16_t>(atoi(row[11])) : 0;
|
||||
e.min_expansion = row[12] ? static_cast<int8_t>(atoi(row[12])) : -1;
|
||||
e.max_expansion = row[13] ? static_cast<int8_t>(atoi(row[13])) : -1;
|
||||
e.content_flags = row[14] ? row[14] : "";
|
||||
e.content_flags_disabled = row[15] ? row[15] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -358,18 +394,22 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
NpcSpellsEntries e{};
|
||||
|
||||
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<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;
|
||||
e.manacost = row[6] ? static_cast<int16_t>(atoi(row[6])) : -1;
|
||||
e.recast_delay = row[7] ? static_cast<int32_t>(atoi(row[7])) : -1;
|
||||
e.priority = row[8] ? static_cast<int16_t>(atoi(row[8])) : 0;
|
||||
e.resist_adjust = row[9] ? static_cast<int32_t>(atoi(row[9])) : 0;
|
||||
e.min_hp = row[10] ? static_cast<int16_t>(atoi(row[10])) : 0;
|
||||
e.max_hp = row[11] ? static_cast<int16_t>(atoi(row[11])) : 0;
|
||||
e.id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[1] ? static_cast<int32_t>(atoi(row[1])) : 0;
|
||||
e.spellid = 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;
|
||||
e.manacost = row[6] ? static_cast<int16_t>(atoi(row[6])) : -1;
|
||||
e.recast_delay = row[7] ? static_cast<int32_t>(atoi(row[7])) : -1;
|
||||
e.priority = row[8] ? static_cast<int16_t>(atoi(row[8])) : 0;
|
||||
e.resist_adjust = row[9] ? static_cast<int32_t>(atoi(row[9])) : 0;
|
||||
e.min_hp = row[10] ? static_cast<int16_t>(atoi(row[10])) : 0;
|
||||
e.max_hp = row[11] ? static_cast<int16_t>(atoi(row[11])) : 0;
|
||||
e.min_expansion = row[12] ? static_cast<int8_t>(atoi(row[12])) : -1;
|
||||
e.max_expansion = row[13] ? static_cast<int8_t>(atoi(row[13])) : -1;
|
||||
e.content_flags = row[14] ? row[14] : "";
|
||||
e.content_flags_disabled = row[15] ? row[15] : "";
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -456,6 +496,10 @@ public:
|
||||
v.push_back(std::to_string(e.resist_adjust));
|
||||
v.push_back(std::to_string(e.min_hp));
|
||||
v.push_back(std::to_string(e.max_hp));
|
||||
v.push_back(std::to_string(e.min_expansion));
|
||||
v.push_back(std::to_string(e.max_expansion));
|
||||
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -490,6 +534,10 @@ public:
|
||||
v.push_back(std::to_string(e.resist_adjust));
|
||||
v.push_back(std::to_string(e.min_hp));
|
||||
v.push_back(std::to_string(e.max_hp));
|
||||
v.push_back(std::to_string(e.min_expansion));
|
||||
v.push_back(std::to_string(e.max_expansion));
|
||||
v.push_back("'" + Strings::Escape(e.content_flags) + "'");
|
||||
v.push_back("'" + Strings::Escape(e.content_flags_disabled) + "'");
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ public:
|
||||
int64_t mana_regen_rate;
|
||||
uint32_t loottable_id;
|
||||
uint32_t merchant_id;
|
||||
uint8_t greed;
|
||||
uint32_t alt_currency_id;
|
||||
uint32_t npc_spells_id;
|
||||
uint32_t npc_spells_effects_id;
|
||||
@@ -176,6 +177,7 @@ public:
|
||||
"mana_regen_rate",
|
||||
"loottable_id",
|
||||
"merchant_id",
|
||||
"greed",
|
||||
"alt_currency_id",
|
||||
"npc_spells_id",
|
||||
"npc_spells_effects_id",
|
||||
@@ -310,6 +312,7 @@ public:
|
||||
"mana_regen_rate",
|
||||
"loottable_id",
|
||||
"merchant_id",
|
||||
"greed",
|
||||
"alt_currency_id",
|
||||
"npc_spells_id",
|
||||
"npc_spells_effects_id",
|
||||
@@ -478,6 +481,7 @@ public:
|
||||
e.mana_regen_rate = 0;
|
||||
e.loottable_id = 0;
|
||||
e.merchant_id = 0;
|
||||
e.greed = 0;
|
||||
e.alt_currency_id = 0;
|
||||
e.npc_spells_id = 0;
|
||||
e.npc_spells_effects_id = 0;
|
||||
@@ -642,115 +646,116 @@ public:
|
||||
e.mana_regen_rate = row[16] ? strtoll(row[16], nullptr, 10) : 0;
|
||||
e.loottable_id = row[17] ? static_cast<uint32_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||
e.merchant_id = row[18] ? static_cast<uint32_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||
e.alt_currency_id = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.npc_spells_effects_id = row[21] ? static_cast<uint32_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||
e.npc_faction_id = row[22] ? static_cast<int32_t>(atoi(row[22])) : 0;
|
||||
e.adventure_template_id = row[23] ? static_cast<uint32_t>(strtoul(row[23], nullptr, 10)) : 0;
|
||||
e.trap_template = row[24] ? static_cast<uint32_t>(strtoul(row[24], nullptr, 10)) : 0;
|
||||
e.mindmg = row[25] ? static_cast<uint32_t>(strtoul(row[25], nullptr, 10)) : 0;
|
||||
e.maxdmg = row[26] ? static_cast<uint32_t>(strtoul(row[26], nullptr, 10)) : 0;
|
||||
e.attack_count = row[27] ? static_cast<int16_t>(atoi(row[27])) : -1;
|
||||
e.npcspecialattks = row[28] ? row[28] : "";
|
||||
e.special_abilities = row[29] ? row[29] : "";
|
||||
e.aggroradius = row[30] ? static_cast<uint32_t>(strtoul(row[30], nullptr, 10)) : 0;
|
||||
e.assistradius = row[31] ? static_cast<uint32_t>(strtoul(row[31], nullptr, 10)) : 0;
|
||||
e.face = row[32] ? static_cast<uint32_t>(strtoul(row[32], nullptr, 10)) : 1;
|
||||
e.luclin_hairstyle = row[33] ? static_cast<uint32_t>(strtoul(row[33], nullptr, 10)) : 1;
|
||||
e.luclin_haircolor = row[34] ? static_cast<uint32_t>(strtoul(row[34], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor = row[35] ? static_cast<uint32_t>(strtoul(row[35], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor2 = row[36] ? static_cast<uint32_t>(strtoul(row[36], nullptr, 10)) : 1;
|
||||
e.luclin_beardcolor = row[37] ? static_cast<uint32_t>(strtoul(row[37], nullptr, 10)) : 1;
|
||||
e.luclin_beard = row[38] ? static_cast<uint32_t>(strtoul(row[38], nullptr, 10)) : 0;
|
||||
e.drakkin_heritage = row[39] ? static_cast<int32_t>(atoi(row[39])) : 0;
|
||||
e.drakkin_tattoo = row[40] ? static_cast<int32_t>(atoi(row[40])) : 0;
|
||||
e.drakkin_details = row[41] ? static_cast<int32_t>(atoi(row[41])) : 0;
|
||||
e.armortint_id = row[42] ? static_cast<uint32_t>(strtoul(row[42], nullptr, 10)) : 0;
|
||||
e.armortint_red = row[43] ? static_cast<uint8_t>(strtoul(row[43], nullptr, 10)) : 0;
|
||||
e.armortint_green = row[44] ? static_cast<uint8_t>(strtoul(row[44], nullptr, 10)) : 0;
|
||||
e.armortint_blue = row[45] ? static_cast<uint8_t>(strtoul(row[45], nullptr, 10)) : 0;
|
||||
e.d_melee_texture1 = row[46] ? static_cast<uint32_t>(strtoul(row[46], nullptr, 10)) : 0;
|
||||
e.d_melee_texture2 = row[47] ? static_cast<uint32_t>(strtoul(row[47], nullptr, 10)) : 0;
|
||||
e.ammo_idfile = row[48] ? row[48] : "IT10";
|
||||
e.prim_melee_type = row[49] ? static_cast<uint8_t>(strtoul(row[49], nullptr, 10)) : 28;
|
||||
e.sec_melee_type = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 28;
|
||||
e.ranged_type = row[51] ? static_cast<uint8_t>(strtoul(row[51], nullptr, 10)) : 7;
|
||||
e.runspeed = row[52] ? strtof(row[52], nullptr) : 0;
|
||||
e.MR = row[53] ? static_cast<int16_t>(atoi(row[53])) : 0;
|
||||
e.CR = row[54] ? static_cast<int16_t>(atoi(row[54])) : 0;
|
||||
e.DR = row[55] ? static_cast<int16_t>(atoi(row[55])) : 0;
|
||||
e.FR = row[56] ? static_cast<int16_t>(atoi(row[56])) : 0;
|
||||
e.PR = row[57] ? static_cast<int16_t>(atoi(row[57])) : 0;
|
||||
e.Corrup = row[58] ? static_cast<int16_t>(atoi(row[58])) : 0;
|
||||
e.PhR = row[59] ? static_cast<uint16_t>(strtoul(row[59], nullptr, 10)) : 0;
|
||||
e.see_invis = row[60] ? static_cast<int16_t>(atoi(row[60])) : 0;
|
||||
e.see_invis_undead = row[61] ? static_cast<int16_t>(atoi(row[61])) : 0;
|
||||
e.qglobal = row[62] ? static_cast<uint32_t>(strtoul(row[62], nullptr, 10)) : 0;
|
||||
e.AC = row[63] ? static_cast<int16_t>(atoi(row[63])) : 0;
|
||||
e.npc_aggro = row[64] ? static_cast<int8_t>(atoi(row[64])) : 0;
|
||||
e.spawn_limit = row[65] ? static_cast<int8_t>(atoi(row[65])) : 0;
|
||||
e.attack_speed = row[66] ? strtof(row[66], nullptr) : 0;
|
||||
e.attack_delay = row[67] ? static_cast<uint8_t>(strtoul(row[67], nullptr, 10)) : 30;
|
||||
e.findable = row[68] ? static_cast<int8_t>(atoi(row[68])) : 0;
|
||||
e.STR = row[69] ? static_cast<uint32_t>(strtoul(row[69], nullptr, 10)) : 75;
|
||||
e.STA = row[70] ? static_cast<uint32_t>(strtoul(row[70], nullptr, 10)) : 75;
|
||||
e.DEX = row[71] ? static_cast<uint32_t>(strtoul(row[71], nullptr, 10)) : 75;
|
||||
e.AGI = row[72] ? static_cast<uint32_t>(strtoul(row[72], nullptr, 10)) : 75;
|
||||
e._INT = row[73] ? static_cast<uint32_t>(strtoul(row[73], nullptr, 10)) : 80;
|
||||
e.WIS = row[74] ? static_cast<uint32_t>(strtoul(row[74], nullptr, 10)) : 75;
|
||||
e.CHA = row[75] ? static_cast<uint32_t>(strtoul(row[75], nullptr, 10)) : 75;
|
||||
e.see_hide = row[76] ? static_cast<int8_t>(atoi(row[76])) : 0;
|
||||
e.see_improved_hide = row[77] ? static_cast<int8_t>(atoi(row[77])) : 0;
|
||||
e.trackable = row[78] ? static_cast<int8_t>(atoi(row[78])) : 1;
|
||||
e.isbot = row[79] ? static_cast<int8_t>(atoi(row[79])) : 0;
|
||||
e.exclude = row[80] ? static_cast<int8_t>(atoi(row[80])) : 1;
|
||||
e.ATK = row[81] ? static_cast<int32_t>(atoi(row[81])) : 0;
|
||||
e.Accuracy = row[82] ? static_cast<int32_t>(atoi(row[82])) : 0;
|
||||
e.Avoidance = row[83] ? static_cast<uint32_t>(strtoul(row[83], nullptr, 10)) : 0;
|
||||
e.slow_mitigation = row[84] ? static_cast<int16_t>(atoi(row[84])) : 0;
|
||||
e.version = row[85] ? static_cast<uint16_t>(strtoul(row[85], nullptr, 10)) : 0;
|
||||
e.maxlevel = row[86] ? static_cast<int8_t>(atoi(row[86])) : 0;
|
||||
e.scalerate = row[87] ? static_cast<int32_t>(atoi(row[87])) : 100;
|
||||
e.private_corpse = row[88] ? static_cast<uint8_t>(strtoul(row[88], nullptr, 10)) : 0;
|
||||
e.unique_spawn_by_name = row[89] ? static_cast<uint8_t>(strtoul(row[89], nullptr, 10)) : 0;
|
||||
e.underwater = row[90] ? static_cast<uint8_t>(strtoul(row[90], nullptr, 10)) : 0;
|
||||
e.isquest = row[91] ? static_cast<int8_t>(atoi(row[91])) : 0;
|
||||
e.emoteid = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 0;
|
||||
e.spellscale = row[93] ? strtof(row[93], nullptr) : 100;
|
||||
e.healscale = row[94] ? strtof(row[94], nullptr) : 100;
|
||||
e.no_target_hotkey = row[95] ? static_cast<uint8_t>(strtoul(row[95], nullptr, 10)) : 0;
|
||||
e.raid_target = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 0;
|
||||
e.armtexture = row[97] ? static_cast<int8_t>(atoi(row[97])) : 0;
|
||||
e.bracertexture = row[98] ? static_cast<int8_t>(atoi(row[98])) : 0;
|
||||
e.handtexture = row[99] ? static_cast<int8_t>(atoi(row[99])) : 0;
|
||||
e.legtexture = row[100] ? static_cast<int8_t>(atoi(row[100])) : 0;
|
||||
e.feettexture = row[101] ? static_cast<int8_t>(atoi(row[101])) : 0;
|
||||
e.light = row[102] ? static_cast<int8_t>(atoi(row[102])) : 0;
|
||||
e.walkspeed = row[103] ? static_cast<int8_t>(atoi(row[103])) : 0;
|
||||
e.peqid = row[104] ? static_cast<int32_t>(atoi(row[104])) : 0;
|
||||
e.unique_ = row[105] ? static_cast<int8_t>(atoi(row[105])) : 0;
|
||||
e.fixed = row[106] ? static_cast<int8_t>(atoi(row[106])) : 0;
|
||||
e.ignore_despawn = row[107] ? static_cast<int8_t>(atoi(row[107])) : 0;
|
||||
e.show_name = row[108] ? static_cast<int8_t>(atoi(row[108])) : 1;
|
||||
e.untargetable = row[109] ? static_cast<int8_t>(atoi(row[109])) : 0;
|
||||
e.charm_ac = row[110] ? static_cast<int16_t>(atoi(row[110])) : 0;
|
||||
e.charm_min_dmg = row[111] ? static_cast<int32_t>(atoi(row[111])) : 0;
|
||||
e.charm_max_dmg = row[112] ? static_cast<int32_t>(atoi(row[112])) : 0;
|
||||
e.charm_attack_delay = row[113] ? static_cast<int8_t>(atoi(row[113])) : 0;
|
||||
e.charm_accuracy_rating = row[114] ? static_cast<int32_t>(atoi(row[114])) : 0;
|
||||
e.charm_avoidance_rating = row[115] ? static_cast<int32_t>(atoi(row[115])) : 0;
|
||||
e.charm_atk = row[116] ? static_cast<int32_t>(atoi(row[116])) : 0;
|
||||
e.skip_global_loot = row[117] ? static_cast<int8_t>(atoi(row[117])) : 0;
|
||||
e.rare_spawn = row[118] ? static_cast<int8_t>(atoi(row[118])) : 0;
|
||||
e.stuck_behavior = row[119] ? static_cast<int8_t>(atoi(row[119])) : 0;
|
||||
e.model = row[120] ? static_cast<int16_t>(atoi(row[120])) : 0;
|
||||
e.flymode = row[121] ? static_cast<int8_t>(atoi(row[121])) : -1;
|
||||
e.always_aggro = row[122] ? static_cast<int8_t>(atoi(row[122])) : 0;
|
||||
e.exp_mod = row[123] ? static_cast<int32_t>(atoi(row[123])) : 100;
|
||||
e.heroic_strikethrough = row[124] ? static_cast<int32_t>(atoi(row[124])) : 0;
|
||||
e.faction_amount = row[125] ? static_cast<int32_t>(atoi(row[125])) : 0;
|
||||
e.keeps_sold_items = row[126] ? static_cast<uint8_t>(strtoul(row[126], nullptr, 10)) : 1;
|
||||
e.is_parcel_merchant = row[127] ? static_cast<uint8_t>(strtoul(row[127], nullptr, 10)) : 0;
|
||||
e.greed = row[19] ? static_cast<uint8_t>(strtoul(row[19], nullptr, 10)) : 0;
|
||||
e.alt_currency_id = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[21] ? static_cast<uint32_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||
e.npc_spells_effects_id = row[22] ? static_cast<uint32_t>(strtoul(row[22], nullptr, 10)) : 0;
|
||||
e.npc_faction_id = row[23] ? static_cast<int32_t>(atoi(row[23])) : 0;
|
||||
e.adventure_template_id = row[24] ? static_cast<uint32_t>(strtoul(row[24], nullptr, 10)) : 0;
|
||||
e.trap_template = row[25] ? static_cast<uint32_t>(strtoul(row[25], nullptr, 10)) : 0;
|
||||
e.mindmg = row[26] ? static_cast<uint32_t>(strtoul(row[26], nullptr, 10)) : 0;
|
||||
e.maxdmg = row[27] ? static_cast<uint32_t>(strtoul(row[27], nullptr, 10)) : 0;
|
||||
e.attack_count = row[28] ? static_cast<int16_t>(atoi(row[28])) : -1;
|
||||
e.npcspecialattks = row[29] ? row[29] : "";
|
||||
e.special_abilities = row[30] ? row[30] : "";
|
||||
e.aggroradius = row[31] ? static_cast<uint32_t>(strtoul(row[31], nullptr, 10)) : 0;
|
||||
e.assistradius = row[32] ? static_cast<uint32_t>(strtoul(row[32], nullptr, 10)) : 0;
|
||||
e.face = row[33] ? static_cast<uint32_t>(strtoul(row[33], nullptr, 10)) : 1;
|
||||
e.luclin_hairstyle = row[34] ? static_cast<uint32_t>(strtoul(row[34], nullptr, 10)) : 1;
|
||||
e.luclin_haircolor = row[35] ? static_cast<uint32_t>(strtoul(row[35], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor = row[36] ? static_cast<uint32_t>(strtoul(row[36], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor2 = row[37] ? static_cast<uint32_t>(strtoul(row[37], nullptr, 10)) : 1;
|
||||
e.luclin_beardcolor = row[38] ? static_cast<uint32_t>(strtoul(row[38], nullptr, 10)) : 1;
|
||||
e.luclin_beard = row[39] ? static_cast<uint32_t>(strtoul(row[39], nullptr, 10)) : 0;
|
||||
e.drakkin_heritage = row[40] ? static_cast<int32_t>(atoi(row[40])) : 0;
|
||||
e.drakkin_tattoo = row[41] ? static_cast<int32_t>(atoi(row[41])) : 0;
|
||||
e.drakkin_details = row[42] ? static_cast<int32_t>(atoi(row[42])) : 0;
|
||||
e.armortint_id = row[43] ? static_cast<uint32_t>(strtoul(row[43], nullptr, 10)) : 0;
|
||||
e.armortint_red = row[44] ? static_cast<uint8_t>(strtoul(row[44], nullptr, 10)) : 0;
|
||||
e.armortint_green = row[45] ? static_cast<uint8_t>(strtoul(row[45], nullptr, 10)) : 0;
|
||||
e.armortint_blue = row[46] ? static_cast<uint8_t>(strtoul(row[46], nullptr, 10)) : 0;
|
||||
e.d_melee_texture1 = row[47] ? static_cast<uint32_t>(strtoul(row[47], nullptr, 10)) : 0;
|
||||
e.d_melee_texture2 = row[48] ? static_cast<uint32_t>(strtoul(row[48], nullptr, 10)) : 0;
|
||||
e.ammo_idfile = row[49] ? row[49] : "IT10";
|
||||
e.prim_melee_type = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 28;
|
||||
e.sec_melee_type = row[51] ? static_cast<uint8_t>(strtoul(row[51], nullptr, 10)) : 28;
|
||||
e.ranged_type = row[52] ? static_cast<uint8_t>(strtoul(row[52], nullptr, 10)) : 7;
|
||||
e.runspeed = row[53] ? strtof(row[53], nullptr) : 0;
|
||||
e.MR = row[54] ? static_cast<int16_t>(atoi(row[54])) : 0;
|
||||
e.CR = row[55] ? static_cast<int16_t>(atoi(row[55])) : 0;
|
||||
e.DR = row[56] ? static_cast<int16_t>(atoi(row[56])) : 0;
|
||||
e.FR = row[57] ? static_cast<int16_t>(atoi(row[57])) : 0;
|
||||
e.PR = row[58] ? static_cast<int16_t>(atoi(row[58])) : 0;
|
||||
e.Corrup = row[59] ? static_cast<int16_t>(atoi(row[59])) : 0;
|
||||
e.PhR = row[60] ? static_cast<uint16_t>(strtoul(row[60], nullptr, 10)) : 0;
|
||||
e.see_invis = row[61] ? static_cast<int16_t>(atoi(row[61])) : 0;
|
||||
e.see_invis_undead = row[62] ? static_cast<int16_t>(atoi(row[62])) : 0;
|
||||
e.qglobal = row[63] ? static_cast<uint32_t>(strtoul(row[63], nullptr, 10)) : 0;
|
||||
e.AC = row[64] ? static_cast<int16_t>(atoi(row[64])) : 0;
|
||||
e.npc_aggro = row[65] ? static_cast<int8_t>(atoi(row[65])) : 0;
|
||||
e.spawn_limit = row[66] ? static_cast<int8_t>(atoi(row[66])) : 0;
|
||||
e.attack_speed = row[67] ? strtof(row[67], nullptr) : 0;
|
||||
e.attack_delay = row[68] ? static_cast<uint8_t>(strtoul(row[68], nullptr, 10)) : 30;
|
||||
e.findable = row[69] ? static_cast<int8_t>(atoi(row[69])) : 0;
|
||||
e.STR = row[70] ? static_cast<uint32_t>(strtoul(row[70], nullptr, 10)) : 75;
|
||||
e.STA = row[71] ? static_cast<uint32_t>(strtoul(row[71], nullptr, 10)) : 75;
|
||||
e.DEX = row[72] ? static_cast<uint32_t>(strtoul(row[72], nullptr, 10)) : 75;
|
||||
e.AGI = row[73] ? static_cast<uint32_t>(strtoul(row[73], nullptr, 10)) : 75;
|
||||
e._INT = row[74] ? static_cast<uint32_t>(strtoul(row[74], nullptr, 10)) : 80;
|
||||
e.WIS = row[75] ? static_cast<uint32_t>(strtoul(row[75], nullptr, 10)) : 75;
|
||||
e.CHA = row[76] ? static_cast<uint32_t>(strtoul(row[76], nullptr, 10)) : 75;
|
||||
e.see_hide = row[77] ? static_cast<int8_t>(atoi(row[77])) : 0;
|
||||
e.see_improved_hide = row[78] ? static_cast<int8_t>(atoi(row[78])) : 0;
|
||||
e.trackable = row[79] ? static_cast<int8_t>(atoi(row[79])) : 1;
|
||||
e.isbot = row[80] ? static_cast<int8_t>(atoi(row[80])) : 0;
|
||||
e.exclude = row[81] ? static_cast<int8_t>(atoi(row[81])) : 1;
|
||||
e.ATK = row[82] ? static_cast<int32_t>(atoi(row[82])) : 0;
|
||||
e.Accuracy = row[83] ? static_cast<int32_t>(atoi(row[83])) : 0;
|
||||
e.Avoidance = row[84] ? static_cast<uint32_t>(strtoul(row[84], nullptr, 10)) : 0;
|
||||
e.slow_mitigation = row[85] ? static_cast<int16_t>(atoi(row[85])) : 0;
|
||||
e.version = row[86] ? static_cast<uint16_t>(strtoul(row[86], nullptr, 10)) : 0;
|
||||
e.maxlevel = row[87] ? static_cast<int8_t>(atoi(row[87])) : 0;
|
||||
e.scalerate = row[88] ? static_cast<int32_t>(atoi(row[88])) : 100;
|
||||
e.private_corpse = row[89] ? static_cast<uint8_t>(strtoul(row[89], nullptr, 10)) : 0;
|
||||
e.unique_spawn_by_name = row[90] ? static_cast<uint8_t>(strtoul(row[90], nullptr, 10)) : 0;
|
||||
e.underwater = row[91] ? static_cast<uint8_t>(strtoul(row[91], nullptr, 10)) : 0;
|
||||
e.isquest = row[92] ? static_cast<int8_t>(atoi(row[92])) : 0;
|
||||
e.emoteid = row[93] ? static_cast<uint32_t>(strtoul(row[93], nullptr, 10)) : 0;
|
||||
e.spellscale = row[94] ? strtof(row[94], nullptr) : 100;
|
||||
e.healscale = row[95] ? strtof(row[95], nullptr) : 100;
|
||||
e.no_target_hotkey = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 0;
|
||||
e.raid_target = row[97] ? static_cast<uint8_t>(strtoul(row[97], nullptr, 10)) : 0;
|
||||
e.armtexture = row[98] ? static_cast<int8_t>(atoi(row[98])) : 0;
|
||||
e.bracertexture = row[99] ? static_cast<int8_t>(atoi(row[99])) : 0;
|
||||
e.handtexture = row[100] ? static_cast<int8_t>(atoi(row[100])) : 0;
|
||||
e.legtexture = row[101] ? static_cast<int8_t>(atoi(row[101])) : 0;
|
||||
e.feettexture = row[102] ? static_cast<int8_t>(atoi(row[102])) : 0;
|
||||
e.light = row[103] ? static_cast<int8_t>(atoi(row[103])) : 0;
|
||||
e.walkspeed = row[104] ? static_cast<int8_t>(atoi(row[104])) : 0;
|
||||
e.peqid = row[105] ? static_cast<int32_t>(atoi(row[105])) : 0;
|
||||
e.unique_ = row[106] ? static_cast<int8_t>(atoi(row[106])) : 0;
|
||||
e.fixed = row[107] ? static_cast<int8_t>(atoi(row[107])) : 0;
|
||||
e.ignore_despawn = row[108] ? static_cast<int8_t>(atoi(row[108])) : 0;
|
||||
e.show_name = row[109] ? static_cast<int8_t>(atoi(row[109])) : 1;
|
||||
e.untargetable = row[110] ? static_cast<int8_t>(atoi(row[110])) : 0;
|
||||
e.charm_ac = row[111] ? static_cast<int16_t>(atoi(row[111])) : 0;
|
||||
e.charm_min_dmg = row[112] ? static_cast<int32_t>(atoi(row[112])) : 0;
|
||||
e.charm_max_dmg = row[113] ? static_cast<int32_t>(atoi(row[113])) : 0;
|
||||
e.charm_attack_delay = row[114] ? static_cast<int8_t>(atoi(row[114])) : 0;
|
||||
e.charm_accuracy_rating = row[115] ? static_cast<int32_t>(atoi(row[115])) : 0;
|
||||
e.charm_avoidance_rating = row[116] ? static_cast<int32_t>(atoi(row[116])) : 0;
|
||||
e.charm_atk = row[117] ? static_cast<int32_t>(atoi(row[117])) : 0;
|
||||
e.skip_global_loot = row[118] ? static_cast<int8_t>(atoi(row[118])) : 0;
|
||||
e.rare_spawn = row[119] ? static_cast<int8_t>(atoi(row[119])) : 0;
|
||||
e.stuck_behavior = row[120] ? static_cast<int8_t>(atoi(row[120])) : 0;
|
||||
e.model = row[121] ? static_cast<int16_t>(atoi(row[121])) : 0;
|
||||
e.flymode = row[122] ? static_cast<int8_t>(atoi(row[122])) : -1;
|
||||
e.always_aggro = row[123] ? static_cast<int8_t>(atoi(row[123])) : 0;
|
||||
e.exp_mod = row[124] ? static_cast<int32_t>(atoi(row[124])) : 100;
|
||||
e.heroic_strikethrough = row[125] ? static_cast<int32_t>(atoi(row[125])) : 0;
|
||||
e.faction_amount = row[126] ? static_cast<int32_t>(atoi(row[126])) : 0;
|
||||
e.keeps_sold_items = row[127] ? static_cast<uint8_t>(strtoul(row[127], nullptr, 10)) : 1;
|
||||
e.is_parcel_merchant = row[128] ? static_cast<uint8_t>(strtoul(row[128], nullptr, 10)) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -802,115 +807,116 @@ public:
|
||||
v.push_back(columns[16] + " = " + std::to_string(e.mana_regen_rate));
|
||||
v.push_back(columns[17] + " = " + std::to_string(e.loottable_id));
|
||||
v.push_back(columns[18] + " = " + std::to_string(e.merchant_id));
|
||||
v.push_back(columns[19] + " = " + std::to_string(e.alt_currency_id));
|
||||
v.push_back(columns[20] + " = " + std::to_string(e.npc_spells_id));
|
||||
v.push_back(columns[21] + " = " + std::to_string(e.npc_spells_effects_id));
|
||||
v.push_back(columns[22] + " = " + std::to_string(e.npc_faction_id));
|
||||
v.push_back(columns[23] + " = " + std::to_string(e.adventure_template_id));
|
||||
v.push_back(columns[24] + " = " + std::to_string(e.trap_template));
|
||||
v.push_back(columns[25] + " = " + std::to_string(e.mindmg));
|
||||
v.push_back(columns[26] + " = " + std::to_string(e.maxdmg));
|
||||
v.push_back(columns[27] + " = " + std::to_string(e.attack_count));
|
||||
v.push_back(columns[28] + " = '" + Strings::Escape(e.npcspecialattks) + "'");
|
||||
v.push_back(columns[29] + " = '" + Strings::Escape(e.special_abilities) + "'");
|
||||
v.push_back(columns[30] + " = " + std::to_string(e.aggroradius));
|
||||
v.push_back(columns[31] + " = " + std::to_string(e.assistradius));
|
||||
v.push_back(columns[32] + " = " + std::to_string(e.face));
|
||||
v.push_back(columns[33] + " = " + std::to_string(e.luclin_hairstyle));
|
||||
v.push_back(columns[34] + " = " + std::to_string(e.luclin_haircolor));
|
||||
v.push_back(columns[35] + " = " + std::to_string(e.luclin_eyecolor));
|
||||
v.push_back(columns[36] + " = " + std::to_string(e.luclin_eyecolor2));
|
||||
v.push_back(columns[37] + " = " + std::to_string(e.luclin_beardcolor));
|
||||
v.push_back(columns[38] + " = " + std::to_string(e.luclin_beard));
|
||||
v.push_back(columns[39] + " = " + std::to_string(e.drakkin_heritage));
|
||||
v.push_back(columns[40] + " = " + std::to_string(e.drakkin_tattoo));
|
||||
v.push_back(columns[41] + " = " + std::to_string(e.drakkin_details));
|
||||
v.push_back(columns[42] + " = " + std::to_string(e.armortint_id));
|
||||
v.push_back(columns[43] + " = " + std::to_string(e.armortint_red));
|
||||
v.push_back(columns[44] + " = " + std::to_string(e.armortint_green));
|
||||
v.push_back(columns[45] + " = " + std::to_string(e.armortint_blue));
|
||||
v.push_back(columns[46] + " = " + std::to_string(e.d_melee_texture1));
|
||||
v.push_back(columns[47] + " = " + std::to_string(e.d_melee_texture2));
|
||||
v.push_back(columns[48] + " = '" + Strings::Escape(e.ammo_idfile) + "'");
|
||||
v.push_back(columns[49] + " = " + std::to_string(e.prim_melee_type));
|
||||
v.push_back(columns[50] + " = " + std::to_string(e.sec_melee_type));
|
||||
v.push_back(columns[51] + " = " + std::to_string(e.ranged_type));
|
||||
v.push_back(columns[52] + " = " + std::to_string(e.runspeed));
|
||||
v.push_back(columns[53] + " = " + std::to_string(e.MR));
|
||||
v.push_back(columns[54] + " = " + std::to_string(e.CR));
|
||||
v.push_back(columns[55] + " = " + std::to_string(e.DR));
|
||||
v.push_back(columns[56] + " = " + std::to_string(e.FR));
|
||||
v.push_back(columns[57] + " = " + std::to_string(e.PR));
|
||||
v.push_back(columns[58] + " = " + std::to_string(e.Corrup));
|
||||
v.push_back(columns[59] + " = " + std::to_string(e.PhR));
|
||||
v.push_back(columns[60] + " = " + std::to_string(e.see_invis));
|
||||
v.push_back(columns[61] + " = " + std::to_string(e.see_invis_undead));
|
||||
v.push_back(columns[62] + " = " + std::to_string(e.qglobal));
|
||||
v.push_back(columns[63] + " = " + std::to_string(e.AC));
|
||||
v.push_back(columns[64] + " = " + std::to_string(e.npc_aggro));
|
||||
v.push_back(columns[65] + " = " + std::to_string(e.spawn_limit));
|
||||
v.push_back(columns[66] + " = " + std::to_string(e.attack_speed));
|
||||
v.push_back(columns[67] + " = " + std::to_string(e.attack_delay));
|
||||
v.push_back(columns[68] + " = " + std::to_string(e.findable));
|
||||
v.push_back(columns[69] + " = " + std::to_string(e.STR));
|
||||
v.push_back(columns[70] + " = " + std::to_string(e.STA));
|
||||
v.push_back(columns[71] + " = " + std::to_string(e.DEX));
|
||||
v.push_back(columns[72] + " = " + std::to_string(e.AGI));
|
||||
v.push_back(columns[73] + " = " + std::to_string(e._INT));
|
||||
v.push_back(columns[74] + " = " + std::to_string(e.WIS));
|
||||
v.push_back(columns[75] + " = " + std::to_string(e.CHA));
|
||||
v.push_back(columns[76] + " = " + std::to_string(e.see_hide));
|
||||
v.push_back(columns[77] + " = " + std::to_string(e.see_improved_hide));
|
||||
v.push_back(columns[78] + " = " + std::to_string(e.trackable));
|
||||
v.push_back(columns[79] + " = " + std::to_string(e.isbot));
|
||||
v.push_back(columns[80] + " = " + std::to_string(e.exclude));
|
||||
v.push_back(columns[81] + " = " + std::to_string(e.ATK));
|
||||
v.push_back(columns[82] + " = " + std::to_string(e.Accuracy));
|
||||
v.push_back(columns[83] + " = " + std::to_string(e.Avoidance));
|
||||
v.push_back(columns[84] + " = " + std::to_string(e.slow_mitigation));
|
||||
v.push_back(columns[85] + " = " + std::to_string(e.version));
|
||||
v.push_back(columns[86] + " = " + std::to_string(e.maxlevel));
|
||||
v.push_back(columns[87] + " = " + std::to_string(e.scalerate));
|
||||
v.push_back(columns[88] + " = " + std::to_string(e.private_corpse));
|
||||
v.push_back(columns[89] + " = " + std::to_string(e.unique_spawn_by_name));
|
||||
v.push_back(columns[90] + " = " + std::to_string(e.underwater));
|
||||
v.push_back(columns[91] + " = " + std::to_string(e.isquest));
|
||||
v.push_back(columns[92] + " = " + std::to_string(e.emoteid));
|
||||
v.push_back(columns[93] + " = " + std::to_string(e.spellscale));
|
||||
v.push_back(columns[94] + " = " + std::to_string(e.healscale));
|
||||
v.push_back(columns[95] + " = " + std::to_string(e.no_target_hotkey));
|
||||
v.push_back(columns[96] + " = " + std::to_string(e.raid_target));
|
||||
v.push_back(columns[97] + " = " + std::to_string(e.armtexture));
|
||||
v.push_back(columns[98] + " = " + std::to_string(e.bracertexture));
|
||||
v.push_back(columns[99] + " = " + std::to_string(e.handtexture));
|
||||
v.push_back(columns[100] + " = " + std::to_string(e.legtexture));
|
||||
v.push_back(columns[101] + " = " + std::to_string(e.feettexture));
|
||||
v.push_back(columns[102] + " = " + std::to_string(e.light));
|
||||
v.push_back(columns[103] + " = " + std::to_string(e.walkspeed));
|
||||
v.push_back(columns[104] + " = " + std::to_string(e.peqid));
|
||||
v.push_back(columns[105] + " = " + std::to_string(e.unique_));
|
||||
v.push_back(columns[106] + " = " + std::to_string(e.fixed));
|
||||
v.push_back(columns[107] + " = " + std::to_string(e.ignore_despawn));
|
||||
v.push_back(columns[108] + " = " + std::to_string(e.show_name));
|
||||
v.push_back(columns[109] + " = " + std::to_string(e.untargetable));
|
||||
v.push_back(columns[110] + " = " + std::to_string(e.charm_ac));
|
||||
v.push_back(columns[111] + " = " + std::to_string(e.charm_min_dmg));
|
||||
v.push_back(columns[112] + " = " + std::to_string(e.charm_max_dmg));
|
||||
v.push_back(columns[113] + " = " + std::to_string(e.charm_attack_delay));
|
||||
v.push_back(columns[114] + " = " + std::to_string(e.charm_accuracy_rating));
|
||||
v.push_back(columns[115] + " = " + std::to_string(e.charm_avoidance_rating));
|
||||
v.push_back(columns[116] + " = " + std::to_string(e.charm_atk));
|
||||
v.push_back(columns[117] + " = " + std::to_string(e.skip_global_loot));
|
||||
v.push_back(columns[118] + " = " + std::to_string(e.rare_spawn));
|
||||
v.push_back(columns[119] + " = " + std::to_string(e.stuck_behavior));
|
||||
v.push_back(columns[120] + " = " + std::to_string(e.model));
|
||||
v.push_back(columns[121] + " = " + std::to_string(e.flymode));
|
||||
v.push_back(columns[122] + " = " + std::to_string(e.always_aggro));
|
||||
v.push_back(columns[123] + " = " + std::to_string(e.exp_mod));
|
||||
v.push_back(columns[124] + " = " + std::to_string(e.heroic_strikethrough));
|
||||
v.push_back(columns[125] + " = " + std::to_string(e.faction_amount));
|
||||
v.push_back(columns[126] + " = " + std::to_string(e.keeps_sold_items));
|
||||
v.push_back(columns[127] + " = " + std::to_string(e.is_parcel_merchant));
|
||||
v.push_back(columns[19] + " = " + std::to_string(e.greed));
|
||||
v.push_back(columns[20] + " = " + std::to_string(e.alt_currency_id));
|
||||
v.push_back(columns[21] + " = " + std::to_string(e.npc_spells_id));
|
||||
v.push_back(columns[22] + " = " + std::to_string(e.npc_spells_effects_id));
|
||||
v.push_back(columns[23] + " = " + std::to_string(e.npc_faction_id));
|
||||
v.push_back(columns[24] + " = " + std::to_string(e.adventure_template_id));
|
||||
v.push_back(columns[25] + " = " + std::to_string(e.trap_template));
|
||||
v.push_back(columns[26] + " = " + std::to_string(e.mindmg));
|
||||
v.push_back(columns[27] + " = " + std::to_string(e.maxdmg));
|
||||
v.push_back(columns[28] + " = " + std::to_string(e.attack_count));
|
||||
v.push_back(columns[29] + " = '" + Strings::Escape(e.npcspecialattks) + "'");
|
||||
v.push_back(columns[30] + " = '" + Strings::Escape(e.special_abilities) + "'");
|
||||
v.push_back(columns[31] + " = " + std::to_string(e.aggroradius));
|
||||
v.push_back(columns[32] + " = " + std::to_string(e.assistradius));
|
||||
v.push_back(columns[33] + " = " + std::to_string(e.face));
|
||||
v.push_back(columns[34] + " = " + std::to_string(e.luclin_hairstyle));
|
||||
v.push_back(columns[35] + " = " + std::to_string(e.luclin_haircolor));
|
||||
v.push_back(columns[36] + " = " + std::to_string(e.luclin_eyecolor));
|
||||
v.push_back(columns[37] + " = " + std::to_string(e.luclin_eyecolor2));
|
||||
v.push_back(columns[38] + " = " + std::to_string(e.luclin_beardcolor));
|
||||
v.push_back(columns[39] + " = " + std::to_string(e.luclin_beard));
|
||||
v.push_back(columns[40] + " = " + std::to_string(e.drakkin_heritage));
|
||||
v.push_back(columns[41] + " = " + std::to_string(e.drakkin_tattoo));
|
||||
v.push_back(columns[42] + " = " + std::to_string(e.drakkin_details));
|
||||
v.push_back(columns[43] + " = " + std::to_string(e.armortint_id));
|
||||
v.push_back(columns[44] + " = " + std::to_string(e.armortint_red));
|
||||
v.push_back(columns[45] + " = " + std::to_string(e.armortint_green));
|
||||
v.push_back(columns[46] + " = " + std::to_string(e.armortint_blue));
|
||||
v.push_back(columns[47] + " = " + std::to_string(e.d_melee_texture1));
|
||||
v.push_back(columns[48] + " = " + std::to_string(e.d_melee_texture2));
|
||||
v.push_back(columns[49] + " = '" + Strings::Escape(e.ammo_idfile) + "'");
|
||||
v.push_back(columns[50] + " = " + std::to_string(e.prim_melee_type));
|
||||
v.push_back(columns[51] + " = " + std::to_string(e.sec_melee_type));
|
||||
v.push_back(columns[52] + " = " + std::to_string(e.ranged_type));
|
||||
v.push_back(columns[53] + " = " + std::to_string(e.runspeed));
|
||||
v.push_back(columns[54] + " = " + std::to_string(e.MR));
|
||||
v.push_back(columns[55] + " = " + std::to_string(e.CR));
|
||||
v.push_back(columns[56] + " = " + std::to_string(e.DR));
|
||||
v.push_back(columns[57] + " = " + std::to_string(e.FR));
|
||||
v.push_back(columns[58] + " = " + std::to_string(e.PR));
|
||||
v.push_back(columns[59] + " = " + std::to_string(e.Corrup));
|
||||
v.push_back(columns[60] + " = " + std::to_string(e.PhR));
|
||||
v.push_back(columns[61] + " = " + std::to_string(e.see_invis));
|
||||
v.push_back(columns[62] + " = " + std::to_string(e.see_invis_undead));
|
||||
v.push_back(columns[63] + " = " + std::to_string(e.qglobal));
|
||||
v.push_back(columns[64] + " = " + std::to_string(e.AC));
|
||||
v.push_back(columns[65] + " = " + std::to_string(e.npc_aggro));
|
||||
v.push_back(columns[66] + " = " + std::to_string(e.spawn_limit));
|
||||
v.push_back(columns[67] + " = " + std::to_string(e.attack_speed));
|
||||
v.push_back(columns[68] + " = " + std::to_string(e.attack_delay));
|
||||
v.push_back(columns[69] + " = " + std::to_string(e.findable));
|
||||
v.push_back(columns[70] + " = " + std::to_string(e.STR));
|
||||
v.push_back(columns[71] + " = " + std::to_string(e.STA));
|
||||
v.push_back(columns[72] + " = " + std::to_string(e.DEX));
|
||||
v.push_back(columns[73] + " = " + std::to_string(e.AGI));
|
||||
v.push_back(columns[74] + " = " + std::to_string(e._INT));
|
||||
v.push_back(columns[75] + " = " + std::to_string(e.WIS));
|
||||
v.push_back(columns[76] + " = " + std::to_string(e.CHA));
|
||||
v.push_back(columns[77] + " = " + std::to_string(e.see_hide));
|
||||
v.push_back(columns[78] + " = " + std::to_string(e.see_improved_hide));
|
||||
v.push_back(columns[79] + " = " + std::to_string(e.trackable));
|
||||
v.push_back(columns[80] + " = " + std::to_string(e.isbot));
|
||||
v.push_back(columns[81] + " = " + std::to_string(e.exclude));
|
||||
v.push_back(columns[82] + " = " + std::to_string(e.ATK));
|
||||
v.push_back(columns[83] + " = " + std::to_string(e.Accuracy));
|
||||
v.push_back(columns[84] + " = " + std::to_string(e.Avoidance));
|
||||
v.push_back(columns[85] + " = " + std::to_string(e.slow_mitigation));
|
||||
v.push_back(columns[86] + " = " + std::to_string(e.version));
|
||||
v.push_back(columns[87] + " = " + std::to_string(e.maxlevel));
|
||||
v.push_back(columns[88] + " = " + std::to_string(e.scalerate));
|
||||
v.push_back(columns[89] + " = " + std::to_string(e.private_corpse));
|
||||
v.push_back(columns[90] + " = " + std::to_string(e.unique_spawn_by_name));
|
||||
v.push_back(columns[91] + " = " + std::to_string(e.underwater));
|
||||
v.push_back(columns[92] + " = " + std::to_string(e.isquest));
|
||||
v.push_back(columns[93] + " = " + std::to_string(e.emoteid));
|
||||
v.push_back(columns[94] + " = " + std::to_string(e.spellscale));
|
||||
v.push_back(columns[95] + " = " + std::to_string(e.healscale));
|
||||
v.push_back(columns[96] + " = " + std::to_string(e.no_target_hotkey));
|
||||
v.push_back(columns[97] + " = " + std::to_string(e.raid_target));
|
||||
v.push_back(columns[98] + " = " + std::to_string(e.armtexture));
|
||||
v.push_back(columns[99] + " = " + std::to_string(e.bracertexture));
|
||||
v.push_back(columns[100] + " = " + std::to_string(e.handtexture));
|
||||
v.push_back(columns[101] + " = " + std::to_string(e.legtexture));
|
||||
v.push_back(columns[102] + " = " + std::to_string(e.feettexture));
|
||||
v.push_back(columns[103] + " = " + std::to_string(e.light));
|
||||
v.push_back(columns[104] + " = " + std::to_string(e.walkspeed));
|
||||
v.push_back(columns[105] + " = " + std::to_string(e.peqid));
|
||||
v.push_back(columns[106] + " = " + std::to_string(e.unique_));
|
||||
v.push_back(columns[107] + " = " + std::to_string(e.fixed));
|
||||
v.push_back(columns[108] + " = " + std::to_string(e.ignore_despawn));
|
||||
v.push_back(columns[109] + " = " + std::to_string(e.show_name));
|
||||
v.push_back(columns[110] + " = " + std::to_string(e.untargetable));
|
||||
v.push_back(columns[111] + " = " + std::to_string(e.charm_ac));
|
||||
v.push_back(columns[112] + " = " + std::to_string(e.charm_min_dmg));
|
||||
v.push_back(columns[113] + " = " + std::to_string(e.charm_max_dmg));
|
||||
v.push_back(columns[114] + " = " + std::to_string(e.charm_attack_delay));
|
||||
v.push_back(columns[115] + " = " + std::to_string(e.charm_accuracy_rating));
|
||||
v.push_back(columns[116] + " = " + std::to_string(e.charm_avoidance_rating));
|
||||
v.push_back(columns[117] + " = " + std::to_string(e.charm_atk));
|
||||
v.push_back(columns[118] + " = " + std::to_string(e.skip_global_loot));
|
||||
v.push_back(columns[119] + " = " + std::to_string(e.rare_spawn));
|
||||
v.push_back(columns[120] + " = " + std::to_string(e.stuck_behavior));
|
||||
v.push_back(columns[121] + " = " + std::to_string(e.model));
|
||||
v.push_back(columns[122] + " = " + std::to_string(e.flymode));
|
||||
v.push_back(columns[123] + " = " + std::to_string(e.always_aggro));
|
||||
v.push_back(columns[124] + " = " + std::to_string(e.exp_mod));
|
||||
v.push_back(columns[125] + " = " + std::to_string(e.heroic_strikethrough));
|
||||
v.push_back(columns[126] + " = " + std::to_string(e.faction_amount));
|
||||
v.push_back(columns[127] + " = " + std::to_string(e.keeps_sold_items));
|
||||
v.push_back(columns[128] + " = " + std::to_string(e.is_parcel_merchant));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -951,6 +957,7 @@ public:
|
||||
v.push_back(std::to_string(e.mana_regen_rate));
|
||||
v.push_back(std::to_string(e.loottable_id));
|
||||
v.push_back(std::to_string(e.merchant_id));
|
||||
v.push_back(std::to_string(e.greed));
|
||||
v.push_back(std::to_string(e.alt_currency_id));
|
||||
v.push_back(std::to_string(e.npc_spells_id));
|
||||
v.push_back(std::to_string(e.npc_spells_effects_id));
|
||||
@@ -1108,6 +1115,7 @@ public:
|
||||
v.push_back(std::to_string(e.mana_regen_rate));
|
||||
v.push_back(std::to_string(e.loottable_id));
|
||||
v.push_back(std::to_string(e.merchant_id));
|
||||
v.push_back(std::to_string(e.greed));
|
||||
v.push_back(std::to_string(e.alt_currency_id));
|
||||
v.push_back(std::to_string(e.npc_spells_id));
|
||||
v.push_back(std::to_string(e.npc_spells_effects_id));
|
||||
@@ -1269,115 +1277,116 @@ public:
|
||||
e.mana_regen_rate = row[16] ? strtoll(row[16], nullptr, 10) : 0;
|
||||
e.loottable_id = row[17] ? static_cast<uint32_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||
e.merchant_id = row[18] ? static_cast<uint32_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||
e.alt_currency_id = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.npc_spells_effects_id = row[21] ? static_cast<uint32_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||
e.npc_faction_id = row[22] ? static_cast<int32_t>(atoi(row[22])) : 0;
|
||||
e.adventure_template_id = row[23] ? static_cast<uint32_t>(strtoul(row[23], nullptr, 10)) : 0;
|
||||
e.trap_template = row[24] ? static_cast<uint32_t>(strtoul(row[24], nullptr, 10)) : 0;
|
||||
e.mindmg = row[25] ? static_cast<uint32_t>(strtoul(row[25], nullptr, 10)) : 0;
|
||||
e.maxdmg = row[26] ? static_cast<uint32_t>(strtoul(row[26], nullptr, 10)) : 0;
|
||||
e.attack_count = row[27] ? static_cast<int16_t>(atoi(row[27])) : -1;
|
||||
e.npcspecialattks = row[28] ? row[28] : "";
|
||||
e.special_abilities = row[29] ? row[29] : "";
|
||||
e.aggroradius = row[30] ? static_cast<uint32_t>(strtoul(row[30], nullptr, 10)) : 0;
|
||||
e.assistradius = row[31] ? static_cast<uint32_t>(strtoul(row[31], nullptr, 10)) : 0;
|
||||
e.face = row[32] ? static_cast<uint32_t>(strtoul(row[32], nullptr, 10)) : 1;
|
||||
e.luclin_hairstyle = row[33] ? static_cast<uint32_t>(strtoul(row[33], nullptr, 10)) : 1;
|
||||
e.luclin_haircolor = row[34] ? static_cast<uint32_t>(strtoul(row[34], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor = row[35] ? static_cast<uint32_t>(strtoul(row[35], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor2 = row[36] ? static_cast<uint32_t>(strtoul(row[36], nullptr, 10)) : 1;
|
||||
e.luclin_beardcolor = row[37] ? static_cast<uint32_t>(strtoul(row[37], nullptr, 10)) : 1;
|
||||
e.luclin_beard = row[38] ? static_cast<uint32_t>(strtoul(row[38], nullptr, 10)) : 0;
|
||||
e.drakkin_heritage = row[39] ? static_cast<int32_t>(atoi(row[39])) : 0;
|
||||
e.drakkin_tattoo = row[40] ? static_cast<int32_t>(atoi(row[40])) : 0;
|
||||
e.drakkin_details = row[41] ? static_cast<int32_t>(atoi(row[41])) : 0;
|
||||
e.armortint_id = row[42] ? static_cast<uint32_t>(strtoul(row[42], nullptr, 10)) : 0;
|
||||
e.armortint_red = row[43] ? static_cast<uint8_t>(strtoul(row[43], nullptr, 10)) : 0;
|
||||
e.armortint_green = row[44] ? static_cast<uint8_t>(strtoul(row[44], nullptr, 10)) : 0;
|
||||
e.armortint_blue = row[45] ? static_cast<uint8_t>(strtoul(row[45], nullptr, 10)) : 0;
|
||||
e.d_melee_texture1 = row[46] ? static_cast<uint32_t>(strtoul(row[46], nullptr, 10)) : 0;
|
||||
e.d_melee_texture2 = row[47] ? static_cast<uint32_t>(strtoul(row[47], nullptr, 10)) : 0;
|
||||
e.ammo_idfile = row[48] ? row[48] : "IT10";
|
||||
e.prim_melee_type = row[49] ? static_cast<uint8_t>(strtoul(row[49], nullptr, 10)) : 28;
|
||||
e.sec_melee_type = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 28;
|
||||
e.ranged_type = row[51] ? static_cast<uint8_t>(strtoul(row[51], nullptr, 10)) : 7;
|
||||
e.runspeed = row[52] ? strtof(row[52], nullptr) : 0;
|
||||
e.MR = row[53] ? static_cast<int16_t>(atoi(row[53])) : 0;
|
||||
e.CR = row[54] ? static_cast<int16_t>(atoi(row[54])) : 0;
|
||||
e.DR = row[55] ? static_cast<int16_t>(atoi(row[55])) : 0;
|
||||
e.FR = row[56] ? static_cast<int16_t>(atoi(row[56])) : 0;
|
||||
e.PR = row[57] ? static_cast<int16_t>(atoi(row[57])) : 0;
|
||||
e.Corrup = row[58] ? static_cast<int16_t>(atoi(row[58])) : 0;
|
||||
e.PhR = row[59] ? static_cast<uint16_t>(strtoul(row[59], nullptr, 10)) : 0;
|
||||
e.see_invis = row[60] ? static_cast<int16_t>(atoi(row[60])) : 0;
|
||||
e.see_invis_undead = row[61] ? static_cast<int16_t>(atoi(row[61])) : 0;
|
||||
e.qglobal = row[62] ? static_cast<uint32_t>(strtoul(row[62], nullptr, 10)) : 0;
|
||||
e.AC = row[63] ? static_cast<int16_t>(atoi(row[63])) : 0;
|
||||
e.npc_aggro = row[64] ? static_cast<int8_t>(atoi(row[64])) : 0;
|
||||
e.spawn_limit = row[65] ? static_cast<int8_t>(atoi(row[65])) : 0;
|
||||
e.attack_speed = row[66] ? strtof(row[66], nullptr) : 0;
|
||||
e.attack_delay = row[67] ? static_cast<uint8_t>(strtoul(row[67], nullptr, 10)) : 30;
|
||||
e.findable = row[68] ? static_cast<int8_t>(atoi(row[68])) : 0;
|
||||
e.STR = row[69] ? static_cast<uint32_t>(strtoul(row[69], nullptr, 10)) : 75;
|
||||
e.STA = row[70] ? static_cast<uint32_t>(strtoul(row[70], nullptr, 10)) : 75;
|
||||
e.DEX = row[71] ? static_cast<uint32_t>(strtoul(row[71], nullptr, 10)) : 75;
|
||||
e.AGI = row[72] ? static_cast<uint32_t>(strtoul(row[72], nullptr, 10)) : 75;
|
||||
e._INT = row[73] ? static_cast<uint32_t>(strtoul(row[73], nullptr, 10)) : 80;
|
||||
e.WIS = row[74] ? static_cast<uint32_t>(strtoul(row[74], nullptr, 10)) : 75;
|
||||
e.CHA = row[75] ? static_cast<uint32_t>(strtoul(row[75], nullptr, 10)) : 75;
|
||||
e.see_hide = row[76] ? static_cast<int8_t>(atoi(row[76])) : 0;
|
||||
e.see_improved_hide = row[77] ? static_cast<int8_t>(atoi(row[77])) : 0;
|
||||
e.trackable = row[78] ? static_cast<int8_t>(atoi(row[78])) : 1;
|
||||
e.isbot = row[79] ? static_cast<int8_t>(atoi(row[79])) : 0;
|
||||
e.exclude = row[80] ? static_cast<int8_t>(atoi(row[80])) : 1;
|
||||
e.ATK = row[81] ? static_cast<int32_t>(atoi(row[81])) : 0;
|
||||
e.Accuracy = row[82] ? static_cast<int32_t>(atoi(row[82])) : 0;
|
||||
e.Avoidance = row[83] ? static_cast<uint32_t>(strtoul(row[83], nullptr, 10)) : 0;
|
||||
e.slow_mitigation = row[84] ? static_cast<int16_t>(atoi(row[84])) : 0;
|
||||
e.version = row[85] ? static_cast<uint16_t>(strtoul(row[85], nullptr, 10)) : 0;
|
||||
e.maxlevel = row[86] ? static_cast<int8_t>(atoi(row[86])) : 0;
|
||||
e.scalerate = row[87] ? static_cast<int32_t>(atoi(row[87])) : 100;
|
||||
e.private_corpse = row[88] ? static_cast<uint8_t>(strtoul(row[88], nullptr, 10)) : 0;
|
||||
e.unique_spawn_by_name = row[89] ? static_cast<uint8_t>(strtoul(row[89], nullptr, 10)) : 0;
|
||||
e.underwater = row[90] ? static_cast<uint8_t>(strtoul(row[90], nullptr, 10)) : 0;
|
||||
e.isquest = row[91] ? static_cast<int8_t>(atoi(row[91])) : 0;
|
||||
e.emoteid = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 0;
|
||||
e.spellscale = row[93] ? strtof(row[93], nullptr) : 100;
|
||||
e.healscale = row[94] ? strtof(row[94], nullptr) : 100;
|
||||
e.no_target_hotkey = row[95] ? static_cast<uint8_t>(strtoul(row[95], nullptr, 10)) : 0;
|
||||
e.raid_target = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 0;
|
||||
e.armtexture = row[97] ? static_cast<int8_t>(atoi(row[97])) : 0;
|
||||
e.bracertexture = row[98] ? static_cast<int8_t>(atoi(row[98])) : 0;
|
||||
e.handtexture = row[99] ? static_cast<int8_t>(atoi(row[99])) : 0;
|
||||
e.legtexture = row[100] ? static_cast<int8_t>(atoi(row[100])) : 0;
|
||||
e.feettexture = row[101] ? static_cast<int8_t>(atoi(row[101])) : 0;
|
||||
e.light = row[102] ? static_cast<int8_t>(atoi(row[102])) : 0;
|
||||
e.walkspeed = row[103] ? static_cast<int8_t>(atoi(row[103])) : 0;
|
||||
e.peqid = row[104] ? static_cast<int32_t>(atoi(row[104])) : 0;
|
||||
e.unique_ = row[105] ? static_cast<int8_t>(atoi(row[105])) : 0;
|
||||
e.fixed = row[106] ? static_cast<int8_t>(atoi(row[106])) : 0;
|
||||
e.ignore_despawn = row[107] ? static_cast<int8_t>(atoi(row[107])) : 0;
|
||||
e.show_name = row[108] ? static_cast<int8_t>(atoi(row[108])) : 1;
|
||||
e.untargetable = row[109] ? static_cast<int8_t>(atoi(row[109])) : 0;
|
||||
e.charm_ac = row[110] ? static_cast<int16_t>(atoi(row[110])) : 0;
|
||||
e.charm_min_dmg = row[111] ? static_cast<int32_t>(atoi(row[111])) : 0;
|
||||
e.charm_max_dmg = row[112] ? static_cast<int32_t>(atoi(row[112])) : 0;
|
||||
e.charm_attack_delay = row[113] ? static_cast<int8_t>(atoi(row[113])) : 0;
|
||||
e.charm_accuracy_rating = row[114] ? static_cast<int32_t>(atoi(row[114])) : 0;
|
||||
e.charm_avoidance_rating = row[115] ? static_cast<int32_t>(atoi(row[115])) : 0;
|
||||
e.charm_atk = row[116] ? static_cast<int32_t>(atoi(row[116])) : 0;
|
||||
e.skip_global_loot = row[117] ? static_cast<int8_t>(atoi(row[117])) : 0;
|
||||
e.rare_spawn = row[118] ? static_cast<int8_t>(atoi(row[118])) : 0;
|
||||
e.stuck_behavior = row[119] ? static_cast<int8_t>(atoi(row[119])) : 0;
|
||||
e.model = row[120] ? static_cast<int16_t>(atoi(row[120])) : 0;
|
||||
e.flymode = row[121] ? static_cast<int8_t>(atoi(row[121])) : -1;
|
||||
e.always_aggro = row[122] ? static_cast<int8_t>(atoi(row[122])) : 0;
|
||||
e.exp_mod = row[123] ? static_cast<int32_t>(atoi(row[123])) : 100;
|
||||
e.heroic_strikethrough = row[124] ? static_cast<int32_t>(atoi(row[124])) : 0;
|
||||
e.faction_amount = row[125] ? static_cast<int32_t>(atoi(row[125])) : 0;
|
||||
e.keeps_sold_items = row[126] ? static_cast<uint8_t>(strtoul(row[126], nullptr, 10)) : 1;
|
||||
e.is_parcel_merchant = row[127] ? static_cast<uint8_t>(strtoul(row[127], nullptr, 10)) : 0;
|
||||
e.greed = row[19] ? static_cast<uint8_t>(strtoul(row[19], nullptr, 10)) : 0;
|
||||
e.alt_currency_id = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[21] ? static_cast<uint32_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||
e.npc_spells_effects_id = row[22] ? static_cast<uint32_t>(strtoul(row[22], nullptr, 10)) : 0;
|
||||
e.npc_faction_id = row[23] ? static_cast<int32_t>(atoi(row[23])) : 0;
|
||||
e.adventure_template_id = row[24] ? static_cast<uint32_t>(strtoul(row[24], nullptr, 10)) : 0;
|
||||
e.trap_template = row[25] ? static_cast<uint32_t>(strtoul(row[25], nullptr, 10)) : 0;
|
||||
e.mindmg = row[26] ? static_cast<uint32_t>(strtoul(row[26], nullptr, 10)) : 0;
|
||||
e.maxdmg = row[27] ? static_cast<uint32_t>(strtoul(row[27], nullptr, 10)) : 0;
|
||||
e.attack_count = row[28] ? static_cast<int16_t>(atoi(row[28])) : -1;
|
||||
e.npcspecialattks = row[29] ? row[29] : "";
|
||||
e.special_abilities = row[30] ? row[30] : "";
|
||||
e.aggroradius = row[31] ? static_cast<uint32_t>(strtoul(row[31], nullptr, 10)) : 0;
|
||||
e.assistradius = row[32] ? static_cast<uint32_t>(strtoul(row[32], nullptr, 10)) : 0;
|
||||
e.face = row[33] ? static_cast<uint32_t>(strtoul(row[33], nullptr, 10)) : 1;
|
||||
e.luclin_hairstyle = row[34] ? static_cast<uint32_t>(strtoul(row[34], nullptr, 10)) : 1;
|
||||
e.luclin_haircolor = row[35] ? static_cast<uint32_t>(strtoul(row[35], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor = row[36] ? static_cast<uint32_t>(strtoul(row[36], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor2 = row[37] ? static_cast<uint32_t>(strtoul(row[37], nullptr, 10)) : 1;
|
||||
e.luclin_beardcolor = row[38] ? static_cast<uint32_t>(strtoul(row[38], nullptr, 10)) : 1;
|
||||
e.luclin_beard = row[39] ? static_cast<uint32_t>(strtoul(row[39], nullptr, 10)) : 0;
|
||||
e.drakkin_heritage = row[40] ? static_cast<int32_t>(atoi(row[40])) : 0;
|
||||
e.drakkin_tattoo = row[41] ? static_cast<int32_t>(atoi(row[41])) : 0;
|
||||
e.drakkin_details = row[42] ? static_cast<int32_t>(atoi(row[42])) : 0;
|
||||
e.armortint_id = row[43] ? static_cast<uint32_t>(strtoul(row[43], nullptr, 10)) : 0;
|
||||
e.armortint_red = row[44] ? static_cast<uint8_t>(strtoul(row[44], nullptr, 10)) : 0;
|
||||
e.armortint_green = row[45] ? static_cast<uint8_t>(strtoul(row[45], nullptr, 10)) : 0;
|
||||
e.armortint_blue = row[46] ? static_cast<uint8_t>(strtoul(row[46], nullptr, 10)) : 0;
|
||||
e.d_melee_texture1 = row[47] ? static_cast<uint32_t>(strtoul(row[47], nullptr, 10)) : 0;
|
||||
e.d_melee_texture2 = row[48] ? static_cast<uint32_t>(strtoul(row[48], nullptr, 10)) : 0;
|
||||
e.ammo_idfile = row[49] ? row[49] : "IT10";
|
||||
e.prim_melee_type = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 28;
|
||||
e.sec_melee_type = row[51] ? static_cast<uint8_t>(strtoul(row[51], nullptr, 10)) : 28;
|
||||
e.ranged_type = row[52] ? static_cast<uint8_t>(strtoul(row[52], nullptr, 10)) : 7;
|
||||
e.runspeed = row[53] ? strtof(row[53], nullptr) : 0;
|
||||
e.MR = row[54] ? static_cast<int16_t>(atoi(row[54])) : 0;
|
||||
e.CR = row[55] ? static_cast<int16_t>(atoi(row[55])) : 0;
|
||||
e.DR = row[56] ? static_cast<int16_t>(atoi(row[56])) : 0;
|
||||
e.FR = row[57] ? static_cast<int16_t>(atoi(row[57])) : 0;
|
||||
e.PR = row[58] ? static_cast<int16_t>(atoi(row[58])) : 0;
|
||||
e.Corrup = row[59] ? static_cast<int16_t>(atoi(row[59])) : 0;
|
||||
e.PhR = row[60] ? static_cast<uint16_t>(strtoul(row[60], nullptr, 10)) : 0;
|
||||
e.see_invis = row[61] ? static_cast<int16_t>(atoi(row[61])) : 0;
|
||||
e.see_invis_undead = row[62] ? static_cast<int16_t>(atoi(row[62])) : 0;
|
||||
e.qglobal = row[63] ? static_cast<uint32_t>(strtoul(row[63], nullptr, 10)) : 0;
|
||||
e.AC = row[64] ? static_cast<int16_t>(atoi(row[64])) : 0;
|
||||
e.npc_aggro = row[65] ? static_cast<int8_t>(atoi(row[65])) : 0;
|
||||
e.spawn_limit = row[66] ? static_cast<int8_t>(atoi(row[66])) : 0;
|
||||
e.attack_speed = row[67] ? strtof(row[67], nullptr) : 0;
|
||||
e.attack_delay = row[68] ? static_cast<uint8_t>(strtoul(row[68], nullptr, 10)) : 30;
|
||||
e.findable = row[69] ? static_cast<int8_t>(atoi(row[69])) : 0;
|
||||
e.STR = row[70] ? static_cast<uint32_t>(strtoul(row[70], nullptr, 10)) : 75;
|
||||
e.STA = row[71] ? static_cast<uint32_t>(strtoul(row[71], nullptr, 10)) : 75;
|
||||
e.DEX = row[72] ? static_cast<uint32_t>(strtoul(row[72], nullptr, 10)) : 75;
|
||||
e.AGI = row[73] ? static_cast<uint32_t>(strtoul(row[73], nullptr, 10)) : 75;
|
||||
e._INT = row[74] ? static_cast<uint32_t>(strtoul(row[74], nullptr, 10)) : 80;
|
||||
e.WIS = row[75] ? static_cast<uint32_t>(strtoul(row[75], nullptr, 10)) : 75;
|
||||
e.CHA = row[76] ? static_cast<uint32_t>(strtoul(row[76], nullptr, 10)) : 75;
|
||||
e.see_hide = row[77] ? static_cast<int8_t>(atoi(row[77])) : 0;
|
||||
e.see_improved_hide = row[78] ? static_cast<int8_t>(atoi(row[78])) : 0;
|
||||
e.trackable = row[79] ? static_cast<int8_t>(atoi(row[79])) : 1;
|
||||
e.isbot = row[80] ? static_cast<int8_t>(atoi(row[80])) : 0;
|
||||
e.exclude = row[81] ? static_cast<int8_t>(atoi(row[81])) : 1;
|
||||
e.ATK = row[82] ? static_cast<int32_t>(atoi(row[82])) : 0;
|
||||
e.Accuracy = row[83] ? static_cast<int32_t>(atoi(row[83])) : 0;
|
||||
e.Avoidance = row[84] ? static_cast<uint32_t>(strtoul(row[84], nullptr, 10)) : 0;
|
||||
e.slow_mitigation = row[85] ? static_cast<int16_t>(atoi(row[85])) : 0;
|
||||
e.version = row[86] ? static_cast<uint16_t>(strtoul(row[86], nullptr, 10)) : 0;
|
||||
e.maxlevel = row[87] ? static_cast<int8_t>(atoi(row[87])) : 0;
|
||||
e.scalerate = row[88] ? static_cast<int32_t>(atoi(row[88])) : 100;
|
||||
e.private_corpse = row[89] ? static_cast<uint8_t>(strtoul(row[89], nullptr, 10)) : 0;
|
||||
e.unique_spawn_by_name = row[90] ? static_cast<uint8_t>(strtoul(row[90], nullptr, 10)) : 0;
|
||||
e.underwater = row[91] ? static_cast<uint8_t>(strtoul(row[91], nullptr, 10)) : 0;
|
||||
e.isquest = row[92] ? static_cast<int8_t>(atoi(row[92])) : 0;
|
||||
e.emoteid = row[93] ? static_cast<uint32_t>(strtoul(row[93], nullptr, 10)) : 0;
|
||||
e.spellscale = row[94] ? strtof(row[94], nullptr) : 100;
|
||||
e.healscale = row[95] ? strtof(row[95], nullptr) : 100;
|
||||
e.no_target_hotkey = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 0;
|
||||
e.raid_target = row[97] ? static_cast<uint8_t>(strtoul(row[97], nullptr, 10)) : 0;
|
||||
e.armtexture = row[98] ? static_cast<int8_t>(atoi(row[98])) : 0;
|
||||
e.bracertexture = row[99] ? static_cast<int8_t>(atoi(row[99])) : 0;
|
||||
e.handtexture = row[100] ? static_cast<int8_t>(atoi(row[100])) : 0;
|
||||
e.legtexture = row[101] ? static_cast<int8_t>(atoi(row[101])) : 0;
|
||||
e.feettexture = row[102] ? static_cast<int8_t>(atoi(row[102])) : 0;
|
||||
e.light = row[103] ? static_cast<int8_t>(atoi(row[103])) : 0;
|
||||
e.walkspeed = row[104] ? static_cast<int8_t>(atoi(row[104])) : 0;
|
||||
e.peqid = row[105] ? static_cast<int32_t>(atoi(row[105])) : 0;
|
||||
e.unique_ = row[106] ? static_cast<int8_t>(atoi(row[106])) : 0;
|
||||
e.fixed = row[107] ? static_cast<int8_t>(atoi(row[107])) : 0;
|
||||
e.ignore_despawn = row[108] ? static_cast<int8_t>(atoi(row[108])) : 0;
|
||||
e.show_name = row[109] ? static_cast<int8_t>(atoi(row[109])) : 1;
|
||||
e.untargetable = row[110] ? static_cast<int8_t>(atoi(row[110])) : 0;
|
||||
e.charm_ac = row[111] ? static_cast<int16_t>(atoi(row[111])) : 0;
|
||||
e.charm_min_dmg = row[112] ? static_cast<int32_t>(atoi(row[112])) : 0;
|
||||
e.charm_max_dmg = row[113] ? static_cast<int32_t>(atoi(row[113])) : 0;
|
||||
e.charm_attack_delay = row[114] ? static_cast<int8_t>(atoi(row[114])) : 0;
|
||||
e.charm_accuracy_rating = row[115] ? static_cast<int32_t>(atoi(row[115])) : 0;
|
||||
e.charm_avoidance_rating = row[116] ? static_cast<int32_t>(atoi(row[116])) : 0;
|
||||
e.charm_atk = row[117] ? static_cast<int32_t>(atoi(row[117])) : 0;
|
||||
e.skip_global_loot = row[118] ? static_cast<int8_t>(atoi(row[118])) : 0;
|
||||
e.rare_spawn = row[119] ? static_cast<int8_t>(atoi(row[119])) : 0;
|
||||
e.stuck_behavior = row[120] ? static_cast<int8_t>(atoi(row[120])) : 0;
|
||||
e.model = row[121] ? static_cast<int16_t>(atoi(row[121])) : 0;
|
||||
e.flymode = row[122] ? static_cast<int8_t>(atoi(row[122])) : -1;
|
||||
e.always_aggro = row[123] ? static_cast<int8_t>(atoi(row[123])) : 0;
|
||||
e.exp_mod = row[124] ? static_cast<int32_t>(atoi(row[124])) : 100;
|
||||
e.heroic_strikethrough = row[125] ? static_cast<int32_t>(atoi(row[125])) : 0;
|
||||
e.faction_amount = row[126] ? static_cast<int32_t>(atoi(row[126])) : 0;
|
||||
e.keeps_sold_items = row[127] ? static_cast<uint8_t>(strtoul(row[127], nullptr, 10)) : 1;
|
||||
e.is_parcel_merchant = row[128] ? static_cast<uint8_t>(strtoul(row[128], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -1421,115 +1430,116 @@ public:
|
||||
e.mana_regen_rate = row[16] ? strtoll(row[16], nullptr, 10) : 0;
|
||||
e.loottable_id = row[17] ? static_cast<uint32_t>(strtoul(row[17], nullptr, 10)) : 0;
|
||||
e.merchant_id = row[18] ? static_cast<uint32_t>(strtoul(row[18], nullptr, 10)) : 0;
|
||||
e.alt_currency_id = row[19] ? static_cast<uint32_t>(strtoul(row[19], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.npc_spells_effects_id = row[21] ? static_cast<uint32_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||
e.npc_faction_id = row[22] ? static_cast<int32_t>(atoi(row[22])) : 0;
|
||||
e.adventure_template_id = row[23] ? static_cast<uint32_t>(strtoul(row[23], nullptr, 10)) : 0;
|
||||
e.trap_template = row[24] ? static_cast<uint32_t>(strtoul(row[24], nullptr, 10)) : 0;
|
||||
e.mindmg = row[25] ? static_cast<uint32_t>(strtoul(row[25], nullptr, 10)) : 0;
|
||||
e.maxdmg = row[26] ? static_cast<uint32_t>(strtoul(row[26], nullptr, 10)) : 0;
|
||||
e.attack_count = row[27] ? static_cast<int16_t>(atoi(row[27])) : -1;
|
||||
e.npcspecialattks = row[28] ? row[28] : "";
|
||||
e.special_abilities = row[29] ? row[29] : "";
|
||||
e.aggroradius = row[30] ? static_cast<uint32_t>(strtoul(row[30], nullptr, 10)) : 0;
|
||||
e.assistradius = row[31] ? static_cast<uint32_t>(strtoul(row[31], nullptr, 10)) : 0;
|
||||
e.face = row[32] ? static_cast<uint32_t>(strtoul(row[32], nullptr, 10)) : 1;
|
||||
e.luclin_hairstyle = row[33] ? static_cast<uint32_t>(strtoul(row[33], nullptr, 10)) : 1;
|
||||
e.luclin_haircolor = row[34] ? static_cast<uint32_t>(strtoul(row[34], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor = row[35] ? static_cast<uint32_t>(strtoul(row[35], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor2 = row[36] ? static_cast<uint32_t>(strtoul(row[36], nullptr, 10)) : 1;
|
||||
e.luclin_beardcolor = row[37] ? static_cast<uint32_t>(strtoul(row[37], nullptr, 10)) : 1;
|
||||
e.luclin_beard = row[38] ? static_cast<uint32_t>(strtoul(row[38], nullptr, 10)) : 0;
|
||||
e.drakkin_heritage = row[39] ? static_cast<int32_t>(atoi(row[39])) : 0;
|
||||
e.drakkin_tattoo = row[40] ? static_cast<int32_t>(atoi(row[40])) : 0;
|
||||
e.drakkin_details = row[41] ? static_cast<int32_t>(atoi(row[41])) : 0;
|
||||
e.armortint_id = row[42] ? static_cast<uint32_t>(strtoul(row[42], nullptr, 10)) : 0;
|
||||
e.armortint_red = row[43] ? static_cast<uint8_t>(strtoul(row[43], nullptr, 10)) : 0;
|
||||
e.armortint_green = row[44] ? static_cast<uint8_t>(strtoul(row[44], nullptr, 10)) : 0;
|
||||
e.armortint_blue = row[45] ? static_cast<uint8_t>(strtoul(row[45], nullptr, 10)) : 0;
|
||||
e.d_melee_texture1 = row[46] ? static_cast<uint32_t>(strtoul(row[46], nullptr, 10)) : 0;
|
||||
e.d_melee_texture2 = row[47] ? static_cast<uint32_t>(strtoul(row[47], nullptr, 10)) : 0;
|
||||
e.ammo_idfile = row[48] ? row[48] : "IT10";
|
||||
e.prim_melee_type = row[49] ? static_cast<uint8_t>(strtoul(row[49], nullptr, 10)) : 28;
|
||||
e.sec_melee_type = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 28;
|
||||
e.ranged_type = row[51] ? static_cast<uint8_t>(strtoul(row[51], nullptr, 10)) : 7;
|
||||
e.runspeed = row[52] ? strtof(row[52], nullptr) : 0;
|
||||
e.MR = row[53] ? static_cast<int16_t>(atoi(row[53])) : 0;
|
||||
e.CR = row[54] ? static_cast<int16_t>(atoi(row[54])) : 0;
|
||||
e.DR = row[55] ? static_cast<int16_t>(atoi(row[55])) : 0;
|
||||
e.FR = row[56] ? static_cast<int16_t>(atoi(row[56])) : 0;
|
||||
e.PR = row[57] ? static_cast<int16_t>(atoi(row[57])) : 0;
|
||||
e.Corrup = row[58] ? static_cast<int16_t>(atoi(row[58])) : 0;
|
||||
e.PhR = row[59] ? static_cast<uint16_t>(strtoul(row[59], nullptr, 10)) : 0;
|
||||
e.see_invis = row[60] ? static_cast<int16_t>(atoi(row[60])) : 0;
|
||||
e.see_invis_undead = row[61] ? static_cast<int16_t>(atoi(row[61])) : 0;
|
||||
e.qglobal = row[62] ? static_cast<uint32_t>(strtoul(row[62], nullptr, 10)) : 0;
|
||||
e.AC = row[63] ? static_cast<int16_t>(atoi(row[63])) : 0;
|
||||
e.npc_aggro = row[64] ? static_cast<int8_t>(atoi(row[64])) : 0;
|
||||
e.spawn_limit = row[65] ? static_cast<int8_t>(atoi(row[65])) : 0;
|
||||
e.attack_speed = row[66] ? strtof(row[66], nullptr) : 0;
|
||||
e.attack_delay = row[67] ? static_cast<uint8_t>(strtoul(row[67], nullptr, 10)) : 30;
|
||||
e.findable = row[68] ? static_cast<int8_t>(atoi(row[68])) : 0;
|
||||
e.STR = row[69] ? static_cast<uint32_t>(strtoul(row[69], nullptr, 10)) : 75;
|
||||
e.STA = row[70] ? static_cast<uint32_t>(strtoul(row[70], nullptr, 10)) : 75;
|
||||
e.DEX = row[71] ? static_cast<uint32_t>(strtoul(row[71], nullptr, 10)) : 75;
|
||||
e.AGI = row[72] ? static_cast<uint32_t>(strtoul(row[72], nullptr, 10)) : 75;
|
||||
e._INT = row[73] ? static_cast<uint32_t>(strtoul(row[73], nullptr, 10)) : 80;
|
||||
e.WIS = row[74] ? static_cast<uint32_t>(strtoul(row[74], nullptr, 10)) : 75;
|
||||
e.CHA = row[75] ? static_cast<uint32_t>(strtoul(row[75], nullptr, 10)) : 75;
|
||||
e.see_hide = row[76] ? static_cast<int8_t>(atoi(row[76])) : 0;
|
||||
e.see_improved_hide = row[77] ? static_cast<int8_t>(atoi(row[77])) : 0;
|
||||
e.trackable = row[78] ? static_cast<int8_t>(atoi(row[78])) : 1;
|
||||
e.isbot = row[79] ? static_cast<int8_t>(atoi(row[79])) : 0;
|
||||
e.exclude = row[80] ? static_cast<int8_t>(atoi(row[80])) : 1;
|
||||
e.ATK = row[81] ? static_cast<int32_t>(atoi(row[81])) : 0;
|
||||
e.Accuracy = row[82] ? static_cast<int32_t>(atoi(row[82])) : 0;
|
||||
e.Avoidance = row[83] ? static_cast<uint32_t>(strtoul(row[83], nullptr, 10)) : 0;
|
||||
e.slow_mitigation = row[84] ? static_cast<int16_t>(atoi(row[84])) : 0;
|
||||
e.version = row[85] ? static_cast<uint16_t>(strtoul(row[85], nullptr, 10)) : 0;
|
||||
e.maxlevel = row[86] ? static_cast<int8_t>(atoi(row[86])) : 0;
|
||||
e.scalerate = row[87] ? static_cast<int32_t>(atoi(row[87])) : 100;
|
||||
e.private_corpse = row[88] ? static_cast<uint8_t>(strtoul(row[88], nullptr, 10)) : 0;
|
||||
e.unique_spawn_by_name = row[89] ? static_cast<uint8_t>(strtoul(row[89], nullptr, 10)) : 0;
|
||||
e.underwater = row[90] ? static_cast<uint8_t>(strtoul(row[90], nullptr, 10)) : 0;
|
||||
e.isquest = row[91] ? static_cast<int8_t>(atoi(row[91])) : 0;
|
||||
e.emoteid = row[92] ? static_cast<uint32_t>(strtoul(row[92], nullptr, 10)) : 0;
|
||||
e.spellscale = row[93] ? strtof(row[93], nullptr) : 100;
|
||||
e.healscale = row[94] ? strtof(row[94], nullptr) : 100;
|
||||
e.no_target_hotkey = row[95] ? static_cast<uint8_t>(strtoul(row[95], nullptr, 10)) : 0;
|
||||
e.raid_target = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 0;
|
||||
e.armtexture = row[97] ? static_cast<int8_t>(atoi(row[97])) : 0;
|
||||
e.bracertexture = row[98] ? static_cast<int8_t>(atoi(row[98])) : 0;
|
||||
e.handtexture = row[99] ? static_cast<int8_t>(atoi(row[99])) : 0;
|
||||
e.legtexture = row[100] ? static_cast<int8_t>(atoi(row[100])) : 0;
|
||||
e.feettexture = row[101] ? static_cast<int8_t>(atoi(row[101])) : 0;
|
||||
e.light = row[102] ? static_cast<int8_t>(atoi(row[102])) : 0;
|
||||
e.walkspeed = row[103] ? static_cast<int8_t>(atoi(row[103])) : 0;
|
||||
e.peqid = row[104] ? static_cast<int32_t>(atoi(row[104])) : 0;
|
||||
e.unique_ = row[105] ? static_cast<int8_t>(atoi(row[105])) : 0;
|
||||
e.fixed = row[106] ? static_cast<int8_t>(atoi(row[106])) : 0;
|
||||
e.ignore_despawn = row[107] ? static_cast<int8_t>(atoi(row[107])) : 0;
|
||||
e.show_name = row[108] ? static_cast<int8_t>(atoi(row[108])) : 1;
|
||||
e.untargetable = row[109] ? static_cast<int8_t>(atoi(row[109])) : 0;
|
||||
e.charm_ac = row[110] ? static_cast<int16_t>(atoi(row[110])) : 0;
|
||||
e.charm_min_dmg = row[111] ? static_cast<int32_t>(atoi(row[111])) : 0;
|
||||
e.charm_max_dmg = row[112] ? static_cast<int32_t>(atoi(row[112])) : 0;
|
||||
e.charm_attack_delay = row[113] ? static_cast<int8_t>(atoi(row[113])) : 0;
|
||||
e.charm_accuracy_rating = row[114] ? static_cast<int32_t>(atoi(row[114])) : 0;
|
||||
e.charm_avoidance_rating = row[115] ? static_cast<int32_t>(atoi(row[115])) : 0;
|
||||
e.charm_atk = row[116] ? static_cast<int32_t>(atoi(row[116])) : 0;
|
||||
e.skip_global_loot = row[117] ? static_cast<int8_t>(atoi(row[117])) : 0;
|
||||
e.rare_spawn = row[118] ? static_cast<int8_t>(atoi(row[118])) : 0;
|
||||
e.stuck_behavior = row[119] ? static_cast<int8_t>(atoi(row[119])) : 0;
|
||||
e.model = row[120] ? static_cast<int16_t>(atoi(row[120])) : 0;
|
||||
e.flymode = row[121] ? static_cast<int8_t>(atoi(row[121])) : -1;
|
||||
e.always_aggro = row[122] ? static_cast<int8_t>(atoi(row[122])) : 0;
|
||||
e.exp_mod = row[123] ? static_cast<int32_t>(atoi(row[123])) : 100;
|
||||
e.heroic_strikethrough = row[124] ? static_cast<int32_t>(atoi(row[124])) : 0;
|
||||
e.faction_amount = row[125] ? static_cast<int32_t>(atoi(row[125])) : 0;
|
||||
e.keeps_sold_items = row[126] ? static_cast<uint8_t>(strtoul(row[126], nullptr, 10)) : 1;
|
||||
e.is_parcel_merchant = row[127] ? static_cast<uint8_t>(strtoul(row[127], nullptr, 10)) : 0;
|
||||
e.greed = row[19] ? static_cast<uint8_t>(strtoul(row[19], nullptr, 10)) : 0;
|
||||
e.alt_currency_id = row[20] ? static_cast<uint32_t>(strtoul(row[20], nullptr, 10)) : 0;
|
||||
e.npc_spells_id = row[21] ? static_cast<uint32_t>(strtoul(row[21], nullptr, 10)) : 0;
|
||||
e.npc_spells_effects_id = row[22] ? static_cast<uint32_t>(strtoul(row[22], nullptr, 10)) : 0;
|
||||
e.npc_faction_id = row[23] ? static_cast<int32_t>(atoi(row[23])) : 0;
|
||||
e.adventure_template_id = row[24] ? static_cast<uint32_t>(strtoul(row[24], nullptr, 10)) : 0;
|
||||
e.trap_template = row[25] ? static_cast<uint32_t>(strtoul(row[25], nullptr, 10)) : 0;
|
||||
e.mindmg = row[26] ? static_cast<uint32_t>(strtoul(row[26], nullptr, 10)) : 0;
|
||||
e.maxdmg = row[27] ? static_cast<uint32_t>(strtoul(row[27], nullptr, 10)) : 0;
|
||||
e.attack_count = row[28] ? static_cast<int16_t>(atoi(row[28])) : -1;
|
||||
e.npcspecialattks = row[29] ? row[29] : "";
|
||||
e.special_abilities = row[30] ? row[30] : "";
|
||||
e.aggroradius = row[31] ? static_cast<uint32_t>(strtoul(row[31], nullptr, 10)) : 0;
|
||||
e.assistradius = row[32] ? static_cast<uint32_t>(strtoul(row[32], nullptr, 10)) : 0;
|
||||
e.face = row[33] ? static_cast<uint32_t>(strtoul(row[33], nullptr, 10)) : 1;
|
||||
e.luclin_hairstyle = row[34] ? static_cast<uint32_t>(strtoul(row[34], nullptr, 10)) : 1;
|
||||
e.luclin_haircolor = row[35] ? static_cast<uint32_t>(strtoul(row[35], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor = row[36] ? static_cast<uint32_t>(strtoul(row[36], nullptr, 10)) : 1;
|
||||
e.luclin_eyecolor2 = row[37] ? static_cast<uint32_t>(strtoul(row[37], nullptr, 10)) : 1;
|
||||
e.luclin_beardcolor = row[38] ? static_cast<uint32_t>(strtoul(row[38], nullptr, 10)) : 1;
|
||||
e.luclin_beard = row[39] ? static_cast<uint32_t>(strtoul(row[39], nullptr, 10)) : 0;
|
||||
e.drakkin_heritage = row[40] ? static_cast<int32_t>(atoi(row[40])) : 0;
|
||||
e.drakkin_tattoo = row[41] ? static_cast<int32_t>(atoi(row[41])) : 0;
|
||||
e.drakkin_details = row[42] ? static_cast<int32_t>(atoi(row[42])) : 0;
|
||||
e.armortint_id = row[43] ? static_cast<uint32_t>(strtoul(row[43], nullptr, 10)) : 0;
|
||||
e.armortint_red = row[44] ? static_cast<uint8_t>(strtoul(row[44], nullptr, 10)) : 0;
|
||||
e.armortint_green = row[45] ? static_cast<uint8_t>(strtoul(row[45], nullptr, 10)) : 0;
|
||||
e.armortint_blue = row[46] ? static_cast<uint8_t>(strtoul(row[46], nullptr, 10)) : 0;
|
||||
e.d_melee_texture1 = row[47] ? static_cast<uint32_t>(strtoul(row[47], nullptr, 10)) : 0;
|
||||
e.d_melee_texture2 = row[48] ? static_cast<uint32_t>(strtoul(row[48], nullptr, 10)) : 0;
|
||||
e.ammo_idfile = row[49] ? row[49] : "IT10";
|
||||
e.prim_melee_type = row[50] ? static_cast<uint8_t>(strtoul(row[50], nullptr, 10)) : 28;
|
||||
e.sec_melee_type = row[51] ? static_cast<uint8_t>(strtoul(row[51], nullptr, 10)) : 28;
|
||||
e.ranged_type = row[52] ? static_cast<uint8_t>(strtoul(row[52], nullptr, 10)) : 7;
|
||||
e.runspeed = row[53] ? strtof(row[53], nullptr) : 0;
|
||||
e.MR = row[54] ? static_cast<int16_t>(atoi(row[54])) : 0;
|
||||
e.CR = row[55] ? static_cast<int16_t>(atoi(row[55])) : 0;
|
||||
e.DR = row[56] ? static_cast<int16_t>(atoi(row[56])) : 0;
|
||||
e.FR = row[57] ? static_cast<int16_t>(atoi(row[57])) : 0;
|
||||
e.PR = row[58] ? static_cast<int16_t>(atoi(row[58])) : 0;
|
||||
e.Corrup = row[59] ? static_cast<int16_t>(atoi(row[59])) : 0;
|
||||
e.PhR = row[60] ? static_cast<uint16_t>(strtoul(row[60], nullptr, 10)) : 0;
|
||||
e.see_invis = row[61] ? static_cast<int16_t>(atoi(row[61])) : 0;
|
||||
e.see_invis_undead = row[62] ? static_cast<int16_t>(atoi(row[62])) : 0;
|
||||
e.qglobal = row[63] ? static_cast<uint32_t>(strtoul(row[63], nullptr, 10)) : 0;
|
||||
e.AC = row[64] ? static_cast<int16_t>(atoi(row[64])) : 0;
|
||||
e.npc_aggro = row[65] ? static_cast<int8_t>(atoi(row[65])) : 0;
|
||||
e.spawn_limit = row[66] ? static_cast<int8_t>(atoi(row[66])) : 0;
|
||||
e.attack_speed = row[67] ? strtof(row[67], nullptr) : 0;
|
||||
e.attack_delay = row[68] ? static_cast<uint8_t>(strtoul(row[68], nullptr, 10)) : 30;
|
||||
e.findable = row[69] ? static_cast<int8_t>(atoi(row[69])) : 0;
|
||||
e.STR = row[70] ? static_cast<uint32_t>(strtoul(row[70], nullptr, 10)) : 75;
|
||||
e.STA = row[71] ? static_cast<uint32_t>(strtoul(row[71], nullptr, 10)) : 75;
|
||||
e.DEX = row[72] ? static_cast<uint32_t>(strtoul(row[72], nullptr, 10)) : 75;
|
||||
e.AGI = row[73] ? static_cast<uint32_t>(strtoul(row[73], nullptr, 10)) : 75;
|
||||
e._INT = row[74] ? static_cast<uint32_t>(strtoul(row[74], nullptr, 10)) : 80;
|
||||
e.WIS = row[75] ? static_cast<uint32_t>(strtoul(row[75], nullptr, 10)) : 75;
|
||||
e.CHA = row[76] ? static_cast<uint32_t>(strtoul(row[76], nullptr, 10)) : 75;
|
||||
e.see_hide = row[77] ? static_cast<int8_t>(atoi(row[77])) : 0;
|
||||
e.see_improved_hide = row[78] ? static_cast<int8_t>(atoi(row[78])) : 0;
|
||||
e.trackable = row[79] ? static_cast<int8_t>(atoi(row[79])) : 1;
|
||||
e.isbot = row[80] ? static_cast<int8_t>(atoi(row[80])) : 0;
|
||||
e.exclude = row[81] ? static_cast<int8_t>(atoi(row[81])) : 1;
|
||||
e.ATK = row[82] ? static_cast<int32_t>(atoi(row[82])) : 0;
|
||||
e.Accuracy = row[83] ? static_cast<int32_t>(atoi(row[83])) : 0;
|
||||
e.Avoidance = row[84] ? static_cast<uint32_t>(strtoul(row[84], nullptr, 10)) : 0;
|
||||
e.slow_mitigation = row[85] ? static_cast<int16_t>(atoi(row[85])) : 0;
|
||||
e.version = row[86] ? static_cast<uint16_t>(strtoul(row[86], nullptr, 10)) : 0;
|
||||
e.maxlevel = row[87] ? static_cast<int8_t>(atoi(row[87])) : 0;
|
||||
e.scalerate = row[88] ? static_cast<int32_t>(atoi(row[88])) : 100;
|
||||
e.private_corpse = row[89] ? static_cast<uint8_t>(strtoul(row[89], nullptr, 10)) : 0;
|
||||
e.unique_spawn_by_name = row[90] ? static_cast<uint8_t>(strtoul(row[90], nullptr, 10)) : 0;
|
||||
e.underwater = row[91] ? static_cast<uint8_t>(strtoul(row[91], nullptr, 10)) : 0;
|
||||
e.isquest = row[92] ? static_cast<int8_t>(atoi(row[92])) : 0;
|
||||
e.emoteid = row[93] ? static_cast<uint32_t>(strtoul(row[93], nullptr, 10)) : 0;
|
||||
e.spellscale = row[94] ? strtof(row[94], nullptr) : 100;
|
||||
e.healscale = row[95] ? strtof(row[95], nullptr) : 100;
|
||||
e.no_target_hotkey = row[96] ? static_cast<uint8_t>(strtoul(row[96], nullptr, 10)) : 0;
|
||||
e.raid_target = row[97] ? static_cast<uint8_t>(strtoul(row[97], nullptr, 10)) : 0;
|
||||
e.armtexture = row[98] ? static_cast<int8_t>(atoi(row[98])) : 0;
|
||||
e.bracertexture = row[99] ? static_cast<int8_t>(atoi(row[99])) : 0;
|
||||
e.handtexture = row[100] ? static_cast<int8_t>(atoi(row[100])) : 0;
|
||||
e.legtexture = row[101] ? static_cast<int8_t>(atoi(row[101])) : 0;
|
||||
e.feettexture = row[102] ? static_cast<int8_t>(atoi(row[102])) : 0;
|
||||
e.light = row[103] ? static_cast<int8_t>(atoi(row[103])) : 0;
|
||||
e.walkspeed = row[104] ? static_cast<int8_t>(atoi(row[104])) : 0;
|
||||
e.peqid = row[105] ? static_cast<int32_t>(atoi(row[105])) : 0;
|
||||
e.unique_ = row[106] ? static_cast<int8_t>(atoi(row[106])) : 0;
|
||||
e.fixed = row[107] ? static_cast<int8_t>(atoi(row[107])) : 0;
|
||||
e.ignore_despawn = row[108] ? static_cast<int8_t>(atoi(row[108])) : 0;
|
||||
e.show_name = row[109] ? static_cast<int8_t>(atoi(row[109])) : 1;
|
||||
e.untargetable = row[110] ? static_cast<int8_t>(atoi(row[110])) : 0;
|
||||
e.charm_ac = row[111] ? static_cast<int16_t>(atoi(row[111])) : 0;
|
||||
e.charm_min_dmg = row[112] ? static_cast<int32_t>(atoi(row[112])) : 0;
|
||||
e.charm_max_dmg = row[113] ? static_cast<int32_t>(atoi(row[113])) : 0;
|
||||
e.charm_attack_delay = row[114] ? static_cast<int8_t>(atoi(row[114])) : 0;
|
||||
e.charm_accuracy_rating = row[115] ? static_cast<int32_t>(atoi(row[115])) : 0;
|
||||
e.charm_avoidance_rating = row[116] ? static_cast<int32_t>(atoi(row[116])) : 0;
|
||||
e.charm_atk = row[117] ? static_cast<int32_t>(atoi(row[117])) : 0;
|
||||
e.skip_global_loot = row[118] ? static_cast<int8_t>(atoi(row[118])) : 0;
|
||||
e.rare_spawn = row[119] ? static_cast<int8_t>(atoi(row[119])) : 0;
|
||||
e.stuck_behavior = row[120] ? static_cast<int8_t>(atoi(row[120])) : 0;
|
||||
e.model = row[121] ? static_cast<int16_t>(atoi(row[121])) : 0;
|
||||
e.flymode = row[122] ? static_cast<int8_t>(atoi(row[122])) : -1;
|
||||
e.always_aggro = row[123] ? static_cast<int8_t>(atoi(row[123])) : 0;
|
||||
e.exp_mod = row[124] ? static_cast<int32_t>(atoi(row[124])) : 100;
|
||||
e.heroic_strikethrough = row[125] ? static_cast<int32_t>(atoi(row[125])) : 0;
|
||||
e.faction_amount = row[126] ? static_cast<int32_t>(atoi(row[126])) : 0;
|
||||
e.keeps_sold_items = row[127] ? static_cast<uint8_t>(strtoul(row[127], nullptr, 10)) : 1;
|
||||
e.is_parcel_merchant = row[128] ? static_cast<uint8_t>(strtoul(row[128], nullptr, 10)) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -1623,6 +1633,7 @@ public:
|
||||
v.push_back(std::to_string(e.mana_regen_rate));
|
||||
v.push_back(std::to_string(e.loottable_id));
|
||||
v.push_back(std::to_string(e.merchant_id));
|
||||
v.push_back(std::to_string(e.greed));
|
||||
v.push_back(std::to_string(e.alt_currency_id));
|
||||
v.push_back(std::to_string(e.npc_spells_id));
|
||||
v.push_back(std::to_string(e.npc_spells_effects_id));
|
||||
@@ -1773,6 +1784,7 @@ public:
|
||||
v.push_back(std::to_string(e.mana_regen_rate));
|
||||
v.push_back(std::to_string(e.loottable_id));
|
||||
v.push_back(std::to_string(e.merchant_id));
|
||||
v.push_back(std::to_string(e.greed));
|
||||
v.push_back(std::to_string(e.alt_currency_id));
|
||||
v.push_back(std::to_string(e.npc_spells_id));
|
||||
v.push_back(std::to_string(e.npc_spells_effects_id));
|
||||
|
||||
@@ -19,40 +19,70 @@
|
||||
class BaseTraderRepository {
|
||||
public:
|
||||
struct Trader {
|
||||
uint64_t id;
|
||||
uint32_t char_id;
|
||||
uint32_t item_id;
|
||||
uint32_t serialnumber;
|
||||
int32_t charges;
|
||||
uint32_t item_cost;
|
||||
uint32_t aug_slot_1;
|
||||
uint32_t aug_slot_2;
|
||||
uint32_t aug_slot_3;
|
||||
uint32_t aug_slot_4;
|
||||
uint32_t aug_slot_5;
|
||||
uint32_t aug_slot_6;
|
||||
int32_t item_sn;
|
||||
int32_t item_charges;
|
||||
uint64_t item_cost;
|
||||
uint8_t slot_id;
|
||||
uint32_t char_entity_id;
|
||||
uint32_t char_zone_id;
|
||||
int8_t active_transaction;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("char_id");
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"char_id",
|
||||
"item_id",
|
||||
"serialnumber",
|
||||
"charges",
|
||||
"aug_slot_1",
|
||||
"aug_slot_2",
|
||||
"aug_slot_3",
|
||||
"aug_slot_4",
|
||||
"aug_slot_5",
|
||||
"aug_slot_6",
|
||||
"item_sn",
|
||||
"item_charges",
|
||||
"item_cost",
|
||||
"slot_id",
|
||||
"char_entity_id",
|
||||
"char_zone_id",
|
||||
"active_transaction",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"char_id",
|
||||
"item_id",
|
||||
"serialnumber",
|
||||
"charges",
|
||||
"aug_slot_1",
|
||||
"aug_slot_2",
|
||||
"aug_slot_3",
|
||||
"aug_slot_4",
|
||||
"aug_slot_5",
|
||||
"aug_slot_6",
|
||||
"item_sn",
|
||||
"item_charges",
|
||||
"item_cost",
|
||||
"slot_id",
|
||||
"char_entity_id",
|
||||
"char_zone_id",
|
||||
"active_transaction",
|
||||
};
|
||||
}
|
||||
|
||||
@@ -93,12 +123,22 @@ public:
|
||||
{
|
||||
Trader e{};
|
||||
|
||||
e.char_id = 0;
|
||||
e.item_id = 0;
|
||||
e.serialnumber = 0;
|
||||
e.charges = 0;
|
||||
e.item_cost = 0;
|
||||
e.slot_id = 0;
|
||||
e.id = 0;
|
||||
e.char_id = 0;
|
||||
e.item_id = 0;
|
||||
e.aug_slot_1 = 0;
|
||||
e.aug_slot_2 = 0;
|
||||
e.aug_slot_3 = 0;
|
||||
e.aug_slot_4 = 0;
|
||||
e.aug_slot_5 = 0;
|
||||
e.aug_slot_6 = 0;
|
||||
e.item_sn = 0;
|
||||
e.item_charges = 0;
|
||||
e.item_cost = 0;
|
||||
e.slot_id = 0;
|
||||
e.char_entity_id = 0;
|
||||
e.char_zone_id = 0;
|
||||
e.active_transaction = 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -109,7 +149,7 @@ public:
|
||||
)
|
||||
{
|
||||
for (auto &trader : traders) {
|
||||
if (trader.char_id == trader_id) {
|
||||
if (trader.id == trader_id) {
|
||||
return trader;
|
||||
}
|
||||
}
|
||||
@@ -135,12 +175,22 @@ public:
|
||||
if (results.RowCount() == 1) {
|
||||
Trader e{};
|
||||
|
||||
e.char_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.item_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.serialnumber = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.charges = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_cost = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.slot_id = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 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.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.item_sn = row[9] ? static_cast<int32_t>(atoi(row[9])) : 0;
|
||||
e.item_charges = row[10] ? static_cast<int32_t>(atoi(row[10])) : 0;
|
||||
e.item_cost = row[11] ? strtoull(row[11], nullptr, 10) : 0;
|
||||
e.slot_id = row[12] ? static_cast<uint8_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.char_entity_id = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.char_zone_id = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.active_transaction = row[15] ? static_cast<int8_t>(atoi(row[15])) : 0;
|
||||
|
||||
return e;
|
||||
}
|
||||
@@ -174,12 +224,21 @@ public:
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.char_id));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.item_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.serialnumber));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.charges));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.item_cost));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.slot_id));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.char_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.item_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.aug_slot_1));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.aug_slot_2));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.aug_slot_3));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.aug_slot_4));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.aug_slot_5));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.aug_slot_6));
|
||||
v.push_back(columns[9] + " = " + std::to_string(e.item_sn));
|
||||
v.push_back(columns[10] + " = " + std::to_string(e.item_charges));
|
||||
v.push_back(columns[11] + " = " + std::to_string(e.item_cost));
|
||||
v.push_back(columns[12] + " = " + std::to_string(e.slot_id));
|
||||
v.push_back(columns[13] + " = " + std::to_string(e.char_entity_id));
|
||||
v.push_back(columns[14] + " = " + std::to_string(e.char_zone_id));
|
||||
v.push_back(columns[15] + " = " + std::to_string(e.active_transaction));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -187,7 +246,7 @@ public:
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.char_id
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
@@ -201,12 +260,22 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.serialnumber));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.aug_slot_1));
|
||||
v.push_back(std::to_string(e.aug_slot_2));
|
||||
v.push_back(std::to_string(e.aug_slot_3));
|
||||
v.push_back(std::to_string(e.aug_slot_4));
|
||||
v.push_back(std::to_string(e.aug_slot_5));
|
||||
v.push_back(std::to_string(e.aug_slot_6));
|
||||
v.push_back(std::to_string(e.item_sn));
|
||||
v.push_back(std::to_string(e.item_charges));
|
||||
v.push_back(std::to_string(e.item_cost));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
v.push_back(std::to_string(e.active_transaction));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -217,7 +286,7 @@ public:
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.char_id = results.LastInsertedID();
|
||||
e.id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
@@ -236,12 +305,22 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.serialnumber));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.aug_slot_1));
|
||||
v.push_back(std::to_string(e.aug_slot_2));
|
||||
v.push_back(std::to_string(e.aug_slot_3));
|
||||
v.push_back(std::to_string(e.aug_slot_4));
|
||||
v.push_back(std::to_string(e.aug_slot_5));
|
||||
v.push_back(std::to_string(e.aug_slot_6));
|
||||
v.push_back(std::to_string(e.item_sn));
|
||||
v.push_back(std::to_string(e.item_charges));
|
||||
v.push_back(std::to_string(e.item_cost));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
v.push_back(std::to_string(e.active_transaction));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
@@ -275,12 +354,22 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Trader e{};
|
||||
|
||||
e.char_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.item_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.serialnumber = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.charges = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_cost = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.slot_id = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 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.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.item_sn = row[9] ? static_cast<int32_t>(atoi(row[9])) : 0;
|
||||
e.item_charges = row[10] ? static_cast<int32_t>(atoi(row[10])) : 0;
|
||||
e.item_cost = row[11] ? strtoull(row[11], nullptr, 10) : 0;
|
||||
e.slot_id = row[12] ? static_cast<uint8_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.char_entity_id = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.char_zone_id = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.active_transaction = row[15] ? static_cast<int8_t>(atoi(row[15])) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -305,12 +394,22 @@ public:
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
Trader e{};
|
||||
|
||||
e.char_id = row[0] ? static_cast<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
|
||||
e.item_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
|
||||
e.serialnumber = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.charges = row[3] ? static_cast<int32_t>(atoi(row[3])) : 0;
|
||||
e.item_cost = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.slot_id = row[5] ? static_cast<uint8_t>(strtoul(row[5], nullptr, 10)) : 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.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
|
||||
e.aug_slot_1 = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
|
||||
e.aug_slot_2 = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
|
||||
e.aug_slot_3 = row[5] ? static_cast<uint32_t>(strtoul(row[5], nullptr, 10)) : 0;
|
||||
e.aug_slot_4 = row[6] ? static_cast<uint32_t>(strtoul(row[6], nullptr, 10)) : 0;
|
||||
e.aug_slot_5 = row[7] ? static_cast<uint32_t>(strtoul(row[7], nullptr, 10)) : 0;
|
||||
e.aug_slot_6 = row[8] ? static_cast<uint32_t>(strtoul(row[8], nullptr, 10)) : 0;
|
||||
e.item_sn = row[9] ? static_cast<int32_t>(atoi(row[9])) : 0;
|
||||
e.item_charges = row[10] ? static_cast<int32_t>(atoi(row[10])) : 0;
|
||||
e.item_cost = row[11] ? strtoull(row[11], nullptr, 10) : 0;
|
||||
e.slot_id = row[12] ? static_cast<uint8_t>(strtoul(row[12], nullptr, 10)) : 0;
|
||||
e.char_entity_id = row[13] ? static_cast<uint32_t>(strtoul(row[13], nullptr, 10)) : 0;
|
||||
e.char_zone_id = row[14] ? static_cast<uint32_t>(strtoul(row[14], nullptr, 10)) : 0;
|
||||
e.active_transaction = row[15] ? static_cast<int8_t>(atoi(row[15])) : 0;
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
@@ -385,12 +484,22 @@ public:
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.serialnumber));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.aug_slot_1));
|
||||
v.push_back(std::to_string(e.aug_slot_2));
|
||||
v.push_back(std::to_string(e.aug_slot_3));
|
||||
v.push_back(std::to_string(e.aug_slot_4));
|
||||
v.push_back(std::to_string(e.aug_slot_5));
|
||||
v.push_back(std::to_string(e.aug_slot_6));
|
||||
v.push_back(std::to_string(e.item_sn));
|
||||
v.push_back(std::to_string(e.item_charges));
|
||||
v.push_back(std::to_string(e.item_cost));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
v.push_back(std::to_string(e.active_transaction));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
@@ -413,12 +522,22 @@ public:
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
v.push_back(std::to_string(e.char_id));
|
||||
v.push_back(std::to_string(e.item_id));
|
||||
v.push_back(std::to_string(e.serialnumber));
|
||||
v.push_back(std::to_string(e.charges));
|
||||
v.push_back(std::to_string(e.aug_slot_1));
|
||||
v.push_back(std::to_string(e.aug_slot_2));
|
||||
v.push_back(std::to_string(e.aug_slot_3));
|
||||
v.push_back(std::to_string(e.aug_slot_4));
|
||||
v.push_back(std::to_string(e.aug_slot_5));
|
||||
v.push_back(std::to_string(e.aug_slot_6));
|
||||
v.push_back(std::to_string(e.item_sn));
|
||||
v.push_back(std::to_string(e.item_charges));
|
||||
v.push_back(std::to_string(e.item_cost));
|
||||
v.push_back(std::to_string(e.slot_id));
|
||||
v.push_back(std::to_string(e.char_entity_id));
|
||||
v.push_back(std::to_string(e.char_zone_id));
|
||||
v.push_back(std::to_string(e.active_transaction));
|
||||
|
||||
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
|
||||
}
|
||||
|
||||
@@ -64,6 +64,22 @@ public:
|
||||
|
||||
return Strings::ToUnsignedInt(row[0]);
|
||||
}
|
||||
|
||||
static CharacterData FindByName(
|
||||
Database& db,
|
||||
const std::string& character_name
|
||||
)
|
||||
{
|
||||
auto l = CharacterDataRepository::GetWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"`name` = '{}' LIMIT 1",
|
||||
Strings::Escape(character_name)
|
||||
)
|
||||
);
|
||||
|
||||
return l.empty() ? CharacterDataRepository::NewEntity() : l.front();
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_CHARACTER_DATA_REPOSITORY_H
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef EQEMU_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H
|
||||
#define EQEMU_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_character_parcels_containers_repository.h"
|
||||
|
||||
class CharacterParcelsContainersRepository: public BaseCharacterParcelsContainersRepository {
|
||||
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
|
||||
*
|
||||
* CharacterParcelsContainersRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* CharacterParcelsContainersRepository::GetWhereNeverExpires()
|
||||
* CharacterParcelsContainersRepository::GetWhereXAndY()
|
||||
* CharacterParcelsContainersRepository::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
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_CHARACTER_PARCELS_CONTAINERS_REPOSITORY_H
|
||||
@@ -7,43 +7,6 @@
|
||||
|
||||
class ItemsRepository: public BaseItemsRepository {
|
||||
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
|
||||
*
|
||||
* ItemsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* ItemsRepository::GetWhereNeverExpires()
|
||||
* ItemsRepository::GetWhereXAndY()
|
||||
* ItemsRepository::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<int32> GetItemIDsBySearchCriteria(
|
||||
Database& db,
|
||||
std::string search_string,
|
||||
@@ -73,6 +36,8 @@ public:
|
||||
|
||||
return item_id_list;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_ITEMS_REPOSITORY_H
|
||||
|
||||
@@ -1,50 +1,224 @@
|
||||
#ifndef EQEMU_TRADER_REPOSITORY_H
|
||||
#define EQEMU_TRADER_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../../common/shareddb.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_trader_repository.h"
|
||||
#include "items_repository.h"
|
||||
#include "../../common/item_data.h"
|
||||
#include "../../common/races.h"
|
||||
#include "../cereal/include/cereal/archives/binary.hpp"
|
||||
#include "../cereal/include/cereal/types/string.hpp"
|
||||
|
||||
class TraderRepository: public BaseTraderRepository {
|
||||
class TraderRepository : public BaseTraderRepository {
|
||||
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
|
||||
*
|
||||
* TraderRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* TraderRepository::GetWhereNeverExpires()
|
||||
* TraderRepository::GetWhereXAndY()
|
||||
* TraderRepository::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
|
||||
*/
|
||||
struct DistinctTraders_Struct {
|
||||
uint32 trader_id;
|
||||
uint32 zone_id;
|
||||
uint32 entity_id;
|
||||
std::string trader_name;
|
||||
};
|
||||
|
||||
// Custom extended repository methods here
|
||||
struct BulkTraders_Struct {
|
||||
uint32 count{0};
|
||||
uint32 name_length{0};
|
||||
std::vector<DistinctTraders_Struct> traders{};
|
||||
};
|
||||
|
||||
struct WelcomeData_Struct {
|
||||
uint32 count_of_traders;
|
||||
uint32 count_of_items;
|
||||
};
|
||||
|
||||
static std::vector<BazaarSearchResultsFromDB_Struct>
|
||||
GetBazaarSearchResults(
|
||||
SharedDatabase &db,
|
||||
BazaarSearchCriteria_Struct search,
|
||||
uint32 char_zone_id
|
||||
);
|
||||
|
||||
static BulkTraders_Struct GetDistinctTraders(Database &db)
|
||||
{
|
||||
BulkTraders_Struct all_entries{};
|
||||
std::vector<DistinctTraders_Struct> distinct_traders;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
"SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_entity_id, c.name "
|
||||
"FROM trader AS t "
|
||||
"JOIN character_data AS c ON t.char_id = c.id;"
|
||||
);
|
||||
|
||||
distinct_traders.reserve(results.RowCount());
|
||||
|
||||
for (auto row: results) {
|
||||
DistinctTraders_Struct e{};
|
||||
|
||||
e.trader_id = Strings::ToInt(row[0]);
|
||||
e.zone_id = Strings::ToInt(row[1]);
|
||||
e.entity_id = Strings::ToInt(row[2]);
|
||||
e.trader_name = row[3] ? row[3] : "";
|
||||
all_entries.name_length += e.trader_name.length() + 1;
|
||||
|
||||
all_entries.traders.push_back(e);
|
||||
}
|
||||
all_entries.count = results.RowCount();
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static WelcomeData_Struct GetWelcomeData(Database &db)
|
||||
{
|
||||
WelcomeData_Struct e{};
|
||||
|
||||
auto results = db.QueryDatabase("SELECT COUNT(DISTINCT char_id), count(char_id) FROM trader;");
|
||||
|
||||
if (!results.RowCount()) {
|
||||
return e;
|
||||
}
|
||||
|
||||
auto r = results.begin();
|
||||
e.count_of_traders = Strings::ToInt(r[0]);
|
||||
e.count_of_items = Strings::ToInt(r[1]);
|
||||
return e;
|
||||
}
|
||||
|
||||
static int UpdateItem(Database &db, uint32 char_id, uint32 new_price, uint32 item_id, uint32 item_charges)
|
||||
{
|
||||
std::vector<BaseTraderRepository::Trader> items{};
|
||||
if (item_charges == 0) {
|
||||
items = GetWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"char_id = '{}' AND item_id = '{}'",
|
||||
char_id,
|
||||
item_id
|
||||
)
|
||||
);
|
||||
}
|
||||
else {
|
||||
items = GetWhere(
|
||||
db,
|
||||
fmt::format(
|
||||
"char_id = '{}' AND item_id = '{}' AND item_charges = '{}'",
|
||||
char_id,
|
||||
item_id,
|
||||
item_charges
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (items.empty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (auto &i: items) {
|
||||
i.item_cost = new_price;
|
||||
}
|
||||
|
||||
return ReplaceMany(db, items);
|
||||
}
|
||||
|
||||
static Trader GetTraderItem(Database &db, uint32 trader_id, uint32 item_id, uint32 item_cost)
|
||||
{
|
||||
Trader item{};
|
||||
|
||||
auto query = fmt::format(
|
||||
"SELECT t.char_id, t.item_id, t.serialnumber, t.charges, t.item_cost, t.slot_id, t.entity_id FROM trader AS t "
|
||||
"WHERE t.entity_id = {} AND t.item_id = {} AND t.item_cost = {} "
|
||||
"LIMIT 1;",
|
||||
trader_id,
|
||||
item_id,
|
||||
item_cost
|
||||
);
|
||||
auto results = db.QueryDatabase(query);
|
||||
|
||||
if (results.RowCount() == 0) {
|
||||
return item;
|
||||
}
|
||||
|
||||
auto row = results.begin();
|
||||
item.char_id = Strings::ToInt(row[0]);
|
||||
item.item_id = Strings::ToInt(row[1]);
|
||||
item.item_sn = Strings::ToInt(row[2]);
|
||||
item.item_charges = Strings::ToInt(row[3]);
|
||||
item.item_cost = Strings::ToInt(row[4]);
|
||||
item.slot_id = Strings::ToInt(row[5]);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
static int UpdateQuantity(Database &db, uint32 char_id, uint32 serial_number, int16 quantity)
|
||||
{
|
||||
const auto trader_item = GetWhere(
|
||||
db,
|
||||
fmt::format("char_id = '{}' AND item_sn = '{}' ", char_id, serial_number)
|
||||
);
|
||||
|
||||
if (trader_item.empty() || trader_item.size() > 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto m = trader_item[0];
|
||||
m.item_charges = quantity;
|
||||
|
||||
return UpdateOne(db, m);
|
||||
}
|
||||
|
||||
static Trader GetItemBySerialNumber(Database &db, uint32 serial_number)
|
||||
{
|
||||
Trader e{};
|
||||
const auto trader_item = GetWhere(
|
||||
db,
|
||||
fmt::format("`item_sn` = '{}' LIMIT 1", serial_number)
|
||||
);
|
||||
|
||||
if (trader_item.empty()) {
|
||||
return e;
|
||||
}
|
||||
else {
|
||||
return trader_item.at(0);
|
||||
}
|
||||
}
|
||||
|
||||
static Trader GetItemBySerialNumber(Database &db, std::string serial_number)
|
||||
{
|
||||
Trader e{};
|
||||
auto sn = Strings::ToUnsignedBigInt(serial_number);
|
||||
const auto trader_item = GetWhere(
|
||||
db,
|
||||
fmt::format("`item_sn` = '{}' LIMIT 1", sn)
|
||||
);
|
||||
|
||||
if (trader_item.empty()) {
|
||||
return e;
|
||||
}
|
||||
else {
|
||||
return trader_item.at(0);
|
||||
}
|
||||
}
|
||||
|
||||
static int UpdateActiveTransaction(Database &db, uint32 id, bool status)
|
||||
{
|
||||
auto e = FindOne(db, id);
|
||||
if (!e.id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
e.active_transaction = status == true ? 1 : 0;
|
||||
|
||||
return UpdateOne(db, e);
|
||||
}
|
||||
|
||||
static int DeleteMany(Database &db, const std::vector<Trader> &entries)
|
||||
{
|
||||
std::vector<std::string> delete_ids;
|
||||
|
||||
for (auto const &e: entries) {
|
||||
delete_ids.push_back(std::to_string(e.id));
|
||||
}
|
||||
|
||||
return DeleteWhere(db, fmt::format("`id` IN({})", Strings::Implode(",", delete_ids)));
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_TRADER_REPOSITORY_H
|
||||
|
||||
+20
-1
@@ -505,6 +505,7 @@ RULE_BOOL(Spells, ManaTapsRequireNPCMana, false, "Enabling will require target t
|
||||
RULE_INT(Spells, HarmTouchCritRatio, 200, "Harmtouch crit bonus, on top of BaseCritRatio")
|
||||
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_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Combat)
|
||||
@@ -520,6 +521,8 @@ RULE_BOOL(Combat, NPCCanCrit, false, "Setting whether an NPC can land critical h
|
||||
RULE_BOOL(Combat, UseIntervalAC, true, "Switch whether bonuses, armour class, multipliers, classes and caps should be considered in the calculation of damage values")
|
||||
RULE_INT(Combat, PetAttackMagicLevel, 10, "Level at which pets can cause magic damage, no longer used")
|
||||
RULE_INT(Combat, NPCAttackMagicLevel, 10, "Level at which NPC and pets can cause magic damage")
|
||||
RULE_INT(Combat, LevelDifferenceRollCheck, -1, "Level Difference to enable LeverDifferenceRollBonus for MeleeMitigation - Default: -1 is disabled, 20 is common")
|
||||
RULE_REAL(Combat, LevelDifferenceRollBonus, 0.5, "Roll Bonus/Detrement if using LevelDifferenceRollCheck")
|
||||
RULE_BOOL(Combat, EnableFearPathing, true, "Setting whether to use pathing during fear")
|
||||
RULE_BOOL(Combat, FleeGray, true, "If true FleeGrayHPRatio will be used")
|
||||
RULE_INT(Combat, FleeGrayHPRatio, 50, "HP percentage when a Gray NPC begins to flee")
|
||||
@@ -589,6 +592,8 @@ RULE_BOOL(Combat, BackstabIgnoresElemental, false, "Enable or disable Elemental
|
||||
RULE_BOOL(Combat, BackstabIgnoresBane, false, "Enable or disable Bane weapon damage affecting backstab damage, false by default.")
|
||||
RULE_INT(Combat, DoubleBackstabLevelRequirement, 55, "Level requirement to enable double backstab attempts. The default is 55.")
|
||||
RULE_BOOL(Combat, SummonMeleeRange, true, "Enable or disable summoning of a player when already in melee range of the summoner.")
|
||||
RULE_REAL(Combat, ArcheryHitPenalty, 0, "Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it - Default: 0")
|
||||
RULE_REAL(Combat, ArcheryBaseDamageBonus, 1, "Percentage modifier to base archery Damage 0.5=50% base damage, 1=100%,2=200% - Default: 1")
|
||||
RULE_BOOL(Combat, WaterMatchRequiredForAutoFireLoS, true, "Enable/Disable the requirement of both the attacker/victim being both in or out of water for AutoFire LoS to pass.")
|
||||
RULE_INT(Combat, ExtraAllowedKickClassesBitmask, 0, "Bitmask for allowing extra classes beyond Warrior, Ranger, Beastlord, and Berserker to kick, No Extra Classes (0) by default")
|
||||
RULE_INT(Combat, MaxProcs, 4, "Adjustable maximum number of procs per round, the hard cap is MAX_PROCS (11). Requires mob repop or client zone when changed")
|
||||
@@ -603,6 +608,13 @@ RULE_INT(Combat, StunDuration, 2000, "Duration of stuns in ms. DEFAULT: 2000")
|
||||
RULE_BOOL(Combat, ClientStunMessage, false, "Client stunning NPC produces message. DEFAULT false")
|
||||
RULE_BOOL(Combat, BashTwoHanderUseShoulderAC, false, "Enable to use shoulder AC for bash calculations when two hander is equipped. Unproven if accurate DEFAULT: false")
|
||||
RULE_REAL(Combat, BashACBonusDivisor, 25.0, "this divides the AC value contribution to bash damage, lower to increase damage")
|
||||
RULE_BOOL(Combat, UseMobStaticOffenseSkill, false, "Toggle to enabled the use of a static offense skill for Mobs. DEFAULT: false")
|
||||
RULE_BOOL(Combat, UseEnhancedMobStaticWeaponSkill, false, "Toggle to enabled the use of an enhanced (slightly higher hit rate) static weapon skill for Mobs. DEFAULT: false")
|
||||
RULE_INT(Combat, PCAttackPowerScaling, 100, "Applies scaling to PC Attack Power (75 = 75%). DEFAULT: 100 to not adjust existing Servers")
|
||||
RULE_INT(Combat, PCAccuracyAvoidanceMod2Scale, 100, "Scale Factor for PC Accuracy and Avoidance (Mod2, found on items). Found a value of 100 to make both too strong (75 = x0.75). DEFAULT: 100 to not adjust existing Servers")
|
||||
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_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(NPC)
|
||||
@@ -661,6 +673,8 @@ RULE_BOOL(Aggro, NPCAggroMaxDistanceEnabled, true, "If enabled, NPC's will drop
|
||||
RULE_BOOL(Aggro, AggroPlayerPets, false, "If enabled, NPCs will aggro player pets")
|
||||
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_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(TaskSystem)
|
||||
@@ -725,7 +739,7 @@ RULE_BOOL(Bots, BotHealOnLevel, false, "Setting whether a bot should heal comple
|
||||
RULE_INT(Bots, AutosaveIntervalSeconds, 300, "Number of seconds after which a timer is triggered which stores the bot data. The value 0 means no periodic automatic saving.")
|
||||
RULE_BOOL(Bots, CazicTouchBotsOwner, true, "Default True. Cazic Touch/DT will hit bot owner rather than bot.")
|
||||
RULE_INT(Bots, BotsClickItemsMinLvl, 1, "Minimum level for bots to be able to use ^clickitem. Default 1.")
|
||||
RULE_BOOL(Bots, BotsCanClickItems, true, "Enabled the ability for bots to click items they have equipped. Default TRUE")
|
||||
RULE_BOOL(Bots, 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_CATEGORY_END()
|
||||
|
||||
@@ -754,6 +768,7 @@ RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Merchant)
|
||||
RULE_BOOL(Merchant, UsePriceMod, true, "Use faction/charisma price modifiers")
|
||||
RULE_BOOL(Merchant, UseClassicPriceMod, false, "Must also set UsePriceMod. Negates other rules for vendor price mods.")
|
||||
RULE_REAL(Merchant, SellCostMod, 1.05, "Modifier for NPC sell price")
|
||||
RULE_REAL(Merchant, BuyCostMod, 0.95, "Modifier for NPC buy price")
|
||||
RULE_INT(Merchant, PriceBonusPct, 4, "Determines maximum price bonus from having good faction/CHA. Value is a percent")
|
||||
@@ -769,6 +784,9 @@ RULE_BOOL(Bazaar, AuditTrail, false, "Setting whether a path to the trader shoul
|
||||
RULE_INT(Bazaar, MaxSearchResults, 50, "Maximum number of search results in Bazaar")
|
||||
RULE_BOOL(Bazaar, EnableWarpToTrader, true, "Setting whether teleport to the selected trader should be active")
|
||||
RULE_INT(Bazaar, MaxBarterSearchResults, 200, "The maximum results returned in the /barter search")
|
||||
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_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Mail)
|
||||
@@ -813,6 +831,7 @@ RULE_INT(AA, ModernAAScalingAAMinimum, 0, "The minimum number of earned AA befor
|
||||
RULE_INT(AA, ModernAAScalingAALimit, 4000, "The number of earned AA when AA experience scaling ends")
|
||||
RULE_BOOL(AA, SoundForAAEarned, false, "Play sound when AA point earned")
|
||||
RULE_INT(AA, UnusedAAPointCap, -1, "Cap for Unused AA Points. Default: -1. NOTE: DO NOT LOWER THIS WITHOUT KNOWING WHAT YOU ARE DOING. MAY RESULT IN PLAYERS LOSING AAs.")
|
||||
RULE_INT(AA, MaxAAEXPPerKill, -1, "Maximum AA EXP per Kill (3425214 is about 7%) - Default: -1 will disable the check")
|
||||
RULE_CATEGORY_END()
|
||||
|
||||
RULE_CATEGORY(Console)
|
||||
|
||||
@@ -138,6 +138,9 @@
|
||||
#define ServerOP_RaidMOTD 0x0113
|
||||
#define ServerOP_RaidNote 0x0114
|
||||
|
||||
#define ServerOP_TraderMessaging 0x0120
|
||||
#define ServerOP_BazaarPurchase 0x0121
|
||||
|
||||
#define ServerOP_InstanceUpdateTime 0x014F
|
||||
#define ServerOP_AdventureRequest 0x0150
|
||||
#define ServerOP_AdventureRequestAccept 0x0151
|
||||
@@ -277,6 +280,7 @@
|
||||
#define ServerOP_ReloadLoot 0x4127
|
||||
#define ServerOP_ReloadBaseData 0x4128
|
||||
#define ServerOP_ReloadSkillCaps 0x4129
|
||||
#define ServerOP_ReloadNPCSpells 0x4130
|
||||
|
||||
#define ServerOP_CZDialogueWindow 0x4500
|
||||
#define ServerOP_CZLDoNUpdate 0x4501
|
||||
@@ -1937,6 +1941,27 @@ struct ServerOP_GuildMessage_Struct {
|
||||
char url[2048]{0};
|
||||
};
|
||||
|
||||
struct TraderMessaging_Struct {
|
||||
uint32 action;
|
||||
uint32 zone_id;
|
||||
uint32 trader_id;
|
||||
uint32 entity_id;
|
||||
char trader_name[64];
|
||||
};
|
||||
|
||||
struct BazaarPurchaseMessaging_Struct {
|
||||
TraderBuy_Struct trader_buy_struct;
|
||||
uint32 item_aug_1;
|
||||
uint32 item_aug_2;
|
||||
uint32 item_aug_3;
|
||||
uint32 item_aug_4;
|
||||
uint32 item_aug_5;
|
||||
uint32 item_aug_6;
|
||||
uint32 buyer_id;
|
||||
uint32 item_quantity_available;
|
||||
uint32 id;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
#endif
|
||||
|
||||
+18
-17
@@ -1,4 +1,5 @@
|
||||
#include "skill_caps.h"
|
||||
#include "timer.h"
|
||||
|
||||
SkillCaps *SkillCaps::SetContentDatabase(Database *db)
|
||||
{
|
||||
@@ -13,39 +14,39 @@ SkillCapsRepository::SkillCaps SkillCaps::GetSkillCap(uint8 class_id, EQ::skills
|
||||
return SkillCapsRepository::NewEntity();
|
||||
}
|
||||
|
||||
uint64_t key = (class_id * 1000000) + (level * 1000) + static_cast<uint32>(skill_id);
|
||||
const uint64_t key = (class_id * 1000000) + (level * 1000) + static_cast<uint32>(skill_id);
|
||||
|
||||
auto pos = m_skill_caps.find(key);
|
||||
if (pos != m_skill_caps.end()) {
|
||||
return pos->second;
|
||||
}
|
||||
|
||||
return SkillCapsRepository::NewEntity();
|
||||
}
|
||||
|
||||
uint8 SkillCaps::GetTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level)
|
||||
uint8 SkillCaps::GetSkillTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level)
|
||||
{
|
||||
if (
|
||||
!IsPlayerClass(class_id) ||
|
||||
class_id > Class::PLAYER_CLASS_COUNT ||
|
||||
static_cast<uint32>(skill_id) > (EQ::skills::HIGHEST_SKILL + 1)
|
||||
) {
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const uint8 skill_cap_max_level = (
|
||||
RuleI(Character, SkillCapMaxLevel) > 0 ?
|
||||
RuleI(Character, SkillCapMaxLevel) :
|
||||
RuleI(Character, MaxLevel)
|
||||
RuleI(Character, SkillCapMaxLevel) :
|
||||
RuleI(Character, MaxLevel)
|
||||
);
|
||||
|
||||
const uint8 max_level = level > skill_cap_max_level ? level : skill_cap_max_level;
|
||||
const uint8 max_level = level > skill_cap_max_level ? level : skill_cap_max_level;
|
||||
const uint64_t key = (class_id * 1000000) + (level * 1000) + static_cast<uint32>(skill_id);
|
||||
|
||||
for (const auto &e: m_skill_caps) {
|
||||
for (uint8 current_level = 1; current_level <= max_level; current_level++) {
|
||||
uint64_t key = (class_id * 1000000) + (level * 1000) + static_cast<uint32>(skill_id);
|
||||
auto pos = m_skill_caps.find(key);
|
||||
if (pos != m_skill_caps.end()) {
|
||||
return current_level;
|
||||
}
|
||||
for (uint8 current_level = 1; current_level <= max_level; current_level++) {
|
||||
auto pos = m_skill_caps.find(key);
|
||||
if (pos != m_skill_caps.end()) {
|
||||
return current_level;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,20 +55,20 @@ uint8 SkillCaps::GetTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, u
|
||||
|
||||
void SkillCaps::LoadSkillCaps()
|
||||
{
|
||||
const auto &l = SkillCapsRepository::All(*m_content_database);
|
||||
const auto& l = SkillCapsRepository::All(*m_content_database);
|
||||
|
||||
m_skill_caps.clear();
|
||||
|
||||
for (const auto &e: l) {
|
||||
for (const auto& e: l) {
|
||||
if (
|
||||
e.level < 1 ||
|
||||
!IsPlayerClass(e.class_id) ||
|
||||
static_cast<EQ::skills::SkillType>(e.skill_id) >= EQ::skills::SkillCount
|
||||
) {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t key = (e.class_id * 1000000) + (e.level * 1000) + e.skill_id;
|
||||
const uint64_t key = (e.class_id * 1000000) + (e.level * 1000) + e.skill_id;
|
||||
m_skill_caps[key] = e;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ class SkillCaps {
|
||||
public:
|
||||
inline void ClearSkillCaps() { m_skill_caps.clear(); }
|
||||
SkillCapsRepository::SkillCaps GetSkillCap(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
|
||||
uint8 GetTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
|
||||
uint8 GetSkillTrainLevel(uint8 class_id, EQ::skills::SkillType skill_id, uint8 level);
|
||||
void LoadSkillCaps();
|
||||
void ReloadSkillCaps();
|
||||
|
||||
|
||||
+1
-1
@@ -904,7 +904,7 @@ typedef enum {
|
||||
#define SE_AlterNPCLevel 107 // implemented - not used on live
|
||||
#define SE_Familiar 108 // implemented
|
||||
#define SE_SummonItemIntoBag 109 // implemented - summons stuff into container
|
||||
//#define SE_IncreaseArchery 110 // not used
|
||||
#define SE_IncreaseArchery 110 // implemented
|
||||
#define SE_ResistAll 111 // implemented - Note: Physical Resists are not modified by this effect.
|
||||
#define SE_CastingLevel 112 // implemented
|
||||
#define SE_SummonHorse 113 // implemented
|
||||
|
||||
@@ -745,6 +745,15 @@ bool Strings::Contains(const std::string& subject, const std::string& search)
|
||||
return subject.find(search) != std::string::npos;
|
||||
}
|
||||
|
||||
bool Strings::ContainsLower(const std::string& subject, const std::string& search)
|
||||
{
|
||||
if (subject.length() < search.length()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ToLower(subject).find(ToLower(search)) != std::string::npos;
|
||||
}
|
||||
|
||||
uint32 Strings::TimeToSeconds(std::string time_string)
|
||||
{
|
||||
if (time_string.empty()) {
|
||||
|
||||
@@ -86,6 +86,7 @@ class Strings {
|
||||
public:
|
||||
static bool Contains(std::vector<std::string> container, const std::string& element);
|
||||
static bool Contains(const std::string& subject, const std::string& search);
|
||||
static bool ContainsLower(const std::string& subject, const std::string& search);
|
||||
static int ToInt(const std::string &s, int fallback = 0);
|
||||
static int64 ToBigInt(const std::string &s, int64 fallback = 0);
|
||||
static uint32 ToUnsignedInt(const std::string &s, uint32 fallback = 0);
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@
|
||||
|
||||
// Build variables
|
||||
// these get injected during the build pipeline
|
||||
#define CURRENT_VERSION "22.50.1-dev" // always append -dev to the current version for custom-builds
|
||||
#define CURRENT_VERSION "22.51.1-dev" // always append -dev to the current version for custom-builds
|
||||
#define LOGIN_VERSION "0.8.0"
|
||||
#define COMPILE_DATE __DATE__
|
||||
#define COMPILE_TIME __TIME__
|
||||
@@ -42,7 +42,7 @@
|
||||
* Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
|
||||
*/
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9276
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9280
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9044
|
||||
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "eqemu-server",
|
||||
"version": "22.50.1",
|
||||
"version": "22.51.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/EQEmu/Server.git"
|
||||
|
||||
@@ -430,6 +430,7 @@ OP_BazaarSearch=0x39d6
|
||||
OP_TraderDelItem=0x5829
|
||||
OP_BecomeTrader=0x61b3
|
||||
OP_TraderShop=0x31df
|
||||
OP_TraderBulkSend=0x6a96
|
||||
OP_Trader=0x4ef5
|
||||
OP_Barter=0x243a
|
||||
OP_TraderBuy=0x0000
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ struct EQ::Net::ConsoleLoginStatus CheckLogin(const std::string &username, const
|
||||
const std::string& account_name = database.GetAccountName(ret.account_id);
|
||||
|
||||
ret.account_name = account_name;
|
||||
ret.status = database.CheckStatus(ret.account_id);
|
||||
ret.status = database.GetAccountStatus(ret.account_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ std::vector<Reload> reload_types = {
|
||||
Reload{.command = "loot", .opcode = ServerOP_ReloadLoot, .desc = "Loot"},
|
||||
Reload{.command = "merchants", .opcode = ServerOP_ReloadMerchants, .desc = "Merchants"},
|
||||
Reload{.command = "npc_emotes", .opcode = ServerOP_ReloadNPCEmotes, .desc = "NPC Emotes"},
|
||||
Reload{.command = "npc_spells", .opcode = ServerOP_ReloadNPCSpells, .desc = "NPC Spells"},
|
||||
Reload{.command = "objects", .opcode = ServerOP_ReloadObjects, .desc = "Objects"},
|
||||
Reload{.command = "opcodes", .opcode = ServerOP_ReloadOpcodes, .desc = "Opcodes"},
|
||||
Reload{.command = "perl_export", .opcode = ServerOP_ReloadPerlExportSettings, .desc = "Perl Event Export Settings"},
|
||||
|
||||
@@ -66,7 +66,7 @@ void LoginServer::ProcessUsertoWorldReqLeg(uint16_t opcode, EQ::Net::Packet &p)
|
||||
|
||||
UsertoWorldRequestLegacy_Struct *utwr = (UsertoWorldRequestLegacy_Struct *) p.Data();
|
||||
uint32 id = database.GetAccountIDFromLSID("eqemu", utwr->lsaccountid);
|
||||
int16 status = database.CheckStatus(id);
|
||||
int16 status = database.GetAccountStatus(id);
|
||||
|
||||
LogDebug(
|
||||
"id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]",
|
||||
@@ -146,7 +146,7 @@ void LoginServer::ProcessUsertoWorldReq(uint16_t opcode, EQ::Net::Packet &p)
|
||||
|
||||
UsertoWorldRequest_Struct *utwr = (UsertoWorldRequest_Struct *) p.Data();
|
||||
uint32 id = database.GetAccountIDFromLSID(utwr->login, utwr->lsaccountid);
|
||||
int16 status = database.CheckStatus(id);
|
||||
int16 status = database.GetAccountStatus(id);
|
||||
|
||||
LogDebug(
|
||||
"id [{}] status [{}] account_id [{}] world_id [{}] from_id [{}] to_id [{}] ip [{}]",
|
||||
|
||||
@@ -1410,6 +1410,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
case ServerOP_ReloadLevelEXPMods:
|
||||
case ServerOP_ReloadMerchants:
|
||||
case ServerOP_ReloadNPCEmotes:
|
||||
case ServerOP_ReloadNPCSpells:
|
||||
case ServerOP_ReloadObjects:
|
||||
case ServerOP_ReloadPerlExportSettings:
|
||||
case ServerOP_ReloadStaticZoneData:
|
||||
@@ -1423,6 +1424,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
case ServerOP_ReloadLoot:
|
||||
case ServerOP_RezzPlayerAccept:
|
||||
case ServerOP_SpawnStatusChange:
|
||||
case ServerOP_TraderMessaging:
|
||||
case ServerOP_UpdateSpawn:
|
||||
case ServerOP_WWDialogueWindow:
|
||||
case ServerOP_WWLDoNUpdate:
|
||||
@@ -1743,6 +1745,18 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) {
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerOP_BazaarPurchase: {
|
||||
auto in = (BazaarPurchaseMessaging_Struct *)pack->pBuffer;
|
||||
if (in->trader_buy_struct.trader_id <= 0) {
|
||||
LogTrading(
|
||||
"World Message <red>[{}] received with invalid trader_id <red>[{}]",
|
||||
"ServerOP_BazaarPurchase",
|
||||
in->trader_buy_struct.trader_id
|
||||
);
|
||||
}
|
||||
|
||||
zoneserver_list.SendPacket(Zones::BAZAAR, pack);
|
||||
}
|
||||
default: {
|
||||
LogInfo("Unknown ServerOPcode from zone {:#04x}, size [{}]", pack->opcode, pack->size);
|
||||
DumpPacket(pack->pBuffer, pack->size);
|
||||
|
||||
+12
-10
@@ -518,7 +518,7 @@ void Mob::WakeTheDead(uint16 spell_id, Corpse *corpse_to_use, Mob *tar, uint32 d
|
||||
|
||||
void Client::ResetAA()
|
||||
{
|
||||
SendClearAA();
|
||||
SendClearPlayerAA();
|
||||
RefundAA();
|
||||
|
||||
memset(&m_pp.aa_array[0], 0, sizeof(AA_Array) * MAX_PP_AA_ARRAY);
|
||||
@@ -540,6 +540,13 @@ void Client::ResetAA()
|
||||
++slot_id;
|
||||
}
|
||||
|
||||
database.DeleteCharacterAAs(CharacterID());
|
||||
}
|
||||
|
||||
void Client::ResetLeadershipAA()
|
||||
{
|
||||
SendClearLeadershipAA();
|
||||
|
||||
for (int slot_id = 0; slot_id < _maxLeaderAA; ++slot_id) {
|
||||
m_pp.leader_abilities.ranks[slot_id] = 0;
|
||||
}
|
||||
@@ -549,16 +556,9 @@ void Client::ResetAA()
|
||||
m_pp.group_leadership_exp = 0;
|
||||
m_pp.raid_leadership_exp = 0;
|
||||
|
||||
database.DeleteCharacterAAs(CharacterID());
|
||||
database.DeleteCharacterLeadershipAbilities(CharacterID());
|
||||
}
|
||||
|
||||
void Client::SendClearAA()
|
||||
{
|
||||
SendClearLeadershipAA();
|
||||
SendClearPlayerAA();
|
||||
}
|
||||
|
||||
void Client::SendClearPlayerAA()
|
||||
{
|
||||
auto outapp = new EQApplicationPacket(OP_ClearAA, 0);
|
||||
@@ -2178,7 +2178,8 @@ void Client::AutoGrantAAPoints() {
|
||||
}
|
||||
}
|
||||
|
||||
SendClearAA();
|
||||
SendClearLeadershipAA();
|
||||
SendClearPlayerAA();
|
||||
SendAlternateAdvancementTable();
|
||||
SendAlternateAdvancementPoints();
|
||||
SendAlternateAdvancementStats();
|
||||
@@ -2211,7 +2212,8 @@ void Client::GrantAllAAPoints(uint8 unlock_level)
|
||||
}
|
||||
|
||||
SaveAA();
|
||||
SendClearAA();
|
||||
SendClearLeadershipAA();
|
||||
SendClearPlayerAA();
|
||||
SendAlternateAdvancementTable();
|
||||
SendAlternateAdvancementPoints();
|
||||
SendAlternateAdvancementStats();
|
||||
|
||||
@@ -59,7 +59,7 @@ EQ::Net::WebsocketLoginStatus CheckLogin(
|
||||
|
||||
ret.account_name = database.GetAccountName(static_cast<uint32>(ret.account_id));
|
||||
ret.logged_in = true;
|
||||
ret.status = database.CheckStatus(ret.account_id);
|
||||
ret.status = database.GetAccountStatus(ret.account_id);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
+159
-19
@@ -157,15 +157,18 @@ int Mob::compute_tohit(EQ::skills::SkillType skillinuse)
|
||||
{
|
||||
int tohit = GetSkill(EQ::skills::SkillOffense) + 7;
|
||||
tohit += GetSkill(skillinuse);
|
||||
if (IsNPC())
|
||||
|
||||
if (IsNPC()) {
|
||||
if (RuleB(Combat, UseMobStaticOffenseSkill)) {
|
||||
tohit = GetMobFixedWeaponSkill() + GetMobFixedOffenseSkill() + 7;
|
||||
}
|
||||
tohit += CastToNPC()->GetAccuracyRating();
|
||||
if (IsClient()) {
|
||||
} else if (IsClient()) {
|
||||
double reduction = CastToClient()->GetIntoxication() / 2.0;
|
||||
if (reduction > 20.0) {
|
||||
reduction = std::min((110 - reduction) / 100.0, 1.0);
|
||||
tohit = reduction * static_cast<double>(tohit);
|
||||
}
|
||||
else if (IsBerserk()) {
|
||||
} else if (IsBerserk()) {
|
||||
tohit += (GetLevel() * 2) / 5;
|
||||
}
|
||||
}
|
||||
@@ -191,8 +194,15 @@ int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod)
|
||||
// unsure on the stacking order of these effects, rather hard to parse
|
||||
// item mod2 accuracy isn't applied to range? Theory crafting and parses back it up I guess
|
||||
// mod2 accuracy -- flat bonus
|
||||
if (skill != EQ::skills::SkillArchery && skill != EQ::skills::SkillThrowing)
|
||||
if (skill != EQ::skills::SkillArchery && skill != EQ::skills::SkillThrowing) {
|
||||
accuracy += itembonuses.HitChance;
|
||||
} else {
|
||||
// Applying a scale factor as sources suggest Accuracy should reduce number of missing by 0.1% per point, so 150 = 15% reduction in misses.
|
||||
// Based on my calculator 150 Accuracy was reducing misses by too much (closer to 20%)
|
||||
// NOTE: This doesn't mean if you have a 30% miss chance you now miss 15%. It means if you have a 30% miss chance you now have a 30% * (100% - 15%) = 30% * 85% = 25.5% miss chance
|
||||
// Using same scale factor for Avoidance and Accuracy since they impact the formula about the same.
|
||||
accuracy += itembonuses.HitChance * RuleI(Combat, PCAccuracyAvoidanceMod2Scale) / 100;
|
||||
}
|
||||
|
||||
//518 Increase ATK accuracy by percentage, stackable
|
||||
auto atkhit_bonus = itembonuses.Attack_Accuracy_Max_Percent + aabonuses.Attack_Accuracy_Max_Percent + spellbonuses.Attack_Accuracy_Max_Percent;
|
||||
@@ -222,6 +232,11 @@ int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod)
|
||||
aabonuses.HitChanceEffect[skill] +
|
||||
spellbonuses.HitChanceEffect[skill];
|
||||
|
||||
if (skill == EQ::skills::SkillArchery) {
|
||||
hit_bonus += spellbonuses.increase_archery + aabonuses.increase_archery + itembonuses.increase_archery;
|
||||
hit_bonus -= hit_bonus * RuleR(Combat, ArcheryHitPenalty);
|
||||
}
|
||||
|
||||
accuracy = (accuracy * (100 + hit_bonus)) / 100;
|
||||
|
||||
// TODO: April 2003 added an archery/throwing PVP accuracy penalty while moving, should be in here some where,
|
||||
@@ -241,19 +256,44 @@ int Mob::GetTotalToHit(EQ::skills::SkillType skill, int chance_mod)
|
||||
int Mob::compute_defense()
|
||||
{
|
||||
int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225;
|
||||
defense += (8000 * (GetAGI() - 40)) / 36000;
|
||||
if (IsOfClientBot()) {
|
||||
defense += itembonuses.heroic_agi_avoidance;
|
||||
|
||||
// In new code, AGI becomes a large contributor to avoidance at low levels, since AGI isn't capped by Level but Defense is
|
||||
// A scale factor is implemented for PCs to reduce the effect of AGI at low levels. This isn't applied to NPCs since they can be
|
||||
// easily controlled via the Database.
|
||||
if (RuleB(Combat, LegacyComputeDefense)) {
|
||||
int agi_scale_factor = 1000;
|
||||
|
||||
if (IsOfClientBot()) {
|
||||
agi_scale_factor = std::min(1000, static_cast<int>(GetLevel()) * 1000 / 70); // Scales Agi Contribution for PC's Level, max Contribution at Level 70
|
||||
}
|
||||
|
||||
defense += agi_scale_factor * (800 * (GetAGI() - 40)) / 3600 / 1000;
|
||||
|
||||
if (IsOfClientBot()) {
|
||||
defense += GetHeroicAGI() / 10;
|
||||
}
|
||||
|
||||
defense += itembonuses.AvoidMeleeChance * RuleI(Combat, PCAccuracyAvoidanceMod2Scale) / 100; // item mod2
|
||||
} else {
|
||||
defense += (8000 * (GetAGI() - 40)) / 36000;
|
||||
|
||||
if (IsOfClientBot()) {
|
||||
defense += itembonuses.heroic_agi_avoidance;
|
||||
}
|
||||
|
||||
defense += itembonuses.AvoidMeleeChance; // item mod2
|
||||
}
|
||||
|
||||
|
||||
//516 SE_AC_Mitigation_Max_Percent
|
||||
auto ac_bonus = itembonuses.AC_Mitigation_Max_Percent + aabonuses.AC_Mitigation_Max_Percent + spellbonuses.AC_Mitigation_Max_Percent;
|
||||
if (ac_bonus)
|
||||
if (ac_bonus) {
|
||||
defense += round(static_cast<double>(defense) * static_cast<double>(ac_bonus) * 0.0001);
|
||||
}
|
||||
|
||||
defense += itembonuses.AvoidMeleeChance; // item mod2
|
||||
if (IsNPC())
|
||||
if (IsNPC()) {
|
||||
defense += CastToNPC()->GetAvoidanceRating();
|
||||
}
|
||||
|
||||
if (IsClient()) {
|
||||
double reduction = CastToClient()->GetIntoxication() / 2.0;
|
||||
@@ -960,6 +1000,10 @@ int Mob::GetBestMeleeSkill()
|
||||
int Mob::offense(EQ::skills::SkillType skill)
|
||||
{
|
||||
int offense = GetSkill(skill);
|
||||
if (RuleB(Combat, UseMobStaticOffenseSkill) && IsNPC() && !IsPet() && !IsTempPet()) {
|
||||
offense = GetMobFixedWeaponSkill();
|
||||
}
|
||||
|
||||
int stat_bonus = GetSTR();
|
||||
|
||||
switch (skill) {
|
||||
@@ -980,10 +1024,20 @@ int Mob::offense(EQ::skills::SkillType skill)
|
||||
break;
|
||||
}
|
||||
|
||||
if (stat_bonus >= 75)
|
||||
if (stat_bonus >= 75) {
|
||||
offense += (2 * stat_bonus - 150) / 3;
|
||||
}
|
||||
|
||||
// GetATK() = ATK + itembonuses.ATK + spellbonuses.ATK. However, ATK appears to already be itembonuses.ATK + spellbonuses.ATK for PCs, so as is, it is double counting attack
|
||||
// This causes attack to be significantly more important than it should be based on era rule of thumbs. I do not want to change the GetATK() function in case doing so breaks something,
|
||||
// so instead I am just adding a /2 to remedy the double counting. NPCs do not have this issue, so they are broken up.
|
||||
// PCAttackPowerScaling is used to help bring attack power further in line with era estimates.
|
||||
if (IsOfClientBotMerc()) {
|
||||
offense += (GetATK() / 2 + GetPetATKBonusFromOwner()) * RuleI(Combat, PCAttackPowerScaling) / 100;
|
||||
} else {
|
||||
offense += GetATK();
|
||||
}
|
||||
|
||||
offense += GetATK() + GetPetATKBonusFromOwner();
|
||||
return offense;
|
||||
}
|
||||
|
||||
@@ -1039,6 +1093,26 @@ void Mob::MeleeMitigation(Mob *attacker, DamageHitInfo &hit, ExtraAttackOptions
|
||||
|
||||
auto roll = RollD20(hit.offense, mitigation);
|
||||
|
||||
// Add bonus to roll if level difference is sufficient
|
||||
const int level_diff = attacker->GetLevel() - GetLevel();
|
||||
const int level_diff_roll_check = RuleI(Combat, LevelDifferenceRollCheck);
|
||||
|
||||
if (level_diff_roll_check >= 0) {
|
||||
if (level_diff > level_diff_roll_check) {
|
||||
roll += RuleR(Combat, LevelDifferenceRollBonus);
|
||||
|
||||
if (roll > 2.0f) {
|
||||
roll = 2.0f;
|
||||
}
|
||||
} else if (level_diff < (-level_diff_roll_check)) {
|
||||
roll -= RuleR(Combat, LevelDifferenceRollBonus);
|
||||
|
||||
if (roll < 0.1f) {
|
||||
roll = 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// +0.5 for rounding, min to 1 dmg
|
||||
hit.damage_done = std::max(static_cast<int>(roll * static_cast<double>(hit.base_damage) + 0.5), 1);
|
||||
|
||||
@@ -2506,7 +2580,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy
|
||||
auto app = new EQApplicationPacket(OP_Death, sizeof(Death_Struct));
|
||||
|
||||
auto d = (Death_Struct*) app->pBuffer;
|
||||
|
||||
|
||||
// Convert last message to color to avoid duplicate damage messages
|
||||
// that occur in these rare cases when this is the death blow.
|
||||
if (IsValidSpell(spell) &&
|
||||
@@ -3082,12 +3156,16 @@ void Mob::AddToHateList(Mob* other, int64 hate /*= 0*/, int64 damage /*= 0*/, bo
|
||||
// Spell Casting Subtlety etc
|
||||
int64 hatemod = 100 + other->spellbonuses.hatemod + other->itembonuses.hatemod + other->aabonuses.hatemod;
|
||||
|
||||
if (hatemod < 1)
|
||||
if (hatemod < 1) {
|
||||
hatemod = 1;
|
||||
}
|
||||
hate = ((hate * (hatemod)) / 100);
|
||||
}
|
||||
else {
|
||||
hate += 100; // 100 bonus initial aggro
|
||||
} else {
|
||||
if (IsCharmed()){
|
||||
hate += RuleI(Aggro, InitialPetAggroBonus);
|
||||
} else {
|
||||
hate += RuleI(Aggro, InitialAggroBonus);
|
||||
}
|
||||
}
|
||||
|
||||
// Pet that is /pet hold on will not add to their hate list if they're not engaged
|
||||
@@ -4762,7 +4840,7 @@ void Mob::HealDamage(uint64 amount, Mob* caster, uint16 spell_id)
|
||||
else
|
||||
acthealed = amount;
|
||||
|
||||
if (acthealed > 100) {
|
||||
if (acthealed > RuleI(Spells, HealAmountMessageFilterThreshold)) {
|
||||
if (caster) {
|
||||
if (IsBuffSpell(spell_id)) { // hots
|
||||
// message to caster
|
||||
@@ -5809,6 +5887,65 @@ const DamageTable &Mob::GetDamageTable() const
|
||||
return which[level - 50];
|
||||
}
|
||||
|
||||
int Mob::GetMobFixedOffenseSkill()
|
||||
{
|
||||
// Due to new code using a combination of Offense and Weapon skill to determine hit, depending on the class
|
||||
// and weapon wielded by a mob, the hit rate of an equal level mob could vary between 15% and 60%, which made
|
||||
// many mobs far too easy. This particular call replaces the class based Offense Skill with a fixed value
|
||||
// equal to that of a Warrior of appropriate Level if UseMobFixedOffenseSkill flag is TRUE.
|
||||
|
||||
int level = EQ::ClampUpper(std::max(1, static_cast<int>(GetLevel())), 60);
|
||||
|
||||
if (level <= 40) {
|
||||
return (level * 5) + 5;
|
||||
} else if (EQ::ValueWithin(level, 41, 50)) {
|
||||
return 210;
|
||||
} else if (EQ::ValueWithin(level, 51, 58)) {
|
||||
return 210 + ((level - 50) * 5);
|
||||
}
|
||||
|
||||
return 252;
|
||||
}
|
||||
|
||||
int Mob::GetMobFixedWeaponSkill()
|
||||
{
|
||||
// Due to new code using a combination of Offense and Weapon skill to determine hit, depending on the class
|
||||
// and weapon wielded by a mob, the hit rate of an equal level mob could vary between 15% and 60%, which made
|
||||
// many mobs far too easy. This particular call replaces the weapon/class based Weapon Skill with a fixed value.
|
||||
// Two tables exist, one equal to a Warrior of appropriate level, and one modified to make hit rate equal to the old code
|
||||
// assuming the UseMobFixedOffenseSkill flag is set TRUE or the mob class is a Warrior (all the the bonus is in Weapon Skill).
|
||||
|
||||
int level = EQ::ClampUpper(std::max(1, static_cast<int>(GetLevel())), 70);
|
||||
|
||||
if (!RuleB(Combat, UseEnhancedMobStaticWeaponSkill)) {
|
||||
if (level <= 39) {
|
||||
return (level * 5) + 5;
|
||||
} else if (EQ::ValueWithin(level, 40, 50)) {
|
||||
return 200;
|
||||
} else if (EQ::ValueWithin(level, 51, 60)) {
|
||||
return 200 + ((level - 50) * 5);
|
||||
} else if (EQ::ValueWithin(level, 61, 65)) {
|
||||
return 250;
|
||||
}
|
||||
|
||||
return 250 + ((level - 65) * 5);
|
||||
}
|
||||
|
||||
if (level <= 39) {
|
||||
return (level * 6) - 1;
|
||||
} else if (EQ::ValueWithin(level, 45, 49)) {
|
||||
return 260;
|
||||
} else if (EQ::ValueWithin(level, 50, 54)) {
|
||||
return (level * 6) + 1;
|
||||
} else if (EQ::ValueWithin(level, 55, 59)) {
|
||||
return (level * 7) + 5;
|
||||
} else if (EQ::ValueWithin(level, 60, 65)) {
|
||||
return (level * 5) + 59;
|
||||
}
|
||||
|
||||
return 330 + (level - 66);
|
||||
}
|
||||
|
||||
void Mob::ApplyDamageTable(DamageHitInfo &hit)
|
||||
{
|
||||
#ifdef LUA_EQEMU
|
||||
@@ -6261,6 +6398,9 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
|
||||
MessageString(Chat::MeleeCrit, BOW_DOUBLE_DAMAGE);
|
||||
}
|
||||
}
|
||||
|
||||
//Scale Factor for Archery Damage Tuning
|
||||
hit.damage_done *= RuleR(Combat, ArcheryBaseDamageBonus);
|
||||
}
|
||||
|
||||
int extra_mincap = 0;
|
||||
|
||||
+18
-2
@@ -2235,6 +2235,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_IncreaseArchery:
|
||||
{
|
||||
new_bonus->increase_archery += effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_TotalHP:
|
||||
{
|
||||
new_bonus->FlatMaxHPChange += effect_value;
|
||||
@@ -3350,6 +3356,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
break;
|
||||
|
||||
case SE_Blind:
|
||||
if (RuleB(Combat, AllowRaidTargetBlind) && IsRaidTarget()) { // do not blind raid targets
|
||||
break;
|
||||
}
|
||||
|
||||
new_bonus->IsBlind = true;
|
||||
break;
|
||||
|
||||
@@ -4147,7 +4157,7 @@ bool Client::CalcItemScale(uint32 slot_x, uint32 slot_y) {
|
||||
continue;
|
||||
|
||||
// TEST CODE: test for bazaar trader crashing with charm items
|
||||
if (Trader)
|
||||
if (IsTrader())
|
||||
if (i >= EQ::invbag::GENERAL_BAGS_BEGIN && i <= EQ::invbag::GENERAL_BAGS_END) {
|
||||
EQ::ItemInstance* parent_item = m_inv.GetItem(EQ::InventoryProfile::CalcSlotId(i));
|
||||
if (parent_item && parent_item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel)
|
||||
@@ -4239,7 +4249,7 @@ bool Client::DoItemEnterZone(uint32 slot_x, uint32 slot_y) {
|
||||
continue;
|
||||
|
||||
// TEST CODE: test for bazaar trader crashing with charm items
|
||||
if (Trader)
|
||||
if (IsTrader())
|
||||
if (i >= EQ::invbag::GENERAL_BAGS_BEGIN && i <= EQ::invbag::GENERAL_BAGS_END) {
|
||||
EQ::ItemInstance* parent_item = m_inv.GetItem(EQ::InventoryProfile::CalcSlotId(i));
|
||||
if (parent_item && parent_item->GetItem()->BagType == EQ::item::BagTypeTradersSatchel)
|
||||
@@ -4533,6 +4543,12 @@ void Mob::NegateSpellEffectBonuses(uint16 spell_id)
|
||||
if (negate_itembonus) { itembonuses.inhibitmelee = effect_value; }
|
||||
break;
|
||||
|
||||
case SE_IncreaseArchery:
|
||||
if (negate_spellbonus) { spellbonuses.increase_archery = effect_value; }
|
||||
if (negate_aabonus) { aabonuses.increase_archery = effect_value; }
|
||||
if (negate_itembonus) { itembonuses.increase_archery = effect_value; }
|
||||
break;
|
||||
|
||||
case SE_TotalHP:
|
||||
if (negate_spellbonus) { spellbonuses.FlatMaxHPChange = effect_value; }
|
||||
if (negate_aabonus) { aabonuses.FlatMaxHPChange = effect_value; }
|
||||
|
||||
+172
-21
@@ -202,11 +202,11 @@ Client::Client(EQStreamInterface *ieqs) : Mob(
|
||||
ip = eqs->GetRemoteIP();
|
||||
port = ntohs(eqs->GetRemotePort());
|
||||
client_state = CLIENT_CONNECTING;
|
||||
Trader=false;
|
||||
SetTrader(false);
|
||||
Buyer = false;
|
||||
Haste = 0;
|
||||
CustomerID = 0;
|
||||
TraderID = 0;
|
||||
SetCustomerID(0);
|
||||
SetTraderID(0);
|
||||
TrackingID = 0;
|
||||
WID = 0;
|
||||
account_id = 0;
|
||||
@@ -421,8 +421,9 @@ Client::~Client() {
|
||||
if (merc)
|
||||
merc->Depop();
|
||||
|
||||
if(Trader)
|
||||
database.DeleteTraderItem(CharacterID());
|
||||
if(IsTrader()) {
|
||||
TraderEndTrader();
|
||||
}
|
||||
|
||||
if(Buyer)
|
||||
ToggleBuyerMode(false);
|
||||
@@ -1849,7 +1850,7 @@ void Client::FriendsWho(char *FriendsString) {
|
||||
void Client::UpdateAdmin(bool from_database) {
|
||||
int16 tmp = admin;
|
||||
if (from_database) {
|
||||
admin = database.CheckStatus(account_id);
|
||||
admin = database.GetAccountStatus(account_id);
|
||||
}
|
||||
|
||||
if (tmp == admin && from_database) {
|
||||
@@ -2163,6 +2164,7 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
ns->spawn.anon = m_pp.anon;
|
||||
ns->spawn.gm = GetGM() ? 1 : 0;
|
||||
ns->spawn.guildID = GuildID();
|
||||
ns->spawn.trader = IsTrader();
|
||||
// ns->spawn.linkdead = IsLD() ? 1 : 0;
|
||||
// ns->spawn.pvp = GetPVP(false) ? 1 : 0;
|
||||
ns->spawn.show_name = true;
|
||||
@@ -2801,7 +2803,7 @@ uint16 Client::MaxSkill(EQ::skills::SkillType skill_id, uint8 class_id, uint8 le
|
||||
return skill_caps.GetSkillCap(class_id, skill_id, level).cap;
|
||||
}
|
||||
|
||||
uint8 Client::SkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id)
|
||||
uint8 Client::GetSkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id)
|
||||
{
|
||||
if (
|
||||
ClientVersion() < EQ::versions::ClientVersion::RoF2 &&
|
||||
@@ -2811,7 +2813,7 @@ uint8 Client::SkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id)
|
||||
skill_id = EQ::skills::Skill2HPiercing;
|
||||
}
|
||||
|
||||
return skill_caps.GetTrainLevel(class_id, skill_id, RuleI(Character, MaxLevel));
|
||||
return skill_caps.GetSkillTrainLevel(class_id, skill_id, RuleI(Character, MaxLevel));
|
||||
}
|
||||
|
||||
uint16 Client::GetMaxSkillAfterSpecializationRules(EQ::skills::SkillType skillid, uint16 maxSkill)
|
||||
@@ -3199,9 +3201,7 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
|
||||
percent_base = 70;
|
||||
}
|
||||
|
||||
int percent_bonus = 0;
|
||||
if (percent_base >= 70)
|
||||
percent_bonus = spellbonuses.MaxBindWound + itembonuses.MaxBindWound + aabonuses.MaxBindWound;
|
||||
int percent_bonus = spellbonuses.MaxBindWound + itembonuses.MaxBindWound + aabonuses.MaxBindWound;
|
||||
|
||||
int max_percent = percent_base + percent_bonus;
|
||||
if (max_percent < 0)
|
||||
@@ -3220,9 +3220,7 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
|
||||
else if (GetSkill(EQ::skills::SkillBindWound) >= 12)
|
||||
bindhps = GetSkill(EQ::skills::SkillBindWound) / 4; // 4:1 skill-to-hp ratio
|
||||
|
||||
int bonus_hp_percent = 0;
|
||||
if (percent_base >= 70)
|
||||
bonus_hp_percent = spellbonuses.BindWound + itembonuses.BindWound + aabonuses.BindWound;
|
||||
int bonus_hp_percent = spellbonuses.BindWound + itembonuses.BindWound + aabonuses.BindWound;
|
||||
|
||||
bindhps += (bindhps * bonus_hp_percent) / 100;
|
||||
|
||||
@@ -3237,9 +3235,9 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
|
||||
bindmob->SendHPUpdate();
|
||||
}
|
||||
else {
|
||||
Message(Chat::Yellow, "You cannot bind wounds above %d%% hitpoints", max_percent);
|
||||
Message(Chat::Yellow, "You cannot bind wounds above %d%% hitpoints.", max_percent);
|
||||
if (bindmob != this && bindmob->IsClient())
|
||||
bindmob->CastToClient()->Message(Chat::Yellow, "You cannot have your wounds bound above %d%% hitpoints", max_percent);
|
||||
bindmob->CastToClient()->Message(Chat::Yellow, "You cannot have your wounds bound above %d%% hitpoints.", max_percent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3761,7 +3759,83 @@ void Client::Escape()
|
||||
MessageString(Chat::Skills, ESCAPE);
|
||||
}
|
||||
|
||||
float Client::CalcPriceMod(Mob* other, bool reverse)
|
||||
float Client::CalcClassicPriceMod(Mob* other, bool reverse) {
|
||||
float price_multiplier = 0.8f;
|
||||
|
||||
if (other && other->IsNPC()) {
|
||||
FACTION_VALUE faction_level = GetFactionLevel(CharacterID(), other->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), other->CastToNPC()->GetPrimaryFaction(), other);
|
||||
int32 cha = GetCHA();
|
||||
|
||||
if (faction_level <= FACTION_AMIABLY) {
|
||||
cha += 11; // amiable faction grants a defacto 11 charisma bonus
|
||||
}
|
||||
|
||||
uint8 greed = other->CastToNPC()->GetGreedPercent();
|
||||
|
||||
// Sony's precise algorithm is unknown, but this produces output that is virtually identical
|
||||
if (faction_level <= FACTION_INDIFFERENTLY) {
|
||||
if (cha > 75) {
|
||||
if (greed) {
|
||||
// this is derived from curve fitting to a lot of price data
|
||||
price_multiplier = -0.2487768 + (1.599635 - -0.2487768) / (1 + pow((cha / 135.1495), 1.001983));
|
||||
price_multiplier += (greed + 25u) / 100.0f; // default vendor markup is 25%; anything above that is 'greedy'
|
||||
price_multiplier = 1.0f / price_multiplier;
|
||||
}
|
||||
else {
|
||||
// non-greedy merchants use a linear scale
|
||||
price_multiplier = 1.0f - ((115.0f - cha) * 0.004f);
|
||||
}
|
||||
}
|
||||
else if (cha > 60) {
|
||||
price_multiplier = 1.0f / (1.25f + (greed / 100.0f));
|
||||
}
|
||||
else {
|
||||
price_multiplier = 1.0f / ((1.0f - (cha - 120.0f) / 220.0f) + (greed / 100.0f));
|
||||
}
|
||||
}
|
||||
else { // apprehensive
|
||||
if (cha > 75) {
|
||||
if (greed) {
|
||||
// this is derived from curve fitting to a lot of price data
|
||||
price_multiplier = -0.25f + (1.823662 - -0.25f) / (1 + (cha / 135.0f));
|
||||
price_multiplier += (greed + 25u) / 100.0f; // default vendor markup is 25%; anything above that is 'greedy'
|
||||
price_multiplier = 1.0f / price_multiplier;
|
||||
}
|
||||
else {
|
||||
price_multiplier = (100.0f - (145.0f - cha) / 2.8f) / 100.0f;
|
||||
}
|
||||
}
|
||||
else if (cha > 60) {
|
||||
price_multiplier = 1.0f / (1.4f + greed / 100.0f);
|
||||
}
|
||||
else {
|
||||
price_multiplier = 1.0f / ((1.0f + (143.574 - cha) / 196.434) + (greed / 100.0f));
|
||||
}
|
||||
}
|
||||
|
||||
float maxResult = 1.0f / 1.05; // price reduction caps at this amount
|
||||
if (price_multiplier > maxResult) {
|
||||
price_multiplier = maxResult;
|
||||
}
|
||||
|
||||
if (!reverse) {
|
||||
price_multiplier = 1.0f / price_multiplier;
|
||||
}
|
||||
}
|
||||
|
||||
LogMerchants(
|
||||
"[{}] [{}] items at [{}] price multiplier [{}] [{}]",
|
||||
other->GetName(),
|
||||
reverse ? "buys" : "sells",
|
||||
price_multiplier,
|
||||
reverse ? "from" : "to",
|
||||
GetName()
|
||||
);
|
||||
|
||||
return price_multiplier;
|
||||
}
|
||||
|
||||
float Client::CalcNewPriceMod(Mob* other, bool reverse)
|
||||
{
|
||||
float chaformula = 0;
|
||||
if (other)
|
||||
@@ -3807,6 +3881,17 @@ float Client::CalcPriceMod(Mob* other, bool reverse)
|
||||
return chaformula; //Returns 1.10, expensive stuff!
|
||||
}
|
||||
|
||||
float Client::CalcPriceMod(Mob* other, bool reverse)
|
||||
{
|
||||
float price_mod = CalcNewPriceMod(other, reverse);
|
||||
|
||||
if (RuleB(Merchant, UseClassicPriceMod)) {
|
||||
price_mod = CalcClassicPriceMod(other, reverse);
|
||||
}
|
||||
|
||||
return price_mod;
|
||||
}
|
||||
|
||||
void Client::GetGroupAAs(GroupLeadershipAA_Struct *into) const {
|
||||
memcpy(into, &m_pp.leader_abilities.group, sizeof(GroupLeadershipAA_Struct));
|
||||
}
|
||||
@@ -6372,9 +6457,9 @@ void Client::NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra)
|
||||
bool is_delete = spawn_type.find("delete") != std::string::npos;
|
||||
bool is_remove = spawn_type.find("remove") != std::string::npos;
|
||||
bool is_update = spawn_type.find("update") != std::string::npos;
|
||||
bool is_clone = spawn_type.find("clone") != std::string::npos;
|
||||
if (is_add || is_create) {
|
||||
// Add: extra tries to create the NPC ID within the range for the current Zone (Zone ID * 1000)
|
||||
// Create: extra sets the Respawn Timer for add
|
||||
// extra sets the Respawn Timer for add/create
|
||||
content_db.NPCSpawnDB(
|
||||
is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn,
|
||||
zone->GetShortName(),
|
||||
@@ -6398,7 +6483,17 @@ void Client::NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra)
|
||||
zone->GetShortName(),
|
||||
zone->GetInstanceVersion(),
|
||||
this,
|
||||
target_npc->CastToNPC()
|
||||
target_npc->CastToNPC(),
|
||||
extra
|
||||
);
|
||||
} else if (is_clone) {
|
||||
content_db.NPCSpawnDB(
|
||||
NPCSpawnTypes::AddSpawnFromSpawngroup,
|
||||
zone->GetShortName(),
|
||||
zone->GetInstanceVersion(),
|
||||
this,
|
||||
target_npc->CastToNPC(),
|
||||
extra
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6689,6 +6784,8 @@ bool Client::RemoveAlternateCurrencyValue(uint32 currency_id, uint32 amount)
|
||||
const uint32 new_amount = (current_amount - amount);
|
||||
|
||||
alternate_currency[currency_id] = new_amount;
|
||||
database.UpdateAltCurrencyValue(CharacterID(), currency_id, new_amount);
|
||||
SendAlternateCurrencyValue(currency_id);
|
||||
|
||||
if (parse->PlayerHasQuestSub(EVENT_ALT_CURRENCY_LOSS)) {
|
||||
const std::string &export_string = fmt::format(
|
||||
@@ -6895,6 +6992,10 @@ void Client::AddAutoXTarget(Mob *m, bool send)
|
||||
|
||||
void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots)
|
||||
{
|
||||
if (!XTargettingAvailable() || !m || !m_activeautohatermgr) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_activeautohatermgr->decrement_count(m);
|
||||
// now we may need to clean up our CurrentTargetNPC entries
|
||||
for (int i = 0; i < GetMaxXTargets(); ++i) {
|
||||
@@ -9270,6 +9371,7 @@ void Client::ShowDevToolsMenu()
|
||||
|
||||
menu_reload_five += Saylink::Silent("#reload merchants", "Merchants");
|
||||
menu_reload_five += " | " + Saylink::Silent("#reload npc_emotes", "NPC Emotes");
|
||||
menu_reload_five += " | " + Saylink::Silent("#reload npc_spells", "NPC Spells");
|
||||
menu_reload_five += " | " + Saylink::Silent("#reload objects", "Objects");
|
||||
menu_reload_five += " | " + Saylink::Silent("#reload opcodes", "Opcodes");
|
||||
|
||||
@@ -11351,6 +11453,15 @@ void Client::SendReloadCommandMessages() {
|
||||
).c_str()
|
||||
);
|
||||
|
||||
auto npc_spells_link = Saylink::Silent("#reload npc_spells");
|
||||
Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} - Reloads NPC Spells globally",
|
||||
npc_spells_link
|
||||
).c_str()
|
||||
);
|
||||
|
||||
auto objects_link = Saylink::Silent("#reload objects");
|
||||
|
||||
Message(
|
||||
@@ -11860,7 +11971,7 @@ void Client::SendPath(Mob* target)
|
||||
RuleB(Bazaar, EnableWarpToTrader) &&
|
||||
target->IsClient() &&
|
||||
(
|
||||
target->CastToClient()->Trader ||
|
||||
target->CastToClient()->IsTrader() ||
|
||||
target->CastToClient()->Buyer
|
||||
)
|
||||
) {
|
||||
@@ -12425,3 +12536,43 @@ uint16 Client::GetSkill(EQ::skills::SkillType skill_id) const
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity)
|
||||
{
|
||||
EQ::ItemInstance *item = 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},
|
||||
{ EQ::invslot::BANK_BEGIN, EQ::invslot::BANK_END },
|
||||
{ EQ::invslot::GUILD_TRIBUTE_BEGIN, EQ::invslot::GUILD_TRIBUTE_END },
|
||||
{ EQ::invbag::BANK_BAGS_BEGIN, EQ::invbag::BANK_BAGS_END },
|
||||
{ EQ::invslot::SHARED_BANK_BEGIN, EQ::invslot::SHARED_BANK_END },
|
||||
{ EQ::invbag::SHARED_BANK_BAGS_BEGIN, EQ::invbag::SHARED_BANK_BAGS_END },
|
||||
};
|
||||
int16 removed_count = 0;
|
||||
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) {
|
||||
if (removed_count == quantity) {
|
||||
break;
|
||||
}
|
||||
|
||||
item = GetInv().GetItem(slot_id);
|
||||
if (item && item->GetSerialNumber() == serial_number) {
|
||||
int16 charges = item->IsStackable() ? item->GetCharges() : 0;
|
||||
int16 stack_size = std::max(charges, static_cast<int16>(1));
|
||||
if ((removed_count + stack_size) <= quantity) {
|
||||
removed_count += stack_size;
|
||||
DeleteItemInInventory(slot_id, charges, true);
|
||||
} else {
|
||||
int16 amount_left = (quantity - removed_count);
|
||||
if (amount_left > 0 && stack_size >= amount_left) {
|
||||
removed_count += amount_left;
|
||||
DeleteItemInInventory(slot_id, amount_left, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+42
-17
@@ -69,6 +69,7 @@ namespace EQ
|
||||
#include "../common/events/player_events.h"
|
||||
#include "../common/data_verification.h"
|
||||
#include "../common/repositories/character_parcels_repository.h"
|
||||
#include "../common/repositories/trader_repository.h"
|
||||
|
||||
#ifdef _WINDOWS
|
||||
// since windows defines these within windef.h (which windows.h include)
|
||||
@@ -280,10 +281,20 @@ public:
|
||||
void AI_Stop();
|
||||
void AI_Process();
|
||||
void AI_SpellCast();
|
||||
void Trader_ShowItems();
|
||||
void TraderShowItems();
|
||||
void Trader_CustomerBrowsing(Client *Customer);
|
||||
void Trader_EndTrader();
|
||||
void Trader_StartTrader();
|
||||
|
||||
void TraderEndTrader();
|
||||
void TraderPriceUpdate(const EQApplicationPacket *app);
|
||||
void SendBazaarDone(uint32 trader_id);
|
||||
void SendBulkBazaarTraders();
|
||||
void DoBazaarInspect(const BazaarInspect_Struct &in);
|
||||
void SendBazaarDeliveryCosts();
|
||||
static std::string DetermineMoneyString(uint64 copper);
|
||||
|
||||
void SendTraderMode(BazaarTraderBarterActions status);
|
||||
void TraderStartTrader(const EQApplicationPacket *app);
|
||||
// void TraderPriceUpdate(const EQApplicationPacket *app);
|
||||
uint8 WithCustomer(uint16 NewCustomer);
|
||||
void KeyRingLoad();
|
||||
void KeyRingAdd(uint32 item_id);
|
||||
@@ -315,15 +326,17 @@ public:
|
||||
void Tell_StringID(uint32 string_id, const char *who, const char *message);
|
||||
void SendColoredText(uint32 color, std::string message);
|
||||
void SendBazaarResults(uint32 trader_id, uint32 in_class, uint32 in_race, uint32 item_stat, uint32 item_slot, uint32 item_type, char item_name[64], uint32 min_price, uint32 max_price);
|
||||
void SendTraderItem(uint32 item_id,uint16 quantity);
|
||||
void SendTraderItem(uint32 item_id,uint16 quantity, TraderRepository::Trader &trader);
|
||||
void DoBazaarSearch(BazaarSearchCriteria_Struct search_criteria);
|
||||
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
|
||||
uint32 FindTraderItemSerialNumber(int32 ItemID);
|
||||
EQ::ItemInstance* FindTraderItemBySerialNumber(int32 SerialNumber);
|
||||
void FindAndNukeTraderItem(int32 item_id,int16 quantity,Client* customer,uint16 traderslot);
|
||||
void NukeTraderItem(uint16 slot, int16 charges, int16 quantity, Client* customer, uint16 traderslot, int32 uniqueid, int32 itemid = 0);
|
||||
void FindAndNukeTraderItem(int32 serial_number, int16 quantity, Client* customer, uint16 trader_slot);
|
||||
void NukeTraderItem(uint16 slot, int16 charges, int16 quantity, Client* customer, uint16 trader_slot, int32 serial_number, int32 item_id = 0);
|
||||
void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges, uint32 itemid = 0);
|
||||
void TradeRequestFailed(const EQApplicationPacket* app);
|
||||
void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app);
|
||||
void BuyTraderItem(TraderBuy_Struct* tbs, Client* trader, const EQApplicationPacket* app);
|
||||
void BuyTraderItemOutsideBazaar(TraderBuy_Struct* tbs, const EQApplicationPacket* app);
|
||||
void FinishTrade(
|
||||
Mob *with,
|
||||
bool finalizer = false,
|
||||
@@ -335,7 +348,7 @@ public:
|
||||
void DoParcelCancel();
|
||||
void DoParcelSend(const Parcel_Struct *parcel_in);
|
||||
void DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in);
|
||||
void SendParcel(const Parcel_Struct &parcel);
|
||||
void SendParcel(Parcel_Struct &parcel);
|
||||
void SendParcelStatus();
|
||||
void SendParcelAck();
|
||||
void SendParcelRetrieveAck();
|
||||
@@ -355,6 +368,13 @@ public:
|
||||
int32 FindNextFreeParcelSlot(uint32 char_id);
|
||||
void SendParcelIconStatus();
|
||||
|
||||
void SendBecomeTraderToWorld(Client *trader, BazaarTraderBarterActions action);
|
||||
void SendBecomeTrader(BazaarTraderBarterActions action, uint32 trader_id);
|
||||
|
||||
bool IsThereACustomer() const { return customer_id ? true : false; }
|
||||
uint32 GetCustomerID() { return customer_id; }
|
||||
void SetCustomerID(uint32 id) { customer_id = id; }
|
||||
|
||||
void SendBuyerResults(char *SearchQuery, uint32 SearchID);
|
||||
void ShowBuyLines(const EQApplicationPacket *app);
|
||||
void SellToBuyer(const EQApplicationPacket *app);
|
||||
@@ -485,7 +505,7 @@ public:
|
||||
|
||||
void ServerFilter(SetServerFilter_Struct* filter);
|
||||
void BulkSendTraderInventory(uint32 char_id);
|
||||
void SendSingleTraderItem(uint32 char_id, int uniqueid);
|
||||
void SendSingleTraderItem(uint32 char_id, int serial_number);
|
||||
void BulkSendMerchantInventory(int merchant_id, int npcid);
|
||||
|
||||
inline uint8 GetLanguageSkill(uint8 language_id) const { return m_pp.languages[language_id]; }
|
||||
@@ -846,7 +866,7 @@ public:
|
||||
|
||||
uint16 MaxSkill(EQ::skills::SkillType skill_id, uint8 class_id, uint8 level) const;
|
||||
inline uint16 MaxSkill(EQ::skills::SkillType skill_id) const { return MaxSkill(skill_id, GetClass(), GetLevel()); }
|
||||
uint8 SkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id);
|
||||
uint8 GetSkillTrainLevel(EQ::skills::SkillType skill_id, uint8 class_id);
|
||||
void MaxSkills();
|
||||
|
||||
void SendTradeskillSearchResults(const std::string &query, unsigned long objtype, unsigned long someid);
|
||||
@@ -1009,8 +1029,8 @@ public:
|
||||
|
||||
//old AA methods that we still use
|
||||
void ResetAA();
|
||||
void ResetLeadershipAA();
|
||||
void RefundAA();
|
||||
void SendClearAA();
|
||||
void SendClearLeadershipAA();
|
||||
void SendClearPlayerAA();
|
||||
inline uint32 GetAAXP() const { return m_pp.expAA; }
|
||||
@@ -1042,6 +1062,7 @@ public:
|
||||
void SetItemCooldown(uint32 item_id, bool use_saved_timer = false, uint32 in_seconds = 1);
|
||||
uint32 GetItemCooldown(uint32 item_id);
|
||||
void RemoveItem(uint32 item_id, uint32 quantity = 1);
|
||||
void RemoveItemBySerialNumber(uint32 serial_number, uint32 quantity = 1);
|
||||
bool SwapItem(MoveItem_Struct* move_in);
|
||||
void SwapItemResync(MoveItem_Struct* move_slots);
|
||||
void QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call = false);
|
||||
@@ -1066,7 +1087,11 @@ public:
|
||||
bool IsValidSlot(uint32 slot);
|
||||
bool IsBankSlot(uint32 slot);
|
||||
|
||||
inline bool IsTrader() const { return(Trader); }
|
||||
bool IsTrader() const { return trader; }
|
||||
void SetTrader(bool status) { trader = status; }
|
||||
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; }
|
||||
@@ -1126,6 +1151,8 @@ public:
|
||||
void GoFish(bool guarantee = false, bool use_bait = true);
|
||||
void ForageItem(bool guarantee = false);
|
||||
//Calculate vendor price modifier based on CHA: (reverse==selling)
|
||||
float CalcClassicPriceMod(Mob* other = 0, bool reverse = false);
|
||||
float CalcNewPriceMod(Mob* other = 0, bool reverse = false);
|
||||
float CalcPriceMod(Mob* other = 0, bool reverse = false);
|
||||
void ResetTrade();
|
||||
void DropInst(const EQ::ItemInstance* inst);
|
||||
@@ -1797,8 +1824,6 @@ private:
|
||||
void RemoveBandolier(const EQApplicationPacket *app);
|
||||
void SetBandolier(const EQApplicationPacket *app);
|
||||
|
||||
void HandleTraderPriceUpdate(const EQApplicationPacket *app);
|
||||
|
||||
int32 CalcItemATKCap() final;
|
||||
int32 CalcHaste();
|
||||
|
||||
@@ -1877,13 +1902,13 @@ private:
|
||||
uint16 controlling_boat_id;
|
||||
uint16 controlled_mob_id;
|
||||
uint16 TrackingID;
|
||||
uint16 CustomerID;
|
||||
uint16 TraderID;
|
||||
bool trader;
|
||||
uint16 trader_id;
|
||||
uint16 customer_id;
|
||||
uint32 account_creation;
|
||||
uint8 firstlogon;
|
||||
uint32 mercid; // current merc
|
||||
uint8 mercSlot; // selected merc slot
|
||||
bool Trader;
|
||||
bool Buyer;
|
||||
std::string BuyerWelcomeMessage;
|
||||
int32 m_parcel_platinum;
|
||||
|
||||
@@ -289,7 +289,7 @@ int64 Client::CalcHPRegen(bool bCombat)
|
||||
|
||||
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
|
||||
auto max_hp = GetMaxHP();
|
||||
int64 fast_regen = 6 * (max_hp / (zone ? zone->newzone_data.fast_regen_hp : 180));
|
||||
int64 fast_regen = 6 * (max_hp / (zone && zone->newzone_data.fast_regen_hp > 0 ? zone->newzone_data.fast_regen_hp : 180));
|
||||
if (base < fast_regen) // weird, but what the client is doing
|
||||
base = fast_regen;
|
||||
}
|
||||
|
||||
+244
-334
@@ -679,7 +679,7 @@ void Client::CompleteConnect()
|
||||
break;
|
||||
}
|
||||
case SE_SummonHorse: {
|
||||
if (RuleB(Character, PreventMountsFromZoning)) {
|
||||
if (RuleB(Character, PreventMountsFromZoning) || !zone->CanCastOutdoor()) {
|
||||
BuffFadeByEffect(SE_SummonHorse);
|
||||
} else {
|
||||
SummonHorse(buffs[j1].spellid);
|
||||
@@ -918,6 +918,10 @@ void Client::CompleteConnect()
|
||||
CastToClient()->FastQueuePacket(&outapp);
|
||||
}
|
||||
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::RoF) {
|
||||
SendBulkBazaarTraders();
|
||||
}
|
||||
|
||||
// TODO: load these states
|
||||
// We at least will set them to the correct state for now
|
||||
if (m_ClientVersionBit & EQ::versions::maskUFAndLater && GetPet()) {
|
||||
@@ -1074,9 +1078,6 @@ void Client::Handle_Connect_OP_ReqClientSpawn(const EQApplicationPacket *app)
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
if (strncasecmp(zone->GetShortName(), "bazaar", 6) == 0)
|
||||
SendBazaarWelcome();
|
||||
|
||||
conn_state = ZoneContentsSent;
|
||||
|
||||
return;
|
||||
@@ -3787,7 +3788,7 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app)
|
||||
|
||||
case Barter_BuyerModeOn:
|
||||
{
|
||||
if (!Trader) {
|
||||
if (!IsTrader()) {
|
||||
ToggleBuyerMode(true);
|
||||
}
|
||||
else {
|
||||
@@ -3864,7 +3865,7 @@ void Client::Handle_OP_Barter(const EQApplicationPacket *app)
|
||||
|
||||
case Barter_Welcome:
|
||||
{
|
||||
SendBazaarWelcome();
|
||||
//SendBazaarWelcome();
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -3918,7 +3919,7 @@ void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app)
|
||||
|
||||
BazaarInspect_Struct* bis = (BazaarInspect_Struct*)app->pBuffer;
|
||||
|
||||
const EQ::ItemData* item = database.GetItem(bis->ItemID);
|
||||
const EQ::ItemData* item = database.GetItem(bis->item_id);
|
||||
|
||||
if (!item) {
|
||||
Message(Chat::Red, "Error: This item does not exist!");
|
||||
@@ -3937,39 +3938,47 @@ void Client::Handle_OP_BazaarInspect(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_BazaarSearch(const EQApplicationPacket *app)
|
||||
{
|
||||
uint32 action = *(uint32 *) app->pBuffer;
|
||||
|
||||
if (app->size == sizeof(BazaarSearch_Struct)) {
|
||||
switch (action) {
|
||||
case BazaarSearch: {
|
||||
BazaarSearchCriteria_Struct *bss = (BazaarSearchCriteria_Struct *) app->pBuffer;
|
||||
BazaarSearchCriteria_Struct search_details{};
|
||||
|
||||
BazaarSearch_Struct* bss = (BazaarSearch_Struct*)app->pBuffer;
|
||||
search_details.action = BazaarSearchResults;
|
||||
search_details.augment = bss->augment;
|
||||
search_details._class = bss->_class;
|
||||
search_details.item_stat = bss->item_stat;
|
||||
search_details.min_cost = bss->min_cost;
|
||||
search_details.max_cost = bss->max_cost;
|
||||
search_details.min_level = bss->min_level;
|
||||
search_details.max_level = bss->max_level;
|
||||
search_details.max_results = bss->max_results;
|
||||
search_details.prestige = bss->prestige;
|
||||
search_details.race = bss->race;
|
||||
search_details.search_scope = bss->search_scope;
|
||||
search_details.slot = bss->slot;
|
||||
search_details.trader_entity_id = bss->trader_entity_id;
|
||||
search_details.trader_id = bss->trader_id;
|
||||
search_details.type = bss->type;
|
||||
strn0cpy(search_details.item_name, bss->item_name, sizeof(search_details.item_name));
|
||||
|
||||
SendBazaarResults(bss->TraderID, bss->Class_, bss->Race, bss->ItemStat, bss->Slot, bss->Type,
|
||||
bss->Name, bss->MinPrice * 1000, bss->MaxPrice * 1000);
|
||||
}
|
||||
else if (app->size == sizeof(BazaarWelcome_Struct)) {
|
||||
|
||||
BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)app->pBuffer;
|
||||
|
||||
if (bws->Beginning.Action == BazaarWelcome)
|
||||
SendBazaarWelcome();
|
||||
}
|
||||
else if (app->size == sizeof(NewBazaarInspect_Struct)) {
|
||||
|
||||
NewBazaarInspect_Struct *nbis = (NewBazaarInspect_Struct*)app->pBuffer;
|
||||
|
||||
Client *c = entity_list.GetClientByName(nbis->Name);
|
||||
if (c) {
|
||||
EQ::ItemInstance* inst = c->FindTraderItemBySerialNumber(nbis->SerialNumber);
|
||||
if (inst)
|
||||
SendItemPacket(0, inst, ItemPacketViewLink);
|
||||
DoBazaarSearch(search_details);
|
||||
break;
|
||||
}
|
||||
case BazaarInspect: {
|
||||
auto in = (BazaarInspect_Struct *) app->pBuffer;
|
||||
DoBazaarInspect(*in);
|
||||
break;
|
||||
}
|
||||
case WelcomeMessage: {
|
||||
SendBazaarWelcome();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogError("Malformed BazaarSearch_Struct packet received, ignoring\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else {
|
||||
LogTrading("Malformed BazaarSearch_Struct packet received, ignoring");
|
||||
LogError("Malformed BazaarSearch_Struct packet received, ignoring\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_Begging(const EQApplicationPacket *app)
|
||||
@@ -4973,8 +4982,8 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app) {
|
||||
|
||||
if (cy != m_Position.y || cx != m_Position.x) {
|
||||
// End trader mode if we move
|
||||
if (Trader) {
|
||||
Trader_EndTrader();
|
||||
if (IsTrader()) {
|
||||
TraderEndTrader();
|
||||
}
|
||||
|
||||
/* Break Hide if moving without sneaking and set rewind timer if moved */
|
||||
@@ -5787,21 +5796,19 @@ void Client::Handle_OP_DeleteItem(const EQApplicationPacket *app)
|
||||
|
||||
SetIntoxication(GetIntoxication()+IntoxicationIncrease);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::ITEM_DESTROY) && inst->GetItem()) {
|
||||
auto e = PlayerEvent::DestroyItemEvent{
|
||||
.item_id = inst->GetItem()->ID,
|
||||
.item_name = inst->GetItem()->Name,
|
||||
.charges = inst->GetCharges(),
|
||||
.reason = "Client deleted",
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::ITEM_DESTROY, e);
|
||||
}
|
||||
}
|
||||
|
||||
DeleteItemInInventory(alc->from_slot, 1);
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::ITEM_DESTROY)) {
|
||||
auto e = PlayerEvent::DestroyItemEvent{
|
||||
.item_id = inst->GetItem()->ID,
|
||||
.item_name = inst->GetItem()->Name,
|
||||
.charges = inst->GetCharges(),
|
||||
.reason = "Client deleted",
|
||||
};
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::ITEM_DESTROY, e);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_DeleteSpawn(const EQApplicationPacket *app)
|
||||
@@ -14041,16 +14048,21 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
|
||||
EQ::ItemInstance* inst = database.CreateItem(item, charges);
|
||||
|
||||
int SinglePrice = 0;
|
||||
if (RuleB(Merchant, UsePriceMod))
|
||||
SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate * Client::CalcPriceMod(tmp, false));
|
||||
else
|
||||
SinglePrice = (item->Price * (RuleR(Merchant, SellCostMod)) * item->SellRate);
|
||||
int single_price = (item->Price * item->SellRate);
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
single_price *= RuleR(Merchant, SellCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
single_price *= Client::CalcPriceMod(tmp, false);
|
||||
}
|
||||
|
||||
if (item->MaxCharges > 1)
|
||||
mpo->price = SinglePrice;
|
||||
mpo->price = single_price;
|
||||
else
|
||||
mpo->price = SinglePrice * mp->quantity;
|
||||
mpo->price = single_price * mp->quantity;
|
||||
|
||||
if (mpo->price < 0)
|
||||
{
|
||||
@@ -14131,7 +14143,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
|
||||
else {
|
||||
// Update the charges/quantity in the merchant window
|
||||
inst->SetCharges(new_charges);
|
||||
inst->SetPrice(SinglePrice);
|
||||
inst->SetPrice(single_price);
|
||||
inst->SetMerchantSlot(mp->itemslot);
|
||||
inst->SetMerchantCount(new_charges);
|
||||
|
||||
@@ -14310,7 +14322,15 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
for (i = 1; i <= cost_quantity; i++) {
|
||||
price = (uint32)((item->Price * i)*(RuleR(Merchant, BuyCostMod))*Client::CalcPriceMod(vendor, true) + 0.5); // need to round up, because client does it automatically when displaying price
|
||||
price = (uint32)(item->Price * i) * Client::CalcPriceMod(vendor, true);
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
price *= RuleR(Merchant, BuyCostMod);
|
||||
}
|
||||
|
||||
price += 0.5; // need to round up, because client does it automatically when displaying price
|
||||
|
||||
if (price > 4000000000) {
|
||||
cost_quantity = i;
|
||||
mp->quantity = i;
|
||||
@@ -14360,11 +14380,12 @@ void Client::Handle_OP_ShopPlayerSell(const EQApplicationPacket *app)
|
||||
break;
|
||||
}
|
||||
|
||||
uint32 price = (
|
||||
item->Price *
|
||||
RuleR(Merchant, SellCostMod) *
|
||||
item->SellRate
|
||||
);
|
||||
uint32 price = (item->Price * item->SellRate);
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
price *= RuleR(Merchant, SellCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
price *= Client::CalcPriceMod(vendor, false);
|
||||
@@ -14571,11 +14592,18 @@ void Client::Handle_OP_ShopRequest(const EQApplicationPacket *app)
|
||||
mco->command = action; // Merchant command 0x01 = open
|
||||
mco->tab_display = tabs_to_display;
|
||||
|
||||
float buy_cost_mod = 1;
|
||||
|
||||
// Only use the BuyCostMod if we're not using the classic function.
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
buy_cost_mod = RuleR(Merchant, BuyCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
mco->rate = 1 / ((RuleR(Merchant, BuyCostMod)) * Client::CalcPriceMod(tmp, true)); // works
|
||||
mco->rate = 1 / (buy_cost_mod * Client::CalcPriceMod(tmp, true));
|
||||
}
|
||||
else {
|
||||
mco->rate = 1 / (RuleR(Merchant, BuyCostMod));
|
||||
mco->rate = 1 / buy_cost_mod;
|
||||
}
|
||||
|
||||
outapp->priority = 6;
|
||||
@@ -15475,174 +15503,49 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
|
||||
//
|
||||
// SoF sends 1 or more unhandled OP_Trader packets of size 96 when a trade has completed.
|
||||
// I don't know what they are for (yet), but it doesn't seem to matter that we ignore them.
|
||||
auto action = *(uint32 *)app->pBuffer;
|
||||
|
||||
|
||||
uint32 max_items = 80;
|
||||
|
||||
/*
|
||||
if (GetClientVersion() >= EQClientRoF)
|
||||
max_items = 200;
|
||||
*/
|
||||
|
||||
//Show Items
|
||||
if (app->size == sizeof(Trader_ShowItems_Struct))
|
||||
{
|
||||
Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)app->pBuffer;
|
||||
|
||||
switch (sis->Code)
|
||||
{
|
||||
case BazaarTrader_EndTraderMode: {
|
||||
Trader_EndTrader();
|
||||
switch (action) {
|
||||
case TraderOff: {
|
||||
TraderEndTrader();
|
||||
LogTrading("End Trader Session");
|
||||
break;
|
||||
}
|
||||
case BazaarTrader_EndTransaction: {
|
||||
|
||||
Client* c = entity_list.GetClientByID(sis->TraderID);
|
||||
if (c)
|
||||
{
|
||||
c->WithCustomer(0);
|
||||
LogTrading("End Transaction");
|
||||
}
|
||||
else
|
||||
LogTrading("Null Client Pointer");
|
||||
|
||||
break;
|
||||
}
|
||||
case BazaarTrader_ShowItems: {
|
||||
Trader_ShowItems();
|
||||
LogTrading("Show Trader Items");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading("Unhandled action code in OP_Trader ShowItems_Struct");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (app->size == sizeof(ClickTrader_Struct))
|
||||
{
|
||||
if (Buyer) {
|
||||
Trader_EndTrader();
|
||||
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
|
||||
return;
|
||||
}
|
||||
|
||||
ClickTrader_Struct* ints = (ClickTrader_Struct*)app->pBuffer;
|
||||
|
||||
if (ints->Code == BazaarTrader_StartTraderMode)
|
||||
{
|
||||
GetItems_Struct* gis = GetTraderItems();
|
||||
|
||||
LogTrading("Start Trader Mode");
|
||||
// Verify there are no NODROP or items with a zero price
|
||||
bool TradeItemsValid = true;
|
||||
|
||||
for (uint32 i = 0; i < max_items; i++) {
|
||||
|
||||
if (gis->Items[i] == 0) break;
|
||||
|
||||
if (ints->ItemCost[i] == 0) {
|
||||
Message(Chat::Red, "Item in Trader Satchel with no price. Unable to start trader mode");
|
||||
TradeItemsValid = false;
|
||||
break;
|
||||
}
|
||||
const EQ::ItemData *Item = database.GetItem(gis->Items[i]);
|
||||
|
||||
if (!Item) {
|
||||
Message(Chat::Red, "Unexpected error. Unable to start trader mode");
|
||||
TradeItemsValid = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Item->NoDrop == 0) {
|
||||
Message(Chat::Red, "NODROP Item in Trader Satchel. Unable to start trader mode");
|
||||
TradeItemsValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!TradeItemsValid) {
|
||||
Trader_EndTrader();
|
||||
safe_delete(gis);
|
||||
case TraderOn: {
|
||||
if (Buyer) {
|
||||
TraderEndTrader();
|
||||
Message(Chat::Red, "You cannot be a Trader and Buyer at the same time.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint32 i = 0; i < max_items; i++) {
|
||||
if (database.GetItem(gis->Items[i])) {
|
||||
database.SaveTraderItem(CharacterID(), gis->Items[i], gis->SerialNumber[i],
|
||||
gis->Charges[i], ints->ItemCost[i], i);
|
||||
|
||||
auto inst = FindTraderItemBySerialNumber(gis->SerialNumber[i]);
|
||||
if (inst)
|
||||
inst->SetPrice(ints->ItemCost[i]);
|
||||
}
|
||||
else {
|
||||
//return; //sony doesnt memset so assume done on first bad item
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
safe_delete(gis);
|
||||
|
||||
Trader_StartTrader();
|
||||
|
||||
// This refreshes the Trader window to display the End Trader button
|
||||
if (ClientVersion() >= EQ::versions::ClientVersion::RoF)
|
||||
{
|
||||
auto outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct));
|
||||
TraderStatus_Struct* tss = (TraderStatus_Struct*)outapp->pBuffer;
|
||||
tss->Code = BazaarTrader_StartTraderMode2;
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LogTrading("Unknown TraderStruct code of: [{}]\n",
|
||||
ints->Code);
|
||||
|
||||
LogError("Unknown TraderStruct code of: [{}]\n", ints->Code);
|
||||
}
|
||||
}
|
||||
else if (app->size == sizeof(TraderStatus_Struct))
|
||||
{
|
||||
TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer;
|
||||
|
||||
LogTrading("Trader Status Code: [{}]", tss->Code);
|
||||
|
||||
switch (tss->Code)
|
||||
{
|
||||
case BazaarTrader_EndTraderMode: {
|
||||
Trader_EndTrader();
|
||||
LogTrading("End Trader Session");
|
||||
TraderStartTrader(app);
|
||||
break;
|
||||
}
|
||||
case BazaarTrader_ShowItems: {
|
||||
Trader_ShowItems();
|
||||
case PriceUpdate:
|
||||
case ItemMove: {
|
||||
LogTrading("Trader Price Update");
|
||||
TraderPriceUpdate(app);
|
||||
break;
|
||||
}
|
||||
case EndTransaction: {
|
||||
auto sis = (Trader_ShowItems_Struct *) app->pBuffer;
|
||||
Client *c = entity_list.GetClientByID(sis->entity_id);
|
||||
if (c) {
|
||||
c->WithCustomer(0);
|
||||
LogTrading("End Transaction");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ListTraderItems: {
|
||||
TraderShowItems();
|
||||
LogTrading("Show Trader Items");
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogTrading("Unhandled action code in OP_Trader ShowItems_Struct");
|
||||
break;
|
||||
LogError("Unknown size for OP_Trader: [{}]\n", app->size);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else if (app->size == sizeof(TraderPriceUpdate_Struct))
|
||||
{
|
||||
LogTrading("Trader Price Update");
|
||||
HandleTraderPriceUpdate(app);
|
||||
}
|
||||
else {
|
||||
LogTrading("Unknown size for OP_Trader: [{}]\n", app->size);
|
||||
LogError("Unknown size for OP_Trader: [{}]\n", app->size);
|
||||
DumpPacket(app);
|
||||
return;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
||||
@@ -15651,23 +15554,80 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
|
||||
//
|
||||
// Client has elected to buy an item from a Trader
|
||||
//
|
||||
if (app->size != sizeof(TraderBuy_Struct)) {
|
||||
LogError("Wrong size: OP_TraderBuy, size=[{}], expected [{}]", app->size, sizeof(TraderBuy_Struct));
|
||||
return;
|
||||
auto in = (TraderBuy_Struct *) app->pBuffer;
|
||||
auto trader = entity_list.GetClientByID(in->trader_id);
|
||||
|
||||
switch (in->method) {
|
||||
case ByVendor: {
|
||||
if (trader) {
|
||||
LogTrading("Buy item directly from vendor id <green>[{}] item_id <green>[{}] quantity <green>[{}] "
|
||||
"serial_number <green>[{}]",
|
||||
in->trader_id,
|
||||
in->item_id,
|
||||
in->quantity,
|
||||
in->serial_number
|
||||
);
|
||||
BuyTraderItem(in, trader, app);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ByParcel: {
|
||||
if (!RuleB(Parcel, EnableParcelMerchants) || !RuleB(Bazaar, EnableParcelDelivery)) {
|
||||
LogTrading(
|
||||
"Bazaar purchase attempt by parcel delivery though 'Parcel:EnableParcelMerchants' or "
|
||||
"'Bazaar::EnableParcelDelivery' not enabled."
|
||||
);
|
||||
Message(
|
||||
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->sub_action = Failed;
|
||||
TradeRequestFailed(app);
|
||||
return;
|
||||
}
|
||||
LogTrading("Buy item by parcel delivery <green>[{}] item_id <green>[{}] quantity <green>[{}] "
|
||||
"serial_number <green>[{}]",
|
||||
in->trader_id,
|
||||
in->item_id,
|
||||
in->quantity,
|
||||
in->serial_number
|
||||
);
|
||||
BuyTraderItemOutsideBazaar(in, app);
|
||||
break;
|
||||
}
|
||||
case ByDirectToInventory: {
|
||||
if (!RuleB(Parcel, EnableDirectToInventoryDelivery)) {
|
||||
LogTrading("Bazaar purchase attempt by direct inventory delivery though "
|
||||
"'Parcel:EnableDirectToInventoryDelivery' not enabled."
|
||||
);
|
||||
Message(
|
||||
Chat::Yellow,
|
||||
"Direct inventory delivey is not enabled on this server. Please visit the vendor directly."
|
||||
);
|
||||
in->method = ByDirectToInventory;
|
||||
in->sub_action = Failed;
|
||||
TradeRequestFailed(app);
|
||||
return;
|
||||
}
|
||||
trader = entity_list.GetClientByCharID(in->trader_id);
|
||||
LogTrading("Buy item by direct inventory delivery <green>[{}] item_id <green>[{}] quantity <green>[{}] "
|
||||
"serial_number <green>[{}]",
|
||||
in->trader_id,
|
||||
in->item_id,
|
||||
in->quantity,
|
||||
in->serial_number
|
||||
);
|
||||
Message(
|
||||
Chat::Yellow,
|
||||
"Direct inventory delivey is not yet implemented. Please visit the vendor directly or purchase via parcel delivery."
|
||||
);
|
||||
in->method = ByDirectToInventory;
|
||||
in->sub_action = Failed;
|
||||
TradeRequestFailed(app);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
|
||||
|
||||
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)) {
|
||||
BuyTraderItem(tbs, Trader, app);
|
||||
LogTrading("Client::Handle_OP_TraderBuy: Buy Trader Item ");
|
||||
}
|
||||
else {
|
||||
LogTrading("Client::Handle_OP_TraderBuy: Null Client Pointer");
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_TradeRequest(const EQApplicationPacket *app)
|
||||
@@ -15730,126 +15690,76 @@ void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_TraderShop(const EQApplicationPacket *app)
|
||||
{
|
||||
// Bazaar Trader:
|
||||
auto in = (TraderClick_Struct *) app->pBuffer;
|
||||
LogTrading("Handle_OP_TraderShop: TraderClick_Struct TraderID [{}], Code [{}], Unknown008 [{}], Approval [{}]",
|
||||
in->TraderID,
|
||||
in->Code,
|
||||
in->Unknown008,
|
||||
in->Approval
|
||||
);
|
||||
|
||||
if (app->size == sizeof(TraderClick_Struct))
|
||||
{
|
||||
switch (in->Code) {
|
||||
case ClickTrader: {
|
||||
LogTrading("Handle_OP_TraderShop case ClickTrader [{}]", in->Code);
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_TraderShop, sizeof(TraderClick_Struct));
|
||||
auto data = (TraderClick_Struct *) outapp->pBuffer;
|
||||
auto trader_client = entity_list.GetClientByID(in->TraderID);
|
||||
|
||||
TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer;
|
||||
|
||||
LogTrading("Handle_OP_TraderShop: TraderClick_Struct TraderID [{}], Code [{}], Unknown008 [{}], Approval [{}]",
|
||||
tcs->TraderID, tcs->Code, tcs->Unknown008, tcs->Approval);
|
||||
|
||||
if (tcs->Code == BazaarWelcome)
|
||||
{
|
||||
LogTrading("Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info");
|
||||
SendBazaarWelcome();
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is when a potential purchaser right clicks on this client who is in Trader mode to
|
||||
// browse their goods.
|
||||
auto outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct));
|
||||
|
||||
TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer;
|
||||
|
||||
Client* Trader = entity_list.GetClientByID(tcs->TraderID);
|
||||
|
||||
if (Trader)
|
||||
{
|
||||
outtcs->Approval = Trader->WithCustomer(GetID());
|
||||
LogTrading("Client::Handle_OP_TraderShop: Shop Request ([{}]) to ([{}]) with Approval: [{}]", GetCleanName(), Trader->GetCleanName(), outtcs->Approval);
|
||||
if (trader_client) {
|
||||
data->Approval = trader_client->WithCustomer(GetID());
|
||||
LogTrading("Client::Handle_OP_TraderShop: Shop Request ([{}]) to ([{}]) with Approval: [{}]",
|
||||
GetCleanName(),
|
||||
trader_client->GetCleanName(),
|
||||
data->Approval
|
||||
);
|
||||
}
|
||||
else {
|
||||
LogTrading("Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)"
|
||||
" returned a nullptr pointer");
|
||||
safe_delete(outapp);
|
||||
" returned a nullptr pointer"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
outtcs->TraderID = tcs->TraderID;
|
||||
data->Code = ClickTrader;
|
||||
data->TraderID = in->TraderID;
|
||||
data->Unknown008 = 0x3f800000;
|
||||
QueuePacket(outapp.get());
|
||||
|
||||
outtcs->Unknown008 = 0x3f800000;
|
||||
|
||||
QueuePacket(outapp);
|
||||
|
||||
|
||||
if (outtcs->Approval) {
|
||||
BulkSendTraderInventory(Trader->CharacterID());
|
||||
Trader->Trader_CustomerBrowsing(this);
|
||||
TraderID = tcs->TraderID;
|
||||
LogTrading("Client::Handle_OP_TraderShop: Trader Inventory Sent");
|
||||
if (data->Approval) {
|
||||
BulkSendTraderInventory(trader_client->CharacterID());
|
||||
trader_client->Trader_CustomerBrowsing(this);
|
||||
SetTraderID(in->TraderID);
|
||||
LogTrading("Client::Handle_OP_TraderShop: Trader Inventory Sent to [{}] from [{}]",
|
||||
GetID(),
|
||||
in->TraderID
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
else {
|
||||
MessageString(Chat::Yellow, TRADER_BUSY);
|
||||
LogTrading("Client::Handle_OP_TraderShop: Trader Busy");
|
||||
}
|
||||
|
||||
safe_delete(outapp);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
else if (app->size == sizeof(BazaarWelcome_Struct))
|
||||
{
|
||||
// RoF+
|
||||
// Client requested Bazaar Welcome Info (Trader and Item Total Counts)
|
||||
SendBazaarWelcome();
|
||||
LogTrading("Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info");
|
||||
}
|
||||
else if (app->size == sizeof(TraderBuy_Struct))
|
||||
{
|
||||
// RoF+
|
||||
// Customer has purchased an item from the Trader
|
||||
|
||||
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
|
||||
|
||||
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID))
|
||||
{
|
||||
BuyTraderItem(tbs, Trader, app);
|
||||
LogTrading("Handle_OP_TraderShop: Buy Action [{}], Price [{}], Trader [{}], ItemID [{}], Quantity [{}], ItemName, [{}]",
|
||||
tbs->Action, tbs->Price, tbs->TraderID, tbs->ItemID, tbs->Quantity, tbs->ItemName);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogTrading("OP_TraderShop: Null Client Pointer");
|
||||
}
|
||||
}
|
||||
else if (app->size == 4)
|
||||
{
|
||||
// RoF+
|
||||
// Customer has closed the trade window
|
||||
uint32 Command = *((uint32 *)app->pBuffer);
|
||||
|
||||
if (Command == 4)
|
||||
{
|
||||
Client* c = entity_list.GetClientByID(TraderID);
|
||||
TraderID = 0;
|
||||
if (c)
|
||||
{
|
||||
case EndTransaction: {
|
||||
Client *c = entity_list.GetClientByID(GetTraderID());
|
||||
SetTraderID(0);
|
||||
if (c) {
|
||||
c->WithCustomer(0);
|
||||
LogTrading("End Transaction - Code [{}]", Command);
|
||||
LogTrading("End Transaction - Code [{}]", in->Code);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogTrading("Null Client Pointer for Trader - Code [{}]", Command);
|
||||
else {
|
||||
LogTrading("Null Client Pointer for Trader - Code [{}]", in->Code);
|
||||
}
|
||||
EQApplicationPacket empty(OP_ShopEndConfirm);
|
||||
QueuePacket(&empty);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_ShopEndConfirm);
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
LogTrading("Unhandled Code [{}]", Command);
|
||||
default: {
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LogTrading("Unknown size for OP_TraderShop: [{}]\n", app->size);
|
||||
LogError("Unknown size for OP_TraderShop: [{}]\n", app->size);
|
||||
DumpPacket(app);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app)
|
||||
|
||||
+18
-8
@@ -902,9 +902,14 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
||||
|
||||
auto inst = database.CreateItem(item, charges);
|
||||
if (inst) {
|
||||
auto item_price = static_cast<uint32>(item->Price * RuleR(Merchant, SellCostMod) * item->SellRate);
|
||||
auto item_price = static_cast<uint32>(item->Price * item->SellRate);
|
||||
auto item_charges = charges ? charges : 1;
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
item_price *= RuleR(Merchant, SellCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
item_price *= Client::CalcPriceMod(npc);
|
||||
}
|
||||
@@ -948,9 +953,14 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
||||
auto charges = item->MaxCharges;
|
||||
auto inst = database.CreateItem(item, charges);
|
||||
if (inst) {
|
||||
auto item_price = static_cast<uint32>(item->Price * RuleR(Merchant, SellCostMod) * item->SellRate);
|
||||
auto item_price = static_cast<uint32>(item->Price * item->SellRate);
|
||||
auto item_charges = charges ? charges : 1;
|
||||
|
||||
// Don't use SellCostMod if using UseClassicPriceMod
|
||||
if (!RuleB(Merchant, UseClassicPriceMod)) {
|
||||
item_price *= RuleR(Merchant, SellCostMod);
|
||||
}
|
||||
|
||||
if (RuleB(Merchant, UsePriceMod)) {
|
||||
item_price *= Client::CalcPriceMod(npc);
|
||||
}
|
||||
@@ -984,22 +994,22 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
||||
uint8 Client::WithCustomer(uint16 NewCustomer){
|
||||
|
||||
if(NewCustomer == 0) {
|
||||
CustomerID = 0;
|
||||
SetCustomerID(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(CustomerID == 0) {
|
||||
CustomerID = NewCustomer;
|
||||
if(GetCustomerID() == 0) {
|
||||
SetCustomerID(NewCustomer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Check that the player browsing our wares hasn't gone away.
|
||||
|
||||
Client* c = entity_list.GetClientByID(CustomerID);
|
||||
Client* c = entity_list.GetClientByID(GetCustomerID());
|
||||
|
||||
if(!c) {
|
||||
LogTrading("Previous customer has gone away");
|
||||
CustomerID = NewCustomer;
|
||||
SetCustomerID(NewCustomer);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1708,7 +1718,7 @@ void Client::OPGMTrainSkill(const EQApplicationPacket *app)
|
||||
|
||||
if (skilllevel == 0) {
|
||||
//this is a new skill..
|
||||
uint16 t_level = SkillTrainLevel(skill, GetClass());
|
||||
uint16 t_level = GetSkillTrainLevel(skill, GetClass());
|
||||
|
||||
if (t_level == 0) {
|
||||
LogSkills("Tried to train a new skill [{}] which is invalid for this race/class.", skill);
|
||||
|
||||
+1
-1
@@ -197,7 +197,7 @@ int command_init(void)
|
||||
command_add("rl", "Reloads logs (alias of #reload logs).", AccountStatus::GMMgmt, command_reload) ||
|
||||
command_add("removeitem", "[Item ID] [Amount] - Removes the specified Item ID by Amount from you or your player target's inventory (Amount defaults to 1 if not used)", AccountStatus::GMAdmin, command_removeitem) ||
|
||||
command_add("repop", "[Force] - Repop the zone with optional force repop", AccountStatus::GMAdmin, command_repop) ||
|
||||
command_add("resetaa", "Resets a Player's AA in their profile and refunds spent AA's to unspent, may disconnect player.", AccountStatus::GMMgmt, command_resetaa) ||
|
||||
command_add("resetaa", "[aa|leadership] - Resets a player's AAs or Leadership AAs and refunds spent AAs (not Leadership AAs) to unspent, may disconnect player.", AccountStatus::GMMgmt, command_resetaa) ||
|
||||
command_add("resetaa_timer", "[All|Timer ID] - Command to reset AA cooldown timers for you or your player target.", AccountStatus::GMMgmt, command_resetaa_timer) ||
|
||||
command_add("resetdisc_timer", "[All|Timer ID] - Command to reset discipline timers.", AccountStatus::GMMgmt, command_resetdisc_timer) ||
|
||||
command_add("revoke", "[Character Name] [0|1] - Revokes or unrevokes a player's ability to talk in OOC by name (0 = Unrevoke, 1 = Revoke)", AccountStatus::GMMgmt, command_revoke) ||
|
||||
|
||||
@@ -336,6 +336,7 @@ struct StatBonuses {
|
||||
int32 hastetype2;
|
||||
int32 hastetype3;
|
||||
int32 inhibitmelee;
|
||||
int32 increase_archery;
|
||||
float AggroRange; // when calculate just replace original value with this
|
||||
float AssistRange;
|
||||
int32 skillmod[EQ::skills::HIGHEST_SKILL + 1];
|
||||
|
||||
+4
-3
@@ -1547,16 +1547,17 @@ void PerlembParser::ExportMobVariables(
|
||||
void PerlembParser::ExportZoneVariables(std::string& package_name)
|
||||
{
|
||||
if (zone) {
|
||||
ExportVar(package_name.c_str(), "zoneid", zone->GetZoneID());
|
||||
ExportVar(package_name.c_str(), "zoneln", zone->GetLongName());
|
||||
ExportVar(package_name.c_str(), "zonesn", zone->GetShortName());
|
||||
ExportVar(package_name.c_str(), "instanceid", zone->GetInstanceID());
|
||||
ExportVar(package_name.c_str(), "instanceversion", zone->GetInstanceVersion());
|
||||
TimeOfDay_Struct eqTime{ };
|
||||
zone->zone_time.GetCurrentEQTimeOfDay(time(0), &eqTime);
|
||||
ExportVar(package_name.c_str(), "zonehour", eqTime.hour - 1);
|
||||
ExportVar(package_name.c_str(), "zoneid", zone->GetZoneID());
|
||||
ExportVar(package_name.c_str(), "zoneln", zone->GetLongName());
|
||||
ExportVar(package_name.c_str(), "zonemin", eqTime.minute);
|
||||
ExportVar(package_name.c_str(), "zonesn", zone->GetShortName());
|
||||
ExportVar(package_name.c_str(), "zonetime", (eqTime.hour - 1) * 100 + eqTime.minute);
|
||||
ExportVar(package_name.c_str(), "zoneuptime", Timer::GetCurrentTime() / 1000);
|
||||
ExportVar(package_name.c_str(), "zoneweather", zone->zone_weather);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-6
@@ -526,7 +526,7 @@ void EntityList::MobProcess()
|
||||
// -- the zone is newly empty and we're allowing mobs to settle
|
||||
if (
|
||||
numclients > 0 || zone->quest_idle_override ||
|
||||
(s2 && s2->PathWhenZoneIdle()) ||
|
||||
(mob && s2 && s2->PathWhenZoneIdle()) ||
|
||||
mob_settle_timer->Enabled()
|
||||
) {
|
||||
mob_dead = !mob->Process();
|
||||
@@ -2954,11 +2954,6 @@ void EntityList::ScanCloseMobs(
|
||||
|
||||
for (auto &e : mob_list) {
|
||||
auto mob = e.second;
|
||||
|
||||
if (!mob->IsNPC() && !mob->IsClient() && !mob->IsBot() && !mob->IsMerc()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (mob->GetID() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -530,6 +530,11 @@ void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, boo
|
||||
aaexp = ScaleAAXPBasedOnCurrentAATotal(GetAAPoints(), aaexp);
|
||||
}
|
||||
|
||||
// Check for AA XP Cap
|
||||
if (RuleI(AA, MaxAAEXPPerKill) >= 0 && aaexp > RuleI(AA, MaxAAEXPPerKill)) {
|
||||
aaexp = RuleI(AA, MaxAAEXPPerKill);
|
||||
}
|
||||
|
||||
// Get current AA XP total
|
||||
uint32 had_aaexp = GetAAXP();
|
||||
|
||||
|
||||
@@ -16,8 +16,14 @@ void command_modifynpcstat(Client *c, const Seperator *sep)
|
||||
|
||||
auto target = c->GetTarget()->CastToNPC();
|
||||
|
||||
std::string stat = sep->arg[1];
|
||||
std::string value = sep->arg[2] ? sep->arg[2] : "";
|
||||
const std::string& stat = sep->arg[1] ? sep->arg[1] : "";
|
||||
const std::string& value = sep->arg[2] ? sep->arg[2] : "";
|
||||
|
||||
if (stat.empty() || value.empty()) {
|
||||
c->Message(Chat::White, "Usage: #modifynpcstat [Stat] [Value]");
|
||||
ListModifyNPCStatMap(c);
|
||||
return;
|
||||
}
|
||||
|
||||
auto stat_description = GetModifyNPCStatDescription(stat);
|
||||
if (!stat_description.length()) {
|
||||
|
||||
@@ -1666,7 +1666,7 @@ void command_npcedit(Client *c, const Seperator *sep)
|
||||
} else if (!strcasecmp(sep->arg[1], "set_grid")) {
|
||||
if (sep->IsNumber(2)) {
|
||||
const uint32 grid_id = Strings::ToUnsignedInt(sep->arg[2]);
|
||||
if (grid_id) {
|
||||
if (grid_id >= 0) {
|
||||
d = fmt::format(
|
||||
"{} now has a Grid ID of {} on Spawn Group ID {}.",
|
||||
npc_id_string,
|
||||
@@ -1674,14 +1674,15 @@ void command_npcedit(Client *c, const Seperator *sep)
|
||||
Strings::Commify(std::to_string(t->GetSpawnGroupId()))
|
||||
);
|
||||
auto query = fmt::format(
|
||||
"UPDATE spawn2 SET pathgrid = {} WHERE spawngroupID = {} AND version = {}",
|
||||
"UPDATE spawn2 SET pathgrid = {} WHERE spawngroupID = {} AND version = {} AND zone = '{}'",
|
||||
grid_id,
|
||||
t->GetSpawnGroupId(),
|
||||
zone->GetInstanceVersion()
|
||||
zone->GetInstanceVersion(),
|
||||
zone->GetShortName()
|
||||
);
|
||||
content_db.QueryDatabase(query);
|
||||
} else {
|
||||
c->Message(Chat::White, "Grid ID must be greater than 0.");
|
||||
c->Message(Chat::White, "Grid ID must be greater than or equal to 0.");
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -9,44 +9,46 @@ void command_npcspawn(Client *c, const Seperator *sep)
|
||||
|
||||
int arguments = sep->argnum;
|
||||
if (!arguments) {
|
||||
c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]");
|
||||
c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update|Clone|Help]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcasecmp(sep->arg[1], "help")) {
|
||||
c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update|Clone|Help] [optional 3rd parameter]");
|
||||
c->Message(Chat::White, "Usage: #npcspawn add [respawntime] - Using the same targeted NPC ID, creates new spawn2 and spawngroup entries");
|
||||
c->Message(Chat::White, "Usage: #npcspawn clone [respawntime] - Copies targeted NPC and spawngroup, creating only a spawn2 entry at the current client location");
|
||||
c->Message(Chat::White, "Usage: #npcspawn create [respawntime] - Creates new NPC type copying the data from the targeted NPC, with new spawn2 and spawngroup entries");
|
||||
c->Message(Chat::White, "Usage: #npcspawn delete - Deletes the spawn2, spawngroup, spawnentry and npc_types rows for targeted NPC");
|
||||
c->Message(Chat::White, "Usage: #npcspawn remove [remove_spawngroups] - Deletes the spawn2 row for targeted NPC, also delete spawngroup and spawnentry rows if remove_spawngroups is > 0");
|
||||
c->Message(Chat::White, "Usage: #npcspawn update - Updates NPC appearance in database");
|
||||
return;
|
||||
}
|
||||
|
||||
auto target = c->GetTarget()->CastToNPC();
|
||||
uint32 extra = 0;
|
||||
bool is_add = !strcasecmp(sep->arg[1], "add");
|
||||
bool is_clone = !strcasecmp(sep->arg[1], "clone");
|
||||
bool is_create = !strcasecmp(sep->arg[1], "create");
|
||||
bool is_delete = !strcasecmp(sep->arg[1], "delete");
|
||||
bool is_remove = !strcasecmp(sep->arg[1], "remove");
|
||||
bool is_update = !strcasecmp(sep->arg[1], "update");
|
||||
if (
|
||||
!is_add &&
|
||||
!is_clone &&
|
||||
!is_create &&
|
||||
!is_delete &&
|
||||
!is_remove &&
|
||||
!is_update
|
||||
) {
|
||||
c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update]");
|
||||
c->Message(Chat::White, "Command Syntax: #npcspawn [Add|Create|Delete|Remove|Update|Clone|Help]");
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_add || is_create) {
|
||||
extra = (
|
||||
sep->IsNumber(2) ?
|
||||
(
|
||||
is_add ?
|
||||
Strings::ToInt(sep->arg[2]) :
|
||||
1
|
||||
) : (
|
||||
is_add ?
|
||||
1200 :
|
||||
0
|
||||
)
|
||||
); // Default to 1200 for Add, 0 for Create if not set
|
||||
if (is_add || is_create || is_clone) {
|
||||
extra = sep->IsNumber(2) ? Strings::ToInt(sep->arg[2]) : 1200; // Extra param is only used for respawn time in Add/Create/Clone, default to 1200 if not set
|
||||
|
||||
content_db.NPCSpawnDB(
|
||||
is_add ? NPCSpawnTypes::AddNewSpawngroup : NPCSpawnTypes::CreateNewSpawn,
|
||||
is_add ? NPCSpawnTypes::AddNewSpawngroup : (is_create ? NPCSpawnTypes::CreateNewSpawn : NPCSpawnTypes::AddSpawnFromSpawngroup),
|
||||
zone->GetShortName(),
|
||||
zone->GetInstanceVersion(),
|
||||
c,
|
||||
@@ -58,7 +60,13 @@ void command_npcspawn(Client *c, const Seperator *sep)
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spawn {} | Name: {}",
|
||||
is_add ? "Added" : "Created",
|
||||
is_add ?
|
||||
"Added" :
|
||||
(
|
||||
is_create ?
|
||||
"Created" :
|
||||
"Cloned"
|
||||
),
|
||||
c->GetTargetDescription(target)
|
||||
).c_str()
|
||||
);
|
||||
@@ -84,12 +92,15 @@ void command_npcspawn(Client *c, const Seperator *sep)
|
||||
)
|
||||
);
|
||||
|
||||
extra = sep->IsNumber(2) ? Strings::ToInt(sep->arg[2]) : 0; // Extra param is used in Remove as a flag to optionally remove spawngroup/spawnentry if 1 (always remove spawn2 entry)
|
||||
|
||||
content_db.NPCSpawnDB(
|
||||
spawn_update_type,
|
||||
zone->GetShortName(),
|
||||
zone->GetInstanceVersion(),
|
||||
c,
|
||||
target
|
||||
target,
|
||||
extra
|
||||
);
|
||||
|
||||
c->Message(
|
||||
|
||||
@@ -41,7 +41,7 @@ void command_parcels(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
auto results = CharacterParcelsRepository::GetWhere(
|
||||
auto results = CharacterParcelsRepository::GetWhere(
|
||||
database,
|
||||
fmt::format("char_id = '{}' ORDER BY slot_id ASC", player_id.at(0).char_id)
|
||||
);
|
||||
@@ -120,8 +120,8 @@ void command_parcels(Client *c, const Seperator *sep)
|
||||
auto note = std::string(sep->argplus[5]);
|
||||
|
||||
auto send_to_client = CharacterParcelsRepository::GetParcelCountAndCharacterName(
|
||||
database,
|
||||
to_name
|
||||
database,
|
||||
to_name
|
||||
);
|
||||
if (send_to_client.at(0).character_name.empty()) {
|
||||
c->MessageString(Chat::Yellow, CANT_FIND_PLAYER, to_name.c_str());
|
||||
@@ -164,14 +164,14 @@ void command_parcels(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
CharacterParcelsRepository::CharacterParcels parcel_out;
|
||||
parcel_out.from_name = c->GetName();
|
||||
parcel_out.note = note;
|
||||
parcel_out.sent_date = time(nullptr);
|
||||
parcel_out.quantity = quantity == 0 ? 1 : quantity;
|
||||
parcel_out.item_id = PARCEL_MONEY_ITEM_ID;
|
||||
parcel_out.char_id = send_to_client.at(0).char_id;
|
||||
parcel_out.slot_id = next_slot;
|
||||
parcel_out.id = 0;
|
||||
parcel_out.from_name = c->GetName();
|
||||
parcel_out.note = note;
|
||||
parcel_out.sent_date = time(nullptr);
|
||||
parcel_out.quantity = quantity == 0 ? 1 : quantity;
|
||||
parcel_out.item_id = PARCEL_MONEY_ITEM_ID;
|
||||
parcel_out.char_id = send_to_client.at(0).char_id;
|
||||
parcel_out.slot_id = next_slot;
|
||||
parcel_out.id = 0;
|
||||
|
||||
auto result = CharacterParcelsRepository::InsertOne(database, parcel_out);
|
||||
if (!result.id) {
|
||||
@@ -205,7 +205,7 @@ void command_parcels(Client *c, const Seperator *sep)
|
||||
e.quantity = parcel_out.quantity;
|
||||
e.sent_date = parcel_out.sent_date;
|
||||
|
||||
RecordPlayerEventLogWithClient(c, PlayerEvent::PARCEL_SEND, e);
|
||||
RecordPlayerEventLogWithClient (c, PlayerEvent::PARCEL_SEND, e);
|
||||
}
|
||||
|
||||
Parcel_Struct ps{};
|
||||
@@ -242,14 +242,14 @@ void command_parcels(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
CharacterParcelsRepository::CharacterParcels parcel_out;
|
||||
parcel_out.from_name = c->GetName();
|
||||
parcel_out.note = note.empty() ? "" : note;
|
||||
parcel_out.sent_date = time(nullptr);
|
||||
parcel_out.quantity = quantity;
|
||||
parcel_out.item_id = item_id;
|
||||
parcel_out.char_id = send_to_client.at(0).char_id;
|
||||
parcel_out.slot_id = next_slot;
|
||||
parcel_out.id = 0;
|
||||
parcel_out.from_name = c->GetName();
|
||||
parcel_out.note = note.empty() ? "" : note;
|
||||
parcel_out.sent_date = time(nullptr);
|
||||
parcel_out.quantity = quantity;
|
||||
parcel_out.item_id = item_id;
|
||||
parcel_out.char_id = send_to_client.at(0).char_id;
|
||||
parcel_out.slot_id = next_slot;
|
||||
parcel_out.id = 0;
|
||||
|
||||
auto result = CharacterParcelsRepository::InsertOne(database, parcel_out);
|
||||
if (!result.id) {
|
||||
@@ -283,7 +283,7 @@ void command_parcels(Client *c, const Seperator *sep)
|
||||
e.quantity = parcel_out.quantity;
|
||||
e.sent_date = parcel_out.sent_date;
|
||||
|
||||
RecordPlayerEventLogWithClient(c, PlayerEvent::PARCEL_SEND, e);
|
||||
RecordPlayerEventLogWithClient (c, PlayerEvent::PARCEL_SEND, e);
|
||||
}
|
||||
|
||||
Parcel_Struct ps{};
|
||||
@@ -300,8 +300,8 @@ void SendParcelsSubCommands(Client *c)
|
||||
c->Message(Chat::White, "#parcels listdb [Character Name]");
|
||||
c->Message(Chat::White, "#parcels listmemory [Character Name] (Must be in the same zone)");
|
||||
c->Message(
|
||||
Chat::White,
|
||||
"#parcels add [Character Name] [item id] [quantity] [note]. To send money use item id of 99990. Quantity is valid for stackable items, charges on an item, or amount of copper."
|
||||
Chat::White,
|
||||
"#parcels add [Character Name] [item id] [quantity] [note]. To send money use item id of 99990. Quantity is valid for stackable items, charges on an item, or amount of copper."
|
||||
);
|
||||
c->Message(Chat::White, "#parcels details [Character Name]");
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ void command_reload(Client *c, const Seperator *sep)
|
||||
bool is_loot = !strcasecmp(sep->arg[1], "loot");
|
||||
bool is_merchants = !strcasecmp(sep->arg[1], "merchants");
|
||||
bool is_npc_emotes = !strcasecmp(sep->arg[1], "npc_emotes");
|
||||
bool is_npc_spells = !strcasecmp(sep->arg[1], "npc_spells");
|
||||
bool is_objects = !strcasecmp(sep->arg[1], "objects");
|
||||
bool is_opcodes = !strcasecmp(sep->arg[1], "opcodes") || is_opcodes_reload_alias;
|
||||
bool is_perl_export = !strcasecmp(sep->arg[1], "perl_export");
|
||||
@@ -62,6 +63,7 @@ void command_reload(Client *c, const Seperator *sep)
|
||||
!is_loot &&
|
||||
!is_merchants &&
|
||||
!is_npc_emotes &&
|
||||
!is_npc_spells &&
|
||||
!is_objects &&
|
||||
!is_opcodes &&
|
||||
!is_perl_export &&
|
||||
@@ -137,6 +139,9 @@ void command_reload(Client *c, const Seperator *sep)
|
||||
} else if (is_npc_emotes) {
|
||||
c->Message(Chat::White, "Attempting to reload NPC Emotes globally.");
|
||||
pack = new ServerPacket(ServerOP_ReloadNPCEmotes, 0);
|
||||
} else if (is_npc_spells) {
|
||||
c->Message(Chat::White, "Attempting to reload NPC Spells globally.");
|
||||
pack = new ServerPacket(ServerOP_ReloadNPCSpells, 0);
|
||||
} else if (is_objects) {
|
||||
c->Message(Chat::White, "Attempting to reload Objects globally.");
|
||||
pack = new ServerPacket(ServerOP_ReloadObjects, 0);
|
||||
|
||||
@@ -2,18 +2,32 @@
|
||||
|
||||
void command_resetaa(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (!c->GetTarget() || !c->GetTarget()->IsClient()) {
|
||||
c->Message(Chat::White, "You must target a player to use this command.");
|
||||
Client* t = c;
|
||||
if (c->GetTarget() && c->GetTarget()->IsClient()) {
|
||||
t = c->GetTarget()->CastToClient();
|
||||
}
|
||||
|
||||
const uint16 arguments = sep->argnum;
|
||||
const bool is_aa = !strcasecmp(sep->arg[1], "aa");
|
||||
const bool is_leadership = !strcasecmp(sep->arg[1], "leadership");
|
||||
|
||||
if (!arguments || (!is_aa && !is_leadership)) {
|
||||
c->Message(Chat::White, "Usage: #resetaa aa - Resets and refunds a player's AAs");
|
||||
c->Message(Chat::White, "Usage: #resetaa leadership - Resets a player's Leadership AAs");
|
||||
return;
|
||||
}
|
||||
|
||||
auto t = c->GetTarget()->CastToClient();
|
||||
t->ResetAA();
|
||||
if (is_aa) {
|
||||
t->ResetAA();
|
||||
} else if (is_leadership) {
|
||||
t->ResetLeadershipAA();
|
||||
}
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Successfully reset all Alternate Advancements for {}.",
|
||||
"Successfully reset all{} AAs for {}.",
|
||||
is_aa ? "" : " Leadership",
|
||||
c->GetTargetDescription(t)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
@@ -164,10 +164,11 @@ void ShowInventory(Client *c, const Seperator *sep)
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} | {} ({}){}",
|
||||
"Slot {} | {} ({}/{}){}",
|
||||
((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(
|
||||
@@ -228,7 +229,7 @@ void ShowInventory(Client *c, const Seperator *sep)
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Slot {} Bag Slot {} | {} ({}){}",
|
||||
"Slot {} Bag Slot {}/{} | {} ({}/{}){}",
|
||||
(
|
||||
(scope_bit & peekWorld) ?
|
||||
INVALID_INDEX :
|
||||
@@ -238,6 +239,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->IsStackable() && inst_sub->GetCharges() > 0 ?
|
||||
fmt::format(
|
||||
|
||||
@@ -6,46 +6,56 @@ extern WorldServer worldserver;
|
||||
|
||||
void command_suspend(Client *c, const Seperator *sep)
|
||||
{
|
||||
auto arguments = sep->argnum;
|
||||
const uint16 arguments = sep->argnum;
|
||||
if (arguments < 2 || !sep->IsNumber(2)) {
|
||||
c->Message(Chat::White, "Usage: #suspend [Character Name] [Days] [Reason]");
|
||||
c->Message(Chat::White, "Note: Specify 0 days to lift a suspension");
|
||||
c->Message(Chat::White, "Note: Specify 0 days to lift a suspension, reason is not required when removing a suspension");
|
||||
return;
|
||||
}
|
||||
|
||||
const std::string character_name = Strings::ToLower(sep->arg[1]);
|
||||
auto days = Strings::ToUnsignedInt(sep->arg[2]);
|
||||
const std::string& character_name = sep->arg[1];
|
||||
|
||||
const std::string reason = sep->arg[3] ? sep->argplus[3] : "";
|
||||
const auto& e = CharacterDataRepository::FindByName(database, character_name);
|
||||
|
||||
auto l = AccountRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"LOWER(charname) = '{}'",
|
||||
Strings::Escape(character_name)
|
||||
)
|
||||
);
|
||||
|
||||
if (l.empty()) {
|
||||
if (!e.id) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Character '{}' does not exist.",
|
||||
sep->arg[1]
|
||||
character_name
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
l[0].status = -1;
|
||||
l[0].suspendeduntil = std::time(nullptr) + (days * 86400);
|
||||
l[0].suspend_reason = reason;
|
||||
auto a = AccountRepository::FindOne(database, e.account_id);
|
||||
|
||||
if (!AccountRepository::UpdateOne(database, l[0])) {
|
||||
if (!a.id) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Failed to suspend {}.",
|
||||
"Character '{}' is not attached to an account.",
|
||||
character_name
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32 days = Strings::ToUnsignedInt(sep->arg[2]);
|
||||
const bool is_suspend = days != 0;
|
||||
|
||||
const std::string reason = sep->arg[3] ? sep->argplus[3] : "";
|
||||
|
||||
a.status = is_suspend ? -1 : 0;
|
||||
a.suspendeduntil = is_suspend ? std::time(nullptr) + (days * 86400) : 0;
|
||||
a.suspend_reason = is_suspend ? reason : "";
|
||||
|
||||
if (!AccountRepository::UpdateOne(database, a)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Failed to {}suspend {}.",
|
||||
is_suspend ? "" : "un",
|
||||
character_name
|
||||
).c_str()
|
||||
);
|
||||
@@ -56,13 +66,13 @@ void command_suspend(Client *c, const Seperator *sep)
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Account {} ({}) with the character {} {}.",
|
||||
l[0].name,
|
||||
l[0].id,
|
||||
a.name,
|
||||
a.id,
|
||||
character_name,
|
||||
(
|
||||
days ?
|
||||
is_suspend ?
|
||||
fmt::format(
|
||||
"has been temporarily suspended for {} day{}.",
|
||||
"has been suspended for {} day{}",
|
||||
days,
|
||||
days != 1 ? "s" : ""
|
||||
) :
|
||||
@@ -71,22 +81,24 @@ void command_suspend(Client *c, const Seperator *sep)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
auto* b = entity_list.GetClientByName(character_name.c_str());
|
||||
if (is_suspend) { // Only kick if we're suspending, otherwise there's no reason to kick someone who is already suspended
|
||||
Client* b = entity_list.GetClientByName(character_name.c_str());
|
||||
|
||||
if (b) {
|
||||
b->WorldKick();
|
||||
return;
|
||||
if (b) {
|
||||
b->WorldKick();
|
||||
return;
|
||||
}
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct));
|
||||
auto* k = (ServerKickPlayer_Struct*) pack->pBuffer;
|
||||
|
||||
strn0cpy(k->adminname, c->GetName(), sizeof(k->adminname));
|
||||
strn0cpy(k->name, character_name.c_str(), sizeof(k->name));
|
||||
k->adminrank = c->Admin();
|
||||
|
||||
worldserver.SendPacket(pack);
|
||||
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
auto pack = new ServerPacket(ServerOP_KickPlayer, sizeof(ServerKickPlayer_Struct));
|
||||
auto* k = (ServerKickPlayer_Struct *) pack->pBuffer;
|
||||
|
||||
strn0cpy(k->adminname, c->GetName(), sizeof(k->adminname));
|
||||
strn0cpy(k->name, character_name.c_str(), sizeof(k->name));
|
||||
k->adminrank = c->Admin();
|
||||
|
||||
worldserver.SendPacket(pack);
|
||||
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
|
||||
+25
-4
@@ -1864,19 +1864,19 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
LogInventory("Dest slot [{}] has item [{}] ([{}]) with [{}] charges in it", dst_slot_id, dst_inst->GetItem()->Name, dst_inst->GetItem()->ID, dst_inst->GetCharges());
|
||||
dstitemid = dst_inst->GetItem()->ID;
|
||||
}
|
||||
if (Trader && srcitemid>0){
|
||||
if (IsTrader() && srcitemid>0){
|
||||
EQ::ItemInstance* srcbag;
|
||||
EQ::ItemInstance* dstbag;
|
||||
uint32 srcbagid =0;
|
||||
uint32 dstbagid = 0;
|
||||
|
||||
if (src_slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN && src_slot_id <= EQ::invbag::GENERAL_BAGS_END) {
|
||||
srcbag = m_inv.GetItem(((int)(src_slot_id / 10)) - 3);
|
||||
srcbag = m_inv.GetItem(EQ::InventoryProfile::CalcSlotId(src_slot_id));
|
||||
if (srcbag)
|
||||
srcbagid = srcbag->GetItem()->ID;
|
||||
}
|
||||
if (dst_slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN && dst_slot_id <= EQ::invbag::GENERAL_BAGS_END) {
|
||||
dstbag = m_inv.GetItem(((int)(dst_slot_id / 10)) - 3);
|
||||
dstbag = m_inv.GetItem(EQ::InventoryProfile::CalcSlotId(dst_slot_id));
|
||||
if (dstbag)
|
||||
dstbagid = dstbag->GetItem()->ID;
|
||||
}
|
||||
@@ -1884,7 +1884,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
(dstbagid && dstbag->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) ||
|
||||
(srcitemid && src_inst && src_inst->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) ||
|
||||
(dstitemid && dst_inst && dst_inst->GetItem()->BagType == EQ::item::BagTypeTradersSatchel)) {
|
||||
Trader_EndTrader();
|
||||
TraderEndTrader();
|
||||
Message(Chat::Red,"You cannot move your Trader Satchels, or items inside them, while Trading.");
|
||||
}
|
||||
}
|
||||
@@ -2180,6 +2180,27 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
|
||||
}
|
||||
|
||||
LogInventory("Moving entire item from slot [{}] to slot [{}]", src_slot_id, dst_slot_id);
|
||||
if (src_inst->IsStackable() &&
|
||||
dst_slot_id >= EQ::invbag::GENERAL_BAGS_BEGIN &&
|
||||
dst_slot_id <= EQ::invbag::GENERAL_BAGS_END
|
||||
) {
|
||||
EQ::ItemInstance *bag = nullptr;
|
||||
bag = m_inv.GetItem(EQ::InventoryProfile::CalcSlotId(dst_slot_id));
|
||||
if (bag) {
|
||||
if (bag->GetItem()->BagType == EQ::item::BagTypeTradersSatchel) {
|
||||
PutItemInInventory(dst_slot_id, *src_inst, true);
|
||||
//This resets the UF client to recognize the new serial item of the placed item
|
||||
//if it came from a stack without having to close the trader window and re-open.
|
||||
//It is not required for the RoF2 client.
|
||||
if (ClientVersion() < EQ::versions::ClientVersion::RoF2) {
|
||||
auto outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderBuy_Struct));
|
||||
auto data = (TraderBuy_Struct *) outapp->pBuffer;
|
||||
data->action = BazaarBuyItem;
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (src_slot_id <= EQ::invslot::EQUIPMENT_END) {
|
||||
if(src_inst) {
|
||||
|
||||
+16
-2
@@ -3369,6 +3369,18 @@ void Lua_Client::DescribeSpecialAbilities(Lua_NPC n)
|
||||
n.DescribeSpecialAbilities(self);
|
||||
}
|
||||
|
||||
void Lua_Client::ResetLeadershipAA()
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->ResetLeadershipAA();
|
||||
}
|
||||
|
||||
uint8 Lua_Client::GetSkillTrainLevel(int skill_id)
|
||||
{
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetSkillTrainLevel(static_cast<EQ::skills::SkillType>(skill_id), self->GetClass());
|
||||
}
|
||||
|
||||
luabind::scope lua_register_client() {
|
||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -3618,6 +3630,7 @@ luabind::scope lua_register_client() {
|
||||
.def("GetScribeableSpells", (luabind::object(Lua_Client::*)(lua_State* L,uint8,uint8))&Lua_Client::GetScribeableSpells)
|
||||
.def("GetScribedSpells", (luabind::object(Lua_Client::*)(lua_State* L))&Lua_Client::GetScribedSpells)
|
||||
.def("GetSkillPoints", (int(Lua_Client::*)(void))&Lua_Client::GetSkillPoints)
|
||||
.def("GetSkillTrainLevel", (uint8(Lua_Client::*)(int))&Lua_Client::GetSkillTrainLevel)
|
||||
.def("GetSpellDamage", (int(Lua_Client::*)(void))&Lua_Client::GetSpellDamage)
|
||||
.def("GetSpellIDByBookSlot", (uint32(Lua_Client::*)(int))&Lua_Client::GetSpellIDByBookSlot)
|
||||
.def("GetSpentAA", (int(Lua_Client::*)(void))&Lua_Client::GetSpentAA)
|
||||
@@ -3751,11 +3764,13 @@ luabind::scope lua_register_client() {
|
||||
.def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(std::string))&Lua_Client::RemoveAllExpeditionLockouts)
|
||||
.def("RemoveAllExpeditionLockouts", (void(Lua_Client::*)(void))&Lua_Client::RemoveAllExpeditionLockouts)
|
||||
.def("RemoveAlternateCurrencyValue", (bool(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveAlternateCurrencyValue)
|
||||
.def("RemoveEbonCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveEbonCrystals)
|
||||
.def("RemoveExpeditionLockout", (void(Lua_Client::*)(std::string, std::string))&Lua_Client::RemoveExpeditionLockout)
|
||||
.def("RemoveItem", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveItem)
|
||||
.def("RemoveItem", (void(Lua_Client::*)(uint32,uint32))&Lua_Client::RemoveItem)
|
||||
.def("RemoveLDoNLoss", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNLoss)
|
||||
.def("RemoveLDoNWin", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveLDoNWin)
|
||||
.def("RemoveRadiantCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveRadiantCrystals)
|
||||
.def("ResetAA", (void(Lua_Client::*)(void))&Lua_Client::ResetAA)
|
||||
.def("ResetAllDisciplineTimers", (void(Lua_Client::*)(void))&Lua_Client::ResetAllDisciplineTimers)
|
||||
.def("ResetAllCastbarCooldowns", (void(Lua_Client::*)(void))&Lua_Client::ResetAllCastbarCooldowns)
|
||||
@@ -3763,9 +3778,8 @@ luabind::scope lua_register_client() {
|
||||
.def("ResetCastbarCooldownBySlot", (void(Lua_Client::*)(int))&Lua_Client::ResetCastbarCooldownBySlot)
|
||||
.def("ResetCastbarCooldownBySpellID", (void(Lua_Client::*)(uint32))&Lua_Client::ResetCastbarCooldownBySpellID)
|
||||
.def("ResetDisciplineTimer", (void(Lua_Client::*)(uint32))&Lua_Client::ResetDisciplineTimer)
|
||||
.def("RemoveEbonCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveEbonCrystals)
|
||||
.def("ResetItemCooldown", (void(Lua_Client::*)(uint32))&Lua_Client::ResetItemCooldown)
|
||||
.def("RemoveRadiantCrystals", (void(Lua_Client::*)(uint32))&Lua_Client::RemoveRadiantCrystals)
|
||||
.def("ResetLeadershipAA", (void(Lua_Client::*)(void))&Lua_Client::ResetLeadershipAA)
|
||||
.def("ResetTrade", (void(Lua_Client::*)(void))&Lua_Client::ResetTrade)
|
||||
.def("RewardFaction", (void(Lua_Client::*)(int,int))&Lua_Client::RewardFaction)
|
||||
.def("Save", (void(Lua_Client::*)(int))&Lua_Client::Save)
|
||||
|
||||
@@ -503,6 +503,8 @@ public:
|
||||
bool SetAutoLoginCharacterName();
|
||||
bool SetAutoLoginCharacterName(std::string character_name);
|
||||
void DescribeSpecialAbilities(Lua_NPC n);
|
||||
void ResetLeadershipAA();
|
||||
uint8 GetSkillTrainLevel(int skill_id);
|
||||
|
||||
void ApplySpell(int spell_id);
|
||||
void ApplySpell(int spell_id, int duration);
|
||||
|
||||
@@ -5576,6 +5576,11 @@ bool lua_send_parcel(luabind::object lua_table)
|
||||
return CharacterParcelsRepository::InsertOne(database, e).id;
|
||||
}
|
||||
|
||||
uint32 lua_get_zone_uptime()
|
||||
{
|
||||
return Timer::GetCurrentTime() / 1000;
|
||||
}
|
||||
|
||||
#define LuaCreateNPCParse(name, c_type, default_value) do { \
|
||||
cur = table[#name]; \
|
||||
if(luabind::type(cur) != LUA_TNIL) { \
|
||||
@@ -6380,6 +6385,7 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("get_zone_id_by_long_name", &lua_get_zone_id_by_long_name),
|
||||
luabind::def("get_zone_short_name_by_long_name", &lua_get_zone_short_name_by_long_name),
|
||||
luabind::def("send_parcel", &lua_send_parcel),
|
||||
luabind::def("get_zone_uptime", &lua_get_zone_uptime),
|
||||
/*
|
||||
Cross Zone
|
||||
*/
|
||||
|
||||
@@ -42,6 +42,8 @@ void LuaMod::Init()
|
||||
m_has_common_damage = parser_->HasFunction("CommonDamage", package_name_);
|
||||
m_has_heal_damage = parser_->HasFunction("HealDamage", package_name_);
|
||||
m_has_is_immune_to_spell = parser_->HasFunction("IsImmuneToSpell", package_name_);
|
||||
m_has_set_aa_exp = parser_->HasFunction("SetAAEXP", package_name_);
|
||||
m_has_set_exp = parser_->HasFunction("SetEXP", package_name_);
|
||||
}
|
||||
|
||||
void PutDamageHitInfo(lua_State *L, luabind::adl::object &e, DamageHitInfo &hit) {
|
||||
|
||||
@@ -446,6 +446,21 @@ int main(int argc, char **argv)
|
||||
auto perl_parser = new PerlembParser();
|
||||
parse->RegisterQuestInterface(perl_parser, "pl");
|
||||
|
||||
#ifdef __linux__
|
||||
std::string current_version = CURRENT_VERSION;
|
||||
// running release binaries
|
||||
if (!Strings::Contains(current_version, "-dev")) {
|
||||
if (!fs::exists("/opt/eqemu-perl")) {
|
||||
LogError("You are running release binaries without having the required eqemu version of perl compiled and installed on this system present at /opt/eqemu-perl");
|
||||
LogError("If you are running an old Linux install, you need to install the required perl version from the eqemu-perl");
|
||||
LogError("Instructions can be referenced at https://github.com/Akkadius/akk-stack/blob/master/containers/eqemu-server/Dockerfile#L92-L106");
|
||||
LogError("Press any key to continue");
|
||||
getchar();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Load Perl Event Export Settings */
|
||||
parse->LoadPerlEventExportSettings(parse->perl_event_export_settings);
|
||||
|
||||
|
||||
@@ -1111,6 +1111,8 @@ public:
|
||||
|
||||
uint8 GetWeaponDamageBonus(const EQ::ItemData* weapon, bool offhand = false);
|
||||
const DamageTable &GetDamageTable() const;
|
||||
int GetMobFixedOffenseSkill();
|
||||
int GetMobFixedWeaponSkill();
|
||||
void ApplyDamageTable(DamageHitInfo &hit);
|
||||
virtual int GetHandToHandDamage(void);
|
||||
|
||||
|
||||
+63
-74
@@ -33,6 +33,9 @@
|
||||
#include "../common/data_verification.h"
|
||||
|
||||
#include "bot.h"
|
||||
#include "../common/repositories/npc_spells_repository.h"
|
||||
#include "../common/repositories/npc_spells_entries_repository.h"
|
||||
#include "../common/repositories/criteria/content_filter_criteria.h"
|
||||
|
||||
#include <glm/gtx/projection.hpp>
|
||||
#include <algorithm>
|
||||
@@ -2841,102 +2844,88 @@ void NPC::AISpellsList(Client *c)
|
||||
return;
|
||||
}
|
||||
|
||||
DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID)
|
||||
DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 npc_spells_id)
|
||||
{
|
||||
if (iDBSpellsID == 0)
|
||||
if (npc_spells_id == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = npc_spells_cache.find(iDBSpellsID);
|
||||
|
||||
auto it = npc_spells_cache.find(npc_spells_id);
|
||||
if (it != npc_spells_cache.end()) { // it's in the cache, easy =)
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
if (!npc_spells_loadtried.count(iDBSpellsID)) { // no reason to ask the DB again if we have failed once already
|
||||
npc_spells_loadtried.insert(iDBSpellsID);
|
||||
if (!npc_spells_loadtried.count(npc_spells_id)) { // no reason to ask the DB again if we have failed once already
|
||||
npc_spells_loadtried.insert(npc_spells_id);
|
||||
|
||||
std::string query = StringFormat("SELECT id, parent_list, attack_proc, proc_chance, "
|
||||
"range_proc, rproc_chance, defensive_proc, dproc_chance, "
|
||||
"fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, "
|
||||
"engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, "
|
||||
"pursue_no_sp_recast_min, pursue_no_sp_recast_max, "
|
||||
"pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, "
|
||||
"idle_b_chance FROM npc_spells WHERE id=%d",
|
||||
iDBSpellsID);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
auto ns = NpcSpellsRepository::FindOne(*this, npc_spells_id);
|
||||
if (!ns.id) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (results.RowCount() != 1)
|
||||
return nullptr;
|
||||
DBnpcspells_Struct ss;
|
||||
|
||||
auto row = results.begin();
|
||||
DBnpcspells_Struct spell_set;
|
||||
ss.parent_list = ns.parent_list;
|
||||
ss.attack_proc = ns.attack_proc;
|
||||
ss.proc_chance = ns.proc_chance;
|
||||
ss.range_proc = ns.range_proc;
|
||||
ss.rproc_chance = ns.rproc_chance;
|
||||
ss.defensive_proc = ns.defensive_proc;
|
||||
ss.dproc_chance = ns.dproc_chance;
|
||||
ss.fail_recast = ns.fail_recast;
|
||||
ss.engaged_no_sp_recast_min = ns.engaged_no_sp_recast_min;
|
||||
ss.engaged_no_sp_recast_max = ns.engaged_no_sp_recast_max;
|
||||
ss.engaged_beneficial_self_chance = ns.engaged_b_self_chance;
|
||||
ss.engaged_beneficial_other_chance = ns.engaged_b_other_chance;
|
||||
ss.engaged_detrimental_chance = ns.engaged_d_chance;
|
||||
ss.pursue_no_sp_recast_min = ns.pursue_no_sp_recast_min;
|
||||
ss.pursue_no_sp_recast_max = ns.pursue_no_sp_recast_max;
|
||||
ss.pursue_detrimental_chance = ns.pursue_d_chance;
|
||||
ss.idle_no_sp_recast_min = ns.idle_no_sp_recast_min;
|
||||
ss.idle_no_sp_recast_max = ns.idle_no_sp_recast_max;
|
||||
ss.idle_beneficial_chance = ns.idle_b_chance;
|
||||
|
||||
spell_set.parent_list = Strings::ToInt(row[1]);
|
||||
spell_set.attack_proc = Strings::ToInt(row[2]);
|
||||
spell_set.proc_chance = Strings::ToInt(row[3]);
|
||||
spell_set.range_proc = Strings::ToInt(row[4]);
|
||||
spell_set.rproc_chance = Strings::ToInt(row[5]);
|
||||
spell_set.defensive_proc = Strings::ToInt(row[6]);
|
||||
spell_set.dproc_chance = Strings::ToInt(row[7]);
|
||||
spell_set.fail_recast = Strings::ToInt(row[8]);
|
||||
spell_set.engaged_no_sp_recast_min = Strings::ToInt(row[9]);
|
||||
spell_set.engaged_no_sp_recast_max = Strings::ToInt(row[10]);
|
||||
spell_set.engaged_beneficial_self_chance = Strings::ToInt(row[11]);
|
||||
spell_set.engaged_beneficial_other_chance = Strings::ToInt(row[12]);
|
||||
spell_set.engaged_detrimental_chance = Strings::ToInt(row[13]);
|
||||
spell_set.pursue_no_sp_recast_min = Strings::ToInt(row[14]);
|
||||
spell_set.pursue_no_sp_recast_max = Strings::ToInt(row[15]);
|
||||
spell_set.pursue_detrimental_chance = Strings::ToInt(row[16]);
|
||||
spell_set.idle_no_sp_recast_min = Strings::ToInt(row[17]);
|
||||
spell_set.idle_no_sp_recast_max = Strings::ToInt(row[18]);
|
||||
spell_set.idle_beneficial_chance = Strings::ToInt(row[19]);
|
||||
auto entries = NpcSpellsEntriesRepository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"npc_spells_id = {} {} ORDER BY minlevel",
|
||||
npc_spells_id,
|
||||
ContentFilterCriteria::apply()
|
||||
)
|
||||
);
|
||||
|
||||
// pulling fixed values from an auto-increment field is dangerous...
|
||||
query = StringFormat(
|
||||
"SELECT spellid, type, minlevel, maxlevel, "
|
||||
"manacost, recast_delay, priority, min_hp, max_hp, resist_adjust "
|
||||
"FROM npc_spells_entries "
|
||||
"WHERE npc_spells_id=%d ORDER BY minlevel",
|
||||
iDBSpellsID);
|
||||
results = QueryDatabase(query);
|
||||
for (auto &e: entries) {
|
||||
DBnpcspells_entries_Struct se{};
|
||||
|
||||
if (!results.Success()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int entryIndex = 0;
|
||||
for (row = results.begin(); row != results.end(); ++row, ++entryIndex) {
|
||||
DBnpcspells_entries_Struct entry;
|
||||
int spell_id = Strings::ToInt(row[0]);
|
||||
entry.spellid = spell_id;
|
||||
entry.type = Strings::ToUnsignedInt(row[1]);
|
||||
entry.minlevel = Strings::ToInt(row[2]);
|
||||
entry.maxlevel = Strings::ToInt(row[3]);
|
||||
entry.manacost = Strings::ToInt(row[4]);
|
||||
entry.recast_delay = Strings::ToInt(row[5]);
|
||||
entry.priority = Strings::ToInt(row[6]);
|
||||
entry.min_hp = Strings::ToInt(row[7]);
|
||||
entry.max_hp = Strings::ToInt(row[8]);
|
||||
se.spellid = e.spellid;
|
||||
se.type = e.type;
|
||||
se.minlevel = e.minlevel;
|
||||
se.maxlevel = e.maxlevel;
|
||||
se.manacost = e.manacost;
|
||||
se.recast_delay = e.recast_delay;
|
||||
se.priority = e.priority;
|
||||
se.min_hp = e.min_hp;
|
||||
se.max_hp = e.max_hp;
|
||||
|
||||
// some spell types don't make much since to be priority 0, so fix that
|
||||
if (!(entry.type & SPELL_TYPES_INNATE) && entry.priority == 0)
|
||||
entry.priority = 1;
|
||||
if (!(se.type & SPELL_TYPES_INNATE) && se.priority == 0) {
|
||||
se.priority = 1;
|
||||
}
|
||||
|
||||
if (row[9])
|
||||
entry.resist_adjust = Strings::ToInt(row[9]);
|
||||
else if (IsValidSpell(spell_id))
|
||||
entry.resist_adjust = spells[spell_id].resist_difficulty;
|
||||
if (e.resist_adjust) {
|
||||
se.resist_adjust = e.resist_adjust;
|
||||
}
|
||||
else if (IsValidSpell(e.id)) {
|
||||
se.resist_adjust = spells[e.id].resist_difficulty;
|
||||
}
|
||||
|
||||
spell_set.entries.push_back(entry);
|
||||
ss.entries.push_back(se);
|
||||
}
|
||||
|
||||
npc_spells_cache.emplace(std::make_pair(iDBSpellsID, spell_set));
|
||||
npc_spells_cache.emplace(std::make_pair(npc_spells_id, ss));
|
||||
|
||||
return &npc_spells_cache[iDBSpellsID];
|
||||
}
|
||||
return &npc_spells_cache[npc_spells_id];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
+67
-16
@@ -1209,6 +1209,7 @@ uint32 ZoneDatabase::CreateNewNPCCommand(
|
||||
e.race = n->GetRace();
|
||||
e.class_ = n->GetClass();
|
||||
e.hp = n->GetMaxHP();
|
||||
e.mana = n->GetMaxMana();
|
||||
e.gender = n->GetGender();
|
||||
e.texture = n->GetTexture();
|
||||
e.helmtexture = n->GetHelmTexture();
|
||||
@@ -1216,8 +1217,50 @@ uint32 ZoneDatabase::CreateNewNPCCommand(
|
||||
e.loottable_id = n->GetLoottableID();
|
||||
e.merchant_id = n->MerchantType;
|
||||
e.runspeed = n->GetRunspeed();
|
||||
e.prim_melee_type = static_cast<uint8_t>(EQ::skills::SkillHandtoHand);
|
||||
e.sec_melee_type = static_cast<uint8_t>(EQ::skills::SkillHandtoHand);
|
||||
e.walkspeed = n->GetWalkspeed();
|
||||
e.prim_melee_type = n->GetPrimSkill();
|
||||
e.sec_melee_type = n->GetSecSkill();
|
||||
|
||||
e.bodytype = n->GetBodyType();
|
||||
e.npc_faction_id = n->GetNPCFactionID();
|
||||
e.aggroradius = n->GetAggroRange();
|
||||
e.assistradius = n->GetAssistRange();
|
||||
|
||||
e.AC = n->GetAC();
|
||||
e.ATK = n->GetATK();
|
||||
e.STR = n->GetSTR();
|
||||
e.STA = n->GetSTA();
|
||||
e.AGI = n->GetAGI();
|
||||
e.DEX = n->GetDEX();
|
||||
e.WIS = n->GetWIS();
|
||||
e._INT = n->GetINT();
|
||||
e.CHA = n->GetCHA();
|
||||
|
||||
e.PR = n->GetPR();
|
||||
e.MR = n->GetMR();
|
||||
e.DR = n->GetDR();
|
||||
e.FR = n->GetFR();
|
||||
e.CR = n->GetCR();
|
||||
e.Corrup = n->GetCorrup();
|
||||
e.PhR = n->GetPhR();
|
||||
|
||||
e.Accuracy = n->GetAccuracyRating();
|
||||
e.slow_mitigation = n->GetSlowMitigation();
|
||||
e.mindmg = n->GetMinDMG();
|
||||
e.maxdmg = n->GetMaxDMG();
|
||||
e.hp_regen_rate = n->GetHPRegen();
|
||||
e.hp_regen_per_second = n->GetHPRegenPerSecond();
|
||||
//e.attack_delay = n->GetAttackDelay(); // Attack delay isn't copying correctly, 3000 becomes 18,400 in the copied NPC?
|
||||
e.spellscale = n->GetSpellScale();
|
||||
e.healscale = n->GetHealScale();
|
||||
e.Avoidance = n->GetAvoidanceRating();
|
||||
e.heroic_strikethrough = n->GetHeroicStrikethrough();
|
||||
|
||||
e.see_hide = n->SeeHide();
|
||||
e.see_improved_hide = n->SeeImprovedHide();
|
||||
e.see_invis = n->SeeInvisible();
|
||||
e.see_invis_undead = n->SeeInvisibleUndead();
|
||||
|
||||
|
||||
e = NpcTypesRepository::InsertOne(*this, e);
|
||||
|
||||
@@ -1228,9 +1271,10 @@ uint32 ZoneDatabase::CreateNewNPCCommand(
|
||||
auto sg = SpawngroupRepository::NewEntity();
|
||||
|
||||
sg.name = fmt::format(
|
||||
"{}-{}",
|
||||
"{}_{}_{}",
|
||||
zone,
|
||||
n->GetName()
|
||||
Strings::Escape(n->GetName()),
|
||||
Timer::GetCurrentTime()
|
||||
);
|
||||
|
||||
sg = SpawngroupRepository::InsertOne(*this, sg);
|
||||
@@ -1249,7 +1293,7 @@ uint32 ZoneDatabase::CreateNewNPCCommand(
|
||||
s2.x = n->GetX();
|
||||
s2.y = n->GetY();
|
||||
s2.z = n->GetZ();
|
||||
s2.respawntime = 1200;
|
||||
s2.respawntime = extra > 0 ? extra : 1200;
|
||||
s2.heading = n->GetHeading();
|
||||
s2.spawngroupID = sg.id;
|
||||
|
||||
@@ -1361,12 +1405,17 @@ uint32 ZoneDatabase::UpdateNPCTypeAppearance(Client* c, NPC* n)
|
||||
return updated;
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Client* c, NPC* n)
|
||||
uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Client* c, NPC* n, uint32 remove_spawngroup_id)
|
||||
{
|
||||
if (!n->respawn2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const auto& l = Spawn2Repository::GetWhere(
|
||||
*this,
|
||||
fmt::format(
|
||||
"`zone` = '{}' AND `spawngroupID` = {}",
|
||||
"`id` = {} AND `zone` = '{}' AND `spawngroupID` = {}",
|
||||
n->respawn2->GetID(),
|
||||
zone,
|
||||
n->GetSpawnGroupId()
|
||||
)
|
||||
@@ -1382,12 +1431,14 @@ uint32 ZoneDatabase::DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Cli
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!SpawngroupRepository::DeleteOne(*this, e.spawngroupID)) {
|
||||
return 0;
|
||||
}
|
||||
if (remove_spawngroup_id > 0) {
|
||||
if (!SpawngroupRepository::DeleteOne(*this, e.spawngroupID)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!SpawnentryRepository::DeleteOne(*this, e.spawngroupID)) {
|
||||
return 0;
|
||||
if (!SpawnentryRepository::DeleteOne(*this, e.spawngroupID)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -1440,7 +1491,7 @@ uint32 ZoneDatabase::AddSpawnFromSpawnGroup(
|
||||
uint32 instance_version,
|
||||
Client* c,
|
||||
NPC* n,
|
||||
uint32 spawngroup_id
|
||||
uint32 extra
|
||||
)
|
||||
{
|
||||
auto e = Spawn2Repository::NewEntity();
|
||||
@@ -1451,8 +1502,8 @@ uint32 ZoneDatabase::AddSpawnFromSpawnGroup(
|
||||
e.y = c->GetY();
|
||||
e.z = c->GetZ();
|
||||
e.heading = c->GetHeading();
|
||||
e.respawntime = 120;
|
||||
e.spawngroupID = spawngroup_id;
|
||||
e.respawntime = extra > 0 ? extra : 1200;
|
||||
e.spawngroupID = n->GetSpawnGroupId();
|
||||
|
||||
e = Spawn2Repository::InsertOne(*this, e);
|
||||
|
||||
@@ -1529,7 +1580,7 @@ uint32 ZoneDatabase::NPCSpawnDB(
|
||||
return UpdateNPCTypeAppearance(c, n);
|
||||
}
|
||||
case NPCSpawnTypes::RemoveSpawn: {
|
||||
return DeleteSpawnLeaveInNPCTypeTable(zone, c, n);
|
||||
return DeleteSpawnLeaveInNPCTypeTable(zone, c, n, extra);
|
||||
}
|
||||
case NPCSpawnTypes::DeleteSpawn: {
|
||||
return DeleteSpawnRemoveFromNPCTypeTable(zone, instance_version, c, n);
|
||||
|
||||
@@ -268,6 +268,7 @@ public:
|
||||
inline void MerchantOpenShop() { merchant_open = true; }
|
||||
inline void MerchantCloseShop() { merchant_open = false; }
|
||||
inline bool IsMerchantOpen() { return merchant_open; }
|
||||
inline uint8 GetGreedPercent() { return NPCTypedata->greed; }
|
||||
inline bool GetParcelMerchant() { return NPCTypedata->is_parcel_merchant; }
|
||||
void Depop(bool start_spawn_timer = false);
|
||||
void Stun(int duration);
|
||||
|
||||
+117
-18
@@ -20,6 +20,7 @@
|
||||
#include "../common/events/player_event_logs.h"
|
||||
#include "../common/repositories/trader_repository.h"
|
||||
#include "../common/repositories/character_parcels_repository.h"
|
||||
#include "../common/repositories/character_parcels_containers_repository.h"
|
||||
#include "worldserver.h"
|
||||
#include "string_ids.h"
|
||||
#include "client.h"
|
||||
@@ -106,7 +107,7 @@ void Client::SendBulkParcels()
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SendParcel(const Parcel_Struct &parcel_in)
|
||||
void Client::SendParcel(Parcel_Struct &parcel_in)
|
||||
{
|
||||
auto results = CharacterParcelsRepository::GetWhere(
|
||||
database,
|
||||
@@ -403,7 +404,30 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in)
|
||||
return;
|
||||
}
|
||||
|
||||
RemoveItem(parcel_out.item_id, parcel_out.quantity);
|
||||
std::vector<CharacterParcelsContainersRepository::CharacterParcelsContainers> all_entries{};
|
||||
if (inst->IsNoneEmptyContainer()) {
|
||||
CharacterParcelsContainersRepository::CharacterParcelsContainers cpc{};
|
||||
|
||||
for (auto const &kv: *inst->GetContents()) {
|
||||
cpc.parcels_id = result.id;
|
||||
cpc.slot_id = kv.first;
|
||||
cpc.item_id = kv.second->GetID();
|
||||
if (kv.second->IsAugmented()) {
|
||||
auto augs = kv.second->GetAugmentIDs();
|
||||
cpc.aug_slot_1 = augs.at(0);
|
||||
cpc.aug_slot_2 = augs.at(1);
|
||||
cpc.aug_slot_3 = augs.at(2);
|
||||
cpc.aug_slot_4 = augs.at(3);
|
||||
cpc.aug_slot_5 = augs.at(4);
|
||||
cpc.aug_slot_6 = augs.at(5);
|
||||
}
|
||||
cpc.quantity = kv.second->GetCharges() > 0 ? kv.second->GetCharges() : 1;
|
||||
all_entries.push_back(cpc);
|
||||
}
|
||||
CharacterParcelsContainersRepository::InsertMany(database, all_entries);
|
||||
}
|
||||
|
||||
RemoveItemBySerialNumber(inst->GetSerialNumber(), parcel_out.quantity);
|
||||
std::unique_ptr<EQApplicationPacket> outapp(new EQApplicationPacket(OP_ShopSendParcel));
|
||||
QueuePacket(outapp.get());
|
||||
|
||||
@@ -435,6 +459,23 @@ void Client::DoParcelSend(const Parcel_Struct *parcel_in)
|
||||
e.sent_date = parcel_out.sent_date;
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::PARCEL_SEND, e);
|
||||
|
||||
if (!all_entries.empty()) {
|
||||
for (auto const &i: all_entries) {
|
||||
e.from_player_name = parcel_out.from_name;
|
||||
e.to_player_name = send_to_client.at(0).character_name;
|
||||
e.item_id = i.item_id;
|
||||
e.aug_slot_1 = i.aug_slot_1;
|
||||
e.aug_slot_2 = i.aug_slot_2;
|
||||
e.aug_slot_3 = i.aug_slot_3;
|
||||
e.aug_slot_4 = i.aug_slot_4;
|
||||
e.aug_slot_5 = i.aug_slot_5;
|
||||
e.aug_slot_6 = i.aug_slot_6;
|
||||
e.quantity = i.quantity;
|
||||
e.sent_date = parcel_out.sent_date;
|
||||
RecordPlayerEventLog(PlayerEvent::PARCEL_SEND, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Parcel_Struct ps{};
|
||||
@@ -663,8 +704,54 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in)
|
||||
}
|
||||
}
|
||||
else if (free_id != INVALID_INDEX) {
|
||||
std::vector<CharacterParcelsContainersRepository::CharacterParcelsContainers> results{};
|
||||
|
||||
if (inst->IsClassBag() && inst->GetItem()->BagSlots > 0) {
|
||||
results = CharacterParcelsContainersRepository::GetWhere(database, fmt::format("`parcels_id` = {}", p->second.id));
|
||||
for (auto const &i : results) {
|
||||
std::unique_ptr<EQ::ItemInstance> item(
|
||||
database.CreateItem(
|
||||
i.item_id,
|
||||
i.quantity,
|
||||
i.aug_slot_1,
|
||||
i.aug_slot_2,
|
||||
i.aug_slot_3,
|
||||
i.aug_slot_4,
|
||||
i.aug_slot_5,
|
||||
i.aug_slot_6
|
||||
)
|
||||
);
|
||||
if (CheckLoreConflict(item->GetItem())) {
|
||||
Message(
|
||||
Chat::Yellow,
|
||||
fmt::format("Lore Item Found in Inventory: {}", item->GetItem()->Name).c_str());
|
||||
MessageString(Chat::Yellow, DUP_LORE);
|
||||
Message(Chat::Red, "Unable to retrieve parcel.");
|
||||
SendParcelRetrieveAck();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
inst->SetCharges(item_quantity > 0 ? item_quantity : 1);
|
||||
if (PutItemInInventory(free_id, *inst.get(), true)) {
|
||||
if (inst->IsClassBag() && inst->GetItem()->BagSlots > 0) {
|
||||
for (auto const &i: results) {
|
||||
std::unique_ptr<EQ::ItemInstance> item(
|
||||
database.CreateItem(
|
||||
i.item_id,
|
||||
i.quantity,
|
||||
i.aug_slot_1,
|
||||
i.aug_slot_2,
|
||||
i.aug_slot_3,
|
||||
i.aug_slot_4,
|
||||
i.aug_slot_5,
|
||||
i.aug_slot_6
|
||||
)
|
||||
);
|
||||
auto bag_slot = EQ::InventoryProfile::CalcSlotId(free_id, i.slot_id);
|
||||
PutItemInInventory(bag_slot, *item.get(), true);
|
||||
}
|
||||
}
|
||||
MessageString(
|
||||
Chat::Yellow,
|
||||
PARCEL_DELIVERED,
|
||||
@@ -672,6 +759,34 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in)
|
||||
inst->GetItem()->Name,
|
||||
p->second.from_name.c_str()
|
||||
);
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_RETRIEVE)) {
|
||||
PlayerEvent::ParcelRetrieve e{};
|
||||
e.from_player_name = p->second.from_name;
|
||||
e.item_id = p->second.item_id;
|
||||
e.aug_slot_1 = p->second.aug_slot_1;
|
||||
e.aug_slot_2 = p->second.aug_slot_2;
|
||||
e.aug_slot_3 = p->second.aug_slot_3;
|
||||
e.aug_slot_4 = p->second.aug_slot_4;
|
||||
e.aug_slot_5 = p->second.aug_slot_5;
|
||||
e.aug_slot_6 = p->second.aug_slot_6;
|
||||
e.quantity = p->second.quantity;
|
||||
e.sent_date = p->second.sent_date;
|
||||
RecordPlayerEventLog(PlayerEvent::PARCEL_RETRIEVE, e);
|
||||
|
||||
for (auto const &i:results) {
|
||||
e.from_player_name = p->second.from_name;
|
||||
e.item_id = i.item_id;
|
||||
e.aug_slot_1 = i.aug_slot_1;
|
||||
e.aug_slot_2 = i.aug_slot_2;
|
||||
e.aug_slot_3 = i.aug_slot_3;
|
||||
e.aug_slot_4 = i.aug_slot_4;
|
||||
e.aug_slot_5 = i.aug_slot_5;
|
||||
e.aug_slot_6 = i.aug_slot_6;
|
||||
e.quantity = i.quantity;
|
||||
e.sent_date = p->second.sent_date;
|
||||
RecordPlayerEventLog(PlayerEvent::PARCEL_RETRIEVE, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
MessageString(Chat::Yellow, PARCEL_INV_FULL, merchant->GetCleanName());
|
||||
@@ -687,22 +802,6 @@ void Client::DoParcelRetrieve(const ParcelRetrieve_Struct &parcel_in)
|
||||
}
|
||||
}
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::PARCEL_RETRIEVE)) {
|
||||
PlayerEvent::ParcelRetrieve e{};
|
||||
e.from_player_name = p->second.from_name;
|
||||
e.item_id = p->second.item_id;
|
||||
e.aug_slot_1 = p->second.aug_slot_1;
|
||||
e.aug_slot_2 = p->second.aug_slot_2;
|
||||
e.aug_slot_3 = p->second.aug_slot_3;
|
||||
e.aug_slot_4 = p->second.aug_slot_4;
|
||||
e.aug_slot_5 = p->second.aug_slot_5;
|
||||
e.aug_slot_6 = p->second.aug_slot_6;
|
||||
e.quantity = p->second.quantity;
|
||||
e.sent_date = p->second.sent_date;
|
||||
|
||||
RecordPlayerEventLog(PlayerEvent::PARCEL_RETRIEVE, e);
|
||||
}
|
||||
|
||||
DeleteParcel(p->second.id);
|
||||
SendParcelDelete(parcel_in);
|
||||
m_parcels.erase(p);
|
||||
|
||||
@@ -3168,6 +3168,16 @@ void Perl_Client_DescribeSpecialAbilities(Client* self, NPC* n)
|
||||
n->DescribeSpecialAbilities(self);
|
||||
}
|
||||
|
||||
void Perl_Client_ResetLeadershipAA(Client* self)
|
||||
{
|
||||
self->ResetLeadershipAA();
|
||||
}
|
||||
|
||||
uint8 Perl_Client_GetSkillTrainLevel(Client* self, int skill_id)
|
||||
{
|
||||
return self->GetSkillTrainLevel(static_cast<EQ::skills::SkillType>(skill_id), self->GetClass());
|
||||
}
|
||||
|
||||
void perl_register_client()
|
||||
{
|
||||
perl::interpreter perl(PERL_GET_THX);
|
||||
@@ -3426,6 +3436,7 @@ void perl_register_client()
|
||||
package.add("GetTaskActivityDoneCount", &Perl_Client_GetTaskActivityDoneCount);
|
||||
package.add("GetThirst", &Perl_Client_GetThirst);
|
||||
package.add("GetTotalSecondsPlayed", &Perl_Client_GetTotalSecondsPlayed);
|
||||
package.add("GetSkillTrainLevel", &Perl_Client_GetSkillTrainLevel);
|
||||
package.add("GetWeight", &Perl_Client_GetWeight);
|
||||
package.add("GetPEQZoneFlags", &Perl_Client_GetPEQZoneFlags);
|
||||
package.add("GetZoneFlags", &Perl_Client_GetZoneFlags);
|
||||
@@ -3561,6 +3572,7 @@ void perl_register_client()
|
||||
package.add("ResetCastbarCooldownBySpellID", &Perl_Client_ResetCastbarCooldownBySpellID);
|
||||
package.add("ResetDisciplineTimer", &Perl_Client_ResetDisciplineTimer);
|
||||
package.add("ResetItemCooldown", &Perl_Client_ResetItemCooldown);
|
||||
package.add("ResetLeadershipAA", &Perl_Client_ResetLeadershipAA);
|
||||
package.add("ResetTrade", &Perl_Client_ResetTrade);
|
||||
package.add("Save", &Perl_Client_Save);
|
||||
package.add("ScribeSpell", (void(*)(Client*, uint16, int))&Perl_Client_ScribeSpell);
|
||||
|
||||
@@ -257,10 +257,40 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas
|
||||
my_hit.offense = offense(my_hit.skill);
|
||||
my_hit.tohit = GetTotalToHit(my_hit.skill, 0);
|
||||
|
||||
// Rogue Backstab Haste Correction
|
||||
// Haste should only provide a max of a 2 s reduction to Backstab cooldown, but it seems that while BackstabReuseTimer can be reduced, there is another timer (repop on the button)
|
||||
// that is controlling the actual cooldown. I'm not sure how this is implemented, but it is impacted by spell haste (including bard v2 and v3), but not worn haste.
|
||||
// This code applies an adjustment to backstab accuracy to compensate for this so that Rogue DPS doesn't significantly outclass other classes.
|
||||
|
||||
if (
|
||||
RuleB(Combat, RogueBackstabHasteCorrection) &&
|
||||
skill == EQ::skills::SkillBackstab &&
|
||||
GetHaste() > 100
|
||||
) {
|
||||
int haste_spell = spellbonuses.haste - spellbonuses.inhibitmelee + spellbonuses.hastetype2 + spellbonuses.hastetype3;
|
||||
int haste_worn = itembonuses.haste;
|
||||
|
||||
// Compute Intended Cooldown. 100% Spell = 1 s reduction (max), 40% Worn = 1 s reduction (max).
|
||||
int reduction_intended_spell = haste_spell > 100 ? 100 : haste_spell;
|
||||
int reduction_intended_worn = 2.5 * (haste_worn > 40 ? 40 : haste_worn);
|
||||
int16 intended_cooldown = 1000 - reduction_intended_spell - reduction_intended_worn;
|
||||
|
||||
// Compute Actual Cooldown. Actual only impacted by spell haste ( + v2 + v3), and is 10 s / (100 + haste)
|
||||
int actual_cooldown = 100000 / (100 + haste_spell);
|
||||
|
||||
// Compute Accuracy Adjustment
|
||||
int backstab_accuracy_adjust = actual_cooldown * 1000 / intended_cooldown;
|
||||
|
||||
// orig_accuracy = my_hit.tohit;
|
||||
int adjusted_accuracy = my_hit.tohit * backstab_accuracy_adjust / 1000;
|
||||
my_hit.tohit = adjusted_accuracy;
|
||||
}
|
||||
|
||||
my_hit.hand = EQ::invslot::slotPrimary; // Avoid checks hand for throwing/archery exclusion, primary should
|
||||
// work for most
|
||||
if (skill == EQ::skills::SkillThrowing || skill == EQ::skills::SkillArchery)
|
||||
if (skill == EQ::skills::SkillThrowing || skill == EQ::skills::SkillArchery) {
|
||||
my_hit.hand = EQ::invslot::slotRange;
|
||||
}
|
||||
|
||||
DoAttack(who, my_hit);
|
||||
|
||||
@@ -268,16 +298,20 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 bas
|
||||
who->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, skill, false);
|
||||
|
||||
// Make sure 'this' has not killed the target and 'this' is not dead (Damage shield ect).
|
||||
if (!GetTarget())
|
||||
if (!GetTarget()) {
|
||||
return;
|
||||
if (HasDied())
|
||||
}
|
||||
|
||||
if (HasDied()) {
|
||||
return;
|
||||
}
|
||||
|
||||
TryCastOnSkillUse(who, skill);
|
||||
|
||||
if (HasSkillProcs()) {
|
||||
TrySkillProc(who, skill, ReuseTime * 1000);
|
||||
}
|
||||
|
||||
if (my_hit.damage_done > 0 && HasSkillProcSuccess()) {
|
||||
TrySkillProc(who, skill, ReuseTime * 1000, true);
|
||||
}
|
||||
|
||||
@@ -3341,6 +3341,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
case SE_SkillProcAttempt:
|
||||
case SE_SkillProcSuccess:
|
||||
case SE_SpellResistReduction:
|
||||
case SE_IncreaseArchery:
|
||||
case SE_Duration_HP_Pct:
|
||||
case SE_Duration_Mana_Pct:
|
||||
case SE_Duration_Endurance_Pct:
|
||||
|
||||
@@ -3672,6 +3672,10 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
|
||||
);
|
||||
|
||||
// If this is the first buff it would override, use its slot
|
||||
if (!will_overwrite && !IsDisciplineBuff(spell_id)) {
|
||||
emptyslot = buffslot;
|
||||
}
|
||||
|
||||
will_overwrite = true;
|
||||
overwrite_slots.push_back(buffslot);
|
||||
} else if (ret == 2) {
|
||||
|
||||
@@ -195,6 +195,7 @@
|
||||
#define PARCEL_DELAY 734 //%1 tells you, 'You must give me a chance to send the last parcel before I can send another!'
|
||||
#define PARCEL_DUPLICATE_DELETE 737 //Duplicate lore items are not allowed! Your duplicate %1 has been deleted!
|
||||
#define PARCEL_DELIVER_3 741 //%1 told you, 'I will deliver the stack of %2 %3 to %4 as soon as possible!'
|
||||
#define TRADER_MODE_FAILED_ROF2 785 //Your attempt to become a trader has failed.
|
||||
#define PARCEL_INV_FULL 790 //%1 tells you, 'Your inventory appears full! Unable to retrieve parceled item.'
|
||||
#define AA_CAP 1000 //You have reached the AA point cap, and cannot gain any further experience until some of your stored AA point pool is used.
|
||||
#define GM_GAINXP 1002 //[GM] You have gained %1 AXP and %2 EXP (%3).
|
||||
@@ -427,6 +428,9 @@
|
||||
#define GENERIC_STRING 6688 //%1 (used to any basic message)
|
||||
#define SENTINEL_TRIG_YOU 6724 //You have triggered your sentinel.
|
||||
#define SENTINEL_TRIG_OTHER 6725 //%1 has triggered your sentinel.
|
||||
#define TRADER_MODE_OFF 6741 //Bazaar Trader Mode *OFF*
|
||||
#define TRADER_MODE_ON 6742 //Bazaar Trader Mode *ON*
|
||||
#define TRADER_SET_PRICE 6754 //To become a merchant you must assign a price to an item in your list. Do this by selecting an item, then selecting a money amount, and then clicking set price.
|
||||
#define IDENTIFY_SPELL 6765 //Item Lore: %1.
|
||||
#define PET_NOW_HOLDING 6834 //Now holding, Master. I will not start attacks until ordered.
|
||||
#define PET_ON_GHOLD 6843 //Pet greater hold has been set to on.
|
||||
|
||||
@@ -419,7 +419,7 @@ void ClientTaskState::RecordCompletedTask(uint32_t character_id, const TaskInfor
|
||||
[&](const CompletedTaskInformation& completed) { return completed.task_id == client_task.task_id; }
|
||||
), m_completed_tasks.end());
|
||||
|
||||
size_t erased = m_completed_tasks.size() - before;
|
||||
size_t erased = before - m_completed_tasks.size();
|
||||
|
||||
LogTasksDetail("KeepOneRecord erased [{}] elements", erased);
|
||||
|
||||
|
||||
+1265
-774
File diff suppressed because it is too large
Load Diff
+73
-18
@@ -1012,6 +1012,26 @@ void Mob::TuneMeleeMitigation(Mob *attacker, DamageHitInfo &hit, int ac_override
|
||||
|
||||
auto roll = RollD20(hit.offense, mitigation);
|
||||
|
||||
// Add bonus to roll if level difference is sufficient
|
||||
const int level_diff = attacker->GetLevel() - GetLevel();
|
||||
const int level_diff_roll_check = RuleI(Combat, LevelDifferenceRollCheck);
|
||||
|
||||
if (level_diff_roll_check >= 0) {
|
||||
if (level_diff > level_diff_roll_check) {
|
||||
roll += RuleR(Combat, LevelDifferenceRollBonus);
|
||||
|
||||
if (roll > 2.0f) {
|
||||
roll = 2.0f;
|
||||
}
|
||||
} else if (level_diff < (-level_diff_roll_check)) {
|
||||
roll -= RuleR(Combat, LevelDifferenceRollBonus);
|
||||
|
||||
if (roll < 0.1f) {
|
||||
roll = 0.1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// +0.5 for rounding, min to 1 dmg
|
||||
hit.damage_done = std::max(static_cast<int>(roll * static_cast<double>(hit.base_damage) + 0.5), 1);
|
||||
}
|
||||
@@ -1124,17 +1144,28 @@ int64 Mob::Tuneoffense(EQ::skills::SkillType skill, int atk_override, int add_at
|
||||
break;
|
||||
}
|
||||
|
||||
if (stat_bonus >= 75)
|
||||
if (stat_bonus >= 75) {
|
||||
offense += (2 * stat_bonus - 150) / 3;
|
||||
}
|
||||
|
||||
int32 tune_atk = GetATK();
|
||||
|
||||
// GetATK() = ATK + itembonuses.ATK + spellbonuses.ATK. However, ATK appears to already be itembonuses.ATK + spellbonuses.ATK for PCs, so as is, it is double counting attack
|
||||
// This causes attack to be significantly more important than it should be based on era rule of thumbs. I do not want to change the GetATK() function in case doing so breaks something,
|
||||
// so instead I am just adding a /2 to remedy the double counting. NPCs do not have this issue, so they are broken up.
|
||||
// PCAttackPowerScaling is used to help bring attack power further in line with era estimates.
|
||||
if (IsOfClientBotMerc()) {
|
||||
offense += (GetATK() / 2 + GetPetATKBonusFromOwner()) * RuleI(Combat, PCAttackPowerScaling) / 100;
|
||||
} else {
|
||||
offense += GetATK();
|
||||
}
|
||||
|
||||
if (atk_override) {
|
||||
tune_atk = atk_override;
|
||||
}
|
||||
|
||||
tune_atk += add_atk;
|
||||
|
||||
offense += tune_atk + GetPetATKBonusFromOwner();
|
||||
return offense;
|
||||
}
|
||||
|
||||
@@ -1267,8 +1298,15 @@ int64 Mob::TuneGetTotalToHit(EQ::skills::SkillType skill, int chance_mod, int ac
|
||||
// unsure on the stacking order of these effects, rather hard to parse
|
||||
// item mod2 accuracy isn't applied to range? Theory crafting and parses back it up I guess
|
||||
// mod2 accuracy -- flat bonus
|
||||
if (skill != EQ::skills::SkillArchery && skill != EQ::skills::SkillThrowing)
|
||||
if (skill != EQ::skills::SkillArchery && skill != EQ::skills::SkillThrowing) {
|
||||
accuracy += itembonuses.HitChance;
|
||||
} else {
|
||||
// Applying a scale factor as sources suggest Accuracy should reduce number of missing by 0.1% per point, so 150 = 15% reduction in misses.
|
||||
// Based on my calculator 150 Accuracy was reducing misses by too much (closer to 20%)
|
||||
// NOTE: This doesn't mean if you have a 30% miss chance you now miss 15%. It means if you have a 30% miss chance you now have a 30% * (100% - 15%) = 30% * 85% = 25.5% miss chance
|
||||
// Using same scale factor for Avoidance and Accuracy since they impact the formula about the same.
|
||||
accuracy += itembonuses.HitChance * RuleI(Combat, PCAccuracyAvoidanceMod2Scale) / 100;
|
||||
}
|
||||
|
||||
//518 Increase ATK accuracy by percentage, stackable
|
||||
auto atkhit_bonus = itembonuses.Attack_Accuracy_Max_Percent + aabonuses.Attack_Accuracy_Max_Percent + spellbonuses.Attack_Accuracy_Max_Percent;
|
||||
@@ -1305,6 +1343,11 @@ int64 Mob::TuneGetTotalToHit(EQ::skills::SkillType skill, int chance_mod, int ac
|
||||
aabonuses.HitChanceEffect[skill] +
|
||||
spellbonuses.HitChanceEffect[skill];
|
||||
|
||||
if (skill == EQ::skills::SkillArchery) {
|
||||
hit_bonus += spellbonuses.increase_archery + aabonuses.increase_archery + itembonuses.increase_archery;
|
||||
hit_bonus -= hit_bonus * RuleR(Combat, ArcheryHitPenalty);
|
||||
}
|
||||
|
||||
accuracy = (accuracy * (100 + hit_bonus)) / 100;
|
||||
return accuracy;
|
||||
}
|
||||
@@ -1338,31 +1381,43 @@ int64 Mob::TuneGetTotalDefense(int avoidance_override, int add_avoidance)
|
||||
int64 Mob::Tunecompute_defense(int avoidance_override, int add_avoidance)
|
||||
{
|
||||
int defense = GetSkill(EQ::skills::SkillDefense) * 400 / 225;
|
||||
defense += (8000 * (GetAGI() - 40)) / 36000;
|
||||
if (IsOfClientBot()) {
|
||||
if (avoidance_override) {
|
||||
defense = avoidance_override;
|
||||
|
||||
// In new code, AGI becomes a large contributor to avoidance at low levels, since AGI isn't capped by Level but Defense is
|
||||
// A scale factor is implemented for PCs to reduce the effect of AGI at low levels. This isn't applied to NPCs since they can be
|
||||
// easily controlled via the Database.
|
||||
if (RuleB(Combat, LegacyComputeDefense)) {
|
||||
int agi_scale_factor = 1000;
|
||||
|
||||
if (IsOfClientBot()) {
|
||||
agi_scale_factor = std::min(1000, static_cast<int>(GetLevel()) * 1000 / 70); // Scales Agi Contribution for PC's Level, max Contribution at Level 70
|
||||
}
|
||||
else {
|
||||
|
||||
defense += agi_scale_factor * (800 * (GetAGI() - 40)) / 3600 / 1000;
|
||||
|
||||
if (IsOfClientBot()) {
|
||||
defense += GetHeroicAGI() / 10;
|
||||
}
|
||||
|
||||
defense += itembonuses.AvoidMeleeChance * RuleI(Combat, PCAccuracyAvoidanceMod2Scale) / 100; // item mod2
|
||||
} else {
|
||||
defense += (8000 * (GetAGI() - 40)) / 36000;
|
||||
|
||||
if (IsOfClientBot()) {
|
||||
defense += itembonuses.heroic_agi_avoidance;
|
||||
}
|
||||
defense += add_avoidance; //1 pt = 10 heroic agi
|
||||
|
||||
defense += itembonuses.AvoidMeleeChance; // item mod2
|
||||
}
|
||||
|
||||
|
||||
//516 SE_AC_Mitigation_Max_Percent
|
||||
auto ac_bonus = itembonuses.AC_Mitigation_Max_Percent + aabonuses.AC_Mitigation_Max_Percent + spellbonuses.AC_Mitigation_Max_Percent;
|
||||
if (ac_bonus)
|
||||
if (ac_bonus) {
|
||||
defense += round(static_cast<double>(defense) * static_cast<double>(ac_bonus) * 0.0001);
|
||||
}
|
||||
|
||||
defense += itembonuses.AvoidMeleeChance; // item mod2
|
||||
if (IsNPC()) {
|
||||
if (avoidance_override) {
|
||||
defense += avoidance_override;
|
||||
}
|
||||
else {
|
||||
defense += CastToNPC()->GetAvoidanceRating();
|
||||
}
|
||||
defense += add_avoidance;
|
||||
defense += CastToNPC()->GetAvoidanceRating();
|
||||
}
|
||||
|
||||
if (IsClient()) {
|
||||
|
||||
+97
-4
@@ -2086,6 +2086,17 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_ReloadNPCSpells:
|
||||
{
|
||||
if (zone && zone->IsLoaded()) {
|
||||
zone->SendReloadMessage("NPC Spells");
|
||||
content_db.ClearNPCSpells();
|
||||
for (auto& e : entity_list.GetNPCList()) {
|
||||
e.second->ReloadSpells();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_ReloadPerlExportSettings:
|
||||
{
|
||||
zone->SendReloadMessage("Perl Event Export Settings");
|
||||
@@ -3904,10 +3915,92 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogInfo("Unknown ZS Opcode [{}] size [{}]", (int)pack->opcode, pack->size);
|
||||
break;
|
||||
}
|
||||
case ServerOP_TraderMessaging: {
|
||||
auto in = (TraderMessaging_Struct *) pack->pBuffer;
|
||||
for (auto const &c: entity_list.GetClientList()) {
|
||||
if (c.second->ClientVersion() >= EQ::versions::ClientVersion::RoF2) {
|
||||
auto outapp = new EQApplicationPacket(OP_BecomeTrader, sizeof(BecomeTrader_Struct));
|
||||
auto out = (BecomeTrader_Struct *) outapp->pBuffer;
|
||||
switch (in->action) {
|
||||
case TraderOn: {
|
||||
out->action = AddTraderToBazaarWindow;
|
||||
break;
|
||||
}
|
||||
case TraderOff: {
|
||||
out->action = RemoveTraderFromBazaarWindow;
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
out->action = 0;
|
||||
}
|
||||
}
|
||||
out->entity_id = in->entity_id;
|
||||
out->zone_id = in->zone_id;
|
||||
out->trader_id = in->trader_id;
|
||||
strn0cpy(out->trader_name, in->trader_name, sizeof(out->trader_name));
|
||||
|
||||
c.second->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
if (zone && zone->GetZoneID() == Zones::BAZAAR) {
|
||||
if (in->action == TraderOn) {
|
||||
c.second->SendBecomeTrader(TraderOn, in->entity_id);
|
||||
}
|
||||
else {
|
||||
c.second->SendBecomeTrader(TraderOff, in->entity_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ServerOP_BazaarPurchase: {
|
||||
auto in = (BazaarPurchaseMessaging_Struct *) pack->pBuffer;
|
||||
auto trader_pc = entity_list.GetClientByCharID(in->trader_buy_struct.trader_id);
|
||||
if (!trader_pc) {
|
||||
LogTrading("Request trader_id <red>[{}] could not be found in zone_id <red>[{}]",
|
||||
in->trader_buy_struct.trader_id,
|
||||
zone->GetZoneID()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto item_sn = Strings::ToUnsignedBigInt(in->trader_buy_struct.serial_number);
|
||||
auto outapp = std::make_unique<EQApplicationPacket>(OP_Trader, sizeof(TraderBuy_Struct));
|
||||
auto data = (TraderBuy_Struct *) outapp->pBuffer;
|
||||
|
||||
memcpy(data, &in->trader_buy_struct, sizeof(TraderBuy_Struct));
|
||||
|
||||
if (trader_pc->ClientVersion() < EQ::versions::ClientVersion::RoF) {
|
||||
data->price = in->trader_buy_struct.price * in->trader_buy_struct.quantity;
|
||||
}
|
||||
|
||||
TraderRepository::UpdateActiveTransaction(database, in->id, false);
|
||||
|
||||
trader_pc->RemoveItemBySerialNumber(item_sn, in->trader_buy_struct.quantity);
|
||||
trader_pc->AddMoneyToPP(in->trader_buy_struct.price * in->trader_buy_struct.quantity, true);
|
||||
trader_pc->QueuePacket(outapp.get());
|
||||
|
||||
if (player_event_logs.IsEventEnabled(PlayerEvent::TRADER_SELL)) {
|
||||
auto e = PlayerEvent::TraderSellEvent{
|
||||
.item_id = in->trader_buy_struct.item_id,
|
||||
.item_name = in->trader_buy_struct.item_name,
|
||||
.buyer_id = in->buyer_id,
|
||||
.buyer_name = in->trader_buy_struct.buyer_name,
|
||||
.price = in->trader_buy_struct.price,
|
||||
.charges = in->trader_buy_struct.quantity,
|
||||
.total_cost = (in->trader_buy_struct.price * in->trader_buy_struct.quantity),
|
||||
.player_money_balance = trader_pc->GetCarriedMoney(),
|
||||
};
|
||||
|
||||
RecordPlayerEventLogWithClient(trader_pc, PlayerEvent::TRADER_SELL, e);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
LogInfo("Unknown ZS Opcode [{}] size [{}]", (int) pack->opcode, pack->size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+46
-43
@@ -68,6 +68,7 @@
|
||||
#include "../common/repositories/merc_stance_entries_repository.h"
|
||||
#include "../common/repositories/alternate_currency_repository.h"
|
||||
#include "../common/repositories/graveyard_repository.h"
|
||||
#include "../common/repositories/trader_repository.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
@@ -108,9 +109,6 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool is_static) {
|
||||
numclients = 0;
|
||||
zone = new Zone(iZoneID, iInstanceID, zonename);
|
||||
|
||||
parse->Init();
|
||||
parse->ReloadQuests(true);
|
||||
|
||||
//init the zone, loads all the data, etc
|
||||
if (!zone->Init(is_static)) {
|
||||
safe_delete(zone);
|
||||
@@ -1098,20 +1096,21 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
|
||||
|
||||
Zone::~Zone() {
|
||||
spawn2_list.Clear();
|
||||
safe_delete(zonemap);
|
||||
safe_delete(watermap);
|
||||
safe_delete(pathing);
|
||||
if (worldserver.Connected()) {
|
||||
worldserver.SetZoneData(0);
|
||||
}
|
||||
safe_delete_array(short_name);
|
||||
safe_delete_array(long_name);
|
||||
safe_delete(Weather_Timer);
|
||||
npc_emote_list.clear();
|
||||
zone_point_list.Clear();
|
||||
entity_list.Clear();
|
||||
parse->ReloadQuests();
|
||||
ClearBlockedSpells();
|
||||
|
||||
safe_delete_array(short_name);
|
||||
safe_delete_array(long_name);
|
||||
safe_delete(Weather_Timer);
|
||||
safe_delete(zonemap);
|
||||
safe_delete(watermap);
|
||||
safe_delete(pathing);
|
||||
safe_delete(Instance_Timer);
|
||||
safe_delete(Instance_Shutdown_Timer);
|
||||
safe_delete(Instance_Warning_timer);
|
||||
@@ -1140,10 +1139,42 @@ bool Zone::Init(bool is_static) {
|
||||
}
|
||||
}
|
||||
|
||||
if (!map_name) {
|
||||
LogError("No map name found for zone [{}]", GetShortName());
|
||||
return false;
|
||||
}
|
||||
|
||||
zonemap = Map::LoadMapFile(map_name);
|
||||
watermap = WaterMap::LoadWaterMapfile(map_name);
|
||||
pathing = IPathfinder::Load(map_name);
|
||||
|
||||
LogInfo("Loading timezone data");
|
||||
zone_time.setEQTimeZone(content_db.GetZoneTimezone(zoneid, GetInstanceVersion()));
|
||||
|
||||
LoadLDoNTraps();
|
||||
LoadLDoNTrapEntries();
|
||||
|
||||
LoadDynamicZoneTemplates();
|
||||
DynamicZone::CacheAllFromDatabase();
|
||||
Expedition::CacheAllFromDatabase();
|
||||
|
||||
content_db.LoadGlobalLoot();
|
||||
|
||||
npc_scale_manager->LoadScaleData();
|
||||
|
||||
LoadGrids();
|
||||
|
||||
if (RuleB(Zone, LevelBasedEXPMods)) {
|
||||
LoadLevelEXPMods();
|
||||
}
|
||||
|
||||
RespawnTimesRepository::ClearExpiredRespawnTimers(database);
|
||||
|
||||
// make sure that anything that needs to be loaded prior to scripts is loaded before here
|
||||
// this is to ensure that the scripts have access to the data they need
|
||||
parse->Init();
|
||||
parse->ReloadQuests(true);
|
||||
|
||||
spawn_conditions.LoadSpawnConditions(short_name, instanceid);
|
||||
|
||||
content_db.LoadStaticZonePoints(&zone_point_list, short_name, GetInstanceVersion());
|
||||
@@ -1160,37 +1191,23 @@ bool Zone::Init(bool is_static) {
|
||||
|
||||
LogInfo("Loading adventure flavor text");
|
||||
LoadAdventureFlavor();
|
||||
|
||||
LoadGroundSpawns();
|
||||
LoadZoneObjects();
|
||||
|
||||
RespawnTimesRepository::ClearExpiredRespawnTimers(database);
|
||||
|
||||
LoadZoneDoors();
|
||||
LoadZoneBlockedSpells();
|
||||
|
||||
//clear trader items if we are loading the bazaar
|
||||
if (strncasecmp(short_name, "bazaar", 6) == 0) {
|
||||
database.DeleteTraderItem(0);
|
||||
TraderRepository::Truncate(database);
|
||||
database.DeleteBuyLines(0);
|
||||
}
|
||||
|
||||
LoadLDoNTraps();
|
||||
LoadLDoNTrapEntries();
|
||||
LoadVeteranRewards();
|
||||
LoadAlternateCurrencies();
|
||||
LoadNPCEmotes(&npc_emote_list);
|
||||
|
||||
LoadAlternateAdvancement();
|
||||
|
||||
content_db.LoadGlobalLoot();
|
||||
|
||||
LoadBaseData();
|
||||
|
||||
//Load merchant data
|
||||
LoadMerchants();
|
||||
|
||||
//Load temporary merchant data
|
||||
LoadTempMerchantData();
|
||||
|
||||
// Merc data
|
||||
@@ -1199,29 +1216,13 @@ bool Zone::Init(bool is_static) {
|
||||
LoadMercenarySpells();
|
||||
}
|
||||
|
||||
if (RuleB(Zone, LevelBasedEXPMods)) {
|
||||
LoadLevelEXPMods();
|
||||
}
|
||||
|
||||
petition_list.ClearPetitions();
|
||||
petition_list.ReadDatabase();
|
||||
|
||||
LoadDynamicZoneTemplates();
|
||||
|
||||
DynamicZone::CacheAllFromDatabase();
|
||||
Expedition::CacheAllFromDatabase();
|
||||
|
||||
guild_mgr.LoadGuilds();
|
||||
|
||||
LogInfo("Loading timezone data");
|
||||
zone_time.setEQTimeZone(content_db.GetZoneTimezone(zoneid, GetInstanceVersion()));
|
||||
|
||||
LogInfo("Zone booted successfully zone_id [{}] time_offset [{}]", zoneid, zone_time.getEQTimeZone());
|
||||
|
||||
LoadGrids();
|
||||
|
||||
npc_scale_manager->LoadScaleData();
|
||||
|
||||
// logging origination information
|
||||
LogSys.origination_info.zone_short_name = zone->short_name;
|
||||
LogSys.origination_info.zone_long_name = zone->long_name;
|
||||
@@ -1366,9 +1367,9 @@ bool Zone::LoadZoneCFG(const char* filename, uint16 instance_version)
|
||||
newzone_data.maxclip = z->maxclip;
|
||||
newzone_data.time_type = z->time_type;
|
||||
newzone_data.gravity = z->gravity;
|
||||
newzone_data.fast_regen_hp = z->fast_regen_hp;
|
||||
newzone_data.fast_regen_mana = z->fast_regen_mana;
|
||||
newzone_data.fast_regen_endurance = z->fast_regen_endurance;
|
||||
newzone_data.fast_regen_hp = z->fast_regen_hp > 0 ? z->fast_regen_hp : 180;
|
||||
newzone_data.fast_regen_mana = z->fast_regen_mana > 0 ? z->fast_regen_mana : 180;
|
||||
newzone_data.fast_regen_endurance = z->fast_regen_endurance > 0 ? z->fast_regen_endurance : 180;
|
||||
newzone_data.npc_aggro_max_dist = z->npc_max_aggro_dist;
|
||||
newzone_data.underworld_teleport_index = z->underworld_teleport_index;
|
||||
newzone_data.lava_damage = z->lava_damage;
|
||||
@@ -2899,6 +2900,8 @@ std::string Zone::GetZoneDescription()
|
||||
|
||||
void Zone::SendReloadMessage(std::string reload_type)
|
||||
{
|
||||
LogInfo("Reloaded [{}]", reload_type);
|
||||
|
||||
worldserver.SendEmoteMessage(
|
||||
0,
|
||||
0,
|
||||
|
||||
+109
-165
@@ -51,6 +51,9 @@
|
||||
#include "../common/repositories/character_corpse_items_repository.h"
|
||||
#include "../common/repositories/zone_repository.h"
|
||||
|
||||
#include "../common/repositories/trader_repository.h"
|
||||
|
||||
|
||||
#include <ctime>
|
||||
#include <iostream>
|
||||
#include <fmt/format.h>
|
||||
@@ -302,187 +305,114 @@ void ZoneDatabase::DeleteWorldContainer(uint32 parent_id, uint32 zone_id)
|
||||
);
|
||||
}
|
||||
|
||||
Trader_Struct* ZoneDatabase::LoadTraderItem(uint32 char_id)
|
||||
std::unique_ptr<EQ::ItemInstance> ZoneDatabase::LoadSingleTraderItem(uint32 char_id, int serial_number)
|
||||
{
|
||||
auto loadti = new Trader_Struct;
|
||||
memset(loadti,0,sizeof(Trader_Struct));
|
||||
auto results = TraderRepository::GetWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`char_id` = '{}' AND `item_sn` = '{}' ORDER BY slot_id",
|
||||
char_id,
|
||||
serial_number
|
||||
)
|
||||
);
|
||||
|
||||
std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i ORDER BY slot_id LIMIT 80", char_id);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogTrading("Failed to load trader information!\n");
|
||||
return loadti;
|
||||
if (results.empty()) {
|
||||
LogTrading("Could not find item serial number {} for character id {}", serial_number, char_id);
|
||||
}
|
||||
|
||||
loadti->Code = BazaarTrader_ShowItems;
|
||||
for (auto& row = results.begin(); row != results.end(); ++row) {
|
||||
if (Strings::ToInt(row[5]) >= 80 || Strings::ToInt(row[4]) < 0) {
|
||||
LogTrading("Bad Slot number when trying to load trader information!\n");
|
||||
continue;
|
||||
}
|
||||
int item_id = results.at(0).item_id;
|
||||
int charges = results.at(0).item_charges;
|
||||
int cost = results.at(0).item_cost;
|
||||
|
||||
loadti->Items[Strings::ToInt(row[5])] = Strings::ToInt(row[1]);
|
||||
loadti->ItemCost[Strings::ToInt(row[5])] = Strings::ToInt(row[4]);
|
||||
const EQ::ItemData *item = database.GetItem(item_id);
|
||||
if (!item) {
|
||||
LogTrading("Unable to create item.");
|
||||
return nullptr;
|
||||
}
|
||||
return loadti;
|
||||
|
||||
if (item->NoDrop == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<EQ::ItemInstance> inst(
|
||||
database.CreateItem(
|
||||
item_id,
|
||||
charges,
|
||||
results.at(0).aug_slot_1,
|
||||
results.at(0).aug_slot_2,
|
||||
results.at(0).aug_slot_3,
|
||||
results.at(0).aug_slot_4,
|
||||
results.at(0).aug_slot_5,
|
||||
results.at(0).aug_slot_6
|
||||
)
|
||||
);
|
||||
if (!inst) {
|
||||
LogTrading("Unable to create item instance.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inst->SetCharges(charges);
|
||||
inst->SetSerialNumber(serial_number);
|
||||
inst->SetMerchantSlot(serial_number);
|
||||
inst->SetPrice(cost);
|
||||
|
||||
if (inst->IsStackable()) {
|
||||
inst->SetMerchantCount(charges);
|
||||
}
|
||||
|
||||
return std::move(inst);
|
||||
}
|
||||
|
||||
TraderCharges_Struct* ZoneDatabase::LoadTraderItemWithCharges(uint32 char_id)
|
||||
{
|
||||
auto loadti = new TraderCharges_Struct;
|
||||
memset(loadti,0,sizeof(TraderCharges_Struct));
|
||||
void ZoneDatabase::UpdateTraderItemPrice(int char_id, uint32 item_id, uint32 charges, uint32 new_price) {
|
||||
|
||||
std::string query = StringFormat("SELECT * FROM trader WHERE char_id=%i ORDER BY slot_id LIMIT 80", char_id);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogTrading("Failed to load trader information!\n");
|
||||
return loadti;
|
||||
}
|
||||
|
||||
for (auto& row = results.begin(); row != results.end(); ++row) {
|
||||
if (Strings::ToInt(row[5]) >= 80 || Strings::ToInt(row[5]) < 0) {
|
||||
LogTrading("Bad Slot number when trying to load trader information!\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
loadti->ItemID[Strings::ToInt(row[5])] = Strings::ToInt(row[1]);
|
||||
loadti->SerialNumber[Strings::ToInt(row[5])] = Strings::ToInt(row[2]);
|
||||
loadti->Charges[Strings::ToInt(row[5])] = Strings::ToInt(row[3]);
|
||||
loadti->ItemCost[Strings::ToInt(row[5])] = Strings::ToInt(row[4]);
|
||||
}
|
||||
return loadti;
|
||||
}
|
||||
|
||||
EQ::ItemInstance* ZoneDatabase::LoadSingleTraderItem(uint32 CharID, int SerialNumber) {
|
||||
std::string query = StringFormat("SELECT * FROM trader WHERE char_id = %i AND serialnumber = %i "
|
||||
"ORDER BY slot_id LIMIT 80", CharID, SerialNumber);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return nullptr;
|
||||
|
||||
if (results.RowCount() == 0) {
|
||||
LogTrading("Bad result from query\n"); fflush(stdout);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& row = results.begin();
|
||||
|
||||
int ItemID = Strings::ToInt(row[1]);
|
||||
int Charges = Strings::ToInt(row[3]);
|
||||
int Cost = Strings::ToInt(row[4]);
|
||||
|
||||
const EQ::ItemData *item = database.GetItem(ItemID);
|
||||
LogTrading("ZoneDatabase::UpdateTraderPrice([{}], [{}], [{}], [{}])", char_id, item_id, charges, new_price);
|
||||
const EQ::ItemData *item = database.GetItem(item_id);
|
||||
|
||||
if(!item) {
|
||||
LogTrading("Unable to create item\n");
|
||||
fflush(stdout);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (item->NoDrop == 0)
|
||||
return nullptr;
|
||||
|
||||
EQ::ItemInstance* inst = database.CreateItem(item);
|
||||
if(!inst) {
|
||||
LogTrading("Unable to create item instance\n");
|
||||
fflush(stdout);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inst->SetCharges(Charges);
|
||||
inst->SetSerialNumber(SerialNumber);
|
||||
inst->SetMerchantSlot(SerialNumber);
|
||||
inst->SetPrice(Cost);
|
||||
|
||||
if(inst->IsStackable())
|
||||
inst->SetMerchantCount(Charges);
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void ZoneDatabase::SaveTraderItem(uint32 CharID, uint32 ItemID, uint32 SerialNumber, int32 Charges, uint32 ItemCost, uint8 Slot){
|
||||
|
||||
std::string query = StringFormat("REPLACE INTO trader VALUES(%i, %i, %i, %i, %i, %i)",
|
||||
CharID, ItemID, SerialNumber, Charges, ItemCost, Slot);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogDebug("[CLIENT] Failed to save trader item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str());
|
||||
|
||||
}
|
||||
|
||||
void ZoneDatabase::UpdateTraderItemCharges(int CharID, uint32 SerialNumber, int32 Charges) {
|
||||
LogTrading("ZoneDatabase::UpdateTraderItemCharges([{}], [{}], [{}])", CharID, SerialNumber, Charges);
|
||||
|
||||
std::string query = StringFormat("UPDATE trader SET charges = %i WHERE char_id = %i AND serialnumber = %i",
|
||||
Charges, CharID, SerialNumber);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogDebug("[CLIENT] Failed to update charges for trader item: [{}] for char_id: [{}], the error was: [{}]\n", SerialNumber, CharID, results.ErrorMessage().c_str());
|
||||
|
||||
}
|
||||
|
||||
void ZoneDatabase::UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice) {
|
||||
|
||||
LogTrading("ZoneDatabase::UpdateTraderPrice([{}], [{}], [{}], [{}])", CharID, ItemID, Charges, NewPrice);
|
||||
|
||||
const EQ::ItemData *item = database.GetItem(ItemID);
|
||||
|
||||
if(!item)
|
||||
return;
|
||||
}
|
||||
|
||||
if(NewPrice == 0) {
|
||||
LogTrading("Removing Trader items from the DB for CharID [{}], ItemID [{}]", CharID, ItemID);
|
||||
if (new_price == 0) {
|
||||
LogTrading("Removing Trader items from the DB for char_id [{}], item_id [{}]", char_id, item_id);
|
||||
|
||||
std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i AND item_id = %i",CharID, ItemID);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogDebug("[CLIENT] Failed to remove trader item(s): [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str());
|
||||
auto results = TraderRepository::DeleteWhere(
|
||||
database,
|
||||
fmt::format(
|
||||
"`char_id` = '{}' AND `item_id` = {}",
|
||||
char_id,
|
||||
item_id
|
||||
)
|
||||
);
|
||||
if (!results) {
|
||||
LogDebug("[CLIENT] Failed to remove trader item(s): [{}] for char_id: [{}]",
|
||||
item_id,
|
||||
char_id
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!item->Stackable) {
|
||||
std::string query = StringFormat("UPDATE trader SET item_cost = %i "
|
||||
"WHERE char_id = %i AND item_id = %i AND charges=%i",
|
||||
NewPrice, CharID, ItemID, Charges);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogDebug("[CLIENT] Failed to update price for trader item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
std::string query = StringFormat("UPDATE trader SET item_cost = %i "
|
||||
"WHERE char_id = %i AND item_id = %i",
|
||||
NewPrice, CharID, ItemID);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogDebug("[CLIENT] Failed to update price for trader item: [{}] for char_id: [{}], the error was: [{}]\n", ItemID, CharID, results.ErrorMessage().c_str());
|
||||
}
|
||||
|
||||
void ZoneDatabase::DeleteTraderItem(uint32 char_id){
|
||||
|
||||
if(char_id==0) {
|
||||
const std::string query = "DELETE FROM trader";
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogDebug("[CLIENT] Failed to delete all trader items data, the error was: [{}]\n", results.ErrorMessage().c_str());
|
||||
|
||||
return;
|
||||
if (!item->Stackable) {
|
||||
auto results = TraderRepository::UpdateItem(database, char_id, new_price, item_id, charges);
|
||||
if (!results) {
|
||||
LogTrading(
|
||||
"Failed to update price for trader item [{}] for char_id: [{}]",
|
||||
item_id,
|
||||
char_id
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i", char_id);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogDebug("[CLIENT] Failed to delete trader item data for char_id: [{}], the error was: [{}]\n", char_id, results.ErrorMessage().c_str());
|
||||
|
||||
}
|
||||
void ZoneDatabase::DeleteTraderItem(uint32 CharID,uint16 SlotID) {
|
||||
|
||||
std::string query = StringFormat("DELETE FROM trader WHERE char_id = %i And slot_id = %i", CharID, SlotID);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogDebug("[CLIENT] Failed to delete trader item data for char_id: [{}], the error was: [{}]\n",CharID, results.ErrorMessage().c_str());
|
||||
auto results = TraderRepository::UpdateItem(database, char_id, new_price, item_id, 0);
|
||||
if (!results) {
|
||||
LogTrading(
|
||||
"Failed to update price for trader item [{}] for char_id: [{}]",
|
||||
item_id,
|
||||
char_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void ZoneDatabase::DeleteBuyLines(uint32 CharID) {
|
||||
@@ -526,10 +456,22 @@ void ZoneDatabase::UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity)
|
||||
return;
|
||||
}
|
||||
|
||||
std::string query = StringFormat("UPDATE buyer SET quantity = %i WHERE charid = %i AND buyslot = %i", Quantity, CharID, BuySlot);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
LogDebug("[CLIENT] Failed to update quantity in buyslot [{}] for charid: [{}], the error was: [{}]\n", BuySlot, CharID, results.ErrorMessage().c_str());
|
||||
std::string query = StringFormat(
|
||||
"UPDATE buyer SET quantity = %i WHERE charid = %i AND buyslot = %i",
|
||||
Quantity,
|
||||
CharID,
|
||||
BuySlot
|
||||
);
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
LogTrading(
|
||||
"Failed to update quantity in buyslot [{}] for charid [{}], the error was [{}]\n",
|
||||
BuySlot,
|
||||
CharID,
|
||||
results.ErrorMessage().c_str()
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -630,6 +572,7 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct*
|
||||
pp->raidAutoconsent = e.raid_auto_consent;
|
||||
pp->guildAutoconsent = e.guild_auto_consent;
|
||||
pp->RestTimer = e.RestTimer;
|
||||
pp->char_id = e.id;
|
||||
m_epp->aa_effects = e.e_aa_effects;
|
||||
m_epp->perAA = e.e_percent_to_aa;
|
||||
m_epp->expended_aa = e.e_expended_aa_spent;
|
||||
@@ -1793,6 +1736,7 @@ const NPCType *ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
|
||||
t->max_dmg = n.maxdmg;
|
||||
t->attack_count = n.attack_count;
|
||||
t->is_parcel_merchant = n.is_parcel_merchant ? true : false;
|
||||
t->greed = n.greed;
|
||||
|
||||
if (!n.special_abilities.empty()) {
|
||||
strn0cpy(t->special_abilities, n.special_abilities.c_str(), 512);
|
||||
|
||||
+4
-4
@@ -389,11 +389,11 @@ public:
|
||||
/* Traders */
|
||||
void SaveTraderItem(uint32 char_id,uint32 itemid,uint32 uniqueid, int32 charges,uint32 itemcost,uint8 slot);
|
||||
void UpdateTraderItemCharges(int char_id, uint32 ItemInstID, int32 charges);
|
||||
void UpdateTraderItemPrice(int CharID, uint32 ItemID, uint32 Charges, uint32 NewPrice);
|
||||
void UpdateTraderItemPrice(int char_id, uint32 item_id, uint32 charges, uint32 new_price);
|
||||
void DeleteTraderItem(uint32 char_id);
|
||||
void DeleteTraderItem(uint32 char_id,uint16 slot_id);
|
||||
|
||||
EQ::ItemInstance* LoadSingleTraderItem(uint32 char_id, int uniqueid);
|
||||
std::unique_ptr<EQ::ItemInstance> LoadSingleTraderItem(uint32 char_id, int serial_number);
|
||||
Trader_Struct* LoadTraderItem(uint32 char_id);
|
||||
TraderCharges_Struct* LoadTraderItemWithCharges(uint32 char_id);
|
||||
|
||||
@@ -552,7 +552,7 @@ public:
|
||||
uint32 NPCSpawnDB(uint8 command, const std::string& zone, uint32 instance_version, Client *c, NPC* n = 0, uint32 extra = 0); // 0 = Create 1 = Add; 2 = Update; 3 = Remove; 4 = Delete
|
||||
uint32 CreateNewNPCCommand(const std::string& zone, uint32 instance_version, Client* c, NPC* n, uint32 extra);
|
||||
uint32 AddNewNPCSpawnGroupCommand(const std::string& zone, uint32 instance_version, Client* c, NPC* n, uint32 in_respawn_time);
|
||||
uint32 DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Client* c, NPC* n);
|
||||
uint32 DeleteSpawnLeaveInNPCTypeTable(const std::string& zone, Client* c, NPC* n, uint32 remove_spawngroup_id);
|
||||
uint32 DeleteSpawnRemoveFromNPCTypeTable(const std::string& zone, uint32 instance_version, Client* c, NPC* n);
|
||||
uint32 AddSpawnFromSpawnGroup(const std::string& zone, uint32 instance_version, Client* c, NPC* n, uint32 spawngroup_id);
|
||||
uint32 AddNPCTypes(const std::string& zone, uint32 instance_version, Client* c, NPC* n, uint32 spawngroup_id);
|
||||
@@ -569,7 +569,7 @@ public:
|
||||
bool GetAuraEntry(uint16 spell_id, AuraRecord &record);
|
||||
void LoadGlobalLoot();
|
||||
|
||||
DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID);
|
||||
DBnpcspells_Struct* GetNPCSpells(uint32 npc_spells_id);
|
||||
DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);
|
||||
void ClearNPCSpells() { npc_spells_cache.clear(); npc_spells_loadtried.clear(); }
|
||||
const NPCType* LoadNPCTypesData(uint32 id, bool bulk_load = false);
|
||||
|
||||
@@ -155,6 +155,7 @@ struct NPCType
|
||||
int heroic_strikethrough;
|
||||
bool keeps_sold_items;
|
||||
bool is_parcel_merchant;
|
||||
uint8 greed;
|
||||
};
|
||||
|
||||
#pragma pack()
|
||||
|
||||
Reference in New Issue
Block a user