diff --git a/client_files/export/main.cpp b/client_files/export/main.cpp index e90e6c76c..a930d5404 100644 --- a/client_files/export/main.cpp +++ b/client_files/export/main.cpp @@ -36,12 +36,14 @@ #include "../../common/file.h" #include "../../common/events/player_event_logs.h" #include "../../common/skill_caps.h" +#include "../../common/evolving_items.h" -EQEmuLogSys LogSys; -WorldContentService content_service; -ZoneStore zone_store; -PathManager path; -PlayerEventLogs player_event_logs; +EQEmuLogSys LogSys; +WorldContentService content_service; +ZoneStore zone_store; +PathManager path; +PlayerEventLogs player_event_logs; +EvolvingItemsManager evolving_items_manager; void ExportSpells(SharedDatabase *db); void ExportSkillCaps(SharedDatabase *db); diff --git a/client_files/import/main.cpp b/client_files/import/main.cpp index 0d8010221..74f447a23 100644 --- a/client_files/import/main.cpp +++ b/client_files/import/main.cpp @@ -30,12 +30,14 @@ #include "../../common/repositories/base_data_repository.h" #include "../../common/file.h" #include "../../common/events/player_event_logs.h" +#include "../../common/evolving_items.h" -EQEmuLogSys LogSys; -WorldContentService content_service; -ZoneStore zone_store; -PathManager path; -PlayerEventLogs player_event_logs; +EQEmuLogSys LogSys; +WorldContentService content_service; +ZoneStore zone_store; +PathManager path; +PlayerEventLogs player_event_logs; +EvolvingItemsManager evolving_items_manager; void ImportSpells(SharedDatabase *db); void ImportSkillCaps(SharedDatabase *db); diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 0a1766fc8..ff130adb0 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -39,6 +39,7 @@ SET(common_sources event_sub.cpp events/player_event_logs.cpp events/player_event_discord_formatter.cpp + evolving_items.cpp expedition_lockout_timer.cpp extprofile.cpp discord/discord_manager.cpp @@ -172,6 +173,7 @@ SET(repositories repositories/base/base_character_currency_repository.h repositories/base/base_character_data_repository.h repositories/base/base_character_disciplines_repository.h + repositories/base/base_character_evolving_items_repository.h repositories/base/base_character_expedition_lockouts_repository.h repositories/base/base_character_exp_modifiers_repository.h repositories/base/base_character_inspect_messages_repository.h @@ -240,6 +242,7 @@ SET(repositories repositories/base/base_inventory_snapshots_repository.h repositories/base/base_ip_exemptions_repository.h repositories/base/base_items_repository.h + repositories/base/base_items_evolving_details_repository.h repositories/base/base_ldon_trap_entries_repository.h repositories/base/base_ldon_trap_templates_repository.h repositories/base/base_level_exp_mods_repository.h @@ -355,6 +358,7 @@ SET(repositories repositories/character_currency_repository.h repositories/character_data_repository.h repositories/character_disciplines_repository.h + repositories/character_evolving_items_repository.h repositories/character_expedition_lockouts_repository.h repositories/character_exp_modifiers_repository.h repositories/character_inspect_messages_repository.h @@ -423,6 +427,7 @@ SET(repositories repositories/inventory_snapshots_repository.h repositories/ip_exemptions_repository.h repositories/items_repository.h + repositories/items_evolving_details_repository.h repositories/ldon_trap_entries_repository.h repositories/ldon_trap_templates_repository.h repositories/level_exp_mods_repository.h @@ -555,6 +560,7 @@ SET(common_headers events/player_event_discord_formatter.h events/player_events.h event_sub.h + evolving_items.h expedition_lockout_timer.h extprofile.h faction.h diff --git a/common/bazaar.cpp b/common/bazaar.cpp index b24fcb1d1..cce3fcdd3 100644 --- a/common/bazaar.cpp +++ b/common/bazaar.cpp @@ -31,6 +31,7 @@ Bazaar::GetSearchResults( char_zone_instance_id ); + bool convert = false; std::string search_criteria_trader("TRUE "); if (search.search_scope == NonRoFBazaarSearchScope) { @@ -51,8 +52,24 @@ Bazaar::GetSearchResults( ); } else if (search.trader_id > 0) { - search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (search.trader_id >= TraderRepository::TRADER_CONVERT_ID) { + convert = true; + search_criteria_trader.append(fmt::format( + " AND trader.char_zone_id = {} AND trader.char_zone_instance_id = {}", + Zones::BAZAAR, + search.trader_id - TraderRepository::TRADER_CONVERT_ID) + ); + } + else { + search_criteria_trader.append(fmt::format(" AND trader.char_id = {}", search.trader_id)); + } + } + else { + 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 * 1000)); } @@ -355,6 +372,12 @@ Bazaar::GetSearchResults( } LogTradingDetail("Found item [{}] meeting search criteria.", r.item_name); + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (convert || (r.trader_zone_id == Zones::BAZAAR && r.trader_zone_instance_id != char_zone_instance_id)) { + r.trader_id = TraderRepository::TRADER_CONVERT_ID + r.trader_zone_instance_id; + } + } + all_entries.push_back(r); } diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index cd484122b..8e17fb951 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -5803,6 +5803,490 @@ ALTER TABLE `npc_types` MODIFY COLUMN `walkspeed` float NOT NULL DEFAULT 0; .sql = R"( ALTER TABLE `zone` ADD COLUMN `shard_at_player_count` int(11) NULL DEFAULT 0 AFTER `seconds_before_idle`; +)", + .content_schema_update = true + }, + ManifestEntry{ + .version = 9289, + .description = "2025_01_19_evolving_items__character_evolving_items", + .check = "SHOW TABLES LIKE 'character_evolving_items'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `character_evolving_items` ( + `id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, + `character_id` INT(10) UNSIGNED NULL DEFAULT '0', + `item_id` INT(10) UNSIGNED NULL DEFAULT '0', + `activated` TINYINT(1) UNSIGNED NULL DEFAULT '0', + `equipped` TINYINT(3) UNSIGNED NULL DEFAULT '0', + `current_amount` BIGINT(20) NULL DEFAULT '0', + `progression` DOUBLE(22,0) NULL DEFAULT '0', + `final_item_id` INT(10) UNSIGNED NULL DEFAULT '0', + `deleted_at` DATETIME NULL DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB +AUTO_INCREMENT=1 +; +)" + }, + ManifestEntry{ + .version = 9290, + .description = "2025_01_19_evolving_items__items_evolving_details", + .check = "SHOW TABLES LIKE 'items_evolving_details'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `items_evolving_details` ( + `id` int(10) unsigned NOT NULL AUTO_INCREMENT, + `item_evo_id` int(10) unsigned DEFAULT 0 COMMENT 'items.evoid', + `item_evolve_level` int(10) unsigned DEFAULT 0 COMMENT 'items.evolvinglevel', + `item_id` int(10) unsigned DEFAULT 0 COMMENT 'items.id', + `type` int(10) unsigned DEFAULT 0, + `sub_type` int(10) unsigned DEFAULT 0, + `required_amount` bigint(20) DEFAULT NULL, + PRIMARY KEY (`id`) USING BTREE +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=latin1; +INSERT INTO `items_evolving_details` VALUES +(1,1000,1,86900,99,1,0), +(2,1000,2,86901,99,1,0), +(3,1000,3,86902,99,1,0), +(4,1000,4,86903,99,1,0), +(5,1000,5,86904,99,1,0), +(6,1001,1,86910,99,1,0), +(7,1001,2,86911,99,1,0), +(8,1001,3,86912,99,1,0), +(9,1001,4,86913,99,1,0), +(10,1001,5,86914,99,1,0), +(11,1001,6,86915,99,1,0), +(12,1001,7,86916,99,1,0), +(13,1002,1,86920,99,1,0), +(14,1002,2,86921,99,1,0), +(15,1002,3,86922,99,1,0), +(16,1002,4,86923,99,1,0), +(17,1002,5,86924,99,1,0), +(18,1002,6,86925,99,1,0), +(19,1002,7,86926,99,1,0), +(20,1003,1,86930,99,1,0), +(21,1003,2,86931,99,1,0), +(22,1003,3,86932,99,1,0), +(23,1003,4,86933,99,1,0), +(24,1003,5,86934,99,1,0), +(25,1003,6,86935,99,1,0), +(26,1004,1,86940,99,1,0), +(27,1004,2,86941,99,1,0), +(28,1004,3,86942,99,1,0), +(29,1004,4,86943,99,1,0), +(30,1004,5,86944,99,1,0), +(31,1004,6,86945,99,1,0), +(32,1005,1,86950,99,1,0), +(33,1005,2,86951,99,1,0), +(34,1005,3,86952,99,1,0), +(35,1005,4,86953,99,1,0), +(36,1005,5,86954,99,1,0), +(37,1005,6,86955,99,1,0), +(38,1005,7,86956,99,1,0), +(39,1006,1,86960,99,1,0), +(40,1006,2,86961,99,1,0), +(41,1006,3,86962,99,1,0), +(42,1006,4,86963,99,1,0), +(43,1006,5,86964,99,1,0), +(44,1006,6,86965,99,1,0), +(45,1006,7,86966,99,1,0), +(46,1007,1,86970,99,1,0), +(47,1007,2,86971,99,1,0), +(48,1007,3,86972,99,1,0), +(49,1007,4,86973,99,1,0), +(50,1007,5,86974,99,1,0), +(51,1007,6,86975,99,1,0), +(52,1007,7,86976,99,1,0), +(53,1008,1,86980,99,1,0), +(54,1008,2,86981,99,1,0), +(55,1008,3,86982,99,1,0), +(56,1008,4,86983,99,1,0), +(57,1008,5,86984,99,1,0), +(58,1009,1,86990,99,1,0), +(59,1009,2,86991,99,1,0), +(60,1009,3,86992,99,1,0), +(61,1009,4,86993,99,1,0), +(62,1009,5,86994,99,1,0), +(63,1009,6,86995,99,1,0), +(64,1009,7,86996,99,1,0), +(65,1009,8,86997,99,1,0), +(66,1009,9,86998,99,1,0), +(67,1009,10,86999,99,1,0), +(68,1010,1,90001,99,1,0), +(69,1010,2,90002,99,1,0), +(70,1010,3,90003,99,1,0), +(71,1010,6,90006,99,1,0), +(72,1010,7,90007,99,1,0), +(73,1010,8,90008,99,1,0), +(74,1010,10,90010,99,1,0), +(75,1050,1,89270,99,1,0), +(76,1050,2,89271,99,1,0), +(77,1050,3,89272,99,1,0), +(78,1050,4,89273,99,1,0), +(79,1050,5,89274,99,1,0), +(80,1050,6,89275,99,1,0), +(81,1051,1,89280,99,1,0), +(82,1051,2,89281,99,1,0), +(83,1051,3,89282,99,1,0), +(84,1051,4,89283,99,1,0), +(85,1051,5,89284,99,1,0), +(86,1052,1,89290,99,1,0), +(87,1052,2,89291,99,1,0), +(88,1052,3,89292,99,1,0), +(89,1052,4,89293,99,1,0), +(90,1052,5,89294,99,1,0), +(91,1052,6,89295,99,1,0), +(92,1052,7,89296,99,1,0), +(93,1053,1,89300,99,1,0), +(94,1053,2,89301,99,1,0), +(95,1053,3,89302,99,1,0), +(96,1053,4,89303,99,1,0), +(97,1053,5,89304,99,1,0), +(98,1053,6,89305,99,1,0), +(99,1053,7,89306,99,1,0), +(100,1053,8,89307,99,1,0), +(101,1053,9,89308,99,1,0), +(102,1054,1,89310,99,1,0), +(103,1054,2,89311,99,1,0), +(104,1054,3,89312,99,1,0), +(105,1054,4,89313,99,1,0), +(106,1054,5,89314,99,1,0), +(107,1054,6,89315,99,1,0), +(108,1055,1,89320,99,1,0), +(109,1055,2,89321,99,1,0), +(110,1055,3,89322,99,1,0), +(111,1055,4,89323,99,1,0), +(112,1055,5,89324,99,1,0), +(113,1055,6,89325,99,1,0), +(114,1055,7,89326,99,1,0), +(115,1056,1,89330,99,1,0), +(116,1056,2,89331,99,1,0), +(117,1056,3,89332,99,1,0), +(118,1056,4,89333,99,1,0), +(119,1056,5,89334,99,1,0), +(120,1057,1,89340,99,1,0), +(121,1057,2,89341,99,1,0), +(122,1057,3,89342,99,1,0), +(123,1057,4,89343,99,1,0), +(124,1057,5,89344,99,1,0), +(125,1057,6,89345,99,1,0), +(126,1057,7,89346,99,1,0), +(127,1057,8,89347,99,1,0), +(128,1058,1,89350,99,1,0), +(129,1058,2,89351,99,1,0), +(130,1058,3,89352,99,1,0), +(131,1058,4,89353,99,1,0), +(132,1058,5,89354,99,1,0), +(133,1058,6,89355,99,1,0), +(134,1058,7,89356,99,1,0), +(135,1059,1,89360,99,1,0), +(136,1059,2,89361,99,1,0), +(137,1059,3,89362,99,1,0), +(138,1060,1,89490,99,1,0), +(139,1060,2,89491,99,1,0), +(140,1060,3,89492,99,1,0), +(141,1061,1,89500,99,1,0), +(142,1061,2,89501,99,1,0), +(143,1061,3,89502,99,1,0), +(144,1062,1,89510,99,1,0), +(145,1062,2,89511,99,1,0), +(146,1062,3,89512,99,1,0), +(147,1063,1,89520,99,1,0), +(148,1063,2,89521,99,1,0), +(149,1063,3,89522,99,1,0), +(150,1064,1,89530,99,1,0), +(151,1064,2,89531,99,1,0), +(152,1064,3,89532,99,1,0), +(153,1065,1,89540,99,1,0), +(154,1065,2,89541,99,1,0), +(155,1065,3,89542,99,1,0), +(156,1066,1,89550,3,274,500), +(157,1066,2,89551,3,274,1000), +(158,1066,3,89552,3,274,2000), +(159,1067,1,89560,99,1,0), +(160,1067,2,89561,99,1,0), +(161,1067,3,89562,99,1,0), +(162,1069,1,85571,99,1,0), +(163,1069,2,85572,99,1,0), +(164,1069,3,85573,99,1,0), +(165,1200,1,95001,99,1,0), +(166,1200,2,95002,99,1,0), +(167,1200,3,95003,99,1,0), +(168,1200,4,95004,99,1,0), +(169,1200,5,95005,99,1,0), +(170,1200,6,95006,99,1,0), +(171,1200,7,95007,99,1,0), +(172,1201,1,95008,99,1,0), +(173,1201,2,95009,99,1,0), +(174,1201,3,95010,99,1,0), +(175,1201,4,95011,99,1,0), +(176,1201,5,95012,99,1,0), +(177,1201,6,95013,99,1,0), +(178,1201,7,95014,99,1,0), +(179,1202,1,95015,99,1,0), +(180,1202,2,95016,99,1,0), +(181,1202,3,95017,99,1,0), +(182,1202,4,95018,99,1,0), +(183,1202,5,95019,99,1,0), +(184,1202,6,95020,99,1,0), +(185,1202,7,95021,99,1,0), +(186,1203,1,95022,99,1,0), +(187,1203,2,95023,99,1,0), +(188,1203,3,95024,99,1,0), +(189,1203,4,95025,99,1,0), +(190,1203,5,95026,99,1,0), +(191,1203,6,95027,99,1,0), +(192,1203,7,95028,99,1,0), +(193,1204,1,95029,99,1,0), +(194,1204,2,95030,99,1,0), +(195,1204,3,95031,99,1,0), +(196,1204,4,95032,99,1,0), +(197,1204,5,95033,99,1,0), +(198,1204,6,95034,99,1,0), +(199,1204,7,95035,99,1,0), +(200,1205,1,95036,99,1,0), +(201,1205,2,95037,99,1,0), +(202,1205,3,95038,99,1,0), +(203,1205,4,95039,99,1,0), +(204,1205,5,95040,99,1,0), +(205,1205,6,95041,99,1,0), +(206,1205,7,95042,99,1,0), +(207,1206,1,95043,99,1,0), +(208,1206,2,95044,99,1,0), +(209,1206,3,95045,99,1,0), +(210,1206,4,95046,99,1,0), +(211,1206,5,95047,99,1,0), +(212,1206,6,95048,99,1,0), +(213,1206,7,95049,99,1,0), +(214,1207,1,95050,99,1,0), +(215,1207,2,95051,99,1,0), +(216,1207,3,95052,99,1,0), +(217,1207,4,95053,99,1,0), +(218,1207,5,95054,99,1,0), +(219,1207,6,95055,99,1,0), +(220,1207,7,95056,99,1,0), +(221,1208,1,95057,99,1,0), +(222,1208,2,95058,99,1,0), +(223,1208,3,95059,99,1,0), +(224,1208,4,95060,99,1,0), +(225,1208,5,95061,99,1,0), +(226,1208,6,95062,99,1,0), +(227,1208,7,95063,99,1,0), +(228,1209,1,95064,99,1,0), +(229,1209,2,95065,99,1,0), +(230,1209,3,95066,99,1,0), +(231,1209,4,95067,99,1,0), +(232,1209,5,95068,99,1,0), +(233,1209,6,95069,99,1,0), +(234,1209,7,95070,99,1,0), +(235,1210,1,95071,99,1,0), +(236,1210,2,95072,99,1,0), +(237,1210,3,95073,99,1,0), +(238,1210,4,95074,99,1,0), +(239,1210,5,95075,99,1,0), +(240,1210,6,95076,99,1,0), +(241,1210,7,95077,99,1,0), +(242,1211,1,85612,1,1,100000), +(243,1211,2,85613,1,1,200000), +(244,1211,3,85614,1,1,300000), +(245,1214,1,80035,99,1,0), +(246,1301,1,102700,99,1,0), +(247,1301,4,102703,99,1,0), +(248,1302,1,102704,99,1,0), +(249,1302,2,102705,99,1,0), +(250,1303,1,102706,99,1,0), +(251,1303,2,102707,99,1,0), +(252,1303,3,102708,99,1,0), +(253,1304,1,102709,99,1,0), +(254,1304,5,102713,99,1,0), +(255,1305,1,102714,99,1,0), +(256,1306,1,102716,99,1,0), +(257,1306,5,102720,99,1,0), +(258,1307,1,102721,99,1,0), +(259,1307,3,102723,99,1,0), +(260,1308,1,102724,99,1,0), +(261,1309,1,102727,99,1,0), +(262,1309,2,102728,99,1,0), +(263,1310,1,102729,99,1,0), +(264,1310,3,102731,99,1,0), +(265,1311,1,102732,99,1,0), +(266,1311,4,102735,99,1,0), +(267,1312,1,102736,99,1,0), +(268,1312,3,102738,99,1,0), +(269,1313,1,102739,99,1,0), +(270,1314,1,102743,99,1,0), +(271,1314,2,102744,99,1,0), +(272,1314,3,102745,99,1,0), +(273,1315,1,102746,99,1,0), +(274,1315,2,102747,99,1,0), +(275,1316,1,102748,99,1,0), +(276,1316,5,102752,99,1,0), +(277,1317,1,102753,99,1,0), +(278,1318,1,102756,99,1,0), +(279,1319,1,102759,99,1,0), +(280,1319,3,102761,99,1,0), +(281,1320,1,102762,99,1,0), +(282,1321,1,102765,99,1,0), +(283,1321,2,102766,99,1,0), +(284,1321,3,102767,99,1,0), +(285,1322,1,102768,99,1,0), +(286,1322,2,102769,99,1,0), +(287,1322,3,102770,99,1,0), +(288,1323,1,102771,99,1,0), +(289,1324,1,102774,99,1,0), +(290,1400,1,102800,99,1,0), +(291,1401,1,102807,99,1,0), +(292,1401,7,102813,99,1,0), +(293,1402,1,102814,99,1,0), +(294,1402,7,102820,99,1,0), +(295,1403,1,102821,99,1,0), +(296,1404,1,102828,99,1,0), +(297,1405,1,102835,99,1,0), +(298,1406,1,102842,99,1,0), +(299,1408,1,109310,99,1,0), +(300,1408,5,109314,99,1,0), +(301,1409,1,109315,99,1,0), +(302,1409,5,109319,99,1,0), +(303,1410,1,109320,99,1,0), +(304,1410,5,109324,99,1,0), +(305,1411,1,109325,99,1,0), +(306,1411,5,109329,99,1,0), +(307,1412,1,109330,99,1,0), +(308,1412,5,109334,99,1,0), +(309,1413,1,109335,99,1,0), +(310,1413,5,109339,99,1,0), +(311,1414,1,109340,99,1,0), +(312,1414,5,109344,99,1,0), +(313,1415,1,109345,99,1,0), +(314,1415,5,109349,99,1,0), +(315,1416,1,109350,99,1,0), +(316,1416,2,109351,99,1,0), +(317,1416,5,109354,99,1,0), +(318,1417,1,109355,99,1,0), +(319,1417,5,109359,99,1,0), +(320,1418,1,109360,99,1,0), +(321,1418,5,109364,99,1,0), +(322,1419,1,109365,99,1,0), +(323,1419,3,109367,99,1,0), +(324,1419,5,109369,99,1,0), +(325,1420,1,109370,99,1,0), +(326,1420,5,109374,99,1,0), +(327,1421,1,109375,99,1,0), +(328,1421,5,109379,99,1,0), +(329,1422,1,109380,99,1,0), +(330,1422,5,109384,99,1,0), +(331,1423,1,109385,99,1,0), +(332,1423,2,109386,99,1,0), +(333,1423,5,109389,99,1,0), +(334,1436,1,120378,99,1,0), +(335,1436,2,120379,99,1,0), +(336,1436,3,120380,99,1,0), +(337,1436,4,120381,99,1,0), +(338,1436,5,120382,99,1,0), +(339,1436,6,120383,99,1,0), +(340,1436,7,120384,99,1,0), +(341,1436,8,120385,99,1,0), +(342,1436,9,120386,99,1,0), +(343,1436,10,120387,99,1,0), +(344,1436,11,120388,99,1,0), +(345,1436,12,120389,99,1,0), +(346,1436,13,120390,99,1,0), +(347,1436,14,120391,99,1,0), +(348,1436,15,120392,99,1,0), +(349,1436,16,120393,99,1,0), +(350,1436,17,120394,99,1,0), +(351,1436,18,120395,99,1,0), +(352,1436,19,120396,99,1,0), +(353,1436,20,120397,99,1,0), +(354,1440,1,56992,99,1,0), +(355,1440,2,56993,99,1,0), +(356,1440,3,56994,99,1,0), +(357,1440,4,56995,99,1,0), +(358,1440,5,56996,99,1,0), +(359,1441,1,132787,99,1,0), +(360,1441,2,132788,99,1,0), +(361,1441,3,132789,99,1,0), +(362,1441,4,132790,99,1,0), +(363,1441,5,132791,99,1,0), +(364,1441,6,132792,99,1,0), +(365,1441,7,132793,99,1,0), +(366,1441,8,132794,99,1,0), +(367,1441,9,132795,99,1,0), +(368,1441,10,132796,99,1,0), +(369,1441,11,132797,99,1,0), +(370,1441,13,132799,99,1,0), +(371,1441,14,132800,99,1,0), +(372,1441,15,132801,99,1,0), +(373,1441,16,132802,99,1,0), +(374,1441,17,132803,99,1,0), +(375,1441,18,132804,99,1,0), +(376,1441,19,132805,99,1,0), +(377,1441,20,132806,99,1,0), +(378,1442,1,133137,99,1,0), +(379,1442,2,133138,99,1,0), +(380,1442,3,133139,99,1,0), +(381,1442,4,133140,99,1,0), +(382,1442,10,133146,99,1,0), +(383,1442,11,133147,99,1,0), +(384,1442,12,133148,99,1,0), +(385,1442,13,133149,99,1,0), +(386,1442,14,133150,99,1,0), +(387,1442,15,133151,99,1,0), +(388,1442,16,133152,99,1,0), +(389,1442,17,133153,99,1,0), +(390,1442,18,133154,99,1,0), +(391,1442,19,133155,99,1,0), +(392,1442,20,133156,99,1,0), +(393,1443,1,133406,99,1,0), +(394,1443,2,133407,99,1,0), +(395,1443,3,133408,99,1,0), +(396,1443,4,133409,99,1,0), +(397,1443,5,133410,99,1,0), +(398,1443,6,133411,99,1,0), +(399,1443,7,133412,99,1,0), +(400,1443,8,133413,99,1,0), +(401,1443,9,133414,99,1,0), +(402,1443,10,133415,99,1,0), +(403,1443,11,133416,99,1,0), +(404,1443,12,133417,99,1,0), +(405,1443,13,133418,99,1,0), +(406,1443,14,133419,99,1,0), +(407,1443,15,133420,99,1,0), +(408,1443,16,133421,99,1,0), +(409,1443,17,133422,99,1,0), +(410,1443,18,133423,99,1,0), +(411,1443,19,133424,99,1,0), +(412,1443,20,133425,99,1,0), +(413,1444,1,94938,99,1,0), +(414,1444,2,94939,99,1,0), +(415,1444,3,94940,99,1,0), +(416,1444,4,94941,99,1,0), +(417,1444,5,94942,99,1,0), +(418,1444,6,94943,99,1,0), +(419,1444,7,94944,99,1,0), +(420,1444,8,94945,99,1,0), +(421,1444,9,94946,99,1,0), +(422,1444,10,94947,99,1,0), +(423,1444,11,94948,99,1,0), +(424,1444,12,94949,99,1,0), +(425,1444,13,94950,99,1,0), +(426,1444,14,94951,99,1,0), +(427,1444,15,94952,99,1,0), +(428,1444,16,94953,99,1,0), +(429,1444,17,94954,99,1,0), +(430,1444,18,94955,99,1,0), +(431,1444,19,94956,99,1,0), +(432,1444,20,94957,99,1,0), +(433,1445,1,98858,99,1,0), +(434,1445,2,98859,99,1,0), +(435,1445,3,98860,99,1,0), +(436,1445,4,98861,99,1,0), +(437,1445,5,98862,99,1,0); + )", .content_schema_update = true } diff --git a/common/database_schema.h b/common/database_schema.h index fa13534c1..0d2b7237d 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -51,6 +51,7 @@ namespace DatabaseSchema { {"character_enabledtasks", "charid"}, {"character_expedition_lockouts", "character_id"}, {"character_exp_modifiers", "character_id"}, + {"character_evolving_items", "character_id"}, {"character_inspect_messages", "id"}, {"character_instance_safereturns", "character_id"}, {"character_item_recast", "id"}, @@ -124,6 +125,7 @@ namespace DatabaseSchema { "character_enabledtasks", "character_expedition_lockouts", "character_exp_modifiers", + "character_evolving_items", "character_inspect_messages", "character_instance_safereturns", "character_item_recast", @@ -212,6 +214,7 @@ namespace DatabaseSchema { "ground_spawns", "horses", "items", + "items_evolving_details", "ldon_trap_entries", "ldon_trap_templates", "lootdrop", diff --git a/common/emu_oplist.h b/common/emu_oplist.h index e41e4994b..6b5ab12ab 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -162,6 +162,7 @@ N(OP_EnduranceUpdate), N(OP_EnterChat), N(OP_EnterWorld), N(OP_EnvDamage), +N(OP_EvolveItem), N(OP_ExpansionInfo), N(OP_ExpUpdate), N(OP_FaceChange), @@ -289,6 +290,7 @@ N(OP_ItemLinkText), N(OP_ItemName), N(OP_ItemPacket), N(OP_ItemPreview), +N(OP_ItemPreviewRequest), N(OP_ItemRecastDelay), N(OP_ItemVerifyReply), N(OP_ItemVerifyRequest), diff --git a/common/eq_constants.h b/common/eq_constants.h index fd9cd346a..e048a9321 100644 --- a/common/eq_constants.h +++ b/common/eq_constants.h @@ -1114,4 +1114,5 @@ enum ExpSource namespace DoorType { constexpr uint32 BuyerStall = 155; } + #endif /*COMMON_EQ_CONSTANTS_H*/ diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index e4e3c28f8..bfe5abf44 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -6460,6 +6460,70 @@ struct PickZone_Struct { int32 selection_id; }; +struct EvolveItemToggle { + uint32 action; + uint32 unknown_004; + uint64 unique_id; + uint32 percentage; + uint32 activated; +}; + +struct EvolveXPWindowReceive { + uint32 action; + uint32 unknown_004; + uint64 item1_unique_id; + uint64 item2_unique_id; +}; + +struct EvolveItemMessaging { + uint32 action; + char serialized_data[]; +}; + +struct EvolveXPWindowSend { + /*000*/ uint32 action; + /*004*/ uint64 item1_unique_id; + /*012*/ uint64 item2_unique_id; + /*020*/ uint32 compatibility; + /*024*/ uint32 max_transfer_level; + /*028*/ uint8 item1_present; + /*029*/ uint8 item2_present; + /*030*/ std::string serialize_item_1; + /*034*/ std::string serialize_item_2; + + template + void serialize(Archive &archive) + { + archive( + CEREAL_NVP(action), + CEREAL_NVP(item1_unique_id), + CEREAL_NVP(item2_unique_id), + CEREAL_NVP(compatibility), + CEREAL_NVP(max_transfer_level), + CEREAL_NVP(item1_present), + CEREAL_NVP(item2_present), + CEREAL_NVP(serialize_item_1), + CEREAL_NVP(serialize_item_2) + ); + } +}; + +struct EvolveTransfer { + uint32 item_from_id; + uint32 item_from_current_amount; + uint32 item_to_id; + uint32 item_to_current_amount; + uint32 compatibility; + uint32 max_transfer_level; +}; + +struct EvolveGetNextItem { + uint32 new_item_id; + uint64 new_current_amount; + uint64 from_current_amount; + uint32 max_transfer_level; +}; + // Restore structure packing to default #pragma pack() diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 0fa85b75a..7e39f3c44 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -142,6 +142,7 @@ namespace Logs { EqTime, Corpses, XTargets, + EvolveItem, BotSettings, BotPreChecks, BotHoldChecks, @@ -250,6 +251,7 @@ namespace Logs { "EqTime", "Corpses", "XTargets", + "EvolveItem" "Bot Settings", "Bot Pre Checks", "Bot Hold Checks", diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index fdd33be14..616ee203a 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -131,6 +131,16 @@ OutF(LogSys, Logs::Detail, Logs::Error, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogEvolveItem(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::EvolveItem))\ + OutF(LogSys, Logs::General, Logs::EvolveItem, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogEvolveItemDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::EvolveItem))\ + OutF(LogSys, Logs::Detail, Logs::EvolveItem, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define LogGuilds(message, ...) do {\ if (LogSys.IsLogEnabled(Logs::General, Logs::Guilds))\ OutF(LogSys, Logs::General, Logs::Guilds, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/events/player_event_logs.cpp b/common/events/player_event_logs.cpp index 5baa3e548..5603fb7b8 100644 --- a/common/events/player_event_logs.cpp +++ b/common/events/player_event_logs.cpp @@ -707,6 +707,7 @@ void PlayerEventLogs::SetSettingsDefaults() m_settings[PlayerEvent::PARCEL_RETRIEVE].event_enabled = 1; m_settings[PlayerEvent::PARCEL_DELETE].event_enabled = 1; m_settings[PlayerEvent::BARTER_TRANSACTION].event_enabled = 1; + m_settings[PlayerEvent::EVOLVE_ITEM].event_enabled = 1; for (int i = PlayerEvent::GM_COMMAND; i != PlayerEvent::MAX; i++) { m_settings[i].retention_days = RETENTION_DAYS_DEFAULT; diff --git a/common/events/player_events.h b/common/events/player_events.h index 6384f8e0e..6a01a5cf6 100644 --- a/common/events/player_events.h +++ b/common/events/player_events.h @@ -62,6 +62,7 @@ namespace PlayerEvent { PARCEL_RETRIEVE, PARCEL_DELETE, BARTER_TRANSACTION, + EVOLVE_ITEM, MAX // dont remove }; @@ -124,7 +125,8 @@ namespace PlayerEvent { "Parcel Item Sent", "Parcel Item Retrieved", "Parcel Prune Routine", - "Barter Transaction" + "Barter Transaction", + "Evolve Item Update" }; // Generic struct used by all events @@ -1115,6 +1117,29 @@ namespace PlayerEvent { ); } }; + + struct EvolveItem { + std::string status; + uint32 item_id; + uint64 unique_id; + std::string item_name; + uint32 level; + double progression; + + // cereal + template + void serialize(Archive &ar) + { + ar( + CEREAL_NVP(status), + CEREAL_NVP(item_id), + CEREAL_NVP(unique_id), + CEREAL_NVP(item_name), + CEREAL_NVP(level), + CEREAL_NVP(progression) + ); + } + }; } #endif //EQEMU_PLAYER_EVENTS_H diff --git a/common/evolving_items.cpp b/common/evolving_items.cpp new file mode 100644 index 000000000..2b7eb38c6 --- /dev/null +++ b/common/evolving_items.cpp @@ -0,0 +1,303 @@ +#include "evolving_items.h" +#include "item_instance.h" +#include "events/player_event_logs.h" +#include "repositories/character_evolving_items_repository.h" + +EvolvingItemsManager::EvolvingItemsManager() +{ + m_db = nullptr; + m_content_db = nullptr; +} + +void EvolvingItemsManager::LoadEvolvingItems() const +{ + auto const &results = ItemsEvolvingDetailsRepository::All(*m_content_db); + + if (results.empty()) { + return; + } + + std::ranges::transform( + results.begin(), + results.end(), + std::inserter( + evolving_items_manager.GetEvolvingItemsCache(), + evolving_items_manager.GetEvolvingItemsCache().end() + ), + [](const ItemsEvolvingDetailsRepository::ItemsEvolvingDetails &x) { + return std::make_pair(x.item_id, x); + } + ); +} + +void EvolvingItemsManager::SetDatabase(Database *db) +{ + m_db = db; +} + +void EvolvingItemsManager::SetContentDatabase(Database *db) +{ + m_content_db = db; +} + +double EvolvingItemsManager::CalculateProgression(const uint64 current_amount, const uint32 item_id) +{ + if (!evolving_items_manager.GetEvolvingItemsCache().contains(item_id)) { + return 0; + } + + return evolving_items_manager.GetEvolvingItemsCache().at(item_id).required_amount > 0 + ? static_cast(current_amount) + / static_cast(evolving_items_manager.GetEvolvingItemsCache().at(item_id).required_amount) * 100 + : 0; +} + +void EvolvingItemsManager::DoLootChecks(const uint32 char_id, const uint16 slot_id, const EQ::ItemInstance &inst) const +{ + inst.SetEvolveEquipped(false); + if (inst.IsEvolving() && slot_id <= EQ::invslot::EQUIPMENT_END && slot_id >= EQ::invslot::EQUIPMENT_BEGIN) { + inst.SetEvolveEquipped(true); + } + + if (!inst.IsEvolving()) { + return; + } + + if (!inst.GetEvolveUniqueID()) { + auto e = CharacterEvolvingItemsRepository::NewEntity(); + + e.character_id = char_id; + e.item_id = inst.GetID(); + e.equipped = inst.GetEvolveEquipped(); + e.final_item_id = evolving_items_manager.GetFinalItemID(inst); + + auto r = CharacterEvolvingItemsRepository::InsertOne(*m_db, e); + e.id = r.id; + + inst.SetEvolveUniqueID(e.id); + inst.SetEvolveCharID(e.character_id); + inst.SetEvolveItemID(e.item_id); + inst.SetEvolveFinalItemID(e.final_item_id); + + return; + } + + CharacterEvolvingItemsRepository::SetEquipped(*m_db, inst.GetEvolveUniqueID(), inst.GetEvolveEquipped()); +} + +uint32 EvolvingItemsManager::GetFinalItemID(const EQ::ItemInstance &inst) const +{ + const auto start_iterator = std::ranges::find_if( + evolving_items_manager.GetEvolvingItemsCache().cbegin(), + evolving_items_manager.GetEvolvingItemsCache().cend(), + [&](const std::pair &a) { + return a.second.item_evo_id == inst.GetEvolveLoreID(); + } + ); + + if (start_iterator == std::end(evolving_items_manager.GetEvolvingItemsCache())) { + return 0; + } + + const auto final_id = std::ranges::max_element( + start_iterator, + evolving_items_manager.GetEvolvingItemsCache().cend(), + [&]( + const std::pair &a, + const std::pair &b + ) { + return a.second.item_evo_id == b.second.item_evo_id && + a.second.item_evolve_level < b.second.item_evolve_level; + } + ); + + return final_id->first; +} + +uint32 EvolvingItemsManager::GetNextEvolveItemID(const EQ::ItemInstance &inst) const +{ + int8 const current_level = inst.GetEvolveLvl(); + + const auto iterator = std::ranges::find_if( + evolving_items_manager.GetEvolvingItemsCache().cbegin(), + evolving_items_manager.GetEvolvingItemsCache().cend(), + [&](const std::pair &a) { + return a.second.item_evo_id == inst.GetEvolveLoreID() && + a.second.item_evolve_level == current_level + 1; + } + ); + + if (iterator == std::end(evolving_items_manager.GetEvolvingItemsCache())) { + return 0; + } + + return iterator->first; +} + +ItemsEvolvingDetailsRepository::ItemsEvolvingDetails EvolvingItemsManager::GetEvolveItemDetails(const uint64 unique_id) +{ + if (GetEvolvingItemsCache().contains(unique_id)) { + return GetEvolvingItemsCache().at(unique_id); + } + + return ItemsEvolvingDetailsRepository::NewEntity(); +} + +std::vector EvolvingItemsManager::GetEvolveIDItems( + const uint32 evolve_id +) +{ + std::vector e{}; + + for (auto const &[key, value]: GetEvolvingItemsCache()) { + if (value.item_evo_id == evolve_id) { + e.push_back(value); + } + } + + std::ranges::sort( + e.begin(), + e.end(), + [&]( + ItemsEvolvingDetailsRepository::ItemsEvolvingDetails const &a, + ItemsEvolvingDetailsRepository::ItemsEvolvingDetails const &b + ) { + return a.item_evolve_level < b.item_evolve_level; + } + ); + + return e; +} + +uint64 EvolvingItemsManager::GetTotalEarnedXP(const EQ::ItemInstance &inst) +{ + if (!inst) { + return 0; + } + + uint64 xp = inst.GetEvolveCurrentAmount(); + auto evolve_id_item_cache = GetEvolveIDItems(inst.GetEvolveLoreID()); + auto current_level = inst.GetEvolveLvl(); + + for (auto const &i: evolve_id_item_cache) { + if (i.item_evolve_level < current_level) { + xp += i.required_amount; + } + } + + return xp; +} + +EvolveGetNextItem EvolvingItemsManager::GetNextItemByXP(const EQ::ItemInstance &inst_in, const int64 in_xp) +{ + EvolveGetNextItem ets{}; + const auto evolve_items = GetEvolveIDItems(inst_in.GetEvolveLoreID()); + uint32 max_transfer_level = 0; + int64 xp = in_xp; + + for (auto const &e: evolve_items) { + if (e.item_evolve_level < inst_in.GetEvolveLvl()) { + continue; + } + + int64 have = 0; + if (e.item_evolve_level == inst_in.GetEvolveLvl()) { + have = inst_in.GetEvolveCurrentAmount(); + } + + const auto required = e.required_amount; + const int64 need = required - have; + const int64 balance = xp - need; + + if (balance <= 0) { + ets.new_current_amount = have + xp; + ets.new_item_id = e.item_id; + ets.from_current_amount = 0; + ets.max_transfer_level = max_transfer_level; + return ets; + } + + xp = balance; + max_transfer_level += 1; + + ets.new_current_amount = required; + ets.new_item_id = e.item_id; + ets.from_current_amount = balance - required; + ets.max_transfer_level = max_transfer_level; + } + + return ets; +} + +EvolveTransfer EvolvingItemsManager::DetermineTransferResults( + const EQ::ItemInstance &inst_from, + const EQ::ItemInstance &inst_to +) +{ + EvolveTransfer ets{}; + + auto evolving_details_inst_from = evolving_items_manager.GetEvolveItemDetails(inst_from.GetID()); + auto evolving_details_inst_to = evolving_items_manager.GetEvolveItemDetails(inst_to.GetID()); + + if (!evolving_details_inst_from.id || !evolving_details_inst_to.id) { + return ets; + } + + if (evolving_details_inst_from.type == evolving_details_inst_to.type) { + uint32 compatibility = 0; + uint64 xp = 0; + if (evolving_details_inst_from.sub_type == evolving_details_inst_to.sub_type) { + compatibility = 100; + } + else { + compatibility = 30; + } + + xp = evolving_items_manager.GetTotalEarnedXP(inst_from) * compatibility / 100; + auto results = evolving_items_manager.GetNextItemByXP(inst_to, xp); + + ets.item_from_id = evolving_items_manager.GetFirstItemInLoreGroup(inst_from.GetEvolveLoreID()); + ets.item_from_current_amount = results.from_current_amount; + ets.item_to_id = results.new_item_id; + ets.item_to_current_amount = results.new_current_amount; + ets.compatibility = compatibility; + ets.max_transfer_level = results.max_transfer_level; + } + + return ets; +} + +uint32 EvolvingItemsManager::GetFirstItemInLoreGroup(const uint32 lore_id) +{ + for (auto const &[key, value]: GetEvolvingItemsCache()) { + if (value.item_evo_id == lore_id && value.item_evolve_level == 1) { + return key; + } + } + + return 0; +} + +uint32 EvolvingItemsManager::GetFirstItemInLoreGroupByItemID(const uint32 item_id) +{ + for (auto const &[key, value]: GetEvolvingItemsCache()) { + if (value.item_id == item_id) { + for (auto const &[key2, value2]: GetEvolvingItemsCache()) { + if (value2.item_evo_id == value.item_evo_id && value2.item_evolve_level == 1) { + return key; + } + } + } + } + + return 0; +} + +void EvolvingItemsManager::LoadPlayerEvent(const EQ::ItemInstance &inst, PlayerEvent::EvolveItem &e) +{ + e.item_id = inst.GetID(); + e.item_name = inst.GetItem() ? inst.GetItem()->Name : std::string(); + e.level = inst.GetEvolveLvl(); + e.progression = inst.GetEvolveProgression(); + e.unique_id = inst.GetEvolveUniqueID(); +} diff --git a/common/evolving_items.h b/common/evolving_items.h new file mode 100644 index 000000000..463b58493 --- /dev/null +++ b/common/evolving_items.h @@ -0,0 +1,67 @@ +#ifndef EVOLVING_H +#define EVOLVING_H + +#include "shareddb.h" +#include "events/player_events.h" +#include "repositories/items_evolving_details_repository.h" + +namespace EQ { + class ItemInstance; +} + +namespace EvolvingItems { + namespace Actions { + constexpr int8 UPDATE_ITEMS = 0; + constexpr int8 TRANSFER_WINDOW_OPEN = 1; + constexpr int8 TRANSFER_WINDOW_DETAILS = 2; + constexpr int8 TRANSFER_XP = 3; + constexpr int8 FINAL_RESULT = 4; + } + + namespace Types { + constexpr int8 AMOUNT_OF_EXP = 1; + constexpr int8 NUMBER_OF_KILLS = 2; + constexpr int8 SPECIFIC_MOB_RACE = 3; + constexpr int8 SPECIFIC_ZONE_ID = 4; + } + + namespace SubTypes { + constexpr int8 ALL_EXP = 0; + constexpr int8 SOLO_EXP = 1; + constexpr int8 GROUP_EXP = 2; + constexpr int8 RAID_EXP = 3; + } +} + +class EvolvingItemsManager +{ +public: + EvolvingItemsManager(); + void SetDatabase(Database *db); + void SetContentDatabase(Database *db); + + void LoadEvolvingItems() const; + void DoLootChecks(uint32 char_id, uint16 slot_id, const EQ::ItemInstance &inst) const; + uint32 GetFinalItemID(const EQ::ItemInstance &inst) const; + uint32 GetNextEvolveItemID(const EQ::ItemInstance &inst) const; + uint32 GetFirstItemInLoreGroup(uint32 lore_id); + uint32 GetFirstItemInLoreGroupByItemID(uint32 item_id); + uint64 GetTotalEarnedXP(const EQ::ItemInstance &inst); + static double CalculateProgression(uint64 current_amount, uint32 item_id); + static void LoadPlayerEvent(const EQ::ItemInstance &inst, PlayerEvent::EvolveItem &e); + + ItemsEvolvingDetailsRepository::ItemsEvolvingDetails GetEvolveItemDetails(uint64 id); + EvolveTransfer DetermineTransferResults(const EQ::ItemInstance& inst_from, const EQ::ItemInstance& inst_to); + EvolveGetNextItem GetNextItemByXP(const EQ::ItemInstance &inst_in, int64 in_xp); + std::map& GetEvolvingItemsCache() { return evolving_items_cache; } + std::vector GetEvolveIDItems(uint32 evolve_id); + +private: + std::map evolving_items_cache; + Database * m_db; + Database * m_content_db; +}; + +extern EvolvingItemsManager evolving_items_manager; + +#endif //EVOLVING_H diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp index 595b9f025..d83c2ab16 100644 --- a/common/inventory_profile.cpp +++ b/common/inventory_profile.cpp @@ -344,6 +344,8 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapNotAllowed; return false; } + + source_item_instance->SetEvolveEquipped(false); if ((destination_slot >= invslot::EQUIPMENT_BEGIN && destination_slot <= invslot::EQUIPMENT_END)) { auto source_item = source_item_instance->GetItem(); if (!source_item) { @@ -362,6 +364,9 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapLevel; return false; } + if (source_item_instance->IsEvolving() > 0) { + source_item_instance->SetEvolveEquipped(true); + } } } @@ -370,6 +375,8 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapNotAllowed; return false; } + + destination_item_instance->SetEvolveEquipped(false); if ((source_slot >= invslot::EQUIPMENT_BEGIN && source_slot <= invslot::EQUIPMENT_END)) { auto destination_item = destination_item_instance->GetItem(); if (!destination_item) { @@ -388,6 +395,9 @@ bool EQ::InventoryProfile::SwapItem( fail_state = swapLevel; return false; } + if (destination_item_instance->IsEvolving()) { + destination_item_instance->SetEvolveEquipped(true); + } } } @@ -1402,6 +1412,8 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) int16 result = INVALID_INDEX; int16 parentSlot = INVALID_INDEX; + inst->SetEvolveEquipped(false); + if (slot_id == invslot::slotCursor) { // Replace current item on cursor, if exists m_cursor.pop(); // no memory delete, clients of this function know what they are doing @@ -1410,6 +1422,9 @@ int16 EQ::InventoryProfile::_PutItem(int16 slot_id, ItemInstance* inst) } else if (slot_id >= invslot::EQUIPMENT_BEGIN && slot_id <= invslot::EQUIPMENT_END) { if ((((uint64)1 << slot_id) & m_lookup->PossessionsBitmask) != 0) { + if (inst->IsEvolving()) { + inst->SetEvolveEquipped(true); + } m_worn[slot_id] = inst; result = slot_id; } @@ -1808,3 +1823,182 @@ int16 EQ::InventoryProfile::FindFirstFreeSlotThatFitsItem(const EQ::ItemData *it } return 0; } + +//This function has the same flaw as noted above +// Helper functions for evolving items +int16 EQ::InventoryProfile::HasEvolvingItem(uint64 evolve_unique_id, uint8 quantity, uint8 where) +{ + int16 slot_id = INVALID_INDEX; + + // Altered by Father Nitwit to support a specification of + // where to search, with a default value to maintain compatibility + + // Check each inventory bucket + if (where & invWhereWorn) { + slot_id = _HasEvolvingItem(m_worn, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + if (where & invWherePersonal) { + slot_id = _HasEvolvingItem(m_inv, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + if (where & invWhereBank) { + slot_id = _HasEvolvingItem(m_bank, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + if (where & invWhereSharedBank) { + slot_id = _HasEvolvingItem(m_shbank, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + if (where & invWhereTrading) { + slot_id = _HasEvolvingItem(m_trade, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + // Behavioral change - Limbo is no longer checked due to improper handling of return value + if (where & invWhereCursor) { + // Check cursor queue + slot_id = _HasEvolvingItem(m_cursor, evolve_unique_id, quantity); + if (slot_id != INVALID_INDEX) { + return slot_id; + } + } + + return slot_id; +} + +// Internal Method: Checks an inventory bucket for a particular evolving item unique id +int16 EQ::InventoryProfile::_HasEvolvingItem( + std::map &bucket, uint64 evolve_unique_id, uint8 quantity) +{ + uint32 quantity_found = 0; + + for (auto const &[key, value]: bucket) { + if (!value) { + continue; + } + + if (key <= EQ::invslot::POSSESSIONS_END && key >= EQ::invslot::POSSESSIONS_BEGIN) { + if (((uint64) 1 << key & m_lookup->PossessionsBitmask) == 0) { + continue; + } + } + else if (key <= EQ::invslot::BANK_END && key >= EQ::invslot::BANK_BEGIN) { + if (key - EQ::invslot::BANK_BEGIN >= m_lookup->InventoryTypeSize.Bank) { + continue; + } + } + + + if (value->GetEvolveUniqueID() == evolve_unique_id) { + quantity_found += value->GetCharges() <= 0 ? 1 : value->GetCharges(); + if (quantity_found >= quantity) { + return key; + } + } + + for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { + if (value->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) { + return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } + } + + if (!value->IsClassBag()) { + continue; + } + + for (auto const &[bag_key, bag_value]: *value->GetContents()) { + if (!bag_value) { + continue; + } + + if (bag_value->GetEvolveUniqueID() == evolve_unique_id) { + quantity_found += bag_value->GetCharges() <= 0 ? 1 : bag_value->GetCharges(); + if (quantity_found >= quantity) { + return CalcSlotId(key, bag_key); + } + } + + for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { + if (bag_value->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) { + return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } + } + } + } + + return INVALID_INDEX; +} + +// Internal Method: Checks an inventory queue type bucket for a particular item +int16 EQ::InventoryProfile::_HasEvolvingItem(ItemInstQueue &iqueue, uint64 evolve_unique_id, uint8 quantity) +{ + // The downfall of this (these) queue procedure is that callers presume that when an item is + // found, it is presented as being available on the cursor. In cases of a parity check, this + // is sufficient. However, in cases where referential criteria is considered, this can lead + // to unintended results. Funtionality should be observed when referencing the return value + // of this query + + uint32 quantity_found = 0; + + for (auto const &inst: iqueue) { + if (!inst) { + continue; + } + + if (inst->GetEvolveUniqueID() == evolve_unique_id) { + quantity_found += inst->GetCharges() <= 0 ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) { + return invslot::slotCursor; + } + } + + for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { + if (inst->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) { + return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } + } + + if (!inst->IsClassBag()) { + continue; + } + + for (auto const &[bag_key, bag_value]: *inst->GetContents()) { + if (!bag_value) { + continue; + } + + if (bag_value->GetEvolveUniqueID() == evolve_unique_id) { + quantity_found += bag_value->GetCharges() <= 0 ? 1 : bag_value->GetCharges(); + if (quantity_found >= quantity) { + return CalcSlotId(invslot::slotCursor, bag_key); + } + } + + for (int index = invaug::SOCKET_BEGIN; index <= invaug::SOCKET_END; ++index) { + if (bag_value->GetAugmentEvolveUniqueID(index) == evolve_unique_id && quantity <= 1) { + return invslot::SLOT_AUGMENT_GENERIC_RETURN; + } + } + } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; + } + + return INVALID_INDEX; +} diff --git a/common/inventory_profile.h b/common/inventory_profile.h index 4761e1b97..b1d1e4850 100644 --- a/common/inventory_profile.h +++ b/common/inventory_profile.h @@ -57,6 +57,8 @@ public: inline std::list::const_iterator cbegin() { return m_list.cbegin(); } inline std::list::const_iterator cend() { return m_list.cend(); } + inline std::list::iterator begin() { return m_list.begin(); } + inline std::list::iterator end() { return m_list.end(); } inline int size() { return static_cast(m_list.size()); } // TODO: change to size_t inline bool empty() { return m_list.empty(); } @@ -211,6 +213,11 @@ namespace EQ void SetCustomItemData(uint32 character_id, int16 slot_id, const std::string &identifier, bool value); std::string GetCustomItemData(int16 slot_id, const std::string& identifier); static const int GetItemStatValue(uint32 item_id, const std::string& identifier); + + std::map& GetWorn() { return m_worn; } + std::map& GetPersonal() { return m_inv; } + int16 HasEvolvingItem(uint64 evolve_unique_id, uint8 quantity, uint8 where); + protected: /////////////////////////////// // Protected Methods @@ -233,6 +240,8 @@ namespace EQ int16 _HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity); int16 _HasItemByLoreGroup(std::map& bucket, uint32 loregroup); int16 _HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup); + int16 _HasEvolvingItem(std::map& bucket, uint64 evolve_unique_id, uint8 quantity); + int16 _HasEvolvingItem(ItemInstQueue& iqueue, uint64 evolve_unique_id, uint8 quantity); // Player inventory diff --git a/common/item_instance.cpp b/common/item_instance.cpp index 6aeb32223..1e1e550b5 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -25,6 +25,7 @@ #include "rulesys.h" #include "shareddb.h" #include "strings.h" +#include "evolving_items.h" //#include "../common/light_source.h" @@ -76,6 +77,10 @@ EQ::ItemInstance::ItemInstance(const ItemData* item, int16 charges) { m_color = m_item->Color; } + if (IsEvolving()) { + SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping)); + } + m_SerialNumber = GetNextItemInstSerialNumber(); } @@ -95,6 +100,10 @@ EQ::ItemInstance::ItemInstance(SharedDatabase *db, uint32 item_id, int16 charges m_color = 0; } + if (IsEvolving()) { + SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping)); + } + m_SerialNumber = GetNextItemInstSerialNumber(); } @@ -146,7 +155,6 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy) m_exp = copy.m_exp; m_evolveLvl = copy.m_evolveLvl; - m_activated = copy.m_activated; if (copy.m_scaledItem) { m_scaledItem = new ItemData(*copy.m_scaledItem); @@ -154,12 +162,7 @@ EQ::ItemInstance::ItemInstance(const ItemInstance& copy) m_scaledItem = nullptr; } - if (copy.m_evolveInfo) { - m_evolveInfo = new EvolveInfo(*copy.m_evolveInfo); - } else { - m_evolveInfo = nullptr; - } - + m_evolving_details = copy.m_evolving_details; m_scaling = copy.m_scaling; m_ornamenticon = copy.m_ornamenticon; m_ornamentidfile = copy.m_ornamentidfile; @@ -174,7 +177,6 @@ EQ::ItemInstance::~ItemInstance() Clear(); safe_delete(m_item); safe_delete(m_scaledItem); - safe_delete(m_evolveInfo); } // Query item type @@ -1030,29 +1032,6 @@ void EQ::ItemInstance::ScaleItem() { m_scaledItem->CharmFileID = 0; // this stops the client from trying to scale the item itself. } -bool EQ::ItemInstance::EvolveOnAllKills() const { - return (m_evolveInfo && m_evolveInfo->AllKills); -} - -int8 EQ::ItemInstance::GetMaxEvolveLvl() const { - if (m_evolveInfo) - return m_evolveInfo->MaxLvl; - else - return 0; -} - -uint32 EQ::ItemInstance::GetKillsNeeded(uint8 currentlevel) { - uint32 kills = -1; // default to -1 (max uint32 value) because this value is usually divided by, so we don't want to ever return zero. - if (m_evolveInfo) - if (currentlevel != m_evolveInfo->MaxLvl) - kills = m_evolveInfo->LvlKills[currentlevel - 1]; - - if (kills == 0) - kills = -1; - - return kills; -} - void EQ::ItemInstance::SetTimer(std::string name, uint32 time) { Timer t(time); t.Start(time, false); @@ -1951,28 +1930,54 @@ void EQ::ItemInstance::ClearGUIDMap() { guids.clear(); } -// -// class EvolveInfo -// -EvolveInfo::EvolveInfo() { - // nothing here yet + +bool EQ::ItemInstance::TransferOwnership(Database &db, const uint32 to_char_id) const +{ + if (!to_char_id || !IsEvolving()) { + return false; + } + + SetEvolveCharID(to_char_id); + CharacterEvolvingItemsRepository::UpdateCharID(db, GetEvolveUniqueID(), to_char_id); + return true; } -EvolveInfo::EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10) { - FirstItem = first; - MaxLvl = max; - AllKills = allkills; - LvlKills[0] = L2; - LvlKills[1] = L3; - LvlKills[2] = L4; - LvlKills[3] = L5; - LvlKills[4] = L6; - LvlKills[5] = L7; - LvlKills[6] = L8; - LvlKills[7] = L9; - LvlKills[8] = L10; +uint32 EQ::ItemInstance::GetAugmentEvolveUniqueID(uint8 augment_index) const +{ + if (!m_item || !m_item->IsClassCommon()) { + return 0; + } + + const auto item = GetItem(augment_index); + if (item) { + return item->GetEvolveUniqueID(); + } + + return 0; } -EvolveInfo::~EvolveInfo() { - +void EQ::ItemInstance::SetTimer(std::string name, uint32 time) const{ + Timer t(time); + t.Start(time, false); + m_timers[name] = t; +} + +void EQ::ItemInstance::SetEvolveEquipped(const bool in) const +{ + if (!IsEvolving()) { + return; + } + + m_evolving_details.equipped = in; + if (in && !GetTimers().contains("evolve")) { + SetTimer("evolve", RuleI(EvolvingItems, DelayUponEquipping)); + return; + } + + if (in) { + GetTimers().at("evolve").SetTimer(RuleI(EvolvingItems, DelayUponEquipping)); + return; + } + + GetTimers().at("evolve").Disable(); } diff --git a/common/item_instance.h b/common/item_instance.h index 928a6aabc..4005a70c2 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -23,6 +23,7 @@ #ifndef COMMON_ITEM_INSTANCE_H #define COMMON_ITEM_INSTANCE_H +#include "evolving_items.h" class ItemParse; // Parses item packets @@ -34,6 +35,7 @@ class EvolveInfo; // Stores information about an evolving item family #include "../common/bodytypes.h" #include "../common/deity.h" #include "../common/memory_buffer.h" +#include "../common/repositories/character_evolving_items_repository.h" #include @@ -205,13 +207,9 @@ namespace EQ bool IsDroppable(bool recurse = true) const; bool IsScaling() const { return m_scaling; } - bool IsEvolving() const { return (m_evolveLvl >= 1); } uint32 GetExp() const { return m_exp; } void SetExp(uint32 exp) { m_exp = exp; } void AddExp(uint32 exp) { m_exp += exp; } - bool IsActivated() { return m_activated; } - void SetActivated(bool activated) { m_activated = activated; } - int8 GetEvolveLvl() const { return m_evolveLvl; } void SetScaling(bool v) { m_scaling = v; } uint32 GetOrnamentationIcon() const { return m_ornamenticon; } void SetOrnamentIcon(uint32 ornament_icon) { m_ornamenticon = ornament_icon; } @@ -226,9 +224,6 @@ namespace EQ void Initialize(SharedDatabase *db = nullptr); void ScaleItem(); - bool EvolveOnAllKills() const; - int8 GetMaxEvolveLvl() const; - uint32 GetKillsNeeded(uint8 currentlevel); std::string Serialize(int16 slot_id) const { InternalSerializedItem_Struct s; s.slot_id = slot_id; s.inst = (const void*)this; std::string ser; ser.assign((char*)&s, sizeof(InternalSerializedItem_Struct)); return ser; } void Serialize(OutBuffer& ob, int16 slot_id) const { InternalSerializedItem_Struct isi; isi.slot_id = slot_id; isi.inst = (const void*)this; ob.write((const char*)&isi, sizeof(isi)); } @@ -236,8 +231,9 @@ namespace EQ inline int32 GetSerialNumber() const { return m_SerialNumber; } inline void SetSerialNumber(int32 id) { m_SerialNumber = id; } - std::map& GetTimers() { return m_timers; } + std::map& GetTimers() const { return m_timers; } void SetTimer(std::string name, uint32 time); + void SetTimer(std::string name, uint32 time) const; void StopTimer(std::string name); void ClearTimers(); @@ -312,6 +308,34 @@ namespace EQ static void AddGUIDToMap(uint64 existing_serial_number); static void ClearGUIDMap(); + // evolving items stuff + CharacterEvolvingItemsRepository::CharacterEvolvingItems &GetEvolvingDetails() const { return m_evolving_details; } + + int8 GetEvolveLvl() const { if (GetItem()) { return GetItem()->EvolvingLevel; } return false; } + bool IsEvolving() const { if (GetItem()) { return GetItem()->EvolvingItem; } return false; } + int8 GetMaxEvolveLvl() const { if (GetItem()) { return GetItem()->EvolvingMax; } return false; } + bool GetEvolveActivated() const { return m_evolving_details.activated ? true : false; } + bool GetEvolveEquipped() const { return m_evolving_details.equipped ? true : false; } + double GetEvolveProgression() const { return m_evolving_details.progression; } + uint64 GetEvolveUniqueID() const { return m_evolving_details.id; } + uint32 GetEvolveCharID() const { return m_evolving_details.character_id; } + uint32 GetEvolveItemID() const { return m_evolving_details.item_id; } + uint32 GetEvolveLoreID() const { if (GetItem()) { return GetItem()->EvolvingID; } return false; } + uint64 GetEvolveCurrentAmount() const { return m_evolving_details.current_amount; } + uint32 GetEvolveFinalItemID() const { return m_evolving_details.final_item_id; } + uint32 GetAugmentEvolveUniqueID(uint8 augment_index) const; + void SetEvolveEquipped(const bool in) const; + void SetEvolveActivated(const bool in) const { m_evolving_details.activated = in; } + void SetEvolveProgression(const double in) const { m_evolving_details.progression = in; } + void SetEvolveUniqueID(const uint64 in) const { m_evolving_details.id = in; } + void SetEvolveCharID(const uint32 in) const { m_evolving_details.character_id = in; } + void SetEvolveItemID(const uint32 in) const { m_evolving_details.item_id = in; } + void SetEvolveCurrentAmount(const uint64 in) const { m_evolving_details.current_amount = in; } + void SetEvolveAddToCurrentAmount(const uint64 in) const { m_evolving_details.current_amount += in; } + void SetEvolveFinalItemID(const uint32 in) const { m_evolving_details.final_item_id = in; } + bool TransferOwnership(Database& db, const uint32 to_char_id) const; + void CalculateEvolveProgression() const { m_evolving_details.progression = evolving_items_manager.CalculateProgression(GetEvolveCurrentAmount(), GetID()); } + protected: ////////////////////////// // Protected Members @@ -323,48 +347,32 @@ namespace EQ void _PutItem(uint8 index, ItemInstance* inst) { m_contents[index] = inst; } - ItemInstTypes m_use_type {ItemInstNormal}; // Usage type for item - const ItemData* m_item {nullptr}; // Ptr to item data - int16 m_charges {0}; // # of charges for chargeable items - uint32 m_price {0}; // Bazaar /trader price - uint32 m_color {0}; - uint32 m_merchantslot {0}; - int16 m_currentslot {0}; - bool m_attuned {false}; - int32 m_merchantcount {1}; //number avaliable on the merchant, -1=unlimited - int32 m_SerialNumber {0}; // Unique identifier for this instance of an item. Needed for Bazaar. - uint32 m_exp {0}; - int8 m_evolveLvl {0}; - bool m_activated {false}; - ItemData* m_scaledItem {nullptr}; - ::EvolveInfo* m_evolveInfo {nullptr}; - bool m_scaling {false}; - uint32 m_ornamenticon {0}; - uint32 m_ornamentidfile {0}; - uint32 m_new_id_file {0}; - uint32 m_ornament_hero_model {0}; - uint32 m_recast_timestamp {0}; - int m_task_delivered_count {0}; + ItemInstTypes m_use_type{ItemInstNormal};// Usage type for item + const ItemData * m_item{nullptr}; // Ptr to item data + int16 m_charges{0}; // # of charges for chargeable items + uint32 m_price{0}; // Bazaar /trader price + uint32 m_color{0}; + uint32 m_merchantslot{0}; + int16 m_currentslot{0}; + bool m_attuned{false}; + int32 m_merchantcount{1};//number avaliable on the merchant, -1=unlimited + int32 m_SerialNumber{0}; // Unique identifier for this instance of an item. Needed for Bazaar. + uint32 m_exp{0}; + int8 m_evolveLvl{0}; + ItemData * m_scaledItem{nullptr}; + bool m_scaling{false}; + uint32 m_ornamenticon{0}; + uint32 m_ornamentidfile{0}; + uint32 m_new_id_file{0}; + uint32 m_ornament_hero_model{0}; + uint32 m_recast_timestamp{0}; + int m_task_delivered_count{0}; + mutable CharacterEvolvingItemsRepository::CharacterEvolvingItems m_evolving_details{}; // Items inside of this item (augs or contents) {}; - std::map m_contents {}; // Zero-based index: min=0, max=9 - std::map m_custom_data {}; - std::map m_timers {}; + std::map m_contents {}; // Zero-based index: min=0, max=9 + std::map m_custom_data {}; + mutable std::map m_timers {}; }; } - -class EvolveInfo { -public: - friend class EQ::ItemInstance; - //temporary - uint16 LvlKills[9]; - uint32 FirstItem; - uint8 MaxLvl; - bool AllKills; - - EvolveInfo(); - EvolveInfo(uint32 first, uint8 max, bool allkills, uint32 L2, uint32 L3, uint32 L4, uint32 L5, uint32 L6, uint32 L7, uint32 L8, uint32 L9, uint32 L10); - ~EvolveInfo(); -}; - #endif /*COMMON_ITEM_INSTANCE_H*/ diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 7d16d95e4..9a6facac7 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -5188,7 +5188,14 @@ namespace RoF //sprintf(hdr.unknown000, "06e0002Y1W00"); - snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%016d", item->ID); + strn0cpy( + hdr.unknown000, + fmt::format( + "{:016}\0", + packet_type == ItemPacketInvalid ? 0 : inst->GetSerialNumber() + ).c_str(), + sizeof(hdr.unknown000) + ); hdr.stacksize = (inst->IsStackable() ? ((inst->GetCharges() > 1000) ? 0xFFFFFFFF : inst->GetCharges()) : 1); hdr.unknown004 = 0; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 783a9d77f..3e05972e6 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -1350,6 +1350,58 @@ namespace RoF2 dest->FastQueuePacket(&in, ack_req); } + ENCODE(OP_EvolveItem) + { + EQApplicationPacket *in = *p; + *p = nullptr; + + auto action = *reinterpret_cast(in->pBuffer); + + switch (action) { + case EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS: { + auto emu = reinterpret_cast(in->pBuffer); + + EvolveXPWindowSend e{}; + EQ::Util::MemoryStreamReader ss(emu->serialized_data, in->size - sizeof(emu->action)); + cereal::BinaryInputArchive ar(ss); + ar(e); + + auto item_1 = static_cast(reinterpret_cast(e.serialize_item_1.data())->inst); + auto item_2 = static_cast(reinterpret_cast(e.serialize_item_2.data())->inst); + + EQ::OutBuffer ob; + + SerializeItem(ob, item_1, 0, 0, ItemPacketMerchant); + SerializeItem(ob, item_2, 0, 0, ItemPacketMerchant); + + auto out = std::make_unique( + OP_EvolveItem, + sizeof(EvolveXPWindowSendDetails_Struct) + ob.size() + ); + auto data = reinterpret_cast(out->pBuffer); + + data->action = e.action; + data->compatibility = e.compatibility; + data->max_transfer_level = e.max_transfer_level; + data->item1_unique_id = e.item1_unique_id; + data->item2_unique_id = e.item2_unique_id; + data->item1_present = e.item1_present; + data->item2_present = e.item2_present; + + memcpy(data->serialize_data, ob.str().data(), ob.size()); + dest->QueuePacket(out.get()); + safe_delete(in); + break; + } + default: { + dest->FastQueuePacket(&in); + break; + } + } + } + ENCODE(OP_ExpansionInfo) { ENCODE_LENGTH_EXACT(ExpansionInfo_Struct); @@ -2053,6 +2105,33 @@ namespace RoF2 } } + ENCODE(OP_ItemPreviewRequest) + { + EQApplicationPacket* in = *p; + *p = nullptr; + + uchar* in_buf = in->pBuffer; + + auto int_item = (EQ::InternalSerializedItem_Struct*) in_buf; + + EQ::OutBuffer buf; + EQ::OutBuffer::pos_type last_pos = buf.tellp(); + + SerializeItem(buf, (const EQ::ItemInstance*) int_item->inst, int_item->slot_id, 0, ItemPacketInvalid); + if (buf.tellp() == last_pos) { + LogNetcode("RoF2::ENCODE(OP_ItemPreviewRequest) Serialization failed"); + safe_delete_array(in_buf); + safe_delete(in); + return; + } + + in->size = buf.size(); + in->pBuffer = buf.detach(); + + safe_delete_array(in_buf); + dest->FastQueuePacket(&in, ack_req); + } + ENCODE(OP_ItemVerifyReply) { ENCODE_LENGTH_EXACT(ItemVerifyReply_Struct); @@ -6405,6 +6484,11 @@ namespace RoF2 hdr.scaled_value = (inst->IsScaling() ? (inst->GetExp() / 100) : 0); hdr.instance_id = (inst->GetMerchantSlot() ? inst->GetMerchantSlot() : inst->GetSerialNumber()); hdr.parcel_item_id = packet_type == ItemPacketParcel ? inst->GetID() : 0; + if (item->EvolvingItem) { + hdr.instance_id = inst->GetEvolveUniqueID() & 0xFFFFFFFF; //lower dword + hdr.parcel_item_id = inst->GetEvolveUniqueID() >> 32; //upper dword + } + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (inst->IsStackable() ? (item->MaxCharges ? 1 : 0) : ((inst->GetCharges() > 254) ? 0xFFFFFFFF @@ -6418,18 +6502,15 @@ namespace RoF2 ob.write((const char*)&hdr, sizeof(RoF2::structs::ItemSerializationHeader)); if (item->EvolvingItem > 0) { - RoF2::structs::EvolvingItem evotop; + RoF2::structs::EvolvingItem_Struct evotop; - evotop.unknown001 = 0; - evotop.unknown002 = 0; - evotop.unknown003 = 0; - evotop.unknown004 = 0; - evotop.evoLevel = item->EvolvingLevel; - evotop.progress = 0; - evotop.Activated = 1; - evotop.evomaxlevel = item->EvolvingMax; + evotop.final_item_id = inst->GetEvolveFinalItemID(); + evotop.evolve_level = item->EvolvingLevel; + evotop.progress = inst->GetEvolveProgression(); + evotop.activated = inst->GetEvolveActivated(); + evotop.evolve_max_level = item->EvolvingMax; - ob.write((const char*)&evotop, sizeof(RoF2::structs::EvolvingItem)); + ob.write((const char*)&evotop, sizeof(RoF2::structs::EvolvingItem_Struct)); } /** @@ -6803,12 +6884,13 @@ namespace RoF2 iqbs.Heirloom = 0; iqbs.Placeable = 0; iqbs.unknown28 = -1; + iqbs.unknown29 = packet_type == ItemPacketInvalid ? 0xFF : 0; iqbs.unknown30 = -1; iqbs.NoZone = 0; iqbs.NoGround = 0; iqbs.unknown37a = 0; // (guessed position) New to RoF2 iqbs.unknown38 = 0; - iqbs.unknown39 = 1; + iqbs.unknown39 = packet_type == ItemPacketInvalid ? 0 : 1;; ob.write((const char*)&iqbs, sizeof(RoF2::structs::ItemQuaternaryBodyStruct)); diff --git a/common/patches/rof2_ops.h b/common/patches/rof2_ops.h index 08d8b9985..754635e41 100644 --- a/common/patches/rof2_ops.h +++ b/common/patches/rof2_ops.h @@ -1,5 +1,5 @@ /* EQEMu: Everquest Server Emulator - + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) This program is free software; you can redistribute it and/or modify @@ -70,6 +70,7 @@ E(OP_DzMemberListName) E(OP_DzMemberListStatus) E(OP_DzSetLeaderName) E(OP_Emote) +E(OP_EvolveItem) E(OP_ExpansionInfo) E(OP_FormattedMessage) E(OP_GMLastName) @@ -91,6 +92,7 @@ E(OP_InspectBuffs) E(OP_InspectRequest) E(OP_ItemLinkResponse) E(OP_ItemPacket) +E(OP_ItemPreviewRequest) E(OP_ItemVerifyReply) E(OP_LeadershipExpUpdate) E(OP_LogServer) diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index 1bbf7c26c..4a8d277eb 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -4746,16 +4746,13 @@ struct ItemSerializationHeader uint8 isEvolving; }; -struct EvolvingItem { - uint8 unknown001; - uint8 unknown002; - uint8 unknown003; - uint8 unknown004; - int32 evoLevel; +struct EvolvingItem_Struct { + uint32 final_item_id; + int32 evolve_level; double progress; - uint8 Activated; - int32 evomaxlevel; - uint8 unknown005[4]; + uint8 activated; + int32 evolve_max_level; + uint8 unknown005[4]; }; struct ItemSerializationHeaderFinish @@ -5428,6 +5425,32 @@ struct Parcel_Struct }; }; /*structs*/ +struct EvolveItemToggle_Struct { + uint32 action; + uint32 unknown_004; + uint64 unique_id; + uint32 percentage; + uint32 activated; +}; + +struct EvolveXPWindowReceive_Struct { + uint32 action; + uint32 unknown_004; + uint64 item1_unique_id; + uint64 item2_unique_id; +}; + +struct EvolveXPWindowSendDetails_Struct { + /*000*/ uint32 action; + /*004*/ uint64 item1_unique_id; + /*012*/ uint64 item2_unique_id; + /*020*/ uint32 compatibility; + /*024*/ uint32 max_transfer_level; + /*028*/ uint8 item1_present; + /*029*/ uint8 item2_present; + /*030*/ char serialize_data[]; +}; + }; /*RoF2*/ #endif /*COMMON_ROF2_STRUCTS_H*/ diff --git a/common/repositories/base/base_character_evolving_items_repository.h b/common/repositories/base/base_character_evolving_items_repository.h new file mode 100644 index 000000000..9aa6c1773 --- /dev/null +++ b/common/repositories/base/base_character_evolving_items_repository.h @@ -0,0 +1,475 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://docs.eqemu.io/developer/repositories + */ + +#ifndef EQEMU_BASE_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H +#define EQEMU_BASE_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseCharacterEvolvingItemsRepository { +public: + struct CharacterEvolvingItems { + uint64_t id; + uint32_t character_id; + uint32_t item_id; + uint8_t activated; + uint8_t equipped; + int64_t current_amount; + double progression; + uint32_t final_item_id; + time_t deleted_at; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "character_id", + "item_id", + "activated", + "equipped", + "current_amount", + "progression", + "final_item_id", + "deleted_at", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "character_id", + "item_id", + "activated", + "equipped", + "current_amount", + "progression", + "final_item_id", + "UNIX_TIMESTAMP(deleted_at)", + }; + } + + 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_evolving_items"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static CharacterEvolvingItems NewEntity() + { + CharacterEvolvingItems e{}; + + e.id = 0; + e.character_id = 0; + e.item_id = 0; + e.activated = 0; + e.equipped = 0; + e.current_amount = 0; + e.progression = 0; + e.final_item_id = 0; + e.deleted_at = 0; + + return e; + } + + static CharacterEvolvingItems GetCharacterEvolvingItems( + const std::vector &character_evolving_itemss, + int character_evolving_items_id + ) + { + for (auto &character_evolving_items : character_evolving_itemss) { + if (character_evolving_items.id == character_evolving_items_id) { + return character_evolving_items; + } + } + + return NewEntity(); + } + + static CharacterEvolvingItems FindOne( + Database& db, + int character_evolving_items_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + character_evolving_items_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + CharacterEvolvingItems e{}; + + e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; + e.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.activated = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.equipped = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.current_amount = row[5] ? strtoll(row[5], nullptr, 10) : 0; + e.progression = row[6] ? strtod(row[6], nullptr) : 0; + e.final_item_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.deleted_at = strtoll(row[8] ? row[8] : "-1", nullptr, 10); + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int character_evolving_items_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + character_evolving_items_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const CharacterEvolvingItems &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[1] + " = " + std::to_string(e.character_id)); + v.push_back(columns[2] + " = " + std::to_string(e.item_id)); + v.push_back(columns[3] + " = " + std::to_string(e.activated)); + v.push_back(columns[4] + " = " + std::to_string(e.equipped)); + v.push_back(columns[5] + " = " + std::to_string(e.current_amount)); + v.push_back(columns[6] + " = " + std::to_string(e.progression)); + v.push_back(columns[7] + " = " + std::to_string(e.final_item_id)); + v.push_back(columns[8] + " = FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static CharacterEvolvingItems InsertOne( + Database& db, + CharacterEvolvingItems e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.activated)); + v.push_back(std::to_string(e.equipped)); + v.push_back(std::to_string(e.current_amount)); + v.push_back(std::to_string(e.progression)); + v.push_back(std::to_string(e.final_item_id)); + v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + 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 &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.activated)); + v.push_back(std::to_string(e.equipped)); + v.push_back(std::to_string(e.current_amount)); + v.push_back(std::to_string(e.progression)); + v.push_back(std::to_string(e.final_item_id)); + v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + CharacterEvolvingItems e{}; + + e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; + e.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.activated = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.equipped = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.current_amount = row[5] ? strtoll(row[5], nullptr, 10) : 0; + e.progression = row[6] ? strtod(row[6], nullptr) : 0; + e.final_item_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.deleted_at = strtoll(row[8] ? row[8] : "-1", nullptr, 10); + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector 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) { + CharacterEvolvingItems e{}; + + e.id = row[0] ? strtoull(row[0], nullptr, 10) : 0; + e.character_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.activated = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.equipped = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.current_amount = row[5] ? strtoll(row[5], nullptr, 10) : 0; + e.progression = row[6] ? strtod(row[6], nullptr) : 0; + e.final_item_id = row[7] ? static_cast(strtoul(row[7], nullptr, 10)) : 0; + e.deleted_at = strtoll(row[8] ? row[8] : "-1", nullptr, 10); + + 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 CharacterEvolvingItems &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.activated)); + v.push_back(std::to_string(e.equipped)); + v.push_back(std::to_string(e.current_amount)); + v.push_back(std::to_string(e.progression)); + v.push_back(std::to_string(e.final_item_id)); + v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseReplace(), + Strings::Implode(",", v) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int ReplaceMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.character_id)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.activated)); + v.push_back(std::to_string(e.equipped)); + v.push_back(std::to_string(e.current_amount)); + v.push_back(std::to_string(e.progression)); + v.push_back(std::to_string(e.final_item_id)); + v.push_back("FROM_UNIXTIME(" + (e.deleted_at > 0 ? std::to_string(e.deleted_at) : "null") + ")"); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_BASE_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H diff --git a/common/repositories/base/base_items_evolving_details_repository.h b/common/repositories/base/base_items_evolving_details_repository.h new file mode 100644 index 000000000..0f932bfb8 --- /dev/null +++ b/common/repositories/base/base_items_evolving_details_repository.h @@ -0,0 +1,451 @@ +/** + * 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_ITEMS_EVOLVING_DETAILS_REPOSITORY_H +#define EQEMU_BASE_ITEMS_EVOLVING_DETAILS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseItemsEvolvingDetailsRepository { +public: + struct ItemsEvolvingDetails { + uint32_t id; + uint32_t item_evo_id; + uint32_t item_evolve_level; + uint32_t item_id; + uint32_t type; + uint32_t sub_type; + int64_t required_amount; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "item_evo_id", + "item_evolve_level", + "item_id", + "type", + "sub_type", + "required_amount", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "item_evo_id", + "item_evolve_level", + "item_id", + "type", + "sub_type", + "required_amount", + }; + } + + 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("items_evolving_details"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static ItemsEvolvingDetails NewEntity() + { + ItemsEvolvingDetails e{}; + + e.id = 0; + e.item_evo_id = 0; + e.item_evolve_level = 0; + e.item_id = 0; + e.type = 0; + e.sub_type = 0; + e.required_amount = 0; + + return e; + } + + static ItemsEvolvingDetails GetItemsEvolvingDetails( + const std::vector &items_evolving_detailss, + int items_evolving_details_id + ) + { + for (auto &items_evolving_details : items_evolving_detailss) { + if (items_evolving_details.id == items_evolving_details_id) { + return items_evolving_details; + } + } + + return NewEntity(); + } + + static ItemsEvolvingDetails FindOne( + Database& db, + int items_evolving_details_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + items_evolving_details_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + ItemsEvolvingDetails e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.item_evo_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_evolve_level = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.sub_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int items_evolving_details_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + items_evolving_details_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const ItemsEvolvingDetails &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[1] + " = " + std::to_string(e.item_evo_id)); + v.push_back(columns[2] + " = " + std::to_string(e.item_evolve_level)); + v.push_back(columns[3] + " = " + std::to_string(e.item_id)); + v.push_back(columns[4] + " = " + std::to_string(e.type)); + v.push_back(columns[5] + " = " + std::to_string(e.sub_type)); + v.push_back(columns[6] + " = " + std::to_string(e.required_amount)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static ItemsEvolvingDetails InsertOne( + Database& db, + ItemsEvolvingDetails e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.item_evo_id)); + v.push_back(std::to_string(e.item_evolve_level)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.type)); + v.push_back(std::to_string(e.sub_type)); + v.push_back(std::to_string(e.required_amount)); + + 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 &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.item_evo_id)); + v.push_back(std::to_string(e.item_evolve_level)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.type)); + v.push_back(std::to_string(e.sub_type)); + v.push_back(std::to_string(e.required_amount)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + ItemsEvolvingDetails e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.item_evo_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_evolve_level = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.sub_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.required_amount = row[6] ? strtoll(row[6], nullptr, 10) : 0; + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector 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) { + ItemsEvolvingDetails e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.item_evo_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.item_evolve_level = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.item_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.sub_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.required_amount = row[6] ? strtoll(row[6], 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 ItemsEvolvingDetails &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.item_evo_id)); + v.push_back(std::to_string(e.item_evolve_level)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.type)); + v.push_back(std::to_string(e.sub_type)); + v.push_back(std::to_string(e.required_amount)); + + 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 &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.item_evo_id)); + v.push_back(std::to_string(e.item_evolve_level)); + v.push_back(std::to_string(e.item_id)); + v.push_back(std::to_string(e.type)); + v.push_back(std::to_string(e.sub_type)); + v.push_back(std::to_string(e.required_amount)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_BASE_ITEMS_EVOLVING_DETAILS_REPOSITORY_H diff --git a/common/repositories/character_evolving_items_repository.h b/common/repositories/character_evolving_items_repository.h new file mode 100644 index 000000000..8bc379505 --- /dev/null +++ b/common/repositories/character_evolving_items_repository.h @@ -0,0 +1,66 @@ +#ifndef EQEMU_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H +#define EQEMU_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_character_evolving_items_repository.h" + +#include + +class CharacterEvolvingItemsRepository: public BaseCharacterEvolvingItemsRepository { +public: + // Custom extended repository methods here + + static CharacterEvolvingItems SetCurrentAmountAndProgression(Database& db, const uint64 id, const uint64 amount, const double progression) + { + auto e = FindOne(db, id); + if (e.id == 0) { + return NewEntity(); + } + + e.current_amount = amount; + e.progression = progression; + e.deleted_at = 0; + UpdateOne(db, e); + return e; + } + + static CharacterEvolvingItems SetEquipped(Database& db, const uint64 id, const bool equipped) + { + auto e = FindOne(db, id); + if (e.id == 0) { + return NewEntity(); + } + + e.equipped = equipped; + e.deleted_at = 0; + UpdateOne(db, e); + return e; + } + + static CharacterEvolvingItems SoftDelete(Database& db, const uint64 id) + { + auto e = FindOne(db, id); + if (e.id == 0) { + return NewEntity(); + } + + e.deleted_at = time(nullptr); + UpdateOne(db, e); + return e; + } + + static bool UpdateCharID(Database &db, const uint64 id, const uint32 to_char_id) + { + auto e = FindOne(db, id); + if (e.id == 0) { + return false; + } + + e.character_id = to_char_id; + e.deleted_at = 0; + return UpdateOne(db, e); + } +}; + +#endif //EQEMU_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H diff --git a/common/repositories/items_evolving_details_repository.h b/common/repositories/items_evolving_details_repository.h new file mode 100644 index 000000000..906bb6095 --- /dev/null +++ b/common/repositories/items_evolving_details_repository.h @@ -0,0 +1,14 @@ +#ifndef EQEMU_ITEMS_EVOLVING_DETAILS_REPOSITORY_H +#define EQEMU_ITEMS_EVOLVING_DETAILS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_items_evolving_details_repository.h" + +class ItemsEvolvingDetailsRepository: public BaseItemsEvolvingDetailsRepository { +public: + // Custom extended repository methods here + +}; + +#endif //EQEMU_ITEMS_EVOLVING_DETAILS_REPOSITORY_H diff --git a/common/repositories/trader_repository.h b/common/repositories/trader_repository.h index 2999b738a..a8e3c13c1 100644 --- a/common/repositories/trader_repository.h +++ b/common/repositories/trader_repository.h @@ -12,6 +12,7 @@ class TraderRepository : public BaseTraderRepository { public: + static constexpr uint32 TRADER_CONVERT_ID = 4000000000; struct DistinctTraders_Struct { uint32 trader_id; @@ -40,7 +41,11 @@ public: int32 char_zone_instance_id ); - static BulkTraders_Struct GetDistinctTraders(Database &db, uint32 char_zone_instance_id, uint32 max_results) + static BulkTraders_Struct GetDistinctTraders( + Database &db, + uint32 char_zone_instance_id, + uint32 max_results = std::numeric_limits::max() + ) { BulkTraders_Struct all_entries{}; std::vector distinct_traders; @@ -49,7 +54,9 @@ public: "SELECT DISTINCT(t.char_id), t.char_zone_id, t.char_zone_instance_id, t.char_entity_id, c.name " "FROM trader AS t " "JOIN character_data AS c ON t.char_id = c.id " - "ORDER BY t.char_zone_instance_id = {} DESC LIMIT {};", + "WHERE t.char_zone_instance_id = {} " + "ORDER BY t.char_zone_instance_id ASC " + "LIMIT {}", char_zone_instance_id, max_results) ); @@ -227,6 +234,37 @@ public: return DeleteWhere(db, fmt::format("`id` IN({})", Strings::Implode(",", delete_ids))); } + + static DistinctTraders_Struct GetTraderByInstanceAndSerialnumber( + Database &db, + uint32 instance_id, + const char *serial_number + ) + { + DistinctTraders_Struct trader{}; + + auto query = fmt::format( + "SELECT t.id, t.char_id, c.name " + "FROM trader AS t " + "JOIN character_data AS c ON c.id = t.char_id " + "WHERE t.char_zone_id = 151 AND t.char_zone_instance_id = {} AND t.item_sn = {} LIMIT 1", + instance_id, + serial_number + ); + + auto results = db.QueryDatabase(query); + + if (results.RowCount() == 0) { + return trader; + } + + auto row = results.begin(); + std::string name = row[2]; + trader.trader_id = Strings::ToUnsignedInt(row[1]); + trader.trader_name = row[2] ? row[2] : ""; + + return trader; + } }; #endif //EQEMU_TRADER_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index 99ff386a7..5ca50dc7e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -946,6 +946,7 @@ RULE_REAL(Bazaar, ParcelDeliveryCostMod, 0.20, "Cost of parcel delivery for a ba RULE_INT(Bazaar, VoucherDeliveryCost, 200, "Number of vouchers for direct delivery for a bazaar purchase. Default is 200 vouchers. RoF+ Only.") RULE_BOOL(Bazaar, EnableParcelDelivery, true, "Enable bazaar purchases via parcel delivery. Default is True.") RULE_INT(Bazaar, MaxBuyerInventorySearchResults, 200, "Maximum number of search results when a Buyer searches the global item list. Default is 200. RoF+ Only.") +RULE_BOOL(Bazaar, UseAlternateBazaarSearch, false, "Allows the bazaar search window to search across bazaar shards. Default is false.") RULE_CATEGORY_END() RULE_CATEGORY(Mail) @@ -1178,6 +1179,13 @@ RULE_INT(Parcel, ParcelMaxItems, 50, "The maximum number of parcels a player is RULE_INT(Parcel, ParcelPruneDelay, 30, "The number of days after which a parcel is deleted. Items are lost!") RULE_CATEGORY_END() +RULE_CATEGORY(EvolvingItems) +RULE_REAL(EvolvingItems, PercentOfSoloExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.") +RULE_REAL(EvolvingItems, PercentOfGroupExperience, 0.1, "Percentage of group experience allocated to evolving items that require experience.") +RULE_REAL(EvolvingItems, PercentOfRaidExperience, 0.1, "Percentage of solo experience allocated to evolving items that require experience.") +RULE_INT(EvolvingItems, DelayUponEquipping, 30000, "Delay in ms before an evolving item will earn rewards after equipping. Default is 30000ms or 30s.") +RULE_CATEGORY_END() + #undef RULE_CATEGORY #undef RULE_INT #undef RULE_REAL diff --git a/common/shareddb.cpp b/common/shareddb.cpp index ac7a3a173..c51b79885 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -37,11 +37,13 @@ #include "strings.h" #include "eqemu_config.h" #include "data_verification.h" +#include "evolving_items.h" #include "repositories/criteria/content_filter_criteria.h" #include "repositories/account_repository.h" #include "repositories/faction_association_repository.h" #include "repositories/starting_items_repository.h" #include "path_manager.h" +#include "../zone/client.h" #include "repositories/loottable_repository.h" #include "repositories/character_item_recast_repository.h" #include "repositories/character_corpses_repository.h" @@ -645,15 +647,23 @@ bool SharedDatabase::GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is return true; } - // Overloaded: Retrieve character inventory based on character id (zone entry) -bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) +//bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) +bool SharedDatabase::GetInventory(Client *c) { - if (!char_id || !inv) + if (!c) { return false; + } + + uint32 char_id = c->CharacterID(); + EQ::InventoryProfile &inv = c->GetInv(); // Retrieve character inventory - auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`;", char_id)); + auto results = InventoryRepository::GetWhere(*this, fmt::format("`charid` = '{}' ORDER BY `slotid`", char_id)); + auto e_results = CharacterEvolvingItemsRepository::GetWhere( + *this, fmt::format("`char_id` = '{}' AND `deleted_at` IS NULL", char_id) + ); + if (results.empty()) { LogError("Error loading inventory for char_id {} from the database.", char_id); return false; @@ -667,8 +677,8 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) const auto timestamps = GetItemRecastTimestamps(char_id); auto cv_conflict = false; - const auto pmask = inv->GetLookup()->PossessionsBitmask; - const auto bank_size = inv->GetLookup()->InventoryTypeSize.Bank; + const auto pmask = inv.GetLookup()->PossessionsBitmask; + const auto bank_size = inv.GetLookup()->InventoryTypeSize.Bank; std::vector queue{}; for (auto &row: results) { @@ -785,9 +795,55 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) } } + if (item->EvolvingItem) { + if (slot_id >= EQ::invslot::EQUIPMENT_BEGIN && slot_id <= EQ::invslot::EQUIPMENT_END) { + inst->SetEvolveEquipped(true); + } + + auto t = std::ranges::find_if( + e_results.cbegin(), + e_results.cend(), + [&](const CharacterEvolvingItemsRepository::CharacterEvolvingItems &x) { + return x.item_id == item_id; + } + ); + + if (t == std::end(e_results)) { + auto e = CharacterEvolvingItemsRepository::NewEntity(); + + e.character_id = char_id; + e.item_id = item_id; + e.equipped = inst->GetEvolveEquipped(); + e.final_item_id = evolving_items_manager.GetFinalItemID(*inst); + + auto r = CharacterEvolvingItemsRepository::InsertOne(*this, e); + e.id = r.id; + e_results.push_back(e); + + inst->SetEvolveUniqueID(e.id); + inst->SetEvolveCharID(e.character_id); + inst->SetEvolveItemID(e.item_id); + inst->SetEvolveActivated(e.activated); + inst->SetEvolveEquipped(e.equipped); + inst->SetEvolveCurrentAmount(e.current_amount); + inst->CalculateEvolveProgression(); + inst->SetEvolveFinalItemID(e.final_item_id); + } + else { + inst->SetEvolveUniqueID(t->id); + inst->SetEvolveCharID(t->character_id); + inst->SetEvolveItemID(t->item_id); + inst->SetEvolveActivated(t->activated); + inst->SetEvolveEquipped(t->equipped); + inst->SetEvolveCurrentAmount(t->current_amount); + inst->CalculateEvolveProgression(); + inst->SetEvolveFinalItemID(t->final_item_id); + } + } + int16 put_slot_id; if (slot_id >= 8000 && slot_id <= 8999) { - put_slot_id = inv->PushCursor(*inst); + put_slot_id = inv.PushCursor(*inst); } else if (slot_id >= 3111 && slot_id <= 3179) { // Admins: please report any occurrences of this error @@ -797,10 +853,10 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) item_id, slot_id ); - put_slot_id = inv->PushCursor(*inst); + put_slot_id = inv.PushCursor(*inst); } else { - put_slot_id = inv->PutItem(slot_id, *inst); + put_slot_id = inv.PutItem(slot_id, *inst); } row.guid = inst->GetSerialNumber(); @@ -825,8 +881,8 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) "ClientVersion/Expansion conflict during inventory load at zone entry for [{}] (charid: [{}], inver: [{}], gmi: [{}])", char_name, char_id, - EQ::versions::MobVersionName(inv->InventoryVersion()), - (inv->GMInventory() ? "true" : "false") + EQ::versions::MobVersionName(inv.InventoryVersion()), + (inv.GMInventory() ? "true" : "false") ); } @@ -837,7 +893,7 @@ bool SharedDatabase::GetInventory(uint32 char_id, EQ::InventoryProfile *inv) EQ::ItemInstance::ClearGUIDMap(); // Retrieve shared inventory - return GetSharedBank(char_id, inv, true); + return GetSharedBank(char_id, &inv, true); } // Overloaded: Retrieve character inventory based on account_id and character name (char select) diff --git a/common/shareddb.h b/common/shareddb.h index 67c684206..d36132fd3 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -28,6 +28,8 @@ #include "fixed_memory_variable_hash_set.h" #include "say_link.h" #include "repositories/command_subsettings_repository.h" +#include "repositories/items_evolving_details_repository.h" +#include "../common/repositories/character_evolving_items_repository.h" #include #include @@ -103,7 +105,7 @@ public: bool GetSharedBank(uint32 id, EQ::InventoryProfile *inv, bool is_charid); int32 GetSharedPlatinum(uint32 account_id); bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add); - bool GetInventory(uint32 char_id, EQ::InventoryProfile *inv); + bool GetInventory(Client* c); bool GetInventory(uint32 account_id, char *name, EQ::InventoryProfile *inv); // deprecated std::map GetItemRecastTimestamps(uint32 char_id); uint32 GetItemRecastTimestamp(uint32 char_id, uint32 recast_type); diff --git a/common/version.h b/common/version.h index a272e77ae..4324ae027 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9288 +#define CURRENT_BINARY_DATABASE_VERSION 9290 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 #endif diff --git a/shared_memory/main.cpp b/shared_memory/main.cpp index c598b39f5..f395867ce 100644 --- a/shared_memory/main.cpp +++ b/shared_memory/main.cpp @@ -33,12 +33,14 @@ #include "../common/zone_store.h" #include "../common/path_manager.h" #include "../common/events/player_event_logs.h" +#include "../common/evolving_items.h" -EQEmuLogSys LogSys; -WorldContentService content_service; -ZoneStore zone_store; -PathManager path; -PlayerEventLogs player_event_logs; +EQEmuLogSys LogSys; +WorldContentService content_service; +ZoneStore zone_store; +PathManager path; +PlayerEventLogs player_event_logs; +EvolvingItemsManager evolving_items_manager; #ifdef _WINDOWS #include diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index a63916911..93d6f0463 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -261,6 +261,7 @@ OP_ItemPacket=0x368e OP_ItemLinkResponse=0x70c0 OP_ItemLinkClick=0x4cef OP_ItemPreview=0x6b5c +OP_ItemPreviewRequest=0x7f80 OP_NewSpawn=0x6097 OP_Track=0x17e5 OP_TrackTarget=0x695e @@ -731,3 +732,6 @@ OP_RemoveTrap=0x71da OP_PickZoneWindow=0x72d8 OP_PickZone=0xaaba + +#evolve item related +OP_EvolveItem=0x7cfb diff --git a/utils/scripts/opcode_scripts/opcodelist.txt b/utils/scripts/opcode_scripts/opcodelist.txt index 8a4ebf87d..6e324c40d 100644 --- a/utils/scripts/opcode_scripts/opcodelist.txt +++ b/utils/scripts/opcode_scripts/opcodelist.txt @@ -1,317 +1,317 @@ -RoF2 Built May 10 2013 23:30:08 -0x04d6 -0x5efd -0x0cc1 -0x2c3d -0x7b25 -0x4f44 -0x7592 -0x2b1c +RoF2 Built May 10 2013 23:30:08 +0x04d6 +0x5efd +0x0cc1 +0x2c3d +0x7b25 +0x4f44 +0x7592 +0x2b1c 0x1fd1 OP_Consent -0x15a5 -0x3c7e -0x6262 +0x15a5 +0x3c7e +0x6262 0x471d OP_AckPacket -0x64a8 -0x7c06 -0x2c5e -0x24ab -0x19b5 -0x72fa -0x0770 +0x64a8 +0x7c06 +0x2c5e +0x24ab +0x19b5 +0x72fa +0x0770 0x4cef OP_ItemLinkClick 0x08c1 OP_GMServers 0x318f OP_BeginCast 0x5ec8 OP_Disarm -0x6ae3 +0x6ae3 0x2b2d OP_ChannelMessage -0x1cb1 +0x1cb1 0x5070 OP_TimeOfDay 0x7dfc OP_ClientUpdate -0x35ea -0x3b9a -0x148d +0x35ea +0x3b9a +0x148d 0x345d OP_ClientReady 0x5ae2 OP_WorldObjectsSent -0x03a4 +0x03a4 0x69e2 OP_TradeAcceptClick 0x354c OP_CancelTrade 0x3993 OP_FinishTrade -0x165b -0x2b95 -0x6fe2 -0x42fd -0x25d7 -0x7436 +0x165b +0x2b95 +0x6fe2 +0x42fd +0x25d7 +0x7436 0x4206 OP_TradeCoins 0x14bf OP_TradeRequestAck 0x7349 OP_FinishWindow -0x71fd +0x71fd 0x0b9c OP_SetGuildRank -0x0f7d -0x1bd3 +0x0f7d +0x1bd3 0x5053 OP_GuildPublicNote -0x01f9 +0x01f9 0x2c84 OP_MobUpdate -0x279d -0x5a94 +0x279d +0x5a94 0x579a OP_TradeSkillCombine -0x56b1 -0x0f25 -0x3c14 -0x0c16 +0x56b1 +0x0f25 +0x3c14 +0x0c16 0x4a8f OP_GMFind -0x2ad6 -0x0160 -0x7852 -0x41bf -0x40dd -0x3ec6 -0x6af4 -0x6d9d -0x2538 -0x0017 -0x017 -0x691a -0x0520 -0x200f -0x65ab +0x2ad6 +0x0160 +0x7852 +0x41bf +0x40dd +0x3ec6 +0x6af4 +0x6d9d +0x2538 +0x0017 +0x017 +0x691a +0x0520 +0x200f +0x65ab 0x674b OP_WhoAllRequest -0x6a02 -0x0e48 +0x6a02 +0x0e48 0x12a6 OP_GuildMemberList -0x0eae -0x2921 -0x7056 -0x507a +0x0eae +0x2921 +0x7056 +0x507a 0x6279 OP_GuildList -0x5a26 -0x5b51 -0x0bad -0x59ad -0x2264 -0x004c +0x5a26 +0x5b51 +0x0bad +0x59ad +0x2264 +0x004c 0x04c OP_SkillUpdate 0x0ddd OP_ShopPlayerBuy -0x4101 -0x5f03 -0x3de3 +0x4101 +0x5f03 +0x3de3 0x1901 OP_Petition -0x791b -0x3a5d -0x7013 -0x47f1 +0x791b +0x3a5d +0x7013 +0x47f1 0x6506 OP_PlayerProfile 0x30a8 OP_ShopEnd 0x724f OP_ShopDelItem -0x5829 -0x1ce1 -0x47d6 -0x07cf -0x40c9 -0x38d4 -0x7eb9 -0x1e2c +0x5829 +0x1ce1 +0x47d6 +0x07cf +0x40c9 +0x38d4 +0x7eb9 +0x1e2c 0x3ae1 OP_GMBecomeNPC -0x4849 -0x376b +0x4849 +0x376b 0x1e2a OP_Fishing 0x1eec OP_LevelUpdate -0x4958 +0x4958 0x20ed OP_ExpUpdate -0x3ed4 +0x3ed4 0x701c OP_RezzAnswer 0x640e OP_InstillDoubt 0x0ecf OP_Mend -0x09d3 +0x09d3 0x70a3 OP_IncreaseStats -0x178e -0x27e8 -0x61f8 -0x4322 -0x3f84 +0x178e +0x27e8 +0x61f8 +0x4322 +0x3f84 0x40ef OP_FinishWindow2 -0x166c +0x166c 0x106b OP_RandomReply -0x5bfd -0x11c8 -0x1753 +0x5bfd +0x11c8 +0x1753 0x009f OP_SetRunMode -0x09f -0x4ae6 -0x3dbf -0x0399 -0x5c89 -0x012c -0x381c -0x4b36 -0x36d5 -0x5f21 -0x56f7 -0x46b1 +0x09f +0x4ae6 +0x3dbf +0x0399 +0x5c89 +0x012c +0x381c +0x4b36 +0x36d5 +0x5f21 +0x56f7 +0x46b1 0x357c OP_GMSearchCorpse -0x2422 -0x084e +0x2422 +0x084e 0x67e3 OP_GuildPeace -0x0f83 +0x0f83 0x4577 OP_TrackUnknown 0x5d55 OP_Sneak 0x67fe OP_Hide -0x12e5 -0x2c7a +0x12e5 +0x2c7a 0x600d OP_SaveOnZoneReq -0x16ce +0x16ce 0x46ce OP_GMLastName 0x1ffb OP_GuildWar -0x6718 -0x2f3e +0x6718 +0x2f3e 0x7e09 OP_GuildLeader -0x5ce4 -0x5bcc -0x551d -0x6dbc +0x5ce4 +0x5bcc +0x551d +0x6dbc 0x2219 OP_SafeFallSuccess -0x2686 -0x2022 -0x1c53 -0x33a2 -0x3541 -0x582e -0x3cba -0x190c -0x5067 -0x46d2 -0x49cf -0x1b0c - +0x2686 +0x2022 +0x1c53 +0x33a2 +0x3541 +0x582e +0x3cba +0x190c +0x5067 +0x46d2 +0x49cf +0x1b0c + 0x31e6 OP_ApplyPoison 0x4211 OP_BoardBoat 0x7617 OP_LeaveBoat -0x4466 +0x4466 0x1287 OP_CastSpell 0x5467 OP_ManaChange 0x43af OP_ColoredText -0x6a0d +0x6a0d 0x217c OP_MemorizeSpell 0x260a OP_SenseHeading -0x5886 -0x7e8c -0x1a37 -0x0b6f -0x62ca -0x34a4 -0x6c83 -0x5d10 -0x21a0 -0x3537 -0x2acd -0x7c6d -0x6618 -0x6c65 - - +0x5886 +0x7e8c +0x1a37 +0x0b6f +0x62ca +0x34a4 +0x6c83 +0x5d10 +0x21a0 +0x3537 +0x2acd +0x7c6d +0x6618 +0x6c65 + + 0x32c2 OP_GroupInvite2 0x2a50 OP_CancelInvite 0x2060 OP_GroupFollow2 0x31f4 OP_Jump -0x4420 -0x1256 -0x4d38 -0x50f5 - +0x4420 +0x1256 +0x4d38 +0x50f5 + 0x79c5 OP_GMHideMe -0x2fab -0x57a5 -0x32a4 +0x2fab +0x57a5 +0x32a4 0x590d OP_ExpansionInfo 0x6f15 OP_Damage -0x42ea -0x3091 -0x6b5a -0x3234 -0x30b4 +0x42ea +0x3091 +0x6b5a +0x3234 +0x30b4 0x51fd OP_EnvDamage -0x15f4 +0x15f4 0x1808 OP_DeleteCharacter 0x1795 OP_NewZone 0x7887 OP_ReqNewZone -0x2414 -0x2eb3 -0x3db3 -0x4008 - +0x2414 +0x2eb3 +0x3db3 +0x4008 + 0x373b OP_Emote 0x7280 OP_DeleteSpawn -0x4bc2 -0x15bb +0x4bc2 +0x15bb 0x4fed OP_ShopRequest -0x0c2f +0x0c2f 0x4aa1 OP_ClickObject 0x6fca OP_GroundSpawn -0x1045 -0x2a9e -0x2523 -0x6213 +0x1045 +0x2a9e +0x2523 +0x6213 0x4a39 OP_Save -0x0797 +0x0797 0x35fa OP_ReqClientSpawn 0x5f8e OP_SendExpZonein 0x4c10 OP_GroupDisband 0x747c OP_SomeItemPacketMaybe 0x744c OP_Action 0x00d2 OP_SendCharInfo -0x0d2 -0x11d4 +0x0d2 +0x11d4 0x6bbf OP_CharacterCreate 0x6517 OP_Death -0x19f6 -0x360f -0x1951 +0x19f6 +0x360f +0x1951 0x51d3 OP_GMKill 0x26a7 OP_GMKick 0x7d8e OP_GMGoto -0x475e -0x5253 -0x3c79 -0x5065 -0x0337 -0x2ded -0x777b -0x4434 -0x0e90 -0x57da -0x150e -0x1ba8 -0x70eb +0x475e +0x5253 +0x3c79 +0x5065 +0x0337 +0x2ded +0x777b +0x4434 +0x0e90 +0x57da +0x150e +0x1ba8 +0x70eb 0x0adf OP_LootRequest 0x30f7 OP_EndLootRequest 0x5f44 OP_MoneyOnCorpse -0x3d54 -0x482d -0x58b2 -0x7361 -0x604f -0x3d87 -0x6909 -0x4f73 -0x0a0f +0x3d54 +0x482d +0x58b2 +0x7361 +0x604f +0x3d87 +0x6909 +0x4f73 +0x0a0f 0x56a2 OP_ApproveName -0x69e6 +0x69e6 0x3a8f OP_ClickDoor 0x08e8 OP_MoveDoor -0x7dd1 -0x5c66 +0x7dd1 +0x5c66 0x312a OP_Illusion -0x2807 -0x4fe2 -0x2f2e -0x5824 -0x6661 -0x4742 -0x4d02 -0x2105 -0x19e9 -0x2268 +0x2807 +0x4fe2 +0x2f2e +0x5824 +0x6661 +0x4742 +0x4d02 +0x2105 +0x19e9 +0x2268 0x7994 OP_WearChange 0x0386 OP_Bind_Wound 0x5306 OP_Forage @@ -320,210 +320,210 @@ RoF2 Built May 10 2013 23:30:08 0x7053 OP_GuildInviteAccept 0x1444 OP_GuildRemove 0x3708 OP_GuildDelete -0x7b22 -0x610f -0x324a -0x2889 -0x6f2a -0x2998 +0x7b22 +0x610f +0x324a +0x2889 +0x6f2a +0x2998 0x700c OP_RemoveAllDoors 0x1966 OP_GMTraining 0x4d6b OP_GMEndTraining -0x6e8d -0x2b41 +0x6e8d +0x2b41 0x4dc9 OP_LootItem 0x7177 OP_Animation -0x650e +0x650e 0x2d18 OP_ZoneChange -0x707b -0x0803 -0x3fc3 +0x707b +0x0803 +0x3fc3 0x7326 OP_GuildStatus -0x0804 -0x3f86 -0x6d20 -0x04a3 -0x4714 +0x0804 +0x3f86 +0x6d20 +0x04a3 +0x4714 0x6703 OP_Begging 0x0ae7 OP_ControlBoat -0x27d7 +0x27d7 0x3c21 OP_RezzRequest -0x6f22 +0x6f22 0x32ee OP_MoveItem 0x0bcf OP_MoveCoin -0x2452 -0x0186 -0x0522 +0x2452 +0x0186 +0x0522 0x3a54 OP_Split 0x659c OP_Buff -0x225b - +0x225b + 0x12cc OP_World_Client_CRC1 0x661e OP_Weather 0x742b OP_Consider -0x102e +0x102e 0x0f13 OP_World_Client_CRC2 -0x6944 +0x6944 0x2703 OP_Taunt 0x5602 OP_Feedback 0x68c2 OP_TradeMoneyUpdate -0x07f7 -0x3fe7 -0x43a0 -0x314c +0x07f7 +0x3fe7 +0x43a0 +0x314c 0x4b70 OP_Consume 0x2a79 OP_Stamina -0x402e -0x750e -0x0927 +0x402e +0x750e +0x0927 0x36a4 OP_Stun -0x2b03 +0x2b03 0x68d3 OP_DuelResponse2 0x6a46 OP_DuelResponse 0x5237 OP_ZoneSpawns 0x3eba OP_CombatAbility 0x109d OP_AutoAttack 0x075d OP_TargetMouse -0x05d2 +0x05d2 0x2a85 OP_GMTrainSkill 0x43a3 OP_ConfirmDelete -0x6c57 -0x3906 +0x6c57 +0x3906 0x55c4 OP_LootComplete 0x3196 OP_ShopEndConfirm 0x18ad OP_DeleteItem 0x01b8 OP_DeleteCharge -0x01d7 -0x4d28 -0x4fd9 -0x02c6 -0x3e30 +0x01d7 +0x4d28 +0x4fd9 +0x02c6 +0x3e30 0x3fcf OP_RequestClientZoneChange -0x4e0e -0x1dee +0x4e0e +0x1dee 0x62ac OP_GMZoneRequest 0x4ac6 OP_Logout 0x3526 OP_AutoAttack2 0x7ceb OP_LogServer 0x0423 OP_Surname 0x3956 OP_FriendsWho -0x45dc -0x35e0 -0x173e -0x4853 -0x393b +0x45dc +0x35e0 +0x173e +0x4853 +0x393b 0x0efa OP_SwapSpell -0x3588 -0x26e9 +0x3588 +0x26e9 0x4e56 OP_YellForHelp -0x2551 -0x38c8 +0x2551 +0x38c8 0x7499 OP_ApproveWorld -0x603a +0x603a 0x7b10 OP_RandomReq -0x208f +0x208f 0x607e OP_GMDelCorpse 0x1821 OP_Sacrifice -0x30d6 +0x30d6 0x760d OP_RezzComplete -0x4dd5 -0x0fe8 -0x6e5c -0x2228 -0x21bc -0x78c3 -0x3e57 -0x69d0 -0x1e3a +0x4dd5 +0x0fe8 +0x6e5c +0x2228 +0x21bc +0x78c3 +0x3e57 +0x69d0 +0x1e3a 0x1a30 OP_Sound -0x61a0 +0x61a0 0x048c OP_InterruptCast -0x6e80 -0x3d29 +0x6e80 +0x3d29 0x5d92 OP_Charm 0x0159 OP_PetCommands -0x486e -0x579e -0x49b3 +0x486e +0x579e +0x49b3 0x6db5 OP_GMApproval -0x4759 +0x4759 0x0c22 OP_MOTD 0x2097 OP_GMToggle -0x72f7 +0x72f7 0x640c OP_MoneyUpdate -0x51e5 -0x1802 -0x5de1 -0x2e0c +0x51e5 +0x1802 +0x5de1 +0x2e0c 0x58e2 OP_TargetCommand 0x444d OP_SetServerFilter 0x4478 OP_Assist -0x2a21 -0x7e59 +0x2a21 +0x7e59 0x0b0b OP_SetGuildMOTD 0x3e13 OP_GuildMOTD 0x7a11 OP_ClearObject 0x6580 OP_Translocate 0x28ec OP_Camp 0x61b3 OP_BecomeTrader -0x31af +0x31af 0x0876 OP_TGB -0x4ee1 -0x7e92 +0x4ee1 +0x7e92 0x7d14 OP_AAExpUpdate - + 0x5578 OP_FaceChange -0x1a80 -0x42ff -0x7c2d +0x1a80 +0x42ff +0x7c2d 0x2c57 OP_MobRename -0x6dab -0x4568 -0x7776 -0x5029 -0x696c +0x6dab +0x4568 +0x7776 +0x5029 +0x696c 0x23b9 OP_Dye 0x5204 OP_ConsiderCorpse 0x213f OP_SimpleMessage 0x1024 OP_FormattedMessage -0x2ce5 -0x5cc9 -0x3358 +0x2ce5 +0x5cc9 +0x3358 0x52e5 OP_DeleteSpell 0x48c1 OP_Shielding -0x6f14 +0x6f14 0x1414 OP_Report -0x0e1c -0x1219 +0x0e1c +0x1219 0x6857 OP_KeyRing 0x55ac OP_RaidInvite 0x3973 OP_RaidUpdate -0x56fe -0x7f2d +0x56fe +0x7f2d 0x31df OP_TraderShop 0x4ef5 OP_Trader -0x735d -0x019b -0x5930 -0x2d73 +0x735d +0x019b +0x5930 +0x2d73 0x39d6 OP_BazaarSearch -0x6a96 +0x6a96 0x424e OP_AAAction 0x7a27 OP_RespondAA -0x5eca -0x70c6 -0x0dd4 -0x4598 -0x19b6 -0x728a -0x5232 -0x77bd - +0x5eca +0x70c6 +0x0dd4 +0x4598 +0x19b6 +0x728a +0x5232 +0x77bd + 0x70c0 OP_ItemLinkResponse -0x633c -0x63eb -0x4765 -0x6290 +0x633c +0x63eb +0x4765 +0x6290 0x1db6 OP_RecipesSearch 0x6e02 OP_RecipeReply 0x40d7 OP_RecipeDetails @@ -546,248 +546,248 @@ RoF2 Built May 10 2013 23:30:08 0x0272 OP_TargetHoTT 0x2003 OP_ClearNPCMarks 0x20d3 OP_ClearRaidNPCMarks -0x1c89 -0x4ee2 +0x1c89 +0x4ee2 0x2797 OP_LeadershipExpUpdate 0x6da5 OP_ClearLeadershipAbilities -0x0ca6 -0x7717 -0x509d -0x6bee -0x0b04 +0x0ca6 +0x7717 +0x509d +0x6bee +0x0b04 0x6097 OP_NewSpawn -0x758c -0x2b10 -0x4d09 +0x758c +0x2b10 +0x4d09 0x0083 OP_SpecialMesg 0x3714 OP_TaskDescription 0x08d3 OP_TaskActivity 0x39f0 OP_CancelTask -0x5f7a +0x5f7a 0x006a OP_ItemScriptAdjustment -0x7465 -0x31c0 -0x14ba -0x25e0 -0x62a0 -0x407a -0x7c88 -0x51b8 +0x7465 +0x31c0 +0x14ba +0x25e0 +0x62a0 +0x407a +0x7c88 +0x51b8 0x578c OP_WhoAllResponse -0x1197 -0x2dd3 -0x333a -0x2925 +0x1197 +0x2dd3 +0x333a +0x2925 0x37b1 OP_MobHealth -0x7314 +0x7314 0x2404 OP_MobManaUpdate -0x5f5e +0x5f5e 0x1c81 OP_MobEnduranceUpdate -0x4a78 -0x37a2 -0x5565 -0x2d09 -0x3141 -0x695e +0x4a78 +0x37a2 +0x5565 +0x2d09 +0x3141 +0x695e 0x0029 OP_TrackTarget -0x029 -0x1612 -0x3e69 -0x5c59 +0x029 +0x1612 +0x3e69 +0x5c59 0x7e1a OP_GMZoneRequest2 -0x24d9 +0x24d9 0x5089 OP_ZoneEntry -0x61db -0x542f -0x2006 +0x61db +0x542f +0x2006 0x52fa OP_FeignDeath 0x39e8 OP_PickPocket -0x7c2e -0x2cdd -0x5e3a -0x4214 +0x7c2e +0x2cdd +0x5e3a +0x4214 0x2828 OP_HPUpdate -0x14b8 +0x14b8 0x3791 OP_ManaUpdate -0x0698 +0x0698 0x5f42 OP_EnduranceUpdate -0x7eb5 +0x7eb5 0x73f4 OP_Bug 0x69a4 OP_SendZonepoints 0x57bc OP_InspectRequest 0x71ac OP_InspectAnswer 0x4229 OP_GroupMakeLeader -0x54f7 -0x322b -0x3264 -0x202c -0x29ae +0x54f7 +0x322b +0x3264 +0x202c +0x29ae 0x7a09 OP_SendLoginInfo -0x5206 -0x7fd2 +0x5206 +0x7fd2 0x6259 OP_PostEnterWorld -0x0f70 -0x00d1 -0x0d1 -0x1eac -0x1b54 -0x5e23 -0x7910 -0x152e -0x7555 -0x05ce +0x0f70 +0x00d1 +0x0d1 +0x1eac +0x1b54 +0x5e23 +0x7910 +0x152e +0x7555 +0x05ce 0x1649 OP_GroupFollow 0x6110 OP_GroupInvite -0x0641 +0x0641 0x578f OP_EnterWorld -0x6cb8 -0x6ef5 +0x6cb8 +0x6ef5 0x4c44 OP_ZoneServerInfo 0x4cb4 OP_ZoneUnavail -0x74fb -0xbe6b -0x5ac7 -0x6281 +0x74fb +0xbe6b +0x5ac7 +0x6281 0x1bc5 OP_SetChatServer -0x48c8 -0x677b -0x6820 -0x2623 +0x48c8 +0x677b +0x6820 +0x2623 0x69b9 OP_GuildMemberUpdate -0x2dcb -0x5e04 -0x1d3c +0x2dcb +0x5e04 +0x1d3c 0x6060 OP_LFGCommand 0x0340 OP_LFGGetMatchesRequest 0x49a9 OP_LFPCommand 0x4d7d OP_LFPGetMatchesRequest 0x5048 OP_LFGGetMatchesResponse 0x22c6 OP_LFPGetMatchesResponse -0x499e +0x499e 0x2d4e OP_GuildDemote -0x2009 -0x3b26 -0x69d4 -0x40b1 -0x587d -0x3747 -0x7a66 -0x2f03 -0x01ed +0x2009 +0x3b26 +0x69d4 +0x40b1 +0x587d +0x3747 +0x7a66 +0x2f03 +0x01ed 0x36e0 OP_GetGuildMOTD 0x4f1f OP_GetGuildMOTDReply -0x6087 -0x5f92 -0x439a -0x4334 -0x13c2 +0x6087 +0x5f92 +0x439a +0x4334 +0x13c2 0x7cb8 OP_TestBuff 0x17e5 OP_Track -0x5265 -0x1440 -0x7a4b -0x0648 -0x2529 +0x5265 +0x1440 +0x7a4b +0x0648 +0x2529 0x486f OP_GMSummon 0x458e OP_GMEmoteWorld -0x2549 +0x2549 0x1cfd OP_GMEmoteZone 0x5ca6 OP_CharInventory -0x2710 +0x2710 0x7291 OP_SpawnDoor -0x7afc -0x1f65 -0x3ddd -0x85fc +0x7afc +0x1f65 +0x3ddd +0x85fc 0x72df OP_ReadBook 0x3af1 OP_RequestDuel -0x52f7 +0x52f7 0x77b5 OP_TradeRequest -0x70f2 -0x207d +0x70f2 +0x207d 0x384a OP_ConsentResponse 0x5505 OP_TradeBusy 0x0c1e OP_ClickObjectAction -0x51eb -0x466b -0x41a3 -0x0682 -0x1242 +0x51eb +0x466b +0x41a3 +0x0682 +0x1242 0x261d OP_LoadSpellSet -0x32a6 +0x32a6 0x2c6c OP_AdventureRequest -0x39e5 +0x39e5 0x5648 OP_AdventureDetails 0x5327 OP_LDoNButton -0x4524 +0x4524 0x5954 OP_RandomNameGenerator -0x327d -0x2945 -0x33d7 -0x2651 -0x3b86 -0x0c6c -0x24d4 -0x4441 -0x4e42 -0x50d8 -0x1afb -0x6bd4 +0x327d +0x2945 +0x33d7 +0x2651 +0x3b86 +0x0c6c +0x24d4 +0x4441 +0x4e42 +0x50d8 +0x1afb +0x6bd4 0x3cb0 OP_AdventureInfoRequest 0x4c54 OP_AdventureInfo 0x7171 OP_AdventureData -0x35e8 -0x7d49 -0x1848 -0x2599 -0x0dab -0x6493 -0x34f2 -0x7d40 -0x0e00 -0x1cf3 -0x71f4 +0x35e8 +0x7d49 +0x1848 +0x2599 +0x0dab +0x6493 +0x34f2 +0x7d40 +0x0e00 +0x1cf3 +0x71f4 0x5d18 OP_LeaveAdventure -0x4e25 -0x50c2 +0x4e25 +0x50c2 0x400f OP_AdventureFinish -0x6368 -0x6192 -0x3aa4 +0x6368 +0x6192 +0x3aa4 0x502e OP_Marquee 0x1b01 OP_AdventureUpdate -0x0f24 -0x1015 -0x23a6 -0x5a74 -0x037a +0x0f24 +0x1015 +0x23a6 +0x5a74 +0x037a 0x7a45 OP_ConsentDeny -0x71bc +0x71bc 0x2382 OP_DenyResponse -0x02a0 +0x02a0 0x1126 OP_VetClaimRequest -0x0668 +0x0668 0x16d4 OP_VetClaimReply 0x5cea OP_FindPersonRequest -0x0cc3 +0x0cc3 0x7e58 OP_FindPersonReply 0x9be3 OP_PickLock -0x0438 +0x0438 0x3d5c OP_LDoNOpen -0x15e7 +0x15e7 0x368e OP_ItemPacket -0x7331 -0x4936 -0x2c4e +0x7331 +0x4936 +0x2c4e 0x78bf OP_DisarmTraps 0x02af OP_SenseTraps 0x65c3 OP_AdventurePointsUpdate -0x0dcb +0x0dcb 0x661b OP_AugmentItem -0x5f68 -0x5278 -0x6b53 -0x5dd1 +0x5f68 +0x5278 +0x6b53 +0x5dd1 0x559a OP_WeaponEquip2 0x2d25 OP_WeaponUnequip2 0x23c1 OP_WorldClientReady @@ -795,7 +795,7 @@ RoF2 Built May 10 2013 23:30:08 0x7093 OP_AdventureLeaderboardRequest 0x2370 OP_AdventureStatsReply 0x7f79 OP_AdventureLeaderboardReply -0x4eb3 +0x4eb3 0x3377 OP_BuffCreate 0x5882 OP_PetBuffWindow 0x4f4b OP_TargetBuffs @@ -806,31 +806,31 @@ RoF2 Built May 10 2013 23:30:08 0x4254 OP_TributeInfo 0x79fc OP_SelectTribute 0x073d OP_TributeTimer -0x08bf -0x7697 -0x4b65 -0x51ae -0x51a9 +0x08bf +0x7697 +0x4b65 +0x51ae +0x51a9 0x7666 OP_OpenTributeMaster -0x4290 +0x4290 0x759e OP_DisciplineUpdate -0x01c6 +0x01c6 0x6989 OP_DisciplineTimer 0x58fb OP_TributeMoney -0x7422 -0x0d25 -0x4de1 -0x46d0 -0x4f50 -0x2ddc -0x7441 -0x6a12 -0x29a8 -0x3f7f -0x150b -0x11e3 -0x1ad3 - +0x7422 +0x0d25 +0x4de1 +0x46d0 +0x4f50 +0x2ddc +0x7441 +0x6a12 +0x29a8 +0x3f7f +0x150b +0x11e3 +0x1ad3 + 0x59ca OP_DzAddPlayer 0x4701 OP_DzRemovePlayer 0x1abc OP_DzSwapPlayer @@ -838,96 +838,96 @@ RoF2 Built May 10 2013 23:30:08 0x543d OP_DzPlayerList 0x14c6 OP_DzJoinExpeditionConfirm 0x7f4b OP_DzJoinExpeditionReply -0x1950 -0x64b5 +0x1950 +0x64b5 0x0398 OP_DzListTimers -0x7b68 +0x7b68 0x4f7e OP_DzExpeditionInfo 0x9119 OP_DzExpeditionList 0x205f OP_DzQuit 0xb2e3 OP_DzMemberStatus 0x32f0 OP_DzLeaderStatus 0x3de9 OP_DzMemberList -0x5ae4 +0x5ae4 0x4d6e OP_OnLevelMessage -0x4fd0 -0x575b +0x4fd0 +0x575b 0x7e94 OP_DzExpeditionEndsWarning -0x5189 -0x383c +0x5189 +0x383c 0x791e OP_BankerChange -0x5c74 +0x5c74 0x71b1 OP_RecipesFavorite -0x20ab -0x025f -0x214a +0x20ab +0x025f +0x214a 0x08a6 OP_PopupResponse 0x15a9 OP_ItemRecastDelay 0x3707 OP_PVPLeaderBoardDetailsRequest 0x04aa OP_PVPLeaderBoardRequest 0x25b7 OP_PVPLeaderBoardDetailsReply 0x071f OP_PVPLeaderBoardReply -0x2dee -0x4e62 -0x0c91 -0x18d3 +0x2dee +0x4e62 +0x0c91 +0x18d3 0x6f4b OP_Weblink 0x4b15 OP_PVPStats -0x6755 -0x5c32 +0x6755 +0x5c32 0x5770 OP_PlayMP3 -0x7425 -0x5eed -0x574e -0x11b4 -0x4ed6 -0x0d9f -0x7d23 +0x7425 +0x5eed +0x574e +0x11b4 +0x4ed6 +0x0d9f +0x7d23 0x3fb0 OP_ClearSurname -0xc693 +0xc693 0x7b1e OP_RemoveNimbusEffect -0x20ae -0x0727 -0x3771 -0x7fe0 -0x4d5e +0x20ae +0x0727 +0x3771 +0x7fe0 +0x4d5e 0x1877 OP_SendGuildTributes -0x649f -0x5f3f -0x5bcb -0x43d2 -0x49ea +0x649f +0x5f3f +0x5bcb +0x43d2 +0x49ea 0x378d OP_OpenGuildTributeMaster -0x7f8e -0x02bc -0x7dd2 -0x7c1d -0x7a41 -0x7db5 +0x7f8e +0x02bc +0x7dd2 +0x7c1d +0x7a41 +0x7db5 0x7eec OP_SetChatServer2 0x0904 OP_CorpseDrag 0x7037 OP_CorpseDrop 0x5e19 OP_TaskActivityComplete -0x4e32 +0x4e32 0x241d OP_TributeToggle -0x756a -0x7745 -0x039d +0x756a +0x7745 +0x039d 0x0f50 OP_ClearAA 0x66b5 OP_SendAATable 0x0afb OP_AugmentInfo -0x10f6 -0x1013 - +0x10f6 +0x1013 + 0x6344 OP_RequestTitles 0x2d08 OP_SendTitleList -0x3719 -0x7a48 +0x3719 +0x7a48 0x0a23 OP_AcceptNewTask -0x705b +0x705b 0x3bc9 OP_LevelAppearance -0x60ef -0x1619 +0x60ef +0x1619 0x17fd OP_VoiceMacroIn 0x409a OP_VoiceMacroOut 0x4493 OP_WorldComplete @@ -936,437 +936,437 @@ RoF2 Built May 10 2013 23:30:08 0x127f OP_CameraEffect 0x0d32 OP_NewTitlesAvailable 0x34a7 OP_WeaponEquip1 -0x34cd +0x34cd 0x5936 OP_SpellEffect 0x241e OP_AutoFire 0x66f0 OP_UpdateAA 0x100e OP_CustomTitles -0x6b65 -0x12f5 +0x6b65 +0x12f5 0x7677 OP_Bandolier -0x688f -0x7adf -0x0ed4 +0x688f +0x7adf +0x0ed4 0x243a OP_Barter -0x1a6a -0x5623 +0x1a6a +0x5623 0x43c8 OP_SendAAStats -0x655e +0x655e 0x1a3e OP_PotionBelt 0x6326 OP_SetStartCity -0x7485 -0x5416 -0x3282 -0x3752 -0x425b -0x27c8 -0x2b19 -0x70ce -0x3165 -0x786b -0x0f26 -0x3500 -0x3d04 +0x7485 +0x5416 +0x3282 +0x3752 +0x425b +0x27c8 +0x2b19 +0x70ce +0x3165 +0x786b +0x0f26 +0x3500 +0x3d04 0x5134 OP_GuildBank -0x0521 -0x7850 -0x108b -0x5671 -0x6d2b -0x732f +0x0521 +0x7850 +0x108b +0x5671 +0x6d2b +0x732f 0x748f OP_GuildManageBanker -0x6858 -0x5e74 -0x3f35 -0x35e9 -0x2056 +0x6858 +0x5e74 +0x3f35 +0x35e9 +0x2056 0x6922 OP_AdventureMerchantRequest 0x5b72 OP_AdventureMerchantPurchase 0x2f9b OP_AdventureMerchantSell 0x3e47 OP_AdventureMerchantResponse 0x0b7d OP_DzChooseZone -0x2818 -0x35bd -0x51df -0x1ff7 -0x3926 -0x6265 -0x4ab0 -0x5e6c -0x1350 -0x6288 -0x7348 +0x2818 +0x35bd +0x51df +0x1ff7 +0x3926 +0x6265 +0x4ab0 +0x5e6c +0x1350 +0x6288 +0x7348 0x48a2 OP_OpenNewTasksWindow -0x3010 -0x45db +0x3010 +0x45db 0x36e8 OP_AvaliableTask -0x4865 -0x322e -0x7582 +0x4865 +0x322e +0x7582 0x5727 OP_TaskMemberList -0x6646 -0x37f2 -0x3444 -0x5ffc -0x5cb5 -0x0119 -0x35b5 -0x6cc6 -0x4926 -0x1b1d -0x7299 -0x0bf1 -0x08b4 -0x7f7a -0x3dab -0x1e7d -0x610a -0x04c8 -0x4811 -0x609e -0x65f0 +0x6646 +0x37f2 +0x3444 +0x5ffc +0x5cb5 +0x0119 +0x35b5 +0x6cc6 +0x4926 +0x1b1d +0x7299 +0x0bf1 +0x08b4 +0x7f7a +0x3dab +0x1e7d +0x610a +0x04c8 +0x4811 +0x609e +0x65f0 0x467f OP_CrystalCountUpdate 0x7aee OP_CrystalCreate 0x2439 OP_CrystalReclaim -0x39c1 -0x555e -0x7c9c -0x2c94 -0x2fc2 -0x2067 -0x059e -0x7f74 -0x68b2 -0x6f2b -0x01d6 -0x5182 -0x1da2 -0x5147 -0x51f8 -0x11f3 -0x0d07 -0x272f -0x413f -0x5968 +0x39c1 +0x555e +0x7c9c +0x2c94 +0x2fc2 +0x2067 +0x059e +0x7f74 +0x68b2 +0x6f2b +0x01d6 +0x5182 +0x1da2 +0x5147 +0x51f8 +0x11f3 +0x0d07 +0x272f +0x413f +0x5968 0x3e0e OP_DzCompass -0x282a +0x282a 0x035f OP_GMNameChange -0x4013 -0x0e2f -0x099e +0x4013 +0x0e2f +0x099e 0x4eba OP_CompletedTasks 0x5f1c OP_TaskHistoryRequest 0x3d05 OP_TaskHistoryReply -0x2b8e -0x66d6 -0x0143 -0x6a5d -0x6e61 -0x2b0f +0x2b8e +0x66d6 +0x0143 +0x6a5d +0x6e61 +0x2b0f 0x46c6 OP_FloatListThing -0x3a8d -0x15e5 -0x7d89 -0x4085 -0x1507 -0x5d93 +0x3a8d +0x15e5 +0x7d89 +0x4085 +0x1507 +0x5d93 0x1669 OP_ResetAA -0x4664 -0x312d -0x2215 +0x4664 +0x312d +0x2215 0x1745 OP_Rewind -0x0cf1 -0x6567 -0x4405 -0x72d8 -0xaaba +0x0cf1 +0x6567 +0x4405 +0x72d8 +0xaaba 0x27f8 OP_AssistGroup -0x7cfb -0x1a32 -0x14fd -0x77bb -0x36d1 -0x6193 -0x184c -0x3c47 -0x4e0b -0x7c15 +0x7cfb +0x1a32 +0x14fd +0x77bb +0x36d1 +0x6193 +0x184c +0x3c47 +0x4e0b +0x7c15 0x2ec3 OP_ShroudSelectionWindow 0x2b37 OP_ShroudRequestStats 0x6c04 OP_ShroudRespondStats 0x1cd0 OP_ShroudSelect 0x79da OP_ShroudSelectCancel -0x7806 +0x7806 0x541d OP_ShroudProgress 0x3823 OP_ShroudProgress2 0x6562 OP_Shroud -0x4b25 -0x2507 -0x3acc -0x006e -0x06e -0x0728 -0x66cd -0x3c54 -0x2e8d -0x4eea -0x5a67 -0x3d20 -0x649c -0x21a6 +0x4b25 +0x2507 +0x3acc +0x006e +0x06e +0x0728 +0x66cd +0x3c54 +0x2e8d +0x4eea +0x5a67 +0x3d20 +0x649c +0x21a6 0x28bc OP_RespawnWindow 0x0ecb OP_ZonePlayerToBind -0x08d8 -0x7b71 -0x1115 -0x18cb -0x34f4 -0x5f08 -0x5bd2 -0x5052 -0x5e69 -0x2ccc -0x655c +0x08d8 +0x7b71 +0x1115 +0x18cb +0x34f4 +0x5f08 +0x5bd2 +0x5052 +0x5e69 +0x2ccc +0x655c 0x6773 OP_CharacterCreateRequest 0x590e OP_VetRewardsAvaliable -0x6b0b -0x555a -0x7786 +0x6b0b +0x555a +0x7786 0x67e8 OP_ShroudRemove -0x6381 -0x5628 -0xb52f -0x6084 +0x6381 +0x5628 +0xb52f +0x6084 0x2958 OP_GuildUpdateURLAndChannel 0x2301 OP_WorldUnknown001 -0x610b -0x6f8b +0x610b +0x6f8b 0x4d25 OP_InspectMessageUpdate 0x3033 OP_BlockedBuffs 0x0de7 OP_RemoveBlockedBuffs -0x30e5 -0x618c -0x58e6 +0x30e5 +0x618c +0x58e6 0x1456 OP_ShroudUnknown1 0x053c OP_Untargetable -0x71da -0x333f -0x49bc +0x71da +0x333f +0x49bc 0x64f2 OP_BuffRemoveRequest -0x6b3d +0x6b3d 0x34cb OP_ClearBlockedBuffs -0x5646 -0x7d13 -0x15e0 -0x5710 -0x172b -0x1888 -0x2956 -0x6a68 -0x7631 -0x298e -0x003c -0x03c -0x0b0f -0x5a72 -0x0767 -0x2f1a -0x6c7a -0x1660 -0x344f -0x028b -0x6eea -0x7707 -0x3fb2 -0x289e +0x5646 +0x7d13 +0x15e0 +0x5710 +0x172b +0x1888 +0x2956 +0x6a68 +0x7631 +0x298e +0x003c +0x03c +0x0b0f +0x5a72 +0x0767 +0x2f1a +0x6c7a +0x1660 +0x344f +0x028b +0x6eea +0x7707 +0x3fb2 +0x289e 0x3342 OP_GroupMentor 0x5892 OP_NPCMoveUpdate 0x189c OP_ItemVerifyRequest 0x097b OP_ItemVerifyReply -0x2115 -0x6411 -0x6471 -0x134a -0x1304 +0x2115 +0x6411 +0x6471 +0x134a +0x1304 0x5a79 OP_ShieldGroup -0x2dde -0x7d50 -0x1d47 -0x10ec +0x2dde +0x7d50 +0x1d47 +0x10ec 0x000f OP_RestState -0x00f -0x0f +0x00f +0x0f 0x465b OP_ItemViewUnknown -0x2289 -0x023b -0x4223 -0x7261 -0x2af9 -0x19aa -0x66dd +0x2289 +0x023b +0x4223 +0x7261 +0x2af9 +0x19aa +0x66dd 0x4b64 OP_GMTrainSkillConfirm -0x319e -0x1af3 -0x449c -0x8582 +0x319e +0x1af3 +0x449c +0x8582 0x4b8d #OP_LoginUnknown1 0x298d #OP_LoginUnknown2 -0x06c8 -0x4f93 -0x412d -0x001f -0x01f -0x60f6 -0x1a9e -0x798e -0x17b7 -0x3042 -0x61bd -0x1f6e -0x65a6 -0x740d -0x48da -0x20d9 -0x5258 -0x1b5d -0x49f4 -0x7aa9 -0xb350 -0x6e2a -0x5d4e -0x6e4d -0x4ffc -0x1d15 -0x6f23 -0x2296 -0x765b -0x2e01 -0x26dd -0x72d3 -0x6981 -0x3b30 -0x14ac -0x0d92 -0x0001 -0x001 -0x01 -0x09bb -0x9e18 -0x0d9d -0x7f2b -0x3651 -0x645d -0x3af2 -0x4377 -0x39c9 -0x4924 -0x1e50 -0x4683 -0x0276 -0x6dec -0x56c9 -0x3ee6 -0x7121 -0x62ab -0x5d88 -0x05f0 +0x06c8 +0x4f93 +0x412d +0x001f +0x01f +0x60f6 +0x1a9e +0x798e +0x17b7 +0x3042 +0x61bd +0x1f6e +0x65a6 +0x740d +0x48da +0x20d9 +0x5258 +0x1b5d +0x49f4 +0x7aa9 +0xb350 +0x6e2a +0x5d4e +0x6e4d +0x4ffc +0x1d15 +0x6f23 +0x2296 +0x765b +0x2e01 +0x26dd +0x72d3 +0x6981 +0x3b30 +0x14ac +0x0d92 +0x0001 +0x001 +0x01 +0x09bb +0x9e18 +0x0d9d +0x7f2b +0x3651 +0x645d +0x3af2 +0x4377 +0x39c9 +0x4924 +0x1e50 +0x4683 +0x0276 +0x6dec +0x56c9 +0x3ee6 +0x7121 +0x62ab +0x5d88 +0x05f0 0x6b6d OP_AltCurrency -0x61cb -0x0165 +0x61cb +0x0165 0x74ec OP_MercenaryAssign 0x5409 OP_AltCurrencyMerchantRequest 0x3788 OP_AltCurrencyPurchase 0x40b6 OP_AltCurrencySell 0x27a2 OP_AltCurrencyMerchantReply 0x532a OP_AltCurrencySellSelection -0x3899 -0x7567 -0x4820 +0x3899 +0x7567 +0x4820 0x0339 OP_AltCurrencyReclaim -0x0c12 -0x074d -0x47ba -0x55c8 -0x4c89 -0x18b7 -0x2950 -0x44cb -0x4477 -0x6146 -0x40cf -0x2405 -0x49e2 -0x2aff -0x0bfa -0x26c0 -0x7d39 -0x259f -0x086b -0x6dc1 -0x6f80 -0x042e -0x2ad4 -0x317b -0x73ac -0x18d7 -0x5b8b -0x5c83 -0x1f51 -0x62f7 -0x1c46 -0x2a44 -0x3e7a -0x4374 -0x3b23 -0x1df4 -0x3ed8 -0x4b50 -0x36eb -0x65c7 -0x5620 -0x587e -0x3897 -0x7e62 -0x67ae -0x74f4 +0x0c12 +0x074d +0x47ba +0x55c8 +0x4c89 +0x18b7 +0x2950 +0x44cb +0x4477 +0x6146 +0x40cf +0x2405 +0x49e2 +0x2aff +0x0bfa +0x26c0 +0x7d39 +0x259f +0x086b +0x6dc1 +0x6f80 +0x042e +0x2ad4 +0x317b +0x73ac +0x18d7 +0x5b8b +0x5c83 +0x1f51 +0x62f7 +0x1c46 +0x2a44 +0x3e7a +0x4374 +0x3b23 +0x1df4 +0x3ed8 +0x4b50 +0x36eb +0x65c7 +0x5620 +0x587e +0x3897 +0x7e62 +0x67ae +0x74f4 0x4613 OP_SendFindableNPCs -0x2a92 -0x6d6e -0x2c01 -0x1243 -0x133e -0x67fc - +0x2a92 +0x6d6e +0x2c01 +0x1243 +0x133e +0x67fc + 0x49e1 OP_HideCorpse -0x0e44 -0x239a -0x1a0a -0x398f -0x1ff4 -0x88a1 +0x0e44 +0x239a +0x1a0a +0x398f +0x1ff4 +0x88a1 0x74da OP_GroupDisbandOther 0x21b4 OP_GroupLeaderChange -0x62b7 -0x4ced +0x62b7 +0x4ced 0x3abb OP_GroupUpdate 0x0f6c OP_GroupDelete -0x0cbc +0x0cbc 0x6194 OP_GroupUpdateB -0x7fe6 -0x04d0 +0x7fe6 +0x04d0 0x7323 OP_GroupAcknowledge 0x1ae5 OP_GroupDisbandYou -0x4d9f +0x4d9f 0x70e2 OP_GroupRoles -0x6875 -0x6298 +0x6875 +0x6298 0x02cf OP_GroupLeadershipAAUpdate 0x22b8 OP_MercenarySuspend2 -0x7bff -0x5512 -0x2b57 -0x1380 -0x2bcb +0x7bff +0x5512 +0x2b57 +0x1380 +0x2bcb 0x7b89 OP_MercenaryDataUpdateRequest 0x61a4 OP_MercenaryDataUpdate 0x11c1 OP_MercenaryDataRequest @@ -1375,297 +1375,297 @@ RoF2 Built May 10 2013 23:30:08 0x6e83 OP_MercenaryDismiss 0x31e4 OP_MercenaryTimerRequest 0x0763 OP_MercenaryTimer -0x78f6 -0x1b37 +0x78f6 +0x1b37 0x27f2 OP_MercenaryCommand -0x5df8 -0x4333 -0x69ca -0x6e9f +0x5df8 +0x4333 +0x69ca +0x6e9f 0x4407 OP_MercenarySuspendRequest 0x6f03 OP_MercenarySuspendResponse 0x27a0 OP_MercenaryUnsuspendResponse -0x5f88 -0x2749 -0x038f -0x0e52 -0x20b9 +0x5f88 +0x2749 +0x038f +0x0e52 +0x20b9 0x5d26 OP_MercenaryUnknown1 -0x69e7 -0x66b9 -0x0b72 -0x7e6f -0x29ec -0x6248 -0x702b -0x2b4f -0x6e6d -0x1e9f -0x5bd5 -0x4b7f -0x1490 -0x075e -0x4263 -0x1eba -0x4a74 -0x0a37 -0x3289 -0x3171 -0x0114 -0x5148 -0x76c3 -0x09bf -0x356f -0x77a7 -0x479a -0x209f -0x54e6 -0x6c3e -0xee80 -0x40e5 +0x69e7 +0x66b9 +0x0b72 +0x7e6f +0x29ec +0x6248 +0x702b +0x2b4f +0x6e6d +0x1e9f +0x5bd5 +0x4b7f +0x1490 +0x075e +0x4263 +0x1eba +0x4a74 +0x0a37 +0x3289 +0x3171 +0x0114 +0x5148 +0x76c3 +0x09bf +0x356f +0x77a7 +0x479a +0x209f +0x54e6 +0x6c3e +0xee80 +0x40e5 0x76d9 OP_GuildCreate -0x1dc8 -0x794a -0x35c5 -0x137d -0x004a -0x04a -0x29b4 -0x18f1 -0x17fc +0x1dc8 +0x794a +0x35c5 +0x137d +0x004a +0x04a +0x29b4 +0x18f1 +0x17fc 0x4707 OP_ChangeSize -0x3e15 -0x7248 -0x57c6 -0x7679 -0x6c8b -0x14c3 -0x3a02 -0x7900 -0x5688 -0x3a58 -0x75dd -0x39e1 -0x47cb -0x171e -0xdab0 -0x618f -0x27b1 -0x3d0c -0x0d2a -0x8c30 -0x2b42 -0x17f8 -0x1665 -0x059d -0x72c9 -0x675d -0x28e0 -0x61df -0x3ef8 -0x4d59 -0x3763 +0x3e15 +0x7248 +0x57c6 +0x7679 +0x6c8b +0x14c3 +0x3a02 +0x7900 +0x5688 +0x3a58 +0x75dd +0x39e1 +0x47cb +0x171e +0xdab0 +0x618f +0x27b1 +0x3d0c +0x0d2a +0x8c30 +0x2b42 +0x17f8 +0x1665 +0x059d +0x72c9 +0x675d +0x28e0 +0x61df +0x3ef8 +0x4d59 +0x3763 0x672f OP_XTargetResponse 0x45be OP_XTargetRequest 0x792c OP_XTargetAutoAddHaters -0x66bf -0x66df -0x2aa7 -0x76c6 -0x6032 -0x3e00 -0x0c13 -0x0a59 -0x393a -0x45ed -0x507f -0x68ba -0x5a63 -0x6fd0 -0x63fd -0x4f09 -0x485d -0x3968 -0x69e1 -0x3d94 -0x0351 -0x5f0a -0x36be -0x59f9 -0x7075 -0x6ee6 -0x2691 -0x3c8b -0x01df -0x218c -0x233b -0x2cf7 -0x1097 -0x1baf -0x6f35 -0x1228 -0x1cef -0x7d28 -0x087f -0x1967 -0x6917 -0x613d -0x37d9 -0x58c7 -0x21ec -0x3424 -0x2036 -0x7ad1 -0x01c3 -0x626e -0x711a -0x5b41 -0x21ba -0x2933 -0x050c -0x51e3 -0x64a3 -0x3146 -0x2aa6 -0x16df -0x2698 -0x21ea -0x796c -0x7bb1 -0x6af6 -0x499a -0x3c44 -0x3cbc -0x6561 -0x12f7 -0x6292 -0x3de0 -0x1409 -0x4604 -0x0ca1 -0x754e -0x4f2b -0x6995 -0x3745 -0x789b -0xcadf -0x4abe -0x6014 -0x7fff -0x6228 -0x10e0 -0x7452 -0x6d9f -0x7f80 -0xb07f +0x66bf +0x66df +0x2aa7 +0x76c6 +0x6032 +0x3e00 +0x0c13 +0x0a59 +0x393a +0x45ed +0x507f +0x68ba +0x5a63 +0x6fd0 +0x63fd +0x4f09 +0x485d +0x3968 +0x69e1 +0x3d94 +0x0351 +0x5f0a +0x36be +0x59f9 +0x7075 +0x6ee6 +0x2691 +0x3c8b +0x01df +0x218c +0x233b +0x2cf7 +0x1097 +0x1baf +0x6f35 +0x1228 +0x1cef +0x7d28 +0x087f +0x1967 +0x6917 +0x613d +0x37d9 +0x58c7 +0x21ec +0x3424 +0x2036 +0x7ad1 +0x01c3 +0x626e +0x711a +0x5b41 +0x21ba +0x2933 +0x050c +0x51e3 +0x64a3 +0x3146 +0x2aa6 +0x16df +0x2698 +0x21ea +0x796c +0x7bb1 +0x6af6 +0x499a +0x3c44 +0x3cbc +0x6561 +0x12f7 +0x6292 +0x3de0 +0x1409 +0x4604 +0x0ca1 +0x754e +0x4f2b +0x6995 +0x3745 +0x789b +0xcadf +0x4abe +0x6014 +0x7fff +0x6228 +0x10e0 +0x7452 +0x6d9f +0x7f80 OP_ItemPreviewRequest +0xb07f 0x6b5c OP_ItemPreview -0x02b4 -0x3bd3 -0x3036 -0x5c3f -0x433d -0x78e2 -0x543f -0x58b4 -0x2563 -0x1820 -0x40be -0x62d6 -0x0d68 -0x6d10 -0x3a32 -0x2fed -0x1303 -0x49df -0x3e24 -0x2b35 -0x0910 -0x3849 -0x6873 -0x15e2 -0x2d85 -0x39bb -0x42e9 -0x6860 -0x15a8 -0x52b5 -0x3311 -0x58df -0x2a7f -0x6573 -0x7a4d -0x7497 -0x1fcc -0x7c23 -0x2d28 -0x5dab -0x005a -0x05a -0x4506 -0x046d -0x36db -0x5a40 -0x4cd9 -0x63d7 -0x48d4 -0x035d -0x11f5 -0x7b84 -0x4f05 -0x7369 -0x7b32 -0x4fe6 -0x6cd0 -0x6770 -0x5c24 -0x063a -0x0d93 -0x4c2a -0x2235 -0x7b95 -0x6a1f -0x46f0 -0x2de2 -0xadd7 -0x2cc6 -0x7db7 -0x7588 -0x4957 -0x6a98 - +0x02b4 +0x3bd3 +0x3036 +0x5c3f +0x433d +0x78e2 +0x543f +0x58b4 +0x2563 +0x1820 +0x40be +0x62d6 +0x0d68 +0x6d10 +0x3a32 +0x2fed +0x1303 +0x49df +0x3e24 +0x2b35 +0x0910 +0x3849 +0x6873 +0x15e2 +0x2d85 +0x39bb +0x42e9 +0x6860 +0x15a8 +0x52b5 +0x3311 +0x58df +0x2a7f +0x6573 +0x7a4d +0x7497 +0x1fcc +0x7c23 +0x2d28 +0x5dab +0x005a +0x05a +0x4506 +0x046d +0x36db +0x5a40 +0x4cd9 +0x63d7 +0x48d4 +0x035d +0x11f5 +0x7b84 +0x4f05 +0x7369 +0x7b32 +0x4fe6 +0x6cd0 +0x6770 +0x5c24 +0x063a +0x0d93 +0x4c2a +0x2235 +0x7b95 +0x6a1f +0x46f0 +0x2de2 +0xadd7 +0x2cc6 +0x7db7 +0x7588 +0x4957 +0x6a98 + 0x057b OP_SendMembershipDetails 0x7acc OP_SendMembership -0x431f -0x42c4 -0x05a8 +0x431f +0x42c4 +0x05a8 0x5475 OP_SendMaxCharacters -0x4ad2 -0x03d9 -0x771e -0x2a34 -0x7273 -0x1c9e -0x5f17 -0x37a9 -0x6530 -0x3157 -0x1f5e -0x2bc8 -0x593a -0x1643 -0x16bc -0x1781 -0x53d3 -0x5369 -0x25b3 -0x2fa0 -0x73a9 -0x1595 -0x6b6f -0x65f2 -0x3562 -0x309d -0x4efa -0x1da9 -0x6678 -0x16e8 \ No newline at end of file +0x4ad2 +0x03d9 +0x771e +0x2a34 +0x7273 +0x1c9e +0x5f17 +0x37a9 +0x6530 +0x3157 +0x1f5e +0x2bc8 +0x593a +0x1643 +0x16bc +0x1781 +0x53d3 +0x5369 +0x25b3 +0x2fa0 +0x73a9 +0x1595 +0x6b6f +0x65f2 +0x3562 +0x309d +0x4efa +0x1da9 +0x6678 +0x16e8 diff --git a/world/main.cpp b/world/main.cpp index aff459fb9..71109bb95 100644 --- a/world/main.cpp +++ b/world/main.cpp @@ -46,6 +46,7 @@ #include "client.h" #include "worlddb.h" #include "wguild_mgr.h" +#include "../common/evolving_items.h" #ifdef _WINDOWS #include @@ -111,6 +112,7 @@ WorldContentService content_service; WebInterfaceList web_interface; PathManager path; PlayerEventLogs player_event_logs; +EvolvingItemsManager evolving_items_manager; void CatchSignal(int sig_num); diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 1ead62e1b..798114399 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -17,6 +17,7 @@ SET(zone_sources botspellsai.cpp cheat_manager.cpp client.cpp + client_evolving_items.cpp client_bot.cpp client_mods.cpp client_packet.cpp diff --git a/zone/attack.cpp b/zone/attack.cpp index 5da213bb0..41016be13 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2812,7 +2812,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy if (con_level != ConsiderColor::Gray) { if (!GetOwner() || (GetOwner() && !GetOwner()->IsClient())) { - give_exp_client->AddEXP(ExpSource::Kill, final_exp, con_level); + give_exp_client->AddEXP(ExpSource::Kill, final_exp, con_level, false, this); if ( killer_mob && diff --git a/zone/client.cpp b/zone/client.cpp index 62c654834..a3cad1cf2 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13419,3 +13419,20 @@ void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) return; } + +void Client::SetAAEXPPercentage(uint8 percentage) +{ + const uint32 before_percentage = m_epp.perAA; + + if (before_percentage > 0 && percentage == 0) { + MessageString(Chat::White, AA_OFF); + } + else if (before_percentage == 0 && percentage > 0) { + MessageString(Chat::White, AA_ON); + } + + m_epp.perAA = EQ::Clamp(static_cast(percentage), 0, 100); + + SendAlternateAdvancementStats(); + SendAlternateAdvancementTable(); +} diff --git a/zone/client.h b/zone/client.h index f0dafb877..20e6ebba6 100644 --- a/zone/client.h +++ b/zone/client.h @@ -72,6 +72,7 @@ namespace EQ #include "../common/repositories/trader_repository.h" #include "../common/guild_base.h" #include "../common/repositories/buyer_buy_lines_repository.h" +#include "../common/repositories/character_evolving_items_repository.h" #ifdef _WINDOWS // since windows defines these within windef.h (which windows.h include) @@ -294,7 +295,7 @@ public: void SendBazaarDone(uint32 trader_id); void SendBulkBazaarTraders(); void SendBulkBazaarBuyers(); - void DoBazaarInspect(const BazaarInspect_Struct &in); + void DoBazaarInspect(BazaarInspect_Struct &in); void SendBazaarDeliveryCosts(); static std::string DetermineMoneyString(uint64 copper); @@ -692,6 +693,8 @@ public: void SetAAEXPModifier(uint32 zone_id, float aa_modifier, int16 instance_version = -1); void SetEXPModifier(uint32 zone_id, float exp_modifier, int16 instance_version = -1); + void SetAAEXPPercentage(uint8 percentage); + bool UpdateLDoNPoints(uint32 theme_id, int points); void SetLDoNPoints(uint32 theme_id, uint32 points); void SetPVPPoints(uint32 Points) { m_pp.PVPCurrentPoints = Points; } @@ -708,13 +711,13 @@ public: void SendCrystalCounts(); uint64 GetExperienceForKill(Mob *against); - void AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false); + void AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false, NPC* npc = nullptr); uint64 CalcEXP(uint8 conlevel = 0xFF, bool ignore_mods = false); void CalculateNormalizedAAExp(uint64 &add_aaxp, uint8 conlevel, bool resexp); void CalculateStandardAAExp(uint64 &add_aaxp, uint8 conlevel, bool resexp); void CalculateLeadershipExp(uint64 &add_exp, uint8 conlevel); void CalculateExp(uint64 in_add_exp, uint64 &add_exp, uint64 &add_aaxp, uint8 conlevel, bool resexp); - void SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool resexp = false); + void SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool resexp = false, NPC* npc = nullptr); void AddLevelBasedExp(ExpSource exp_source, uint8 exp_percentage, uint8 max_level = 0, bool ignore_mods = false); void SetLeadershipEXP(uint64 group_exp, uint64 raid_exp); void AddLeadershipEXP(uint64 group_exp, uint64 raid_exp); @@ -1128,6 +1131,10 @@ public: void SetTrader(bool status) { trader = status; } uint16 GetTraderID() { return trader_id; } void SetTraderID(uint16 id) { trader_id = id; } + void SetTraderCount(uint32 no) { m_trader_count = no; } + uint32 GetTraderCount() { return m_trader_count; } + void IncrementTraderCount() { m_trader_count += 1; } + void DecrementTraderCount() { m_trader_count > 0 ? m_trader_count -= 1 : m_trader_count = 0; } eqFilterMode GetFilter(eqFilterType filter_id) const { return ClientFilters[filter_id]; } void SetFilter(eqFilterType filter_id, eqFilterMode filter_mode) { ClientFilters[filter_id] = filter_mode; } @@ -1825,6 +1832,18 @@ public: void RecordKilledNPCEvent(NPC *n); uint32 GetEXPForLevel(uint16 check_level); + + // Evolving Item Info + void ProcessEvolvingItem(const uint64 exp, const Mob* mob); + void SendEvolvingPacket(int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item); + void DoEvolveItemToggle(const EQApplicationPacket* app); + void DoEvolveItemDisplayFinalResult(const EQApplicationPacket* app); + bool DoEvolveCheckProgression(const EQ::ItemInstance &inst); + void SendEvolveXPWindowDetails(const EQApplicationPacket* app); + void DoEvolveTransferXP(const EQApplicationPacket* app); + void SendEvolveXPTransferWindow(); + void SendEvolveTransferResults(const EQ::ItemInstance &inst_from, const EQ::ItemInstance &inst_to, const EQ::ItemInstance &inst_from_new, const EQ::ItemInstance &inst_to_new, const uint32 compatibility, const uint32 max_transfer_level); + protected: friend class Mob; void CalcEdibleBonuses(StatBonuses* newbon); @@ -1961,6 +1980,7 @@ private: uint8 firstlogon; uint32 mercid; // current merc uint8 mercSlot; // selected merc slot + uint32 m_trader_count{}; uint32 m_buyer_id; uint32 m_barter_time; int32 m_parcel_platinum; @@ -1983,6 +2003,8 @@ private: uint16 m_door_tool_entity_id; uint16 m_object_tool_entity_id; + + public: uint16 GetDoorToolEntityId() const; void SetDoorToolEntityId(uint16 door_tool_entity_id); diff --git a/zone/client_evolving_items.cpp b/zone/client_evolving_items.cpp new file mode 100644 index 000000000..4fb2238b0 --- /dev/null +++ b/zone/client_evolving_items.cpp @@ -0,0 +1,451 @@ +#include "../common/evolving_items.h" + +#include "../common/events/player_event_logs.h" +#include "../common/global_define.h" + +#include "client.h" +#include "string_ids.h" +#include "worldserver.h" + +extern WorldServer worldserver; + +void Client::DoEvolveItemToggle(const EQApplicationPacket *app) +{ + const auto in = reinterpret_cast(app->pBuffer); + auto item = CharacterEvolvingItemsRepository::FindOne(database, in->unique_id); + + LogEvolveItemDetail( + "Character ID [{}] requested to set evolve item with unique id [{}] to status [{}]", + CharacterID(), + in->unique_id, + in->activated + ); + + if (!item.id) { + LogEvolveItemDetail( + "Character ID [{}] toggle evolve item unique id [{}] failed", CharacterID(), in->unique_id); + return; + } + + item.activated = in->activated; + const auto inst = GetInv().GetItem(GetInv().HasItem(item.item_id)); + inst->SetEvolveActivated(item.activated ? true : false); + + CharacterEvolvingItemsRepository::ReplaceOne(database, item); + + SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, item); +} + +void Client::SendEvolvingPacket(const int8 action, const CharacterEvolvingItemsRepository::CharacterEvolvingItems &item) +{ + auto out = std::make_unique(OP_EvolveItem, sizeof(EvolveItemToggle)); + const auto data = reinterpret_cast(out->pBuffer); + + LogEvolveItemDetail( + "Character ID [{}] requested info for evolving item with unique id [{}] status [{}] " + "percentage [{}]", + CharacterID(), + item.id, + item.activated, + item.progression + ); + + data->action = action; + data->unique_id = item.id; + data->percentage = item.progression; + data->activated = item.activated; + + QueuePacket(out.get()); + + LogEvolveItem( + "Sent evolve item with unique id [{}] status [{}] percentage [{}] to Character ID " + "[{}]", + data->unique_id, + data->activated, + data->percentage, + CharacterID() + ); +} + +void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob) +{ + std::vector queue{}; + + for (auto &[key, inst]: GetInv().GetWorn()) { + LogEvolveItemDetail( + "CharacterID [{}] found equipped item ID [{}]", CharacterID(), inst->GetID()); + if (!inst->IsEvolving() || !inst->GetEvolveActivated()) { + LogEvolveItemDetail( + "CharacterID [{}], item ID [{}] not an evolving item.", CharacterID(), inst->GetID()); + continue; + } + + if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check()) { + LogEvolveItemDetail( + "CharacterID [{}], item ID [{}] timer not yet expired. [{}] secs remaining.", + CharacterID(), + inst->GetID(), + inst->GetTimers().at("evolve").GetRemainingTime() / 1000); + continue; + } + + auto const type = evolving_items_manager.GetEvolvingItemsCache().at(inst->GetID()).type; + auto const sub_type = evolving_items_manager.GetEvolvingItemsCache().at(inst->GetID()).sub_type; + + LogEvolveItemDetail( + "CharacterID [{}] item id [{}] type {} sub_type {} is Evolving. Continue processing...", + CharacterID(), + inst->GetID(), + type, + sub_type); + + switch (type) { + case EvolvingItems::Types::AMOUNT_OF_EXP: { + LogEvolveItemDetail("Type [{}] Processing sub_type", type); + if (sub_type == EvolvingItems::SubTypes::ALL_EXP || + (sub_type == EvolvingItems::SubTypes::GROUP_EXP && IsGrouped())) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfGroupExperience) / 100); + } + else if ( + sub_type == EvolvingItems::SubTypes::ALL_EXP || + (sub_type == EvolvingItems::SubTypes::RAID_EXP && IsRaidGrouped())) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfRaidExperience) / 100); + } + else if ( + sub_type == EvolvingItems::SubTypes::ALL_EXP || sub_type == EvolvingItems::SubTypes::SOLO_EXP) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(exp * RuleR(EvolvingItems, PercentOfSoloExperience) / 100); + } + + inst->CalculateEvolveProgression(); + + auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression( + database, inst->GetEvolveUniqueID(), inst->GetEvolveCurrentAmount(), inst->GetEvolveProgression()); + if (!e.id) { + break; + } + + SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e); + + LogEvolveItem( + "Processing Complete for item id [{1}] Type 1 Amount of EXP - SubType [{0}] - " + "Assigned [{2}] of exp to [{1}]", + sub_type, + inst->GetID(), + exp * 0.001 + ); + + if (inst->GetEvolveProgression() >= 100) { + queue.push_back(inst); + } + + break; + } + case EvolvingItems::Types::SPECIFIC_MOB_RACE: { + LogEvolveItemDetail("Type [{}] Processing sub type", type); + if (mob && mob->GetRace() == sub_type) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(1); + inst->CalculateEvolveProgression(); + + auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression( + database, + inst->GetEvolveUniqueID(), + inst->GetEvolveCurrentAmount(), + inst->GetEvolveProgression()); + if (!e.id) { + break; + } + + SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e); + + LogEvolveItem( + "Processing Complete for item id [{1}] Type 3 Specific Mob Race - SubType [{0}] " + "- Increased count by 1 for [{1}]", + sub_type, + inst->GetID() + ); + } + + if (inst->GetEvolveProgression() >= 100) { + queue.push_back(inst); + } + + break; + } + case EvolvingItems::Types::SPECIFIC_ZONE_ID: { + LogEvolveItemDetail("Type [{}] Processing sub type", type); + if (mob && mob->GetZoneID() == sub_type) { + LogEvolveItemDetail("Sub_Type [{}] Processing Item", sub_type); + inst->SetEvolveAddToCurrentAmount(1); + inst->CalculateEvolveProgression(); + + auto e = CharacterEvolvingItemsRepository::SetCurrentAmountAndProgression( + database, + inst->GetEvolveUniqueID(), + inst->GetEvolveCurrentAmount(), + inst->GetEvolveProgression()); + if (!e.id) { + break; + } + + SendEvolvingPacket(EvolvingItems::Actions::UPDATE_ITEMS, e); + + LogEvolveItem( + "Processing Complete for item id [{1}] Type 4 Specific Zone ID - SubType [{0}] " + "- Increased count by 1 for [{1}]", + sub_type, + inst->GetID() + ); + } + + if (inst->GetEvolveProgression() >= 100) { + queue.push_back(inst); + } + + break; + } + default: { + } + } + } + + if (!queue.empty()) { + for (auto const &i: queue) { + DoEvolveCheckProgression(*i); + } + } +} + +void Client::DoEvolveItemDisplayFinalResult(const EQApplicationPacket *app) +{ + const auto in = reinterpret_cast(app->pBuffer); + + const uint32 item_id = static_cast(in->unique_id & 0xFFFFFFFF); + if (item_id == 0) { + LogEvolveItem("Error - Item ID of final evolve item is blank."); + return; + } + + std::unique_ptr const inst(database.CreateItem(item_id)); + + LogEvolveItemDetail( + "Character ID [{}] requested to view final evolve item id [{}] for evolve item id [{}]", + CharacterID(), + item_id, + evolving_items_manager.GetFirstItemInLoreGroupByItemID(item_id)); + + inst->SetEvolveProgression(100); + + if (inst) { + LogEvolveItemDetail( + "Sending final result for item id [{}] to Character ID [{}]", item_id, CharacterID()); + SendItemPacket(0, inst.get(), ItemPacketViewLink); + } +} + +bool Client::DoEvolveCheckProgression(const EQ::ItemInstance &inst) +{ + if (inst.GetEvolveProgression() < 100 || inst.GetEvolveLvl() == inst.GetMaxEvolveLvl()) { + return false; + } + + const auto new_item_id = evolving_items_manager.GetNextEvolveItemID(inst); + if (!new_item_id) { + return false; + } + + std::unique_ptr const new_inst(database.CreateItem(new_item_id)); + + PlayerEvent::EvolveItem e{}; + + RemoveItemBySerialNumber(inst.GetSerialNumber()); + evolving_items_manager.LoadPlayerEvent(inst, e); + e.status = "Evolved Item due to obtaining progression - Old Evolve Item removed from inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + PushItemOnCursor(*new_inst, true); + evolving_items_manager.LoadPlayerEvent(*new_inst, e); + e.status = "Evolved Item due to obtaining progression - New Evolve Item placed in inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + MessageString(Chat::Yellow, EVOLVE_ITEM_EVOLVED, inst.GetItem()->Name); + + LogEvolveItem( + "Evolved item id [{}] into item id [{}] for Character ID [{}]", + inst.GetID(), + new_inst->GetID(), + CharacterID()); + + return true; +} + +void Client::SendEvolveXPTransferWindow() +{ + auto out = std::make_unique(OP_EvolveItem, sizeof(EvolveItemToggle)); + const auto data = reinterpret_cast(out->pBuffer); + + data->action = 1; + + QueuePacket(out.get()); +} + +void Client::SendEvolveXPWindowDetails(const EQApplicationPacket *app) +{ + const auto in = reinterpret_cast(app->pBuffer); + + const auto item_1_slot = + GetInv().HasEvolvingItem(in->item1_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor); + const auto item_2_slot = + GetInv().HasEvolvingItem(in->item2_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor); + + if (item_1_slot == INVALID_INDEX || item_2_slot == INVALID_INDEX) { + return; + } + + const auto inst_from = GetInv().GetItem(item_1_slot); + const auto inst_to = GetInv().GetItem(item_2_slot); + + if (!inst_from || !inst_to) { + return; + } + + const auto results = evolving_items_manager.DetermineTransferResults(*inst_from, *inst_to); + + if (!results.item_from_id || !results.item_to_id) { + SendEvolveTransferResults(*inst_from, *inst_to, *inst_from, *inst_to, 0, 0); + return; + } + + std::unique_ptr const inst_from_new(database.CreateItem(results.item_from_id)); + std::unique_ptr const inst_to_new(database.CreateItem(results.item_to_id)); + if (!inst_from_new || !inst_to_new) { + SendEvolveTransferResults(*inst_from, *inst_to, *inst_from, *inst_to, 0, 0); + return; + } + + inst_from_new->SetEvolveCurrentAmount(results.item_from_current_amount); + inst_from_new->CalculateEvolveProgression(); + inst_to_new->SetEvolveCurrentAmount(results.item_to_current_amount); + inst_to_new->CalculateEvolveProgression(); + + SendEvolveTransferResults( + *inst_from, *inst_to, *inst_from_new, *inst_to_new, results.compatibility, results.max_transfer_level); +} + +void Client::DoEvolveTransferXP(const EQApplicationPacket *app) +{ + const auto in = reinterpret_cast(app->pBuffer); + + const auto item_1_slot = + GetInv().HasEvolvingItem(in->item1_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor); + const auto item_2_slot = + GetInv().HasEvolvingItem(in->item2_unique_id, 1, invWherePersonal | invWhereWorn | invWhereCursor); + + if (item_1_slot == INVALID_INDEX || item_2_slot == INVALID_INDEX) { + return; + } + + const auto inst_from = GetInv().GetItem(item_1_slot); + const auto inst_to = GetInv().GetItem(item_2_slot); + + if (!inst_from || !inst_to) { + Message(Chat::Red, "Transfer Failed. Incompatible Items."); + LogEvolveItem("Transfer Failed for Character ID [{}]", CharacterID()); + return; + } + + const auto results = evolving_items_manager.DetermineTransferResults(*inst_from, *inst_to); + + if (!results.item_from_id || !results.item_to_id) { + Message(Chat::Red, "Transfer Failed. Incompatible Items."); + LogEvolveItem("Transfer Failed for Character ID [{}]", CharacterID()); + return; + } + + std::unique_ptr const inst_from_new(database.CreateItem(results.item_from_id)); + std::unique_ptr const inst_to_new(database.CreateItem(results.item_to_id)); + + if (!inst_from_new || !inst_to_new) { + Message(Chat::Red, "Transfer Failed. Incompatible Items."); + LogEvolveItem("Transfer Failed for Character ID [{}]", CharacterID()); + return; + } + + inst_from_new->SetEvolveCurrentAmount(results.item_from_current_amount); + inst_from_new->CalculateEvolveProgression(); + inst_to_new->SetEvolveCurrentAmount(results.item_to_current_amount); + inst_to_new->CalculateEvolveProgression(); + + PlayerEvent::EvolveItem e{}; + + RemoveItemBySerialNumber(inst_from->GetSerialNumber()); + evolving_items_manager.LoadPlayerEvent(*inst_from, e); + e.status = "Transfer XP - Original FROM Evolve Item removed from inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + PushItemOnCursor(*inst_from_new, true); + evolving_items_manager.LoadPlayerEvent(*inst_from_new, e); + e.status = "Transfer XP - Updated FROM item placed in inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + RemoveItemBySerialNumber(inst_to->GetSerialNumber()); + evolving_items_manager.LoadPlayerEvent(*inst_to, e); + e.status = "Transfer XP - Original TO Evolve Item removed from inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + PushItemOnCursor(*inst_to_new, true); + evolving_items_manager.LoadPlayerEvent(*inst_to_new, e); + e.status = "Transfer XP - Updated TO Evolve item placed in inventory."; + RecordPlayerEventLog(PlayerEvent::EVOLVE_ITEM, e); + + LogEvolveItem( + "Evolve Transfer XP resulted in evolved item id [{}] into item id [{}] for Character ID " + "[{}]", + inst_to->GetID(), + inst_to_new->GetID(), + CharacterID() + ); +} + +void Client::SendEvolveTransferResults( + const EQ::ItemInstance &inst_from, + const EQ::ItemInstance &inst_to, + const EQ::ItemInstance &inst_from_new, + const EQ::ItemInstance &inst_to_new, + const uint32 compatibility, + const uint32 max_transfer_level) +{ + std::stringstream ss; + cereal::BinaryOutputArchive ar(ss); + + EvolveXPWindowSend e{}; + e.action = EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS; + e.compatibility = compatibility; + e.item1_unique_id = inst_from.GetEvolveUniqueID(); + e.item2_unique_id = inst_to.GetEvolveUniqueID(); + e.max_transfer_level = max_transfer_level; + e.item1_present = 1; + e.item2_present = 1; + e.serialize_item_1 = inst_from_new.Serialize(0); + e.serialize_item_2 = inst_to_new.Serialize(0); + + { + ar(e); + } + + uint32 packet_size = sizeof(EvolveItemMessaging) + ss.str().length(); + + std::unique_ptr out(new EQApplicationPacket(OP_EvolveItem, packet_size)); + const auto data = reinterpret_cast(out->pBuffer); + + data->action = EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS; + memcpy(data->serialized_data, ss.str().data(), ss.str().length()); + + QueuePacket(out.get()); + + ss.str(""); + ss.clear(); +} diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 648a2fd7e..3dfb24fa2 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -80,7 +80,6 @@ extern PetitionList petition_list; extern EntityList entity_list; typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app); - //Use a map for connecting opcodes since it dosent get used a lot and is sparse std::map ConnectingOpcodes; //Use a static array for connected, for speed @@ -208,6 +207,7 @@ void MapOpcodes() ConnectedOpcodes[OP_Emote] = &Client::Handle_OP_Emote; ConnectedOpcodes[OP_EndLootRequest] = &Client::Handle_OP_EndLootRequest; ConnectedOpcodes[OP_EnvDamage] = &Client::Handle_OP_EnvDamage; + ConnectedOpcodes[OP_EvolveItem] = &Client::Handle_OP_EvolveItem; ConnectedOpcodes[OP_FaceChange] = &Client::Handle_OP_FaceChange; ConnectedOpcodes[OP_FeignDeath] = &Client::Handle_OP_FeignDeath; ConnectedOpcodes[OP_FindPersonRequest] = &Client::Handle_OP_FindPersonRequest; @@ -283,6 +283,7 @@ void MapOpcodes() ConnectedOpcodes[OP_ItemLinkResponse] = &Client::Handle_OP_ItemLinkResponse; ConnectedOpcodes[OP_ItemName] = &Client::Handle_OP_ItemName; ConnectedOpcodes[OP_ItemPreview] = &Client::Handle_OP_ItemPreview; + ConnectedOpcodes[OP_ItemPreviewRequest] = &Client::Handle_OP_ItemPreviewRequest; ConnectedOpcodes[OP_ItemVerifyRequest] = &Client::Handle_OP_ItemVerifyRequest; ConnectedOpcodes[OP_ItemViewUnknown] = &Client::Handle_OP_Ignore; ConnectedOpcodes[OP_Jump] = &Client::Handle_OP_Jump; @@ -1313,7 +1314,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) // set to full support in case they're a gm with items in disabled expansion slots...but, have their gm flag off... // item loss will occur when they use the 'empty' slots, if this is not done m_inv.SetGMInventory(true); - loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ + loaditems = database.GetInventory(this); /* Load Character Inventory */ database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ database.LoadCharacterMaterialColor(cid, &m_pp); /* Load Character Material */ @@ -1873,8 +1874,9 @@ void Client::Handle_OP_AAAction(const EQApplicationPacket *app) PurchaseAlternateAdvancementRank(action->ability); } else if (action->action == aaActionDisableEXP) { //Turn Off AA Exp - if (m_epp.perAA > 0) + if (m_epp.perAA > 0) { MessageString(Chat::White, AA_OFF); + } m_epp.perAA = 0; SendAlternateAdvancementStats(); @@ -9309,6 +9311,30 @@ void Client::Handle_OP_ItemPreview(const EQApplicationPacket *app) return; } +void Client::Handle_OP_ItemPreviewRequest(const EQApplicationPacket* app) +{ + VERIFY_PACKET_LENGTH(OP_ItemPreviewRequest, app, ItemPreview_Struct); + auto ips = (ItemPreview_Struct*) app->pBuffer; + const EQ::ItemData* item = database.GetItem(ips->itemid); + + if (item) { + EQ::ItemInstance* inst = database.CreateItem(item); + if (inst) { + std::string packet = inst->Serialize(-1); + auto outapp = new EQApplicationPacket(OP_ItemPreviewRequest, packet.length()); + memcpy(outapp->pBuffer, packet.c_str(), packet.length()); + +#if EQDEBUG >= 9 + DumpPacket(outapp); +#endif + + QueuePacket(outapp); + safe_delete(outapp); + safe_delete(inst); + } + } +} + void Client::Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app) { using EQ::spells::CastingSlot; @@ -10757,7 +10783,12 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app) InterrogateInventory(this, true, false, true, error); } - return; + for (int slot : {mi->to_slot, mi->from_slot}) { + auto item = GetInv().GetItem(slot); + if (item && item->IsEvolving()) { + CharacterEvolvingItemsRepository::UpdateOne(database, item->GetEvolvingDetails()); + } + } } void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app) @@ -15540,6 +15571,26 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app) // Client has elected to buy an item from a Trader // auto in = (TraderBuy_Struct *) app->pBuffer; + + if (RuleB(Bazaar, UseAlternateBazaarSearch) && in->trader_id >= TraderRepository::TRADER_CONVERT_ID) { + auto trader = TraderRepository::GetTraderByInstanceAndSerialnumber( + database, + in->trader_id - TraderRepository::TRADER_CONVERT_ID, + in->serial_number + ); + + if (!trader.trader_id) { + LogTrading("Unable to convert trader id for {} and serial number {}. Trader Buy aborted.", + in->trader_id - TraderRepository::TRADER_CONVERT_ID, + in->serial_number + ); + return; + } + + in->trader_id = trader.trader_id; + strn0cpy(in->seller_name, trader.trader_name.c_str(), sizeof(in->seller_name)); + } + auto trader = entity_list.GetClientByID(in->trader_id); switch (in->method) { @@ -17175,3 +17226,37 @@ void Client::Handle_OP_ShopRetrieveParcel(const EQApplicationPacket *app) auto parcel_in = (ParcelRetrieve_Struct *)app->pBuffer; DoParcelRetrieve(*parcel_in); } + +void Client::Handle_OP_EvolveItem(const EQApplicationPacket *app) +{ + if (app->size != sizeof(EvolveItemToggle)) { + LogError( + "Received Handle_OP_EvolveItem packet. Expected size {}, received size {}.", + sizeof(EvolveItemToggle), + app->size + ); + return; + } + + auto in = reinterpret_cast(app->pBuffer); + + switch (in->action) { + case EvolvingItems::Actions::UPDATE_ITEMS: { + DoEvolveItemToggle(app); + break; + } + case EvolvingItems::Actions::FINAL_RESULT: { + DoEvolveItemDisplayFinalResult(app); + break; + } + case EvolvingItems::Actions::TRANSFER_XP: { + DoEvolveTransferXP(app); + break; + } + case EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS: { + SendEvolveXPWindowDetails(app); + } + default: { + } + } +} diff --git a/zone/client_packet.h b/zone/client_packet.h index 4ef62bdf9..587e14ac7 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -114,6 +114,7 @@ void Handle_OP_Emote(const EQApplicationPacket *app); void Handle_OP_EndLootRequest(const EQApplicationPacket *app); void Handle_OP_EnvDamage(const EQApplicationPacket *app); + void Handle_OP_EvolveItem(const EQApplicationPacket *app); void Handle_OP_FaceChange(const EQApplicationPacket *app); void Handle_OP_FeignDeath(const EQApplicationPacket *app); void Handle_OP_FindPersonRequest(const EQApplicationPacket *app); @@ -188,6 +189,7 @@ void Handle_OP_ItemLinkResponse(const EQApplicationPacket *app); void Handle_OP_ItemName(const EQApplicationPacket *app); void Handle_OP_ItemPreview(const EQApplicationPacket *app); + void Handle_OP_ItemPreviewRequest(const EQApplicationPacket *app); void Handle_OP_ItemVerifyRequest(const EQApplicationPacket *app); void Handle_OP_Jump(const EQApplicationPacket *app); void Handle_OP_KeyRing(const EQApplicationPacket *app); diff --git a/zone/command.cpp b/zone/command.cpp index f9a353cb1..686255c6d 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -126,6 +126,7 @@ int command_init(void) command_add("enablerecipe", "[Recipe ID] - Enables a Recipe", AccountStatus::QuestTroupe, command_enablerecipe) || command_add("entityvariable", "[clear|delete|set|view] - Modify entity variables for yourself or your target", AccountStatus::GMAdmin, command_entityvariable) || command_add("exptoggle", "[Toggle] - Toggle your or your target's experience gain.", AccountStatus::QuestTroupe, command_exptoggle) || + command_add("evolve", "Evolving Item manipulation commands. Use argument help for more info.", AccountStatus::Steward, command_evolvingitems) || command_add("faction", "[Find (criteria | all ) | Review (criteria | all) | Reset (id)] - Resets Player's Faction", AccountStatus::QuestTroupe, command_faction) || command_add("factionassociation", "[factionid] [amount] - triggers a faction hits via association", AccountStatus::GMLeadAdmin, command_faction_association) || command_add("feature", "Change your or your target's feature's temporarily", AccountStatus::QuestTroupe, command_feature) || @@ -831,6 +832,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/entityvariable.cpp" #include "gm_commands/exptoggle.cpp" #include "gm_commands/faction.cpp" +#include "gm_commands/evolving_items.cpp" #include "gm_commands/feature.cpp" #include "gm_commands/find.cpp" #include "gm_commands/fish.cpp" diff --git a/zone/command.h b/zone/command.h index 308edf293..d3096ce39 100644 --- a/zone/command.h +++ b/zone/command.h @@ -38,6 +38,7 @@ void SendShowInventorySubCommands(Client *c); void SendFixMobSubCommands(Client *c); void SendDataBucketsSubCommands(Client *c); void SendParcelsSubCommands(Client *c); +void SendEvolvingItemsSubCommands(Client *c); // Commands void command_acceptrules(Client *c, const Seperator *sep); @@ -80,6 +81,7 @@ void command_emptyinventory(Client *c, const Seperator *sep); void command_enablerecipe(Client *c, const Seperator *sep); void command_entityvariable(Client *c, const Seperator *sep); void command_exptoggle(Client *c, const Seperator *sep); +void command_evolvingitems(Client *c, const Seperator *sep); void command_faction(Client *c, const Seperator *sep); void command_faction_association(Client *c, const Seperator *sep); void command_feature(Client *c, const Seperator *sep); diff --git a/zone/doors.cpp b/zone/doors.cpp index 3ec9c86af..036f38928 100644 --- a/zone/doors.cpp +++ b/zone/doors.cpp @@ -30,6 +30,7 @@ #include "string_ids.h" #include "worldserver.h" #include "zonedb.h" +#include "../common/evolving_items.h" #include "../common/repositories/criteria/content_filter_criteria.h" #include @@ -610,6 +611,10 @@ void Doors::HandleClick(Client *sender, uint8 trigger) } } } + + if (GetOpenType() == 40 && GetZone(GetDoorZone(),0)) { + sender->SendEvolveXPTransferWindow(); + } } void Doors::Open(Mob *sender, bool alt_mode) diff --git a/zone/doors.h b/zone/doors.h index 3c1ec3704..b93aed657 100644 --- a/zone/doors.h +++ b/zone/doors.h @@ -77,6 +77,8 @@ public: bool IsDoorBlacklisted(); + const char* GetDoorZone() const { return m_zone_name; } + private: bool GetIsDoorBlacklisted(); diff --git a/zone/exp.cpp b/zone/exp.cpp index be6b8af2b..a1f8c8adf 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -497,7 +497,7 @@ void Client::CalculateExp(uint64 in_add_exp, uint64 &add_exp, uint64 &add_aaxp, add_exp = GetEXP() + add_exp; } -void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, bool resexp) { +void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, bool resexp, NPC* npc) { if (!IsEXPEnabled()) { return; } @@ -574,10 +574,10 @@ void Client::AddEXP(ExpSource exp_source, uint64 in_add_exp, uint8 conlevel, boo } // Now update our character's normal and AA xp - SetEXP(exp_source, exp, aaexp, resexp); + SetEXP(exp_source, exp, aaexp, resexp, npc); } -void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool isrezzexp) { +void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool isrezzexp, NPC* npc) { uint64 current_exp = GetEXP(); uint64 current_aa_exp = GetAAXP(); uint64 total_current_exp = current_exp + current_aa_exp; @@ -677,6 +677,7 @@ void Client::SetEXP(ExpSource exp_source, uint64 set_exp, uint64 set_aaxp, bool } } } + ProcessEvolvingItem(exp_gained, npc); } else if(total_add_exp < total_current_exp) { //only loss message if you lose exp, no message if you gained/lost nothing. uint64 exp_lost = current_exp - set_exp; float exp_percent = (float)((float)exp_lost / (float)(GetEXPForLevel(GetLevel() + 1) - GetEXPForLevel(GetLevel())))*(float)100; @@ -1205,7 +1206,7 @@ void Group::SplitExp(ExpSource exp_source, const uint64 exp, Mob* other) { if (diff >= max_diff) { const uint64 tmp = (m->GetLevel() + 3) * (m->GetLevel() + 3) * 75 * 35 / 10; const uint64 tmp2 = group_experience / member_count; - m->CastToClient()->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level); + m->CastToClient()->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level, false, other->CastToNPC()); } } } @@ -1256,7 +1257,7 @@ void Raid::SplitExp(ExpSource exp_source, const uint64 exp, Mob* other) { if (diff >= max_diff) { const uint64 tmp = (m.member->GetLevel() + 3) * (m.member->GetLevel() + 3) * 75 * 35 / 10; const uint64 tmp2 = (raid_experience / member_modifier) + 1; - m.member->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level); + m.member->AddEXP(exp_source, tmp < tmp2 ? tmp : tmp2, consider_level, false, other->CastToNPC()); } } } diff --git a/zone/gm_commands/evolving_items.cpp b/zone/gm_commands/evolving_items.cpp new file mode 100644 index 000000000..3971c0021 --- /dev/null +++ b/zone/gm_commands/evolving_items.cpp @@ -0,0 +1,301 @@ +#include "../client.h" +#include "../command.h" +#include "../../common/evolving_items.h" + +void command_evolvingitems(Client *c, const Seperator *sep) +{ + const uint16 arguments = sep->argnum; + if (!arguments) { + SendEvolvingItemsSubCommands(c); + return; + } + + Client* t = c; + if (c->GetTarget() && c->GetTarget()->IsClient()) { + t = c->GetTarget()->CastToClient(); + } + + const bool is_item = !strcasecmp(sep->arg[1], "item"); + const bool is_target = !strcasecmp(sep->arg[1], "target"); + + if (!is_item && !is_target) { + SendEvolvingItemsSubCommands(c); + return; + } + + if (is_target) { + if (arguments > 1) { + c->Message(Chat::White, "Usage: #evolve target"); + } else { + c->Message(Chat::Red, "Worn Items"); + for (auto const &[key, value]: t->GetInv().GetWorn()) { + if (!value->IsEvolving()) { + continue; + } + + auto item = evolving_items_manager.GetEvolvingItemsCache().at(value->GetID()); + c->Message( + Chat::Yellow, + fmt::format( + "Evolving Items | {:0d}", + value->GetID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Unique ID | {:0d}", + value->GetEvolveUniqueID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Final Item ID | {:0d}", + value->GetEvolveFinalItemID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Activated | {}", + value->GetEvolveActivated() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Equipped | {}", + value->GetEvolveEquipped() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Current Amount | {:0d}", + value->GetEvolveCurrentAmount() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Required Amount | {:0d}", + item.required_amount + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Progression (%) | {:.2f}", + value->GetEvolveProgression() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Type | {}", + item.type + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Subtype | {}", + item.sub_type + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer | {}", + value->GetTimers().at("evolve").Enabled() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer Remaining | {}", + value->GetTimers().at("evolve").GetRemainingTime() + ).c_str() + ); + } + + c->Message(Chat::Red, "Personal Items"); + for (auto const &[key, value]: t->GetInv().GetPersonal()) { + if (!value->IsEvolving()) { + continue; + } + + auto item = evolving_items_manager.GetEvolvingItemsCache().at(value->GetID()); + c->Message( + Chat::Yellow, + fmt::format( + "Evolving Items | {:0d}", + value->GetID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Unique ID | {:0d}", + value->GetEvolveUniqueID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Final Item ID | {:0d}", + value->GetEvolveFinalItemID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Activated | {}", + value->GetEvolveActivated() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Equipped | {}", + value->GetEvolveEquipped() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Current Amount | {:0d}", + value->GetEvolveCurrentAmount() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Required Amount | {:0d}", + item.required_amount + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Progression (%) | {:.2f}", + value->GetEvolveProgression() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Type | {}", + item.type + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Subtype | {}", + item.sub_type + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer | {}", + value->GetTimers().at("evolve").Enabled() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer Remaining | {}", + value->GetTimers().at("evolve").GetRemainingTime() + ).c_str() + ); + } + } + } else if (is_item) { + if (arguments > 2) { + c->Message(Chat::White, "Usage: #evolve item item_id"); + } else if (sep->IsNumber(2)) { + auto item_id = Strings::ToUnsignedInt(sep->arg[2]); + auto item = c->GetInv().GetItem(c->GetInv().HasItem(item_id)); + if (item) { + c->Message( + Chat::Yellow, + fmt::format( + "Evolving Items | {:0d}", + item->GetID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Slot | {:0d}", + c->GetInv().HasItem(item_id) + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Activated | {}", + item->GetEvolveActivated() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Equipped | {}", + item->GetEvolveEquipped() ? "Yes" : "No" + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Unique ID | {:0d}", + item->GetEvolveUniqueID() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Progression (%) | {:.2f}", + item->GetEvolveProgression() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer | {}", + item->GetTimers().at("evolve").Enabled() + ).c_str() + ); + c->Message( + Chat::Yellow, + fmt::format( + "Timer Remaining | {}", + item->GetTimers().at("evolve").GetRemainingTime() + ).c_str() + ); + } else { + c->Message( + Chat::Red, + fmt::format( + "Item {} could not be found in your inventory.", + item_id + ).c_str() + ); + } + } else { + SendEvolvingItemsSubCommands(c); + } + } +} + +void SendEvolvingItemsSubCommands(Client *c) +{ + c->Message(Chat::White, "#evolve item item_id (Shows evolve values within the iteminstance)"); + c->Message( + Chat::White, + "#evolve target (Shows evolve values within the target's cache - Must have a player target selected.)" + ); +} diff --git a/zone/inventory.cpp b/zone/inventory.cpp index 30f4c31da..f89c1005c 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -25,6 +25,7 @@ #include "zonedb.h" #include "../common/events/player_event_logs.h" #include "bot.h" +#include "../common/evolving_items.h" #include "../common/repositories/character_corpse_items_repository.h" extern WorldServer worldserver; @@ -1091,7 +1092,11 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up } // end QS code - bool isDeleted = m_inv.DeleteItem(slot_id, quantity); + uint64 evolve_id = m_inv[slot_id]->GetEvolveUniqueID(); + bool isDeleted = m_inv.DeleteItem(slot_id, quantity); + if (isDeleted && evolve_id && (slot_id > EQ::invslot::TRADE_END || slot_id < EQ::invslot::TRADE_BEGIN)) { + CharacterEvolvingItemsRepository::SoftDelete(database, evolve_id); + } const EQ::ItemInstance* inst = nullptr; if (slot_id == EQ::invslot::slotCursor) { @@ -1143,6 +1148,8 @@ void Client::DeleteItemInInventory(int16 slot_id, int16 quantity, bool client_up bool Client::PushItemOnCursor(const EQ::ItemInstance& inst, bool client_update) { LogInventory("Putting item [{}] ([{}]) on the cursor", inst.GetItem()->Name, inst.GetItem()->ID); + + evolving_items_manager.DoLootChecks(CharacterID(), EQ::invslot::slotCursor, inst); m_inv.PushCursor(inst); if (client_update) { @@ -1163,9 +1170,9 @@ bool Client::PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, boo if (slot_id == EQ::invslot::slotCursor) { // don't trust macros before conditional statements... return PushItemOnCursor(inst, client_update); } - else { - m_inv.PutItem(slot_id, inst); - } + + evolving_items_manager.DoLootChecks(CharacterID(), slot_id, inst); + m_inv.PutItem(slot_id, inst); if (client_update) { @@ -1173,15 +1180,16 @@ bool Client::PutItemInInventory(int16 slot_id, const EQ::ItemInstance& inst, boo //SendWearChange(EQ::InventoryProfile::CalcMaterialFromSlot(slot_id)); } + CalcBonuses(); + if (slot_id == EQ::invslot::slotCursor) { auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); return database.SaveCursor(CharacterID(), s, e); } - else { - return database.SaveInventory(CharacterID(), &inst, slot_id); - } - CalcBonuses(); + return database.SaveInventory(CharacterID(), &inst, slot_id); + + //CalcBonuses(); // this never fires?? // a lot of wasted checks and calls coded above... } @@ -1191,6 +1199,8 @@ void Client::PutLootInInventory(int16 slot_id, const EQ::ItemInstance &inst, Loo bool cursor_empty = m_inv.CursorEmpty(); + evolving_items_manager.DoLootChecks(CharacterID(), slot_id, inst); + if (slot_id == EQ::invslot::slotCursor) { m_inv.PushCursor(inst); auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend(); diff --git a/zone/lua_client.cpp b/zone/lua_client.cpp index 97747d642..566510cf9 100644 --- a/zone/lua_client.cpp +++ b/zone/lua_client.cpp @@ -3458,6 +3458,12 @@ void Lua_Client::ShowZoneShardMenu() self->ShowZoneShardMenu(); } +void Lua_Client::SetAAEXPPercentage(uint8 percentage) +{ + Lua_Safe_Call_Void(); + self->SetAAEXPPercentage(percentage); +} + luabind::scope lua_register_client() { return luabind::class_("Client") .def(luabind::constructor<>()) @@ -3890,6 +3896,7 @@ luabind::scope lua_register_client() { .def("SetAAEXPModifier", (void(Lua_Client::*)(float))&Lua_Client::SetAAEXPModifier) .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,float))&Lua_Client::SetAAEXPModifier) .def("SetAAEXPModifier", (void(Lua_Client::*)(uint32,float,int16))&Lua_Client::SetAAEXPModifier) + .def("SetAAEXPPercentage", (void(Lua_Client::*)(uint8))&Lua_Client::SetAAEXPPercentage) .def("SetAAPoints", (void(Lua_Client::*)(int))&Lua_Client::SetAAPoints) .def("SetAATitle", (void(Lua_Client::*)(std::string))&Lua_Client::SetAATitle) .def("SetAATitle", (void(Lua_Client::*)(std::string,bool))&Lua_Client::SetAATitle) diff --git a/zone/lua_client.h b/zone/lua_client.h index 4374fe199..d86705f2e 100644 --- a/zone/lua_client.h +++ b/zone/lua_client.h @@ -510,6 +510,7 @@ public: void AreaTaunt(float range); void AreaTaunt(float range, int bonus_hate); luabind::object GetInventorySlots(lua_State* L); + void SetAAEXPPercentage(uint8 percentage); void ApplySpell(int spell_id); void ApplySpell(int spell_id, int duration); diff --git a/zone/lua_iteminst.cpp b/zone/lua_iteminst.cpp index ad9ed1405..f81cfc88b 100644 --- a/zone/lua_iteminst.cpp +++ b/zone/lua_iteminst.cpp @@ -255,11 +255,6 @@ int8 Lua_ItemInst::GetMaxEvolveLvl() { return self->GetMaxEvolveLvl(); } -uint32 Lua_ItemInst::GetKillsNeeded(uint8 current_level) { - Lua_Safe_Call_Int(); - return self->GetKillsNeeded(current_level); -} - Lua_ItemInst Lua_ItemInst::Clone() { Lua_Safe_Call_Class(Lua_ItemInst); return Lua_ItemInst(self->Clone(), true); @@ -348,6 +343,96 @@ std::string Lua_ItemInst::GetItemLink() return linker.GenerateLink(); } +void Lua_ItemInst::AddEvolveAmount(uint64 amount) +{ + Lua_Safe_Call_Void(); + self->SetEvolveAddToCurrentAmount(amount); +} + +uint32 Lua_ItemInst::GetAugmentEvolveUniqueID(uint8 slot_id) +{ + Lua_Safe_Call_Int(); + return self->GetAugmentEvolveUniqueID(slot_id); +} + +bool Lua_ItemInst::GetEvolveActivated() +{ + Lua_Safe_Call_Bool(); + return self->GetEvolveActivated(); +} + +uint64 Lua_ItemInst::GetEvolveAmount() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveCurrentAmount(); +} + +uint32 Lua_ItemInst::GetEvolveCharacterID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveCharID(); +} + +bool Lua_ItemInst::GetEvolveEquipped() +{ + Lua_Safe_Call_Bool(); + return self->GetEvolveEquipped(); +} + +uint32 Lua_ItemInst::GetEvolveFinalItemID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveFinalItemID(); +} + +uint32 Lua_ItemInst::GetEvolveItemID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveItemID(); +} + +int8 Lua_ItemInst::GetEvolveLevel() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveLvl(); +} + +uint32 Lua_ItemInst::GetEvolveLoreID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveLoreID(); +} + +float Lua_ItemInst::GetEvolveProgression() +{ + Lua_Safe_Call_Real(); + return self->GetEvolveProgression(); +} + +uint64 Lua_ItemInst::GetEvolveUniqueID() +{ + Lua_Safe_Call_Int(); + return self->GetEvolveUniqueID(); +} + +bool Lua_ItemInst::IsEvolving() +{ + Lua_Safe_Call_Bool(); + return self->IsEvolving(); +} + +void Lua_ItemInst::SetEvolveAmount(uint64 amount) +{ + Lua_Safe_Call_Void(); + self->SetEvolveCurrentAmount(amount); +} + +void Lua_ItemInst::SetEvolveProgression(float amount) +{ + Lua_Safe_Call_Void(); + self->SetEvolveProgression(amount); +} + luabind::scope lua_register_iteminst() { return luabind::class_("ItemInst") .def(luabind::constructor<>()) @@ -356,12 +441,14 @@ luabind::scope lua_register_iteminst() { .property("null", &Lua_ItemInst::Null) .property("valid", &Lua_ItemInst::Valid) .def("AddExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::AddExp) + .def("AddEvolveAmount", (void(Lua_ItemInst::*)(uint64))&Lua_ItemInst::AddEvolveAmount) .def("ClearTimers", (void(Lua_ItemInst::*)(void))&Lua_ItemInst::ClearTimers) .def("Clone", (Lua_ItemInst(Lua_ItemInst::*)(void))&Lua_ItemInst::Clone) .def("ContainsAugmentByID", (bool(Lua_ItemInst::*)(uint32))&Lua_ItemInst::ContainsAugmentByID) .def("CountAugmentByID", (int(Lua_ItemInst::*)(uint32))&Lua_ItemInst::CountAugmentByID) .def("DeleteCustomData", (void(Lua_ItemInst::*)(const std::string &))&Lua_ItemInst::DeleteCustomData) .def("GetAugment", (Lua_ItemInst(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugment) + .def("GetAugmentEvolveUniqueID", (uint32(Lua_ItemInst::*)(uint8))&Lua_ItemInst::GetAugmentEvolveUniqueID) .def("GetAugmentIDs", (luabind::object(Lua_ItemInst::*)(lua_State*))&Lua_ItemInst::GetAugmentIDs) .def("GetAugmentItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetAugmentItemID) .def("GetAugmentType", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetAugmentType) @@ -370,13 +457,22 @@ luabind::scope lua_register_iteminst() { .def("GetCustomData", (std::string(Lua_ItemInst::*)(const std::string &))&Lua_ItemInst::GetCustomData) .def("GetCustomDataString", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetCustomDataString) .def("GetExp", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetExp) + .def("GetEvolveActivated", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveActivated) + .def("GetEvolveAmount", (uint64(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveAmount) + .def("GetEvolveCharacterID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveCharacterID) + .def("GetEvolveEquipped", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveEquipped) + .def("GetEvolveFinalItemID", (uint64(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveFinalItemID) + .def("GetEvolveItemID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveItemID) + .def("GetEvolveLevel", (int8(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveLevel) + .def("GetEvolveLoreID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveLoreID) + .def("GetEvolveProgression", (float(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveProgression) + .def("GetEvolveUniqueID", (uint64(Lua_ItemInst::*)(void))&Lua_ItemInst::GetEvolveUniqueID) .def("GetID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetID) .def("GetItem", (Lua_Item(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItem) .def("GetItem", (Lua_ItemInst(Lua_ItemInst::*)(uint8))&Lua_ItemInst::GetItem) .def("GetItemID", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetItemID) .def("GetItemLink", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemLink) .def("GetItemScriptID", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetItemScriptID) - .def("GetKillsNeeded", (uint32(Lua_ItemInst::*)(int))&Lua_ItemInst::GetKillsNeeded) .def("GetMaxEvolveLvl", (int(Lua_ItemInst::*)(void))&Lua_ItemInst::GetMaxEvolveLvl) .def("GetName", (std::string(Lua_ItemInst::*)(void))&Lua_ItemInst::GetName) .def("GetPrice", (uint32(Lua_ItemInst::*)(void))&Lua_ItemInst::GetPrice) @@ -389,6 +485,7 @@ luabind::scope lua_register_iteminst() { .def("IsAugmented", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsAugmented) .def("IsEquipable", (bool(Lua_ItemInst::*)(int16))&Lua_ItemInst::IsEquipable) .def("IsEquipable", (bool(Lua_ItemInst::*)(uint16,uint16))&Lua_ItemInst::IsEquipable) + .def("IsEvolving", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsEvolving) .def("IsExpendable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsExpendable) .def("IsInstNoDrop", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsInstNoDrop) .def("IsStackable", (bool(Lua_ItemInst::*)(void))&Lua_ItemInst::IsStackable) @@ -404,6 +501,8 @@ luabind::scope lua_register_iteminst() { .def("SetCustomData", (void(Lua_ItemInst::*)(const std::string&,float))&Lua_ItemInst::SetCustomData) .def("SetCustomData", (void(Lua_ItemInst::*)(const std::string&,int))&Lua_ItemInst::SetCustomData) .def("SetCustomData", (void(Lua_ItemInst::*)(const std::string&,const std::string&))&Lua_ItemInst::SetCustomData) + .def("SetEvolveAmount", (void(Lua_ItemInst::*)(uint64))&Lua_ItemInst::SetEvolveAmount) + .def("SetEvolveProgression", (void(Lua_ItemInst::*)(float))&Lua_ItemInst::SetEvolveProgression) .def("SetExp", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetExp) .def("SetInstNoDrop", (void(Lua_ItemInst::*)(bool))&Lua_ItemInst::SetInstNoDrop) .def("SetPrice", (void(Lua_ItemInst::*)(uint32))&Lua_ItemInst::SetPrice) diff --git a/zone/lua_iteminst.h b/zone/lua_iteminst.h index 2ff99165c..a4c7529e4 100644 --- a/zone/lua_iteminst.h +++ b/zone/lua_iteminst.h @@ -77,7 +77,6 @@ public: void SetExp(uint32 exp); void AddExp(uint32 exp); int8 GetMaxEvolveLvl(); - uint32 GetKillsNeeded(uint8 current_level); Lua_ItemInst Clone(); void SetTimer(std::string name, uint32 time); void StopTimer(std::string name); @@ -91,6 +90,21 @@ public: void ItemSay(const char* text, uint8 language_id); luabind::object GetAugmentIDs(lua_State* L); std::string GetItemLink(); + void AddEvolveAmount(uint64 amount); + uint32 GetAugmentEvolveUniqueID(uint8 slot_id); + bool GetEvolveActivated(); + uint64 GetEvolveAmount(); + uint32 GetEvolveCharacterID(); + bool GetEvolveEquipped(); + uint32 GetEvolveFinalItemID(); + uint32 GetEvolveItemID(); + int8 GetEvolveLevel(); + uint32 GetEvolveLoreID(); + float GetEvolveProgression(); + uint64 GetEvolveUniqueID(); + bool IsEvolving(); + void SetEvolveAmount(uint64 amount); + void SetEvolveProgression(float amount); private: bool cloned_; diff --git a/zone/lua_packet.cpp b/zone/lua_packet.cpp index 3e8325e4f..61f30e4a7 100644 --- a/zone/lua_packet.cpp +++ b/zone/lua_packet.cpp @@ -896,6 +896,7 @@ luabind::scope lua_register_packet_opcodes() { luabind::value("Weblink", static_cast(OP_Weblink)), luabind::value("InspectMessageUpdate", static_cast(OP_InspectMessageUpdate)), luabind::value("ItemPreview", static_cast(OP_ItemPreview)), + luabind::value("ItemPreviewRequest", static_cast(OP_ItemPreviewRequest)), luabind::value("MercenaryDataRequest", static_cast(OP_MercenaryDataRequest)), luabind::value("MercenaryDataResponse", static_cast(OP_MercenaryDataResponse)), luabind::value("MercenaryHire", static_cast(OP_MercenaryHire)), diff --git a/zone/main.cpp b/zone/main.cpp index a05af774a..5d463fa9b 100644 --- a/zone/main.cpp +++ b/zone/main.cpp @@ -52,6 +52,7 @@ #include "task_manager.h" #include "quest_parser_collection.h" #include "embparser.h" +#include "../common/evolving_items.h" #include "lua_parser.h" #include "questmgr.h" #include "npc_scale_manager.h" @@ -110,6 +111,7 @@ PathManager path; PlayerEventLogs player_event_logs; DatabaseUpdate database_update; SkillCaps skill_caps; +EvolvingItemsManager evolving_items_manager; const SPDat_Spell_Struct* spells; int32 SPDAT_RECORDS = -1; @@ -387,6 +389,11 @@ int main(int argc, char **argv) title_manager.LoadTitles(); content_db.LoadTributes(); + // Load evolving item data + evolving_items_manager.SetDatabase(&database); + evolving_items_manager.SetContentDatabase(&content_db); + evolving_items_manager.LoadEvolvingItems(); + database.GetDecayTimes(npcCorpseDecayTimes); if (!EQ::ProfanityManager::LoadProfanityList(&database)) { diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 9f49dd86e..bf0c4830d 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -3229,6 +3229,11 @@ perl::array Perl_Client_GetInventorySlots(Client* self) return result; } +void Perl_Client_SetAAEXPPercentage(Client* self, uint8 percentage) +{ + self->SetAAEXPPercentage(percentage); +} + void perl_register_client() { perl::interpreter perl(PERL_GET_THX); @@ -3657,6 +3662,7 @@ void perl_register_client() package.add("SetAAEXPModifier", (void(*)(Client*, float))&Perl_Client_SetAAEXPModifier); package.add("SetAAEXPModifier", (void(*)(Client*, uint32, float))&Perl_Client_SetAAEXPModifier); package.add("SetAAEXPModifier", (void(*)(Client*, uint32, float, int16))&Perl_Client_SetAAEXPModifier); + package.add("SetAAEXPPercentage", &Perl_Client_SetAAEXPPercentage); package.add("SetAAPoints", &Perl_Client_SetAAPoints); package.add("SetAATitle", (void(*)(Client*, std::string))&Perl_Client_SetAATitle); package.add("SetAATitle", (void(*)(Client*, std::string, bool))&Perl_Client_SetAATitle); diff --git a/zone/perl_questitem.cpp b/zone/perl_questitem.cpp index fea1bf061..53e906de7 100644 --- a/zone/perl_questitem.cpp +++ b/zone/perl_questitem.cpp @@ -152,11 +152,6 @@ uint32 Perl_QuestItem_GetItemScriptID(EQ::ItemInstance* self) return self->GetItemScriptID(); } -uint32 Perl_QuestItem_GetKillsNeeded(EQ::ItemInstance* self, uint8 current_level) -{ - return self->GetKillsNeeded(current_level); -} - int8 Perl_QuestItem_GetMaxEvolveLevel(EQ::ItemInstance* self) { return self->GetMaxEvolveLvl(); @@ -297,12 +292,88 @@ std::string Perl_QuestItem_GetItemLink(EQ::ItemInstance* self) return linker.GenerateLink(); } +void Perl_QuestItem_AddEvolveAmount(EQ::ItemInstance* self, uint64 amount) +{ + self->SetEvolveAddToCurrentAmount(amount); +} + +uint32 Perl_QuestItem_GetAugmentEvolveUniqueID(EQ::ItemInstance* self, uint8 slot_id) +{ + return self->GetAugmentEvolveUniqueID(slot_id); +} + +bool Perl_QuestItem_GetEvolveActivated(EQ::ItemInstance* self) +{ + return self->GetEvolveActivated(); +} + +uint64 Perl_QuestItem_GetEvolveAmount(EQ::ItemInstance* self) +{ + return self->GetEvolveCurrentAmount(); +} + +uint32 Perl_QuestItem_GetEvolveCharacterID(EQ::ItemInstance* self) +{ + return self->GetEvolveCharID(); +} + +bool Perl_QuestItem_GetEvolveEquipped(EQ::ItemInstance* self) +{ + return self->GetEvolveEquipped(); +} + +uint32 Perl_QuestItem_GetEvolveItemID(EQ::ItemInstance* self) +{ + return self->GetEvolveItemID(); +} + +uint32 Perl_QuestItem_GetEvolveFinalItemID(EQ::ItemInstance* self) +{ + return self->GetEvolveFinalItemID(); +} + +int8 Perl_QuestItem_GetEvolveLevel(EQ::ItemInstance* self) +{ + return self->GetEvolveLvl(); +} + +uint32 Perl_QuestItem_GetEvolveLoreID(EQ::ItemInstance* self) +{ + return self->GetEvolveLoreID(); +} + +double Perl_QuestItem_GetEvolveProgression(EQ::ItemInstance* self) +{ + return self->GetEvolveProgression(); +} + +uint64 Perl_QuestItem_GetEvolveUniqueID(EQ::ItemInstance* self) +{ + return self->GetEvolveUniqueID(); +} + +bool Perl_QuestItem_IsEvolving(EQ::ItemInstance* self) +{ + return self->IsEvolving(); +} + +void Perl_QuestItem_SetEvolveProgression(EQ::ItemInstance* self, float amount) +{ + self->SetEvolveProgression(amount); +} + +void Perl_QuestItem_SetEvolveAmount(EQ::ItemInstance* self, uint64 amount) +{ + self->SetEvolveCurrentAmount(amount); +} + void perl_register_questitem() { perl::interpreter perl(PERL_GET_THX); auto package = perl.new_class("QuestItem"); + package.add("AddEvolveAmount", &Perl_QuestItem_AddEvolveAmount); package.add("AddEXP", &Perl_QuestItem_AddEXP); package.add("ClearTimers", &Perl_QuestItem_ClearTimers); package.add("Clone", &Perl_QuestItem_Clone); @@ -310,6 +381,7 @@ void perl_register_questitem() package.add("CountAugmentByID", &Perl_QuestItem_CountAugmentByID); package.add("DeleteCustomData", &Perl_QuestItem_DeleteCustomData); package.add("GetAugment", &Perl_QuestItem_GetAugment); + package.add("GetAugmentEvolveUniqueID", &Perl_QuestItem_GetAugmentEvolveUniqueID); package.add("GetAugmentIDs", &Perl_QuestItem_GetAugmentIDs); package.add("GetAugmentItemID", &Perl_QuestItem_GetAugmentItemID); package.add("GetAugmentType", &Perl_QuestItem_GetAugmentType); @@ -317,6 +389,16 @@ void perl_register_questitem() package.add("GetColor", &Perl_QuestItem_GetColor); package.add("GetCustomData", &Perl_QuestItem_GetCustomData); package.add("GetCustomDataString", &Perl_QuestItem_GetCustomDataString); + package.add("GetEvolveActivated", &Perl_QuestItem_GetEvolveActivated); + package.add("GetEvolveAmount", &Perl_QuestItem_GetEvolveAmount); + package.add("GetEvolveCharacterID", &Perl_QuestItem_GetEvolveCharacterID); + package.add("GetEvolveEquipped", &Perl_QuestItem_GetEvolveEquipped); + package.add("GetEvolveFinalItemID", &Perl_QuestItem_GetEvolveFinalItemID); + package.add("GetEvolveItemID", &Perl_QuestItem_GetEvolveItemID); + package.add("GetEvolveLevel", &Perl_QuestItem_GetEvolveLevel); + package.add("GetEvolveLoreID", &Perl_QuestItem_GetEvolveLoreID); + package.add("GetEvolveProgression", &Perl_QuestItem_GetEvolveProgression); + package.add("GetEvolveUniqueID", &Perl_QuestItem_GetEvolveUniqueID); package.add("GetEXP", &Perl_QuestItem_GetEXP); package.add("GetID", &Perl_QuestItem_GetID); package.add("GetItem", (EQ::ItemData*(*)(EQ::ItemInstance*))&Perl_QuestItem_GetItem); @@ -324,7 +406,6 @@ void perl_register_questitem() package.add("GetItemID", &Perl_QuestItem_GetItemID); package.add("GetItemLink", &Perl_QuestItem_GetItemLink); package.add("GetItemScriptID", &Perl_QuestItem_GetItemScriptID); - package.add("GetKillsNeeded", &Perl_QuestItem_GetKillsNeeded); package.add("GetMaxEvolveLevel", &Perl_QuestItem_GetMaxEvolveLevel); package.add("GetName", &Perl_QuestItem_GetName); package.add("GetPrice", &Perl_QuestItem_GetPrice); @@ -337,6 +418,7 @@ void perl_register_questitem() package.add("IsAugmented", &Perl_QuestItem_IsAugmented); package.add("IsEquipable", (bool(*)(EQ::ItemInstance*, int16))&Perl_QuestItem_IsEquipable); package.add("IsEquipable", (bool(*)(EQ::ItemInstance*, uint16, uint16))&Perl_QuestItem_IsEquipable); + package.add("IsEvolving", &Perl_QuestItem_IsEvolving); package.add("IsExpendable", &Perl_QuestItem_IsExpendable); package.add("IsInstanceNoDrop", &Perl_QuestItem_IsInstanceNoDrop); package.add("IsStackable", &Perl_QuestItem_IsStackable); @@ -352,6 +434,8 @@ void perl_register_questitem() package.add("SetCustomData", (void(*)(EQ::ItemInstance*, std::string, float))&Perl_QuestItem_SetCustomData); package.add("SetCustomData", (void(*)(EQ::ItemInstance*, std::string, int))&Perl_QuestItem_SetCustomData); package.add("SetCustomData", (void(*)(EQ::ItemInstance*, std::string, std::string))&Perl_QuestItem_SetCustomData); + package.add("SetEvolveAmount", &Perl_QuestItem_SetEvolveAmount); + package.add("SetEvolveProgression", &Perl_QuestItem_SetEvolveProgression); package.add("SetEXP", &Perl_QuestItem_SetEXP); package.add("SetInstanceNoDrop", &Perl_QuestItem_SetInstanceNoDrop); package.add("SetPrice", &Perl_QuestItem_SetPrice); diff --git a/zone/string_ids.h b/zone/string_ids.h index b778170b6..0da8fc03e 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -419,6 +419,11 @@ #define GUILD_BANK_FULL 6098 // There is no more room in the Guild Bank. #define GUILD_BANK_TRANSFERRED 6100 // '%1' transferred to Guild Bank from Deposits. #define GUILD_BANK_EMPTY_HANDS 6108 // You must empty your hands to withdraw from the Guild Bank. +#define EVOLVE_ITEM_EVOLVED 6145 //Your %1 has evolved! +#define EVOLVE_DETAILS 6146 //Evolving: Level %1/%2 %3%% %4 +#define EVOLVE_LEVEL_LIMIT 6147 //Your %1 can not evolve until you reach level %2. +#define EVOLVE_XP_TXFR_CONFIRM 6148 //Are you sure you want to transfer experience between these two items? +#define EVOLVE_XP_TXFRD 6149 //Your item's experience has been transferred! #define TRADESKILL_COMBINE_LORE 6199 // Combine would result in a LORE item (%1) you already possess. #define TRANSFORM_FAILED 6326 //This mold cannot be applied to your %1. #define TRANSFORM_COMPLETE 6327 //You have successfully transformed your %1. diff --git a/zone/trading.cpp b/zone/trading.cpp index 785fd2aec..bdbb6326c 100644 --- a/zone/trading.cpp +++ b/zone/trading.cpp @@ -371,6 +371,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (free_slot != INVALID_INDEX) { if (other->PutItemInInventory(free_slot, *inst, true)) { + inst->TransferOwnership(database, other->CharacterID()); LogTrading("Container [{}] ([{}]) successfully transferred, deleting from trade slot", inst->GetItem()->Name, inst->GetItem()->ID); if (qs_log) { auto detail = new PlayerLogTradeItemsEntry_Struct; @@ -482,6 +483,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (other->PutItemInInventory(partial_slot, *partial_inst, true)) { LogTrading("Partial stack [{}] ([{}]) successfully transferred, deleting [{}] charges from trade slot", inst->GetItem()->Name, inst->GetItem()->ID, (old_charges - inst->GetCharges())); + inst->TransferOwnership(database, other->CharacterID()); if (qs_log) { auto detail = new PlayerLogTradeItemsEntry_Struct; @@ -589,6 +591,7 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st if (free_slot != INVALID_INDEX) { if (other->PutItemInInventory(free_slot, *inst, true)) { + inst->TransferOwnership(database, other->CharacterID()); LogTrading("Item [{}] ([{}]) successfully transferred, deleting from trade slot", inst->GetItem()->Name, inst->GetItem()->ID); if (qs_log) { auto detail = new PlayerLogTradeItemsEntry_Struct; @@ -3219,11 +3222,44 @@ void Client::SendBulkBazaarTraders() return; } - auto results = TraderRepository::GetDistinctTraders( - database, - GetInstanceID(), - EQ::constants::StaticLookup(ClientVersion())->BazaarTraderLimit - ); + TraderRepository::BulkTraders_Struct results{}; + + if (RuleB(Bazaar, UseAlternateBazaarSearch)) + { + if (GetZoneID() == Zones::BAZAAR) { + results = TraderRepository::GetDistinctTraders(database, GetInstanceID()); + } + + uint32 number = 1; + auto shards = CharacterDataRepository::GetInstanceZonePlayerCounts(database, Zones::BAZAAR); + for (auto const &shard: shards) { + if (GetZoneID() != Zones::BAZAAR || (GetZoneID() == Zones::BAZAAR && GetInstanceID() != shard.instance_id)) { + + TraderRepository::DistinctTraders_Struct t{}; + t.entity_id = 0; + t.trader_id = TraderRepository::TRADER_CONVERT_ID + shard.instance_id; + t.trader_name = fmt::format("Bazaar Shard {}", number); + t.zone_id = Zones::BAZAAR; + t.zone_instance_id = shard.instance_id; + results.count += 1; + results.name_length += t.trader_name.length() + 1; + results.traders.push_back(t); + } + + number++; + } + } + else { + results = TraderRepository::GetDistinctTraders( + database, + GetInstanceID(), + EQ::constants::StaticLookup(ClientVersion())->BazaarTraderLimit + ); + } + + SetTraderCount(results.count); + + SetTraderCount(results.count); auto p_size = 4 + 12 * results.count + results.name_length; auto buffer = std::make_unique(p_size); @@ -3246,8 +3282,28 @@ void Client::SendBulkBazaarTraders() QueuePacket(outapp.get()); } -void Client::DoBazaarInspect(const BazaarInspect_Struct &in) +void Client::DoBazaarInspect(BazaarInspect_Struct &in) { + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (in.trader_id >= TraderRepository::TRADER_CONVERT_ID) { + auto trader = TraderRepository::GetTraderByInstanceAndSerialnumber( + database, + in.trader_id - TraderRepository::TRADER_CONVERT_ID, + fmt::format("{}", in.serial_number).c_str() + ); + + if (!trader.trader_id) { + LogTrading("Unable to convert trader id for {} and serial number {}. Trader Buy aborted.", + in.trader_id - TraderRepository::TRADER_CONVERT_ID, + in.serial_number + ); + return; + } + + in.trader_id = trader.trader_id; + } + } + auto items = TraderRepository::GetWhere( database, fmt::format("`char_id` = '{}' AND `item_sn` = '{}'", in.trader_id, in.serial_number) ); diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 768fe4a4f..995887f17 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -3912,21 +3912,8 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) 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; - } - } + auto outapp = new EQApplicationPacket(OP_BecomeTrader, sizeof(BecomeTrader_Struct)); + auto out = (BecomeTrader_Struct *) outapp->pBuffer; out->entity_id = in->entity_id; out->zone_id = in->zone_id; @@ -3934,7 +3921,49 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) out->trader_id = in->trader_id; strn0cpy(out->trader_name, in->trader_name, sizeof(out->trader_name)); - c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + switch (in->action) { + case TraderOn: { + out->action = AddTraderToBazaarWindow; + if (c.second->GetTraderCount() < + EQ::constants::StaticLookup(c.second->ClientVersion())->BazaarTraderLimit) { + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (out->zone_id == Zones::BAZAAR && + out->zone_instance_id == c.second->GetInstanceID()) { + c.second->IncrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } + else { + c.second->IncrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } + break; + } + case TraderOff: { + out->action = RemoveTraderFromBazaarWindow; + if (c.second->GetTraderCount() <= + EQ::constants::StaticLookup(c.second->ClientVersion())->BazaarTraderLimit) { + if (RuleB(Bazaar, UseAlternateBazaarSearch)) { + if (out->zone_id == Zones::BAZAAR && + out->zone_instance_id == c.second->GetInstanceID()) { + c.second->DecrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } + else { + c.second->DecrementTraderCount(); + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } + break; + } + default: { + out->action = 0; + c.second->QueuePacket(outapp, true, Mob::CLIENT_CONNECTED); + } + } + safe_delete(outapp); } if (zone && zone->GetZoneID() == Zones::BAZAAR && in->instance_id == zone->GetInstanceID()) { diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index f346fd57c..4000397a3 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -52,7 +52,7 @@ #include "../common/repositories/zone_repository.h" #include "../common/repositories/trader_repository.h" - +#include "../common/repositories/character_evolving_items_repository.h" #include #include