[Feature] Evolving Item Support for RoF2 (#4496)

* basic evolving items framework created

* Implement evolving tab in the inventory window

* Implement experience and number of kills

* Move zone evolving map to a evolvingitemsmanager class

* rework gm commands

* rework GetInventory

* wip

* wip loot testing

* Fix Duplicate Message

* reworked evolving item looting, swapping, etc

* reworked const functions for evolving methods

* Functioning Player Trade of evolving items test item_id is 89550

* First pass of Final Result link working

* First pass of item upgrading when reaching 100%

* Add strings and logic for displaying the evolving item xp transfer window in Corathus

* Prototype of xp transfer window sending items

* WIP for evolve xp transfer

* WIP for evolve xp transfer.  First tests passed

* XP Transfer Cleanup

* XP Transfer Cleanup

* Add Rule for evolving items equip timer/  default is 30 secs

* Add logging and player events

Add logging and player events

* Formatting

* Database updates

* Updates for linux build

* Perl/Cleanup

* Command cleanup

* Lua

* Added a crash condition check if final item id is blank or not found.

* Review Changes

Updates to resolve review comments and a rebase.

* migrate to content_db for items_evolving_details

migrate to content_db for items_evolving_details

* Simplify, don't hit database unless evolving

* Update 2025_01_19_items_evolving_details.sql

* Update client.cpp

* Update manifest with items_evolving_details

* character_id vs char_id

* Remove _Struct from structs

* Remove license header in evolving.cpp

* Move evolving constants from eq_constants.h to evolving.h since it is more specific

* Update database_schema.h

* General cleanup

* Be more specific with `evolving_items` vs `evolving`

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
Co-authored-by: Akkadius <akkadius1@gmail.com>
This commit is contained in:
Mitch Freeman 2025-01-19 20:10:19 -04:00 committed by GitHub
parent d47bf687d0
commit f21cc170df
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 3569 additions and 183 deletions

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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
}

View File

@ -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",

View File

@ -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),

View File

@ -1114,4 +1114,5 @@ enum ExpSource
namespace DoorType {
constexpr uint32 BuyerStall = 155;
}
#endif /*COMMON_EQ_CONSTANTS_H*/

View File

@ -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<class Archive>
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()

View File

@ -142,6 +142,7 @@ namespace Logs {
EqTime,
Corpses,
XTargets,
EvolveItem,
MaxCategoryID /* Don't Remove this */
};
@ -242,7 +243,8 @@ namespace Logs {
"Zoning",
"EqTime",
"Corpses",
"XTargets"
"XTargets",
"EvolveItem"
};
}

View File

@ -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__);\

View File

@ -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;

View File

@ -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<class Archive>
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

303
common/evolving_items.cpp Normal file
View File

@ -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<double>(current_amount)
/ static_cast<double>(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<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &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<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &a,
const std::pair<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &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<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> &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<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> EvolvingItemsManager::GetEvolveIDItems(
const uint32 evolve_id
)
{
std::vector<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> 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();
}

67
common/evolving_items.h Normal file
View File

@ -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<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails>& GetEvolvingItemsCache() { return evolving_items_cache; }
std::vector<ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> GetEvolveIDItems(uint32 evolve_id);
private:
std::map<uint32, ItemsEvolvingDetailsRepository::ItemsEvolvingDetails> evolving_items_cache;
Database * m_db;
Database * m_content_db;
};
extern EvolvingItemsManager evolving_items_manager;
#endif //EVOLVING_H

View File

@ -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<int16, ItemInstance *> &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;
}

View File

@ -57,6 +57,8 @@ public:
inline std::list<EQ::ItemInstance*>::const_iterator cbegin() { return m_list.cbegin(); }
inline std::list<EQ::ItemInstance*>::const_iterator cend() { return m_list.cend(); }
inline std::list<EQ::ItemInstance*>::iterator begin() { return m_list.begin(); }
inline std::list<EQ::ItemInstance*>::iterator end() { return m_list.end(); }
inline int size() { return static_cast<int>(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<int16, ItemInstance*>& GetWorn() { return m_worn; }
std::map<int16, ItemInstance*>& 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<int16, ItemInstance*>& bucket, uint32 loregroup);
int16 _HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup);
int16 _HasEvolvingItem(std::map<int16, ItemInstance*>& bucket, uint64 evolve_unique_id, uint8 quantity);
int16 _HasEvolvingItem(ItemInstQueue& iqueue, uint64 evolve_unique_id, uint8 quantity);
// Player inventory

View File

@ -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();
}

View File

@ -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 <map>
@ -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<std::string, ::Timer>& GetTimers() { return m_timers; }
std::map<std::string, ::Timer>& 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<uint8, ItemInstance*> m_contents {}; // Zero-based index: min=0, max=9
std::map<std::string, std::string> m_custom_data {};
std::map<std::string, ::Timer> m_timers {};
std::map<uint8, ItemInstance*> m_contents {}; // Zero-based index: min=0, max=9
std::map<std::string, std::string> m_custom_data {};
mutable std::map<std::string, ::Timer> 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*/

View File

@ -1350,6 +1350,58 @@ namespace RoF2
dest->FastQueuePacket(&in, ack_req);
}
ENCODE(OP_EvolveItem)
{
EQApplicationPacket *in = *p;
*p = nullptr;
auto action = *reinterpret_cast<uint32 *>(in->pBuffer);
switch (action) {
case EvolvingItems::Actions::TRANSFER_WINDOW_DETAILS: {
auto emu = reinterpret_cast<EvolveItemMessaging *>(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<const EQ::ItemInstance *>(reinterpret_cast<EQ::InternalSerializedItem_Struct
*>(e.serialize_item_1.data())->inst);
auto item_2 = static_cast<const EQ::ItemInstance *>(reinterpret_cast<EQ::InternalSerializedItem_Struct
*>(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<EQApplicationPacket>(
OP_EvolveItem,
sizeof(EvolveXPWindowSendDetails_Struct) + ob.size()
);
auto data = reinterpret_cast<EvolveXPWindowSendDetails_Struct *>(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);
@ -6405,6 +6457,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 +6475,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));
}
/**

View File

@ -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)

View File

@ -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*/

View File

@ -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 <ctime>
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<std::string> Columns()
{
return {
"id",
"character_id",
"item_id",
"activated",
"equipped",
"current_amount",
"progression",
"final_item_id",
"deleted_at",
};
}
static std::vector<std::string> 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<CharacterEvolvingItems> &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<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.activated = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.equipped = row[4] ? static_cast<uint8_t>(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<uint32_t>(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<std::string> 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<std::string> 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<CharacterEvolvingItems> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.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<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<CharacterEvolvingItems> All(Database& db)
{
std::vector<CharacterEvolvingItems> 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<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.activated = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.equipped = row[4] ? static_cast<uint8_t>(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<uint32_t>(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<CharacterEvolvingItems> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<CharacterEvolvingItems> 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<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_id = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.activated = row[3] ? static_cast<uint8_t>(strtoul(row[3], nullptr, 10)) : 0;
e.equipped = row[4] ? static_cast<uint8_t>(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<uint32_t>(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<std::string> 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<CharacterEvolvingItems> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.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<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_CHARACTER_EVOLVING_ITEMS_REPOSITORY_H

View File

@ -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 <ctime>
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<std::string> Columns()
{
return {
"id",
"item_evo_id",
"item_evolve_level",
"item_id",
"type",
"sub_type",
"required_amount",
};
}
static std::vector<std::string> 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<ItemsEvolvingDetails> &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<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.item_evo_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.sub_type = row[5] ? static_cast<uint32_t>(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<std::string> 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<std::string> 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<ItemsEvolvingDetails> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.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<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<ItemsEvolvingDetails> All(Database& db)
{
std::vector<ItemsEvolvingDetails> 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<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.item_evo_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.sub_type = row[5] ? static_cast<uint32_t>(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<ItemsEvolvingDetails> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<ItemsEvolvingDetails> 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<uint32_t>(strtoul(row[0], nullptr, 10)) : 0;
e.item_evo_id = row[1] ? static_cast<uint32_t>(strtoul(row[1], nullptr, 10)) : 0;
e.item_evolve_level = row[2] ? static_cast<uint32_t>(strtoul(row[2], nullptr, 10)) : 0;
e.item_id = row[3] ? static_cast<uint32_t>(strtoul(row[3], nullptr, 10)) : 0;
e.type = row[4] ? static_cast<uint32_t>(strtoul(row[4], nullptr, 10)) : 0;
e.sub_type = row[5] ? static_cast<uint32_t>(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<std::string> 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<ItemsEvolvingDetails> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.id));
v.push_back(std::to_string(e.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<std::string> 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

View File

@ -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 <boost/tuple/tuple_comparison.hpp>
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

View File

@ -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

View File

@ -1038,6 +1038,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

View File

@ -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<InventoryRepository::Inventory> 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)

View File

@ -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 <list>
#include <map>
@ -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<uint32, uint32> GetItemRecastTimestamps(uint32 char_id);
uint32 GetItemRecastTimestamp(uint32 char_id, uint32 recast_type);

View File

@ -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 9045
#endif

View File

@ -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 <direct.h>

View File

@ -731,3 +731,6 @@ OP_RemoveTrap=0x71da
OP_PickZoneWindow=0x72d8
OP_PickZone=0xaaba
#evolve item related
OP_EvolveItem=0x7cfb

View File

@ -46,6 +46,7 @@
#include "client.h"
#include "worlddb.h"
#include "wguild_mgr.h"
#include "../common/evolving_items.h"
#ifdef _WINDOWS
#include <process.h>
@ -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);

View File

@ -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

View File

@ -2815,7 +2815,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 &&

View File

@ -13113,7 +13113,8 @@ void Client::SetAAEXPPercentage(uint8 percentage)
if (before_percentage > 0 && percentage == 0) {
MessageString(Chat::White, AA_OFF);
} else if (before_percentage == 0 && percentage > 0) {
}
else if (before_percentage == 0 && percentage > 0) {
MessageString(Chat::White, AA_ON);
}

View File

@ -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)
@ -710,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);
@ -1816,6 +1817,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);
@ -1975,6 +1988,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);

View File

@ -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<EvolveItemToggle *>(app->pBuffer);
auto item = CharacterEvolvingItemsRepository::FindOne(database, in->unique_id);
LogEvolveItemDetail(
"Character ID <green>[{}] requested to set evolve item with unique id <yellow>[{}] to status <yellow>[{}]",
CharacterID(),
in->unique_id,
in->activated
);
if (!item.id) {
LogEvolveItemDetail(
"Character ID <green>[{}] toggle evolve item unique id <yellow>[{}] 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<EQApplicationPacket>(OP_EvolveItem, sizeof(EvolveItemToggle));
const auto data = reinterpret_cast<EvolveItemToggle *>(out->pBuffer);
LogEvolveItemDetail(
"Character ID <green>[{}] requested info for evolving item with unique id <yellow>[{}] status <yellow>[{}] "
"percentage <yellow>[{}]",
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 <yellow>[{}] status <yellow>[{}] percentage <yellow>[{}] to Character ID "
"<green>[{}]",
data->unique_id,
data->activated,
data->percentage,
CharacterID()
);
}
void Client::ProcessEvolvingItem(const uint64 exp, const Mob *mob)
{
std::vector<const EQ::ItemInstance *> queue{};
for (auto &[key, inst]: GetInv().GetWorn()) {
LogEvolveItemDetail(
"CharacterID <green>[{}] found equipped item ID <yellow>[{}]", CharacterID(), inst->GetID());
if (!inst->IsEvolving() || !inst->GetEvolveActivated()) {
LogEvolveItemDetail(
"CharacterID <green>[{}], item ID <yellow>[{}] not an evolving item.", CharacterID(), inst->GetID());
continue;
}
if (inst->GetTimers().contains("evolve") && !inst->GetTimers().at("evolve").Check()) {
LogEvolveItemDetail(
"CharacterID <green>[{}], item ID <yellow>[{}] timer not yet expired. <red>[{}] 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 <green>[{}] item id <green>[{}] type {} sub_type {} is Evolving. Continue processing...",
CharacterID(),
inst->GetID(),
type,
sub_type);
switch (type) {
case EvolvingItems::Types::AMOUNT_OF_EXP: {
LogEvolveItemDetail("Type <green>[{}] Processing sub_type", type);
if (sub_type == EvolvingItems::SubTypes::ALL_EXP ||
(sub_type == EvolvingItems::SubTypes::GROUP_EXP && IsGrouped())) {
LogEvolveItemDetail("Sub_Type <green>[{}] 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 <green>[{}] 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 <green>[{}] 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 <green>[{1}] Type 1 Amount of EXP - SubType <yellow>[{0}] - "
"Assigned <yellow>[{2}] of exp to <green>[{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 <green>[{}] Processing sub type", type);
if (mob && mob->GetRace() == sub_type) {
LogEvolveItemDetail("Sub_Type <green>[{}] 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 <green>[{1}] Type 3 Specific Mob Race - SubType <yellow>[{0}] "
"- Increased count by 1 for <green>[{1}]",
sub_type,
inst->GetID()
);
}
if (inst->GetEvolveProgression() >= 100) {
queue.push_back(inst);
}
break;
}
case EvolvingItems::Types::SPECIFIC_ZONE_ID: {
LogEvolveItemDetail("Type <green>[{}] Processing sub type", type);
if (mob && mob->GetZoneID() == sub_type) {
LogEvolveItemDetail("Sub_Type <green>[{}] 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 <green>[{1}] Type 4 Specific Zone ID - SubType <yellow>[{0}] "
"- Increased count by 1 for <green>[{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<EvolveItemToggle *>(app->pBuffer);
const uint32 item_id = static_cast<uint32>(in->unique_id & 0xFFFFFFFF);
if (item_id == 0) {
LogEvolveItem("Error - Item ID of final evolve item is blank.");
return;
}
std::unique_ptr<EQ::ItemInstance> const inst(database.CreateItem(item_id));
LogEvolveItemDetail(
"Character ID <green>[{}] requested to view final evolve item id <yellow>[{}] for evolve item id <yellow>[{}]",
CharacterID(),
item_id,
evolving_items_manager.GetFirstItemInLoreGroupByItemID(item_id));
inst->SetEvolveProgression(100);
if (inst) {
LogEvolveItemDetail(
"Sending final result for item id <yellow>[{}] to Character ID <green>[{}]", 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<EQ::ItemInstance> 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 <red>[{}] into item id <green>[{}] for Character ID <green>[{}]",
inst.GetID(),
new_inst->GetID(),
CharacterID());
return true;
}
void Client::SendEvolveXPTransferWindow()
{
auto out = std::make_unique<EQApplicationPacket>(OP_EvolveItem, sizeof(EvolveItemToggle));
const auto data = reinterpret_cast<EvolveItemToggle *>(out->pBuffer);
data->action = 1;
QueuePacket(out.get());
}
void Client::SendEvolveXPWindowDetails(const EQApplicationPacket *app)
{
const auto in = reinterpret_cast<EvolveXPWindowReceive *>(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<EQ::ItemInstance> const inst_from_new(database.CreateItem(results.item_from_id));
std::unique_ptr<EQ::ItemInstance> 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<EvolveXPWindowReceive *>(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 <green>[{}]", 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 <green>[{}]", CharacterID());
return;
}
std::unique_ptr<const EQ::ItemInstance> const inst_from_new(database.CreateItem(results.item_from_id));
std::unique_ptr<const EQ::ItemInstance> 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 <green>[{}]", 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 <red>[{}] into item id <green>[{}] for Character ID "
"<green>[{}]",
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<EQApplicationPacket> out(new EQApplicationPacket(OP_EvolveItem, packet_size));
const auto data = reinterpret_cast<EvolveItemMessaging *>(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();
}

View File

@ -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<uint32, ClientPacketProc> 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;
@ -1309,7 +1309,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 */
@ -10748,7 +10748,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)
@ -17165,3 +17170,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<EvolveItemToggle *>(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: {
}
}
}

View File

@ -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);

View File

@ -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) ||
@ -826,6 +827,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"

View File

@ -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);

View File

@ -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 <string.h>
@ -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)

View File

@ -77,6 +77,8 @@ public:
bool IsDoorBlacklisted();
const char* GetDoorZone() const { return m_zone_name; }
private:
bool GetIsDoorBlacklisted();

View File

@ -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());
}
}
}

View File

@ -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.)"
);
}

View File

@ -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();

View File

@ -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_<Lua_ItemInst>("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)

View File

@ -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_;

View File

@ -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)) {

View File

@ -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<EQ::ItemInstance>("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);

View File

@ -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.

View File

@ -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;

View File

@ -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 <ctime>
#include <iostream>