From c868e10d9b4f3d9d8b170f7f8200ab45c4b5a787 Mon Sep 17 00:00:00 2001 From: Ovalord <1Don7H4v3@m41L.com> Date: Tue, 7 Nov 2017 22:08:06 +0100 Subject: [PATCH] Core/Skills: implement Archaeology --- .../4.3.4/2017_11_09_00_characters.sql | 41 ++ .../world/4.3.4/2017_11_09_06_world.sql | 373 ++++++++++++ .../game/Achievements/AchievementMgr.cpp | 30 +- src/server/game/DataStores/DBCEnums.h | 12 + src/server/game/DataStores/DBCStores.cpp | 13 +- src/server/game/DataStores/DBCStores.h | 6 + src/server/game/DataStores/DBCStructure.h | 78 ++- src/server/game/DataStores/DBCfmt.h | 8 +- .../game/Entities/GameObject/GameObject.cpp | 5 + .../game/Entities/GameObject/GameObject.h | 1 + src/server/game/Entities/Item/ItemTemplate.h | 1 + .../Entities/Object/Updates/UpdateFields.h | 4 +- src/server/game/Entities/Player/Player.cpp | 53 +- src/server/game/Entities/Player/Player.h | 12 + src/server/game/Handlers/LootHandler.cpp | 2 +- src/server/game/Handlers/MiscHandler.cpp | 6 + src/server/game/Handlers/SpellHandler.cpp | 16 +- src/server/game/Loot/LootMgr.cpp | 34 ++ src/server/game/Loot/LootMgr.h | 27 +- src/server/game/Miscellaneous/Language.h | 2 +- src/server/game/Miscellaneous/SharedDefines.h | 4 +- src/server/game/Server/Protocol/Opcodes.cpp | 2 +- src/server/game/Server/WorldSession.h | 1 + .../game/Skills/Archaeology/Archaeology.cpp | 89 +++ .../game/Skills/Archaeology/Archaeology.h | 103 ++++ .../Skills/Archaeology/ArchaeologyMgr.cpp | 380 ++++++++++++ .../game/Skills/Archaeology/ArchaeologyMgr.h | 152 +++++ .../Archaeology/ArchaeologyProjects.cpp | 234 ++++++++ .../Skills/Archaeology/ArchaeologySites.cpp | 249 ++++++++ .../Archaeology/SitePolygonGeometry.cpp | 565 ++++++++++++++++++ .../Skills/Archaeology/SitePolygonGraph.cpp | 137 +++++ .../Skills/Archaeology/SitePolygonGraph.h | 247 ++++++++ src/server/game/Spells/Spell.cpp | 11 +- src/server/game/Spells/Spell.h | 1 + src/server/game/Spells/SpellEffects.cpp | 33 +- src/server/game/Spells/SpellInfo.cpp | 1 + src/server/game/Spells/SpellInfo.h | 1 + src/server/game/World/World.cpp | 3 + 38 files changed, 2903 insertions(+), 34 deletions(-) create mode 100644 sql/updates/characters/4.3.4/2017_11_09_00_characters.sql create mode 100644 sql/updates/world/4.3.4/2017_11_09_06_world.sql create mode 100644 src/server/game/Skills/Archaeology/Archaeology.cpp create mode 100644 src/server/game/Skills/Archaeology/Archaeology.h create mode 100644 src/server/game/Skills/Archaeology/ArchaeologyMgr.cpp create mode 100644 src/server/game/Skills/Archaeology/ArchaeologyMgr.h create mode 100644 src/server/game/Skills/Archaeology/ArchaeologyProjects.cpp create mode 100644 src/server/game/Skills/Archaeology/ArchaeologySites.cpp create mode 100644 src/server/game/Skills/Archaeology/SitePolygonGeometry.cpp create mode 100644 src/server/game/Skills/Archaeology/SitePolygonGraph.cpp create mode 100644 src/server/game/Skills/Archaeology/SitePolygonGraph.h diff --git a/sql/updates/characters/4.3.4/2017_11_09_00_characters.sql b/sql/updates/characters/4.3.4/2017_11_09_00_characters.sql new file mode 100644 index 0000000000..d5e8efc6bf --- /dev/null +++ b/sql/updates/characters/4.3.4/2017_11_09_00_characters.sql @@ -0,0 +1,41 @@ +/*Table structure for table `character_archaeology_completed` */ + +DROP TABLE IF EXISTS `character_archaeology_completed`; + +CREATE TABLE `character_archaeology_completed` ( + `guid` int(10) unsigned NOT NULL, + `project` smallint(5) unsigned NOT NULL, + `time` int(10) NOT NULL, + `count` int(10) NOT NULL, + PRIMARY KEY (`guid`,`project`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +/*Data for the table `character_archaeology_completed` */ + +/*Table structure for table `character_archaeology_projects` */ + +DROP TABLE IF EXISTS `character_archaeology_projects`; + +CREATE TABLE `character_archaeology_projects` ( + `guid` int(10) unsigned NOT NULL, + `branch` tinyint(3) unsigned NOT NULL, + `project` mediumint(8) unsigned NOT NULL, + PRIMARY KEY (`guid`,`branch`) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +/*Data for the table `character_archaeology_projects` */ + +/*Table structure for table `character_archaeology_sites` */ + +DROP TABLE IF EXISTS `character_archaeology_sites`; + +CREATE TABLE `character_archaeology_sites` ( + `guid` int(10) unsigned NOT NULL, + `site` tinyint(3) unsigned NOT NULL, + `type` smallint(5) unsigned NOT NULL, + `finds` tinyint(3) unsigned NOT NULL, + PRIMARY KEY (`guid`,`site`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +/*Data for the table `character_archaeology_sites` */ + diff --git a/sql/updates/world/4.3.4/2017_11_09_06_world.sql b/sql/updates/world/4.3.4/2017_11_09_06_world.sql new file mode 100644 index 0000000000..fe3bf6f197 --- /dev/null +++ b/sql/updates/world/4.3.4/2017_11_09_06_world.sql @@ -0,0 +1,373 @@ + +/*Table structure for table `archaeology_sites` */ + +DROP TABLE IF EXISTS `archaeology_sites`; + +CREATE TABLE `archaeology_sites` ( + `Id` smallint(5) unsigned NOT NULL, + `MapId` int(10) unsigned NOT NULL, + `QuestPOI` smallint(5) unsigned NOT NULL, + `ReqSkill` int(10) unsigned NOT NULL, + `MinLevel` int(10) unsigned NOT NULL, + `GameobjectEntry` int(10) unsigned NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +/*Data for the table `archaeology_sites` */ + +INSERT INTO `archaeology_sites`(`Id`,`MapId`,`QuestPOI`,`ReqSkill`,`MinLevel`,`GameobjectEntry`) VALUES +(9,0,54097,0,10,204282), +(10,0,54124,0,20,204282), +(12,0,54126,0,20,204282), +(13,0,54127,0,20,206836), +(15,0,54129,0,25,204282), +(18,0,54132,0,25,202655), +(19,0,54133,0,20,204282), +(20,0,54134,0,20,206836), +(21,0,54135,0,20,206836), +(22,0,54136,0,30,204282), +(23,0,54137,0,30,202655), +(24,0,54138,0,30,202655), +(25,0,54139,0,30,202655), +(26,0,54140,0,30,202655), +(27,0,54141,0,30,202655), +(144,0,54832,0,44,204282), +(146,0,54834,0,44,204282), +(150,0,54838,0,44,204282), +(152,0,54862,0,52,202655), +(154,0,54864,0,52,206836), +(163,0,55350,0,20,203071), +(165,0,55352,0,20,206836), +(167,1,55354,0,10,203071), +(169,1,55356,0,20,203071), +(171,1,55398,0,20,203071), +(173,1,55400,0,20,203071), +(175,1,55402,0,20,203071), +(177,1,55404,0,25,203071), +(179,1,55406,0,25,203071), +(181,1,55408,0,25,206836), +(183,1,55410,0,30,204282), +(185,1,55412,0,10,203071), +(187,1,55414,0,10,203071), +(189,0,55416,0,15,206836), +(191,1,55418,0,30,203071), +(193,1,55420,0,30,203071), +(195,1,55422,0,30,206836), +(197,1,55424,0,30,203071), +(199,1,55426,0,30,206836), +(201,1,55428,0,30,203071), +(203,0,55434,0,54,206836), +(205,0,55436,0,54,206836), +(207,0,55438,0,47,204282), +(209,0,55440,0,47,204282), +(211,0,55442,0,49,204282), +(213,0,55444,0,49,204282), +(215,0,55446,0,49,206836), +(217,0,55448,0,40,202655), +(219,0,55450,0,40,203071), +(221,0,55452,0,40,206836), +(223,0,55454,0,25,202655), +(225,0,55456,0,25,202655), +(227,0,55458,0,25,202655), +(229,0,55460,0,25,202655), +(231,0,55462,0,25,202655), +(233,0,55464,0,25,202655), +(235,0,55466,0,25,202655), +(237,0,55468,0,25,206836), +(239,0,55470,0,30,202655), +(241,0,55472,0,30,202655), +(243,0,55474,0,30,202655), +(245,0,55476,0,30,202655), +(247,0,55478,0,35,206836), +(249,0,55480,0,35,206836), +(251,0,55482,0,35,206836), +(259,1,55755,0,35,206836), +(261,1,55757,0,35,206836), +(279,1,56327,0,35,203071), +(281,1,56329,0,35,203071), +(283,1,56331,0,35,203071), +(285,1,56333,0,35,203071), +(287,1,56335,0,35,203071), +(289,1,56337,0,35,203071), +(291,1,56339,0,35,203071), +(293,1,56341,0,35,203071), +(295,1,56343,0,45,203071), +(297,1,56345,0,45,203071), +(299,1,56347,0,45,203071), +(301,1,56349,0,45,203071), +(303,1,56351,0,50,203071), +(305,1,56354,0,50,203071), +(307,1,56356,0,50,203071), +(309,1,56358,0,30,206836), +(313,1,56362,0,30,203071), +(315,1,56364,0,45,202655), +(317,1,56367,0,45,202655), +(319,1,56369,0,45,202655), +(321,1,56371,0,45,202655), +(323,1,56373,0,45,206836), +(325,1,56375,0,45,206836), +(327,1,56380,0,50,206836), +(329,1,56382,0,50,206836), +(331,1,56384,0,50,206836), +(333,1,56386,0,50,206836), +(335,1,56388,0,50,206836), +(337,1,56390,0,55,203071), +(339,530,56392,300,58,207187), +(341,530,56394,300,58,207187), +(343,530,56396,300,58,207187), +(345,530,56398,300,58,207187), +(347,530,56400,300,58,207188), +(349,530,56402,300,60,207188), +(351,530,56404,300,60,207188), +(353,530,56406,300,67,207188), +(355,530,56408,300,67,207188), +(357,530,56410,300,67,207188), +(359,530,56412,300,64,207187), +(363,530,56416,300,64,207187), +(365,530,56418,300,64,207187), +(367,530,56420,300,64,207187), +(369,530,56422,300,64,207188), +(371,530,56424,300,62,207187), +(373,530,56426,300,62,207188), +(375,530,56428,300,62,207187), +(377,530,56430,300,62,207187), +(379,530,56432,300,62,207188), +(381,530,56434,300,62,207188), +(383,530,56437,300,62,207188), +(385,530,56439,300,67,207188), +(387,530,56441,300,67,207188), +(391,530,56446,300,67,207188), +(393,530,56448,300,67,207188), +(395,530,56450,300,67,207187), +(399,530,56455,300,67,207187), +(401,571,56504,375,68,207189), +(403,571,56506,375,68,207189), +(405,571,56508,375,68,207189), +(407,571,56510,375,68,207189), +(409,571,56512,375,68,207189), +(411,571,56514,375,68,207189), +(413,571,56516,375,68,207189), +(415,571,56518,375,71,203078), +(417,571,56520,375,71,203071), +(419,571,56522,375,68,203078), +(421,571,56524,375,74,203078), +(423,571,56526,375,68,203071), +(425,571,56528,375,77,203071), +(427,571,56530,375,77,203071), +(429,571,56533,375,74,202655), +(431,571,56535,375,74,202655), +(433,571,56537,375,74,202655), +(435,571,56539,375,74,202655), +(437,571,56541,375,68,203078), +(439,571,56543,375,73,207189), +(443,571,56547,375,73,202655), +(445,571,56549,375,77,207189), +(447,571,56551,375,77,207189), +(449,571,56553,375,77,207189), +(451,571,56555,375,77,203078), +(455,571,56560,375,77,207189), +(457,571,56562,375,77,207189), +(459,571,56564,375,77,207189), +(461,1,56566,0,80,203071), +(463,1,56568,0,80,203071), +(465,1,56570,0,80,203071), +(467,1,56572,0,80,203071), +(469,1,56574,0,80,203071), +(477,0,56583,0,84,204282), +(479,0,56585,0,84,204282), +(481,0,56587,0,84,204282), +(485,1,56591,475,83,207190), +(487,1,56593,475,83,207190), +(489,1,56595,475,83,207190), +(491,1,56597,475,83,207190), +(493,1,56599,475,83,207190), +(495,1,56601,475,83,207190), +(497,1,56603,475,83,207190), +(499,1,56605,475,83,207190), +(501,1,56607,475,83,207190), +(570,1,60350,475,83,207190), +(572,1,60352,475,83,207190), +(574,1,60354,475,83,207190), +(576,1,60356,475,83,207190), +(578,1,60358,475,83,207190), +(581,1,60361,475,83,207190), +(583,1,60363,475,83,207190), +(587,571,60367,375,77,203078), +(589,571,60369,375,68,203078), +(615,0,60442,0,40,203078), +(617,0,60444,0,40,203078); + + + +DROP TABLE IF EXISTS `archaeology_projects`; + +CREATE TABLE `archaeology_projects` ( + `Entry` smallint(5) unsigned NOT NULL DEFAULT '0', + `Branch` tinyint(3) unsigned NOT NULL, + `Rarity` tinyint(3) unsigned NOT NULL, + `ReqSkill` smallint(5) unsigned NOT NULL, + `Fragments` tinyint(3) unsigned NOT NULL, + `Keystone` tinyint(3) unsigned NOT NULL, + `Spell` int(10) unsigned NOT NULL, + PRIMARY KEY (`entry`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8; + +/*Data for the table `archaeology_projects` */ + +INSERT INTO `archaeology_projects`(`Entry`,`Branch`,`Rarity`,`ReqSkill`,`Fragments`,`Keystone`,`Spell`) VALUES +(9,1,0,1,35,1,86857), +(40,1,0,1,35,1,86864), +(42,1,0,1,30,0,86865), +(44,1,0,1,32,0,86866), +(70,1,0,1,35,1,88180), +(72,1,0,1,34,1,88181), +(77,8,0,1,27,0,88262), +(87,8,0,1,30,0,88907), +(89,8,0,1,32,0,88908), +(91,1,0,1,28,0,88909), +(93,1,0,1,34,0,88910), +(95,3,0,1,31,0,88929), +(97,3,0,1,25,0,88930), +(99,4,0,1,30,0,89009), +(101,4,0,1,30,0,89012), +(103,4,0,1,30,0,89014), +(106,3,3,50,85,0,89693), +(108,4,0,1,35,1,89696), +(112,8,0,1,35,1,89701), +(114,8,0,1,34,1,89711), +(118,1,0,1,34,1,89717), +(120,8,0,1,35,1,89890), +(122,8,0,1,35,1,89891), +(124,4,0,1,35,1,89893), +(126,4,0,1,35,1,89894), +(128,3,0,1,35,0,89895), +(130,4,0,1,35,1,89896), +(139,1,0,1,35,1,90410), +(141,1,0,1,35,1,90411), +(143,1,0,1,34,1,90412), +(145,1,0,1,35,1,90413), +(147,1,0,1,35,1,90415), +(149,1,0,1,36,1,90419), +(153,8,0,1,35,1,90420), +(155,8,0,1,35,1,90421), +(157,8,0,1,35,1,90423), +(159,8,0,1,35,1,90429), +(161,3,0,1,35,0,90432), +(163,3,0,1,35,0,90433), +(165,4,0,1,35,1,90451), +(167,3,0,1,35,0,90452), +(169,4,0,1,35,1,90453), +(171,4,0,1,35,1,90458), +(173,4,3,150,100,3,90464), +(175,4,3,150,100,3,90493), +(177,1,0,1,35,1,90504), +(179,1,0,1,35,1,90506), +(181,1,0,1,35,1,90509), +(183,1,0,1,35,1,90518), +(185,1,0,1,35,1,90519), +(187,1,3,225,100,3,90521), +(189,1,3,150,100,3,90553), +(191,8,0,1,35,1,90558), +(193,8,0,1,35,1,90581), +(195,8,4,450,150,3,90608), +(197,4,0,1,35,1,90609), +(199,4,0,1,34,1,90610), +(201,4,0,1,35,1,90611), +(203,4,0,1,35,1,90612), +(205,4,3,225,98,3,90614), +(207,4,4,225,100,3,90616), +(209,3,0,1,33,0,90617), +(211,3,0,1,35,0,90618), +(213,3,4,150,100,0,90619), +(215,6,0,300,45,2,90622), +(217,6,0,300,45,2,90720), +(219,6,0,300,45,2,90728), +(221,6,0,300,45,2,90730), +(223,6,0,300,45,2,90732), +(225,6,0,300,45,2,90734), +(231,6,0,300,45,2,90833), +(233,6,0,300,45,2,90832), +(235,6,0,300,45,2,90831), +(237,6,4,300,130,3,90843), +(239,2,0,300,45,2,90853), +(241,2,0,300,45,2,90860), +(243,2,0,300,46,2,90861), +(245,2,0,300,46,2,90864), +(250,2,0,300,46,2,90968), +(252,2,0,300,44,2,90974), +(254,2,0,300,45,2,90975), +(256,2,3,300,124,3,90983), +(258,2,3,300,130,3,90984), +(260,2,0,300,45,2,90987), +(262,27,0,375,45,2,90988), +(264,27,4,375,130,3,90997), +(266,27,0,375,45,2,91008), +(268,27,0,375,45,2,91012), +(270,27,0,375,45,2,91014), +(273,27,0,375,45,2,91084), +(275,3,0,300,45,0,91089), +(277,3,0,300,45,0,91132), +(279,5,0,375,45,2,91133), +(283,5,0,375,45,2,91170), +(285,5,0,375,45,3,91188), +(287,5,0,375,45,2,91191), +(289,5,0,375,45,2,91197), +(291,5,0,375,45,2,91209), +(293,5,0,375,45,2,91211), +(295,5,4,375,140,3,91214), +(297,5,4,375,140,3,91215), +(301,1,0,1,45,2,91219), +(303,1,0,1,45,2,91221), +(305,1,0,1,45,2,91223), +(307,1,0,1,45,2,91224), +(309,1,0,1,45,2,91225), +(313,1,3,150,150,3,91226), +(315,1,4,450,150,3,91227), +(317,4,4,450,150,3,91757), +(319,4,4,450,150,3,91761), +(323,4,0,450,45,2,91762), +(325,4,0,450,45,2,91766), +(327,4,0,450,45,2,91769), +(329,4,4,450,150,3,91773), +(331,7,0,450,45,2,91775), +(333,7,0,450,45,2,91779), +(335,7,0,450,45,2,91780), +(339,7,0,450,45,2,91782), +(341,7,0,450,45,2,91785), +(343,7,1,450,45,2,91790), +(345,7,0,450,45,2,91792), +(347,1,0,1,45,2,91793), +(349,7,4,450,150,3,92137), +(351,7,4,450,150,3,92139), +(353,7,4,450,150,3,92145), +(355,7,4,450,150,3,92148), +(357,7,4,450,150,3,92163), +(359,7,4,450,150,3,92168), +(371,1,0,1,30,0,93440), +(372,4,0,1,30,0,93441), +(374,3,0,1,30,0,93442), +(378,3,0,1,35,0,93443), +(380,8,0,1,32,0,93444), +(628,3,4,450,150,0,98533), +(633,8,3,150,100,3,98556), +(635,3,3,225,100,0,98560), +(637,27,3,500,100,3,98569), +(639,3,3,225,120,0,98582), +(641,8,3,150,100,3,98588); + +DELETE FROM `achievement_criteria_data` WHERE `criteria_id` IN (5315, 5469,5470); +INSERT INTO `achievement_criteria_data` (`criteria_id`, `type`, `value1`, `value2`, `ScriptName`) VALUES +(5315, 8, 10, 3, ""), +(5469, 8, 50, 3, ""), +(5470, 8, 100, 3, ""); + +DELETE FROM `achievement_reward` WHERE `ID` IN (4854, 4855, 4856); +INSERT INTO `achievement_reward` (`ID`, `TitleA`, `TitleH`) VALUES +(4854, 189, 189), +(4855, 190, 190), +(4856, 191, 191); + +DELETE FROM `trinity_string` WHERE `entry`= 810; +INSERT INTO `trinity_string` (`entry`, `content_default`) VALUES +(810, "You can't use that here."); + diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 16db0ee3cb..689e37076b 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -1258,6 +1258,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: case ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM: case ACHIEVEMENT_CRITERIA_TYPE_CURRENCY: + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ARCHAEOLOGY_PROJECTS: SetCriteriaProgress(achievementCriteria, miscValue2, referencePlayer, PROGRESS_ACCUMULATE); break; // std case: high value at miscValue1 @@ -1486,7 +1487,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_GUILD: case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILLS_GUILD: case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE_GUILD: - case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ARCHAEOLOGY_PROJECTS: case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GUILD_CHALLENGE_TYPE: case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_GUILD_CHALLENGE: break; // Not implemented yet :( @@ -1543,6 +1543,8 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achi return progress->counter >= achievementCriteria->win_bg.winCount; case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE: return progress->counter >= achievementCriteria->kill_creature.creatureCount; + case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ARCHAEOLOGY_PROJECTS: + return progress->counter >= achievementCriteria->archaeology.count; case ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL: case ACHIEVEMENT_CRITERIA_TYPE_REACH_GUILD_LEVEL: return progress->counter >= achievementCriteria->reach_level.level; @@ -2913,6 +2915,32 @@ bool AchievementMgr::AdditionalRequirementsSatisfied(AchievementCriteriaEntry if (!unit || unit->GetHealthPct() >= reqValue) return false; break; + case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_PROJECT_RARITY: // 65 + { + if (!miscValue1) + return false; + + ResearchProjectEntry const* rp = sResearchProjectStore.LookupEntry(miscValue1); + if (!rp) return false; + + if (rp->hasReward != reqValue) + return false; + + break; + } + case ACHIEVEMENT_CRITERIA_ADDITIONAL_CONDITION_PROJECT_RACE: // 66 + { + if (!miscValue1) + return false; + + ResearchProjectEntry const* rp = sResearchProjectStore.LookupEntry(miscValue1); + if (!rp) return false; + + if (rp->ResearchBranchID != reqValue) + return false; + + break; + } default: break; } diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index e5f42ca241..2e08061b77 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -592,12 +592,24 @@ enum VehicleSeatFlagsB // CurrencyTypes.dbc enum CurrencyTypes { + // PvE and PvP CURRENCY_TYPE_CONQUEST_POINTS = 390, CURRENCY_TYPE_HONOR_POINTS = 392, CURRENCY_TYPE_JUSTICE_POINTS = 395, CURRENCY_TYPE_VALOR_POINTS = 396, CURRENCY_TYPE_CONQUEST_META_ARENA = 483, CURRENCY_TYPE_CONQUEST_META_RBG = 484, + + // Archaeology + CURRENCY_TYPE_DWARF_FRAGMENT = 384, + CURRENCY_TYPE_TROLL_FRAGMENT = 385, + CURRENCY_TYPE_FOSSIL_FRAGMENT = 393, + CURRENCY_TYPE_NELF_FRAGMENT = 394, + CURRENCY_TYPE_ORC_FRAGMENT = 397, + CURRENCY_TYPE_DRAENEI_FRAGMENT = 398, + CURRENCY_TYPE_VRYKUL_FRAGMENT = 399, + CURRENCY_TYPE_NERUBIAN_FRAGMENT = 400, + CURRENCY_TYPE_TOLVIR_FRAGMENT = 401, }; #endif diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 8c59ce0909..47ed0da65b 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -188,6 +188,8 @@ DBCStorage sPvPDifficultyStore(PvPDifficultyfmt); DBCStorage sQuestSortStore(QuestSortEntryfmt); DBCStorage sQuestXPStore(QuestXPfmt); DBCStorage sQuestFactionRewardStore(QuestFactionRewardfmt); +DBCStorage sQuestPOIBlobStore(QuestPOIBlobfmt); +DBCStorage sQuestPOIPointStore(QuestPOIPointfmt); DBCStorage sRandomPropertiesPointsStore(RandomPropertiesPointsfmt); DBCStorage sScalingStatDistributionStore(ScalingStatDistributionfmt); DBCStorage sScalingStatValuesStore(ScalingStatValuesfmt); @@ -205,6 +207,10 @@ DBCStorage sSpellItemEnchantmentConditionSt DBCStorage sSpellStore(SpellEntryfmt); PetFamilySpellsStore sPetFamilySpellsStore; +DBCStorage sResearchBranchStore(ResearchBranchEntryfmt); +DBCStorage sResearchFieldStore(ResearchFieldEntryfmt); +DBCStorage sResearchProjectStore(ResearchProjectEntryfmt); +DBCStorage sResearchSiteStore(ResearchSiteEntryfmt); DBCStorage sSpellReagentsStore(SpellReagentsEntryfmt); DBCStorage sSpellScalingStore(SpellScalingEntryfmt); @@ -563,9 +569,15 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sQuestXPStore, dbcPath, "QuestXP.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sQuestFactionRewardStore, dbcPath, "QuestFactionReward.dbc");//15595 + LoadDBC(availableDbcLocales, bad_dbc_files, sQuestPOIBlobStore, dbcPath, "QuestPOIBlob.dbc");//15595 + LoadDBC(availableDbcLocales, bad_dbc_files, sQuestPOIPointStore, dbcPath, "QuestPOIPoint.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sQuestSortStore, dbcPath, "QuestSort.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sRandomPropertiesPointsStore, dbcPath, "RandPropPoints.dbc");//15595 + LoadDBC(availableDbcLocales, bad_dbc_files, sResearchBranchStore, dbcPath, "ResearchBranch.dbc");//15595 + LoadDBC(availableDbcLocales, bad_dbc_files, sResearchFieldStore, dbcPath, "ResearchField.dbc");//15595 + LoadDBC(availableDbcLocales, bad_dbc_files, sResearchProjectStore, dbcPath, "ResearchProject.dbc");//15595 + LoadDBC(availableDbcLocales, bad_dbc_files, sResearchSiteStore, dbcPath, "ResearchSite.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sScalingStatDistributionStore, dbcPath, "ScalingStatDistribution.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sScalingStatValuesStore, dbcPath, "ScalingStatValues.dbc");//15595 @@ -582,7 +594,6 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sSpellStore, dbcPath, "Spell.dbc", &CustomSpellEntryfmt, &CustomSpellEntryIndex);// LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCategoriesStore, dbcPath,"SpellCategories.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sSpellCategoryStore, dbcPath, "SpellCategory.dbc"); - LoadDBC(availableDbcLocales, bad_dbc_files, sSpellReagentsStore, dbcPath,"SpellReagents.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sSpellScalingStore, dbcPath,"SpellScaling.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sSpellTotemsStore, dbcPath,"SpellTotems.dbc");//15595 diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index efb6c4fd5d..a4fc6fe567 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -193,7 +193,13 @@ TC_GAME_API extern DBCStorage sPowerDisplayStore; TC_GAME_API extern DBCStorage sQuestSortStore; TC_GAME_API extern DBCStorage sQuestXPStore; TC_GAME_API extern DBCStorage sQuestFactionRewardStore; +TC_GAME_API extern DBCStorage sQuestPOIPointStore; +TC_GAME_API extern DBCStorage sQuestPOIBlobStore; TC_GAME_API extern DBCStorage sRandomPropertiesPointsStore; +TC_GAME_API extern DBCStorage sResearchBranchStore; +TC_GAME_API extern DBCStorage sResearchFieldStore; +TC_GAME_API extern DBCStorage sResearchProjectStore; +TC_GAME_API extern DBCStorage sResearchSiteStore; TC_GAME_API extern DBCStorage sScalingStatDistributionStore; TC_GAME_API extern DBCStorage sScalingStatValuesStore; TC_GAME_API extern DBCStorage sSkillLineStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index ccb4245c54..5da7145978 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -74,6 +74,13 @@ struct AchievementCriteriaEntry uint64 winCount; // 4 } win_bg; + // ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ARCHAEOLOGY_PROJECTS = 3 + struct + { + uint32 unused; // 3 + uint32 count; // 4 + } archaeology; + // ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5 // ACHIEVEMENT_CRITERIA_TYPE_REACH_GUILD_LEVEL = 125 struct @@ -1782,6 +1789,22 @@ struct QuestFactionRewEntry int32 QuestRewFactionValue[10]; }; +struct QuestPOIBlobEntry +{ + uint32 id; // 0 m_id + uint32 type; // 1 m_type + uint32 mapId; // 2 m_mapId + uint32 unk; // 3 m_unk +}; + +struct QuestPOIPointEntry +{ + uint32 id; // 0 m_id + int32 x; // 1 m_zoneX + int32 y; // 2 m_zoneY + uint32 blobId; // 3 m_blobId +}; + struct RandomPropertiesPointsEntry { //uint32 Id; // 0 hidden key @@ -1791,6 +1814,59 @@ struct RandomPropertiesPointsEntry uint32 UncommonPropertiesPoints[5]; // 12-16 }; +// ResearchBranch.dbc +struct ResearchBranchEntry +{ + uint32 ID; + char* Name; + uint32 FieldID; + uint32 CurrencyID; + char* Icon; + uint32 ItemID; +}; + +// ResearchField.dbc +struct ResearchFieldEntry +{ + uint32 ID; + char* Name; + uint32 Unk3; //Unknown? +}; + +// ResearchProject.dbc +struct ResearchProjectEntry +{ + uint32 ID; + char* Name; + char* Description; + uint32 hasReward; + uint32 ResearchBranchID; + uint32 ProjectSpell; + uint32 Complexity; + char* Path; + uint32 RequiredFragments; +}; + +// ResearchSite.dbc +struct ResearchSiteEntry +{ + uint32 ID; + uint32 MapId; + uint32 QuestPOI; + char* Name; + char* Flags; + + bool IsValid() const + { + return ID != 140 && // template + ID != 142 && // template + ID != 161 && // template + ID != 471 && // Vashj'ir + ID != 473 && // Vashj'ir + ID != 475; // Vashj'ir + } +}; + struct ScalingStatDistributionEntry { uint32 Id; // 0 @@ -2022,7 +2098,7 @@ struct SpellEntry uint32 SpellShapeshiftId; // 44 SpellShapeshift.dbc uint32 SpellTargetRestrictionsId; // 45 SpellTargetRestrictions.dbc uint32 SpellTotemsId; // 46 SpellTotems.dbc - //uint32 ResearchProject; // 47 ResearchProject.dbc + uint32 ResearchProjectId; // 47 ResearchProject.dbc }; // SpellCategories.dbc diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index b82380b5d4..3255b02046 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -119,11 +119,17 @@ char const NameGenfmt[] = "dsii"; char const NumTalentsAtLevelfmt[] = "df"; char const OverrideSpellDatafmt[] = "niiiiiiiiiixx"; char const QuestFactionRewardfmt[] = "niiiiiiiiii"; +const char QuestPOIBlobfmt[] = "niii"; +const char QuestPOIPointfmt[] = "niii"; char const QuestSortEntryfmt[] = "nx"; char const QuestXPfmt[] = "niiiiiiiiii"; char const PowerDisplayfmt[] = "nixxxx"; char const PvPDifficultyfmt[] = "diiiii"; char const RandomPropertiesPointsfmt[] = "niiiiiiiiiiiiiii"; +char const ResearchBranchEntryfmt[] = "nsiisi"; +char const ResearchFieldEntryfmt[] = "nsi"; +char const ResearchProjectEntryfmt[] = "nssiiiisi"; +char const ResearchSiteEntryfmt[] = "niiss"; char const ScalingStatDistributionfmt[] = "niiiiiiiiiiiiiiiiiiiixi"; char const ScalingStatValuesfmt[] = "iniiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii"; char const SkillLinefmt[] = "nisxixi"; @@ -143,7 +149,7 @@ char const SpellEffectEntryfmt[] = "nifiiiffiiiiiifiifiiiiiiiix"; const std::string CustomSpellEffectEntryfmt = "ppppppppppppppappppppppppp"; const std::string CustomSpellEffectEntryIndex = "Id"; // 0 10 20 30 40 47 -char const SpellEntryfmt[] = "niiiiiiiiiiiiiiifiiiissxxiixxifiiiiiiixiiiiiiiix"; +char const SpellEntryfmt[] = "niiiiiiiiiiiiiiifiiiissxxiixxifiiiiiiixiiiiiiiii"; const std::string CustomSpellEntryfmt = "ppppppppppppppapaaaaaaaaapaaaaaapapppaapppaaapa"; const std::string CustomSpellEntryIndex = "Id"; char const SpellFocusObjectfmt[] = "nx"; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index a68dd4b789..274d2a60ce 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -807,6 +807,11 @@ void GameObject::getFishLootJunk(Loot* fishloot, Player* loot_owner) } } +void GameObject::getArchaeologyLoot(Loot* loot, Player* loot_owner) +{ + loot->AddCurrency(sArchaeologyMgr->GetFindCurrency(GetEntry()), 4, 9); +} + void GameObject::SaveToDB() { // this should only be used when the gameobject has already been loaded diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 017ef17af7..fb533d176d 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -776,6 +776,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject void SendGameObjectDespawn(); void getFishLoot(Loot* loot, Player* loot_owner); void getFishLootJunk(Loot* loot, Player* loot_owner); + void getArchaeologyLoot(Loot* loot, Player* loot_owner); GameobjectTypes GetGoType() const { return GameobjectTypes(GetByteValue(GAMEOBJECT_BYTES_1, 1)); } void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); } GOState GetGoState() const { return GOState(GetByteValue(GAMEOBJECT_BYTES_1, 0)); } diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index cf29560861..780307663e 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -240,6 +240,7 @@ enum CurrencyFlags enum CurrencyCategory { // ... + CURRENCY_CATEGORY_ARCHAEOLOGY = 82, CURRENCY_CATEGORY_META_CONQUEST = 89, // ... }; diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.h b/src/server/game/Entities/Object/Updates/UpdateFields.h index acee09bb56..7d67269cb0 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.h +++ b/src/server/game/Entities/Object/Updates/UpdateFields.h @@ -504,8 +504,8 @@ enum EPlayerFields PLAYER_FIELD_GLYPHS_1 = UNIT_END + 0x04B2, // Size: 9, Type: INT, Flags: PRIVATE PLAYER_GLYPHS_ENABLED = UNIT_END + 0x04BB, // Size: 1, Type: INT, Flags: PRIVATE PLAYER_PET_SPELL_POWER = UNIT_END + 0x04BC, // Size: 1, Type: INT, Flags: PRIVATE - PLAYER_FIELD_RESEARCHING_1 = UNIT_END + 0x04BD, // Size: 8, Type: TWO_SHORT, Flags: PRIVATE - PLAYER_FIELD_RESERACH_SITE_1 = UNIT_END + 0x04C5, // Size: 8, Type: TWO_SHORT, Flags: PRIVATE + PLAYER_FIELD_RESEARCH_PROJECT_1 = UNIT_END + 0x04BD, // Size: 8, Type: TWO_SHORT, Flags: PRIVATE + PLAYER_FIELD_RESEARCH_SITE_1 = UNIT_END + 0x04C5, // Size: 8, Type: TWO_SHORT, Flags: PRIVATE PLAYER_PROFESSION_SKILL_LINE_1 = UNIT_END + 0x04CD, // Size: 2, Type: INT, Flags: PRIVATE PLAYER_FIELD_UI_HIT_MODIFIER = UNIT_END + 0x04CF, // Size: 1, Type: FLOAT, Flags: PRIVATE PLAYER_FIELD_UI_SPELL_HIT_MODIFIER = UNIT_END + 0x04D0, // Size: 1, Type: FLOAT, Flags: PRIVATE diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 6693b27825..9be39395f0 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -19,6 +19,7 @@ #include "Player.h" #include "AccountMgr.h" #include "AchievementMgr.h" +#include "Archaeology.h" #include "ArenaTeam.h" #include "ArenaTeamMgr.h" #include "Battlefield.h" @@ -303,7 +304,7 @@ std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi) return ss; } -Player::Player(WorldSession* session): Unit(true) +Player::Player(WorldSession* session): Unit(true), _archaeology(this) { m_speakTime = 0; m_speakCount = 0; @@ -5668,6 +5669,10 @@ bool Player::UpdateSkill(uint32 skill_id, uint32 step) UpdateSkillEnchantments(skill_id, value, new_value); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, skill_id); + + if (skill_id == SKILL_ARCHAEOLOGY) + _archaeology.Update(); + return true; } @@ -5727,6 +5732,8 @@ bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLeve // For skinning and Mining chance decrease with level. 1-74 - no decrease, 75-149 - 2 times, 225-299 - 8 times switch (SkillId) { + case SKILL_ARCHAEOLOGY: + return UpdateSkillPro(SkillId, ((SkillValue < 50) ? 1000 : 0) * Multiplicator, gathering_skill_gain); case SKILL_HERBALISM: case SKILL_LOCKPICKING: case SKILL_JEWELCRAFTING: @@ -5962,6 +5969,10 @@ void Player::SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal) if (newVal > currVal) UpdateSkillEnchantments(id, currVal, newVal); + // archaeology skill updated + if (id == SKILL_ARCHAEOLOGY) + _archaeology.Update(); + UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL, id); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL, id); } @@ -5994,6 +6005,10 @@ void Player::SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal) SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1, 0); else if (GetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + 1) == id) SetUInt32Value(PLAYER_PROFESSION_SKILL_LINE_1 + 1, 0); + + // archaeology skill unlearned + if (id == SKILL_ARCHAEOLOGY) + _archaeology.UnLearn(); } } else if (newVal) //add @@ -6057,6 +6072,10 @@ void Player::SetSkill(uint16 id, uint16 step, uint16 newVal, uint16 maxVal) // Learn all spells for skill LearnSkillRewardedSpells(id, newVal); return; + + // archaeology skill learned + if (id == SKILL_ARCHAEOLOGY) + _archaeology.Learn(); } } } @@ -7141,6 +7160,10 @@ void Player::ModifyCurrency(uint32 id, int32 count, bool printLog/* = true*/, bo return; } + if (itr->second.state == PLAYERCURRENCY_NEW && HasSkill(SKILL_ARCHAEOLOGY) && + currency->Category == CURRENCY_CATEGORY_ARCHAEOLOGY) + _archaeology.ActivateBranch(sArchaeologyMgr->Currency2BranchId(currency->ID)); + WorldPacket packet(SMSG_UPDATE_CURRENCY, 12); packet.WriteBit(weekCap != 0); @@ -8581,6 +8604,8 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) go->getFishLoot(loot, this); else if (loot_type == LOOT_FISHING_JUNK) go->getFishLootJunk(loot, this); + else if (loot_type == LOOT_ARCHAEOLOGY) + go->getArchaeologyLoot(loot, this); if (go->GetGOInfo()->type == GAMEOBJECT_TYPE_CHEST && go->GetGOInfo()->chest.groupLootRules) { @@ -8844,7 +8869,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type) { SetLootGUID(guid); - WorldPacket data(SMSG_LOOT_RESPONSE, (9 + 50)); // we guess size + WorldPacket data(SMSG_LOOT_RESPONSE, 8 + 1 + 50 + 1 + 1); // we guess size data << uint64(guid); data << uint8(loot_type); data << LootView(*loot, this, permission); @@ -8884,9 +8909,9 @@ void Player::SendNotifyLootItemRemoved(uint8 lootSlot) const void Player::SendNotifyCurrencyLootRemoved(uint8 lootSlot) { - WorldPacket data(SMSG_CURRENCY_LOOT_REMOVED, 1); - data << uint8(lootSlot); - GetSession()->SendPacket(&data); + WorldPacket data(SMSG_CURRENCY_LOOT_REMOVED, 1); + data << uint8(lootSlot); + GetSession()->SendPacket(&data); } void Player::SendUpdateWorldState(uint32 Field, uint32 Value) const @@ -25210,6 +25235,21 @@ void Player::StoreLootItem(uint8 lootSlot, Loot* loot) SendEquipError(msg, nullptr, nullptr, item->itemid); } + +void Player::StoreLootCurrency(uint8 lootSlot, Loot* loot) +{ + LootCurrency* currency = loot->LootCurrencyInSlot(lootSlot, this); + + if (!currency) + { + SendEquipError(EQUIP_ERR_LOOT_GONE, nullptr, nullptr); + return; + } + + loot->NotifyCurrencyRemoved(lootSlot); + ModifyCurrency(currency->id, currency->count); +} + uint32 Player::CalculateTalentsPoints() const { // this dbc file has entries only up to level 100 @@ -25360,6 +25400,9 @@ void Player::_LoadSkills(PreparedQueryResult result) mSkillStatus.insert(SkillStatusMap::value_type(skill, SkillStatusData(count, SKILL_UNCHANGED))); loadedSkillValues[skill] = value; + if (skill == SKILL_ARCHAEOLOGY) + _archaeology.Initialize(); + ++count; if (count >= PLAYER_MAX_SKILLS) // client limit diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 08561a8c31..0d6eb33c28 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -23,6 +23,7 @@ #include "GroupReference.h" #include "MapReference.h" +#include "Archaeology.h" #include "Item.h" #include "PetDefines.h" #include "QuestDef.h" @@ -58,6 +59,7 @@ class PlayerSocial; class SpellCastTargets; class UpdateMask; class PlayerAI; +class Archaeology; struct CharacterCustomizeInfo; @@ -1410,6 +1412,7 @@ class TC_GAME_API Player : public Unit, public GridObject void AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast = false); void AutoStoreLoot(uint32 loot_id, LootStore const& store, bool broadcast = false) { AutoStoreLoot(NULL_BAG, NULL_SLOT, loot_id, store, broadcast); } void StoreLootItem(uint8 lootSlot, Loot* loot); + void StoreLootCurrency(uint8 lootSlot, Loot* loot); InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = NULL, uint32* itemLimitCategory = NULL) const; InventoryResult CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item* pItem = NULL, bool swap = false, uint32* no_space_count = NULL) const; @@ -1953,6 +1956,13 @@ class TC_GAME_API Player : public Unit, public GridObject bool UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLevel, uint32 Multiplicator = 1); bool UpdateFishingSkill(); + inline void SurveyDigSite() { _archaeology.UseSite(); } + inline void NotifyRequestResearchHistory() { _archaeology.SendResearchHistory(); } + inline bool ArchProjectCompleteable(uint32 projectId) { return _archaeology.ProjectCompleteable(uint16(projectId)); } + inline bool HasArchProject(uint32 projectId) { return _archaeology.ProjectExists(uint16(projectId)); } + inline void CompleteArchProject(uint32 projectId) { _archaeology.CompleteProject(uint16(projectId)); } + inline void SetArchData(struct ArchData *data) { _archaeology.SetArchData(data); } + float GetHealthBonusFromStamina(); float GetManaBonusFromIntellect(); @@ -2887,6 +2897,8 @@ class TC_GAME_API Player : public Unit, public GridObject uint32 manaBeforeDuel; WorldLocation _corpseLocation; + + Archaeology _archaeology; }; TC_GAME_API void AddItemsSetItem(Player* player, Item* item); diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 0eaf9237b9..f993dc060b 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -136,7 +136,7 @@ void WorldSession::HandleLootCurrencyOpcode(WorldPacket& recvData) return; } - loot->NotifyCurrencyRemoved(lootSlot); + player->StoreLootCurrency(lootSlot, loot); } void WorldSession::HandleLootMoneyOpcode(WorldPacket& /*recvData*/) diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 1a86813528..c33d5904ac 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -2096,3 +2096,9 @@ void WorldSession::SendStreamingMovie() SendPacket(&data); } + +void WorldSession::HandleRequestResearchHistory(WorldPacket & /*recv_data*/) +{ + if (Player* player = GetPlayer()) + player->NotifyRequestResearchHistory(); +} diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 3031ad8cb8..a19bb478eb 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -54,7 +54,8 @@ void WorldSession::HandleClientCastFlags(WorldPacket& recvPacket, uint8 castFlag } else if (castFlags & 0x8) // Archaeology { - uint32 count, entry, usedCount; + ArchData* archData = new ArchData(); + uint32 count; uint8 type; recvPacket >> count; for (uint32 i = 0; i < count; ++i) @@ -63,15 +64,17 @@ void WorldSession::HandleClientCastFlags(WorldPacket& recvPacket, uint8 castFlag switch (type) { case 2: // Keystones - recvPacket >> entry; // Item id - recvPacket >> usedCount; // Item count + recvPacket >> archData->keyId; // Item id + recvPacket >> archData->keyCount; // Item count break; case 1: // Fragments - recvPacket >> entry; // Currency id - recvPacket >> usedCount; // Currency count + recvPacket >> archData->fragId; // Currency id + recvPacket >> archData->fragCount; // Currency count break; } } + if (Player* player = GetPlayer()) + player->SetArchData(archData); } } @@ -389,7 +392,8 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) caster = _player; } - if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(spellInfo->Id) && !spellInfo->IsRaidMarker()) + if (caster->GetTypeId() == TYPEID_PLAYER && !caster->ToPlayer()->HasActiveSpell(spellInfo->Id) && !spellInfo->IsRaidMarker() && + !caster->ToPlayer()->HasArchProject(spellInfo->ResearchProjectId)) { // not have spell in spellbook recvPacket.rfinish(); // prevent spam at ignore packet diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 156efc9e28..5c3dd40f03 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -440,6 +440,27 @@ void Loot::AddItem(LootStoreItem const& item) } } +void Loot::AddCurrency(uint32 entry, uint32 min, uint32 max) +{ + uint32 amount = urand(min, max); + + currencies.push_back(LootCurrency(entry, amount)); + + unlootedCurrency++; +} + +LootCurrency* Loot::LootCurrencyInSlot(uint32 lootSlot, Player* player) +{ + if (currencies[lootSlot].looted) + return NULL; + + currencies[lootSlot].looted = true; + + unlootedCurrency--; + + return &(currencies[lootSlot]); +} + // Calls processor of corresponding LootTemplate (which handles everything including references) bool Loot::FillLoot(uint32 lootId, LootStore const& store, Player* lootOwner, bool personal, bool noEmptyError, uint16 lootMode /*= LOOT_MODE_DEFAULT*/) { @@ -1104,6 +1125,19 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv) } } + for (uint8 i = 0; i < l.currencies.size(); ++i) + { + if (l.currencies[i].count == 0 || l.currencies[i].id == 0 || l.currencies[i].looted) + continue; + + l.currencies[i].index = currenciesShown + itemsShown; + + b << uint8(l.currencies[i].index); + b << uint32(l.currencies[i].id); + b << uint32(l.currencies[i].count); + ++currenciesShown; + } + //update number of items and currencies shown b.put(count_pos, itemsShown); b.put(currency_count_pos, currenciesShown); diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 189c986f84..43484d2601 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -87,6 +87,8 @@ enum LootType : uint8 LOOT_PROSPECTING = 7, LOOT_MILLING = 8, + LOOT_ARCHAEOLOGY = 10, + LOOT_FISHINGHOLE = 20, // unsupported by client, sending LOOT_FISHING instead LOOT_INSIGNIA = 21, // unsupported by client, sending LOOT_CORPSE instead LOOT_FISHING_JUNK = 22 // unsupported by client, sending LOOT_FISHING instead @@ -193,6 +195,20 @@ struct NotNormalLootItem : index(_index), is_looted(_islooted) { } }; +struct LootCurrency +{ + uint8 index; + uint32 id; + uint32 count; + bool looted; + + LootCurrency() + : index(0), id(0), count(0), looted(false) { } + + LootCurrency(uint32 _id, uint32 _count) + : index(0), id(_id), count(_count), looted(false) { } +}; + struct Loot; class LootTemplate; @@ -316,8 +332,11 @@ struct TC_GAME_API Loot std::vector items; std::vector quest_items; + std::vector currencies; + uint32 gold; uint8 unlootedCount; + uint8 unlootedCurrency; ObjectGuid roundRobinPlayer; // GUID of the player having the Round-Robin ownership for the loot. If 0, round robin owner has released. LootType loot_type; // required for achievement system uint8 maxDuplicates; // Max amount of items with the same entry that can drop (default is 1; on 25 man raid mode 3) @@ -326,7 +345,7 @@ struct TC_GAME_API Loot // Only set for inventory items that can be right-click looted uint32 containerID; - Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), roundRobinPlayer(), loot_type(LOOT_CORPSE), maxDuplicates(1), containerID(0) { } + Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), roundRobinPlayer(), unlootedCurrency(0), loot_type(LOOT_CORPSE), maxDuplicates(1), containerID(0) { } ~Loot() { clear(); } // For deleting items at loot removal since there is no backward interface to the Item() @@ -359,13 +378,14 @@ struct TC_GAME_API Loot quest_items.clear(); gold = 0; unlootedCount = 0; + unlootedCurrency = 0; roundRobinPlayer.Clear(); loot_type = LOOT_NONE; i_LootValidatorRefManager.clearReferences(); } bool empty() const { return items.empty() && gold == 0; } - bool isLooted() const { return gold == 0 && unlootedCount == 0; } + bool isLooted() const { return gold == 0 && unlootedCount == 0 && unlootedCurrency == 0; } void NotifyItemRemoved(uint8 lootIndex); void NotifyQuestItemRemoved(uint8 questIndex); @@ -386,6 +406,9 @@ struct TC_GAME_API Loot bool hasItemFor(Player* player) const; bool hasOverThresholdItem() const; + void AddCurrency(uint32 entry, uint32 min, uint32 max); + LootCurrency* LootCurrencyInSlot(uint32 lootSlot, Player* player); + private: void FillNotNormalLootFor(Player* player, bool presentAtLooting); NotNormalLootItemList* FillFFALoot(Player* player); diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 53813a782a..31c1688d08 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -780,7 +780,7 @@ enum TrinityStrings LANG_NEED_CHARACTER_NAME = 807, LANG_PLAYER_NOT_EXIST_OR_OFFLINE = 808, LANG_ACCOUNT_FOR_PLAYER_NOT_FOUND = 809, - // unused = 810, + LANG_YOU_CANT_USE_THAT_HERE = 810, LANG_GUILD_MASTER = 811, LANG_GUILD_OFFICER = 812, LANG_GUILD_VETERAN = 813, diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index de0c6c9c35..924d9b7838 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -2948,7 +2948,8 @@ enum LockType LOCKTYPE_SLOW_CLOSE = 18, LOCKTYPE_FISHING = 19, LOCKTYPE_INSCRIPTION = 20, - LOCKTYPE_OPEN_FROM_VEHICLE = 21 + LOCKTYPE_OPEN_FROM_VEHICLE = 21, + LOCKTYPE_ARCHAEOLOGY = 22, }; enum TrainerType // this is important type for npcs! @@ -3415,6 +3416,7 @@ inline SkillType SkillByLockType(LockType locktype) case LOCKTYPE_MINING: return SKILL_MINING; case LOCKTYPE_FISHING: return SKILL_FISHING; case LOCKTYPE_INSCRIPTION: return SKILL_INSCRIPTION; + case LOCKTYPE_ARCHAEOLOGY: return SKILL_ARCHAEOLOGY; default: break; } return SKILL_NONE; diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index ddc7f6a7e6..4b198b126a 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -460,7 +460,7 @@ void OpcodeTable::Initialize() DEFINE_OPCODE_HANDLER(CMSG_REQUEST_RAID_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestRaidInfoOpcode ); DEFINE_OPCODE_HANDLER(CMSG_REQUEST_RATED_BG_INFO, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestRatedBgInfo ); DEFINE_OPCODE_HANDLER(CMSG_REQUEST_RATED_BG_STATS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleRequestRatedBgStats ); - DEFINE_OPCODE_HANDLER(CMSG_REQUEST_RESEARCH_HISTORY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL ); + DEFINE_OPCODE_HANDLER(CMSG_REQUEST_RESEARCH_HISTORY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestResearchHistory ); DEFINE_OPCODE_HANDLER(CMSG_REQUEST_VEHICLE_EXIT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleRequestVehicleExit ); DEFINE_OPCODE_HANDLER(CMSG_REQUEST_VEHICLE_NEXT_SEAT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChangeSeatsOnControlledVehicle); DEFINE_OPCODE_HANDLER(CMSG_REQUEST_VEHICLE_PREV_SEAT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleChangeSeatsOnControlledVehicle); diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 37ec2ca3ce..af87b2b585 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -1077,6 +1077,7 @@ class TC_GAME_API WorldSession void HandleObjectUpdateFailedOpcode(WorldPacket& recvPacket); void HandleRequestCategoryCooldowns(WorldPacket& recvPacket); void SendStreamingMovie(); + void HandleRequestResearchHistory(WorldPacket& recv_data); int32 HandleEnableNagleAlgorithm(); public: diff --git a/src/server/game/Skills/Archaeology/Archaeology.cpp b/src/server/game/Skills/Archaeology/Archaeology.cpp new file mode 100644 index 0000000000..80b15865fb --- /dev/null +++ b/src/server/game/Skills/Archaeology/Archaeology.cpp @@ -0,0 +1,89 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#include "Archaeology.h" +#include "DBCStore.h" +#include "Player.h" + +Archaeology::Archaeology(Player* player): _player(player) +{ + memset(_site, 0, sizeof(struct SiteData) * CONTINENT_SITES * COUNT_CONT); + + _continentState[CONT_EASTERN] = STATE_NULL; + _continentState[CONT_KALIMDOR] = STATE_NULL; + _continentState[CONT_OUTLAND] = STATE_NULL; + _continentState[CONT_NORTHREND] = STATE_NULL; +} + +void Archaeology::Initialize() +{ + if (!_player || !_player->HasSkill(SKILL_ARCHAEOLOGY)) + return; + + LoadSitesFromDB(); + VerifySites(); + + LoadCompletedProjectsFromDB(); + LoadCurrentProjectsFromDB(); + VerifyProjects(); +} + +void Archaeology::Learn() +{ + _continentState[CONT_EASTERN] = STATE_USE; + _continentState[CONT_KALIMDOR] = STATE_USE; + _continentState[CONT_OUTLAND] = STATE_NULL; + _continentState[CONT_NORTHREND] = STATE_NULL; + RegenerateAllSites(); + + InitBranches(); + VerifyProjects(); +} + +void Archaeology::UnLearn() +{ + _continentState[CONT_EASTERN] = STATE_NULL; + _continentState[CONT_KALIMDOR] = STATE_NULL; + _continentState[CONT_OUTLAND] = STATE_NULL; + _continentState[CONT_NORTHREND] = STATE_NULL; + RegenerateAllSites(); + + CleanProjects(); +} + +void Archaeology::Update() +{ + uint16 skillValue = _player->GetSkillValue(SKILL_ARCHAEOLOGY); + + if (SKILL_OUTLAND <= skillValue && _continentState[CONT_OUTLAND] == STATE_NULL) + { + _continentState[CONT_OUTLAND] = STATE_USE; + RegenerateContinent(CONT_OUTLAND); + } + + if(SKILL_NORTHREND <= skillValue && _continentState[CONT_NORTHREND] == STATE_NULL) + { + _continentState[CONT_NORTHREND] = STATE_USE; + RegenerateContinent(CONT_NORTHREND); + } + + if(SKILL_CATA <= skillValue) + { + _continentState[CONT_EASTERN] = STATE_EXT; + _continentState[CONT_KALIMDOR] = STATE_EXT; + } +} diff --git a/src/server/game/Skills/Archaeology/Archaeology.h b/src/server/game/Skills/Archaeology/Archaeology.h new file mode 100644 index 0000000000..6ce016c862 --- /dev/null +++ b/src/server/game/Skills/Archaeology/Archaeology.h @@ -0,0 +1,103 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#ifndef _ARCHAEOLOGY_H +#define _ARCHAEOLOGY_H + +#include "Common.h" +#include "WorldSession.h" +#include "ArchaeologyMgr.h" + +#define DIGS_PER_SITE 3 +class Player; + +struct ArchData +{ + ArchData() : fragId(0), fragCount(0), keyId(0), keyCount(0) { } + + uint32 fragId; + uint32 fragCount; + uint32 keyId; + uint32 keyCount; +}; + +enum ContinentState +{ + STATE_NULL = 0, + STATE_USE = 1, + STATE_EXT = 2, +}; + +class Archaeology +{ +public: + Archaeology(Player* player); + + void Initialize(); + + void Learn(); + void UnLearn(); + void Update(); + + void UseSite(); + + void SendResearchHistory(); + + void ActivateBranch(uint8 branchId, bool force = false); + bool ProjectCompleteable(uint16 projectId); + bool ProjectExists(uint16 projectId); + + void CompleteProject(uint16 projectId); + void SetArchData(struct ArchData *data) + { + ASSERT(data); + archData = data; + } + +private: + Player* _player; + ArchData *archData; + + // Site Functionality + ContinentState _continentState[4]; + SiteData _site[CONTINENT_SITES * COUNT_CONT]; + + void LoadSitesFromDB(); + void VerifySites(); + + Continent GetContinent(); + uint32 GetNearestSite(float &distance); + void SetSite(uint32 posi, uint16 entry, uint32 state = 0); + void RegeneratePosition(uint32 position, Continent continent); + void RegenerateContinent(Continent continent); + void RegenerateAllSites(); + + // Project Functionality + std::map > _completedProjects; + std::map _branches; + + void LoadCompletedProjectsFromDB(); + void LoadCurrentProjectsFromDB(); + void InitBranches(); + void VerifyProjects(); + void CleanProjects(); + + void RegenerateBranch(uint8 branch); + void VisualizeBranch(uint8 position, uint16 project); +}; + +#endif diff --git a/src/server/game/Skills/Archaeology/ArchaeologyMgr.cpp b/src/server/game/Skills/Archaeology/ArchaeologyMgr.cpp new file mode 100644 index 0000000000..5b7bbdb299 --- /dev/null +++ b/src/server/game/Skills/Archaeology/ArchaeologyMgr.cpp @@ -0,0 +1,380 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#include "ArchaeologyMgr.h" +#include "DBCStores.h" +#include "SpellMgr.h" +#include "Timer.h" + +namespace archaeology +{ + +ArchaeologyMgr::ArchaeologyMgr() +{ +} + +ArchaeologyMgr::~ArchaeologyMgr() +{ +} + +ArchaeologyMgr* ArchaeologyMgr::instance() +{ + static ArchaeologyMgr instance; + return &instance; +} + +void ArchaeologyMgr::LoadData() +{ + uint32 oldMSTime = getMSTime(); + std::list > sites; + + QueryResult result = WorldDatabase.Query("SELECT Id, MapId, QuestPOI, ReqSkill, MinLevel, GameobjectEntry FROM archaeology_sites"); + + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 archaeology sites. DB table `archaeology_site` is empty!"); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + SiteEntry entry; + entry.id = fields[0].GetUInt16(); + entry.mapId = fields[1].GetUInt32(); + entry.questPOI = fields[2].GetUInt16(); + entry.reqSkill = fields[3].GetUInt32(); + entry.minLevel = fields[4].GetUInt32(); + _objectMap[entry.id] = fields[5].GetUInt32(); + + switch (entry.mapId) + { + case 0: + _siteMap[CONT_EASTERN + COUNT_CONT].push_back(entry); + + if (entry.reqSkill < 450) + _siteMap[CONT_EASTERN].push_back(entry); + break; + case 1: + _siteMap[CONT_KALIMDOR + COUNT_CONT].push_back(entry); + + if (entry.reqSkill < 450) + _siteMap[CONT_KALIMDOR].push_back(entry); + break; + case 530: + _siteMap[CONT_OUTLAND].push_back(entry); + break; + case 571: + _siteMap[CONT_NORTHREND].push_back(entry); + break; + } + + sites.push_back(std::pair(entry.id, entry.questPOI)); + ++count; + } + while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u archaeology sites in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + + oldMSTime = getMSTime(); + count = 0; + + for (uint32 i = 0; i < sQuestPOIPointStore.GetNumRows(); i++) + { + const QuestPOIPointEntry* entry = sQuestPOIPointStore.LookupEntry(i); + if (!entry) + continue; + + for (std::list >::iterator itr = sites.begin(); itr != sites.end();) + { + if (itr->second == entry->blobId) + { + uint16 site = itr->first; + uint16 poiRel = itr->second; + + if (_polygonMap[site]) + { + TC_LOG_ERROR("server.loading", "Archaeology: Tried to overwrite polygonData of site %u", site); + break; + } + + // Create Site Polygon + _polygonMap[site] = new SitePolygonGraph(); + _polygonMap[site]->add_node(entry->x, entry->y); + + for (uint32 j = 1; j < 12; j++) + { + entry = sQuestPOIPointStore.LookupEntry(++i); + if (poiRel != entry->blobId) + { + TC_LOG_ERROR("server.loading", "Archaeology: Tried to use POI %u not related to site %u", entry->id, poiRel); + break; + } + + // Add new perimeter point + _polygonMap[site]->add_node(entry->x, entry->y); + } + + // Generate ALL inscribed and boundary geometries for the polygon! + _polygonMap[site]->finalize_polygon(); + + // NEXT! + count++; + sites.erase(itr++); + } + else + itr++; + } + } + + for (std::list >::iterator itr = sites.begin(); itr != sites.end(); itr++) + TC_LOG_ERROR("player.skills", "Archaeology: SiteEntry:%u links at POIBlob:%u which does not exist", itr->first, itr->second); + + TC_LOG_INFO("server.loading", ">> Loaded %u archaeology site polygons in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + + oldMSTime = getMSTime(); + count = 0; + + for (uint32 branchId = 0; branchId < sResearchBranchStore.GetNumRows(); branchId++) + if (const ResearchBranchEntry* branchEntry = sResearchBranchStore.LookupEntry(branchId)) + { + if (branchEntry->CurrencyID == 0) + continue; + + _currencyMap[branchEntry->CurrencyID] = branchId; + + BranchEntry branch; + branch.fieldId = ++count; + branch.rareChance = 0; + branch.currencyId = branchEntry->CurrencyID; + branch.keystoneId = branchEntry->ItemID; + _branchMap[branchId] = branch; + } + + TC_LOG_INFO("server.loading", ">> Loaded %u archaeology branches in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + + oldMSTime = getMSTime(); + count = 0; + + std::map rareCount, epicCount; + + result = WorldDatabase.Query("SELECT Entry, Branch, Rarity, ReqSkill, Fragments, Keystone, Spell FROM archaeology_projects"); + if (!result) + { + TC_LOG_INFO("server.loading", ">> Loaded 0 archaeology projects. DB table `archaeology_projects` is empty!"); + return; + } + + do + { + Field* fields = result->Fetch(); + + uint16 entry = fields[0].GetUInt16(); + + ProjectEntry project; + project.entry = entry; + project.branch = fields[1].GetUInt8(); + project.rarity = fields[2].GetUInt8(); + project.reqSkill = fields[3].GetUInt16(); + project.fragments = fields[4].GetUInt8(); + project.keystone = fields[5].GetUInt8(); + project.spell = fields[6].GetUInt32(); + + _projects[entry] = project; + + if (ITEM_QUALITY_RARE > project.rarity) + _commonProjects[project.branch].push_back(entry); + else + { + _rareProjects[project.branch].push_back(entry); + if (project.rarity == ITEM_QUALITY_RARE) + rareCount[project.branch]++; + else + epicCount[project.branch]++; + } + ++count; + } + while (result->NextRow()); + TC_LOG_INFO("server.loading", ">> Loaded %u archaeology projects in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + + oldMSTime = getMSTime(); + count = 0; + + for (std::map::iterator itr = _branchMap.begin(); itr != _branchMap.end(); ++itr) + { + uint8 entry = itr->first; + float chance = 1.0f; + chance -= float(epicCount[entry]) * EPIC_CHANCE; + chance /= float(rareCount[entry]); + itr->second.rareChance = std::min(chance, MAX_RARE_CHANCE); + + ++count; + } + TC_LOG_INFO("server.loading", ">> Calculated %u archaeology branch chances in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); +} + +bool ArchaeologyMgr::isSiteValid(SiteData* sites, uint16 entry) +{ + for (uint32 i = 0; i < CONTINENT_SITES; i++) + if (sites[i].entry == entry) + return false; + + return true; +} + +uint16 ArchaeologyMgr::GetNewSite(Continent continent, SiteData* sites, bool extended, uint32 playerLevel) +{ + std::vector* siteList; + uint32 offset = continent* CONTINENT_SITES; + uint16 entry; + + if (extended) + siteList = &(_siteMap[continent + COUNT_CONT]); + else + siteList = &(_siteMap[continent]); + + do + { + uint32 position = urand(0, siteList->size() - 1); + entry = uint16(siteList->at(position).id); + if (siteList->at(position).minLevel > playerLevel) + continue; + } + while (!isSiteValid(&(sites[offset]), entry)); + + return entry; +} + +bool ArchaeologyMgr::SetSiteCoords(SiteData &site) +{ + if (SitePolygonGraph* poly = _polygonMap[site.entry]) + { + // Calculate random point of interest + SitePolygonGraphNode node = poly->randomize_poi(); + + // Get random point into Polygon + site.x = node.getX(); + site.y = node.getY(); + + return true; + } + return false; +} + +uint32 ArchaeologyMgr::GetSiteType(uint16 entry) +{ + if (!_objectMap[entry]) + TC_LOG_ERROR("player.skills", "Archaeology: Entry %u has no find object Type!", entry); + + return _objectMap[entry]; +} + +uint32 ArchaeologyMgr::GetFindCurrency(uint32 go_entry) +{ + switch (go_entry) + { + case FIND_OBJECT_DRAENEI: + return CURRENCY_TYPE_DRAENEI_FRAGMENT; + case FIND_OBJECT_DWARF: + return CURRENCY_TYPE_DWARF_FRAGMENT; + case FIND_OBJECT_FOSSIL: + return CURRENCY_TYPE_FOSSIL_FRAGMENT; + case FIND_OBJECT_NERUBIAN: + return CURRENCY_TYPE_NERUBIAN_FRAGMENT; + case FIND_OBJECT_NIGHT_ELF: + return CURRENCY_TYPE_NELF_FRAGMENT; + case FIND_OBJECT_ORC: + return CURRENCY_TYPE_ORC_FRAGMENT; + case FIND_OBJECT_TOL_VIR: + return CURRENCY_TYPE_TOLVIR_FRAGMENT; + case FIND_OBJECT_TROLL: + return CURRENCY_TYPE_TROLL_FRAGMENT; + case FIND_OBJECT_VRYKUL: + return CURRENCY_TYPE_VRYKUL_FRAGMENT; + } + return 0; +} + +uint16 ArchaeologyMgr::GetNewProject(BranchData* branch, std::map >* completedMap, uint32 playerSkill) +{ + uint8 branchId = branch->branchId; + std::vector* _rares = &_rareProjects[branchId]; + std::vector* _commons = &_commonProjects[branchId]; + + float rareChance = float(_rares->size() - branch->completedRares) / float(_commons->size() + _rares->size()); + float roll1 = frand(.0f, 1.0f); + + // Are we eligible for a Rare Project ? + if (rareChance > roll1) + { + float roll2 = frand(.0f, 1.0f); + float rareChance = _branchMap[branchId].rareChance; + + for (std::vector::iterator itr = _rares->begin(); itr != _rares->end(); ++itr) + { + float chance = (_projects[*itr].rarity == ITEM_QUALITY_RARE)? rareChance: EPIC_CHANCE; + + // Are we in our choose range ? + if (chance < roll2) + { + roll2 -= chance; + continue; + } + + // Do we have the required SkillLevel ? + if (_projects[*itr].reqSkill > playerSkill) + break; + + // Did we already complete the artifact ? + if (completedMap->find(*itr) != completedMap->end()) + break; + + return *itr; + } + } + + // did we already complete all commons of the branch + uint32 begin = urand(0, _commons->size() - 1); + uint32 end = (begin == 0) ? _commons->size() - 1 : begin - 1; + + uint32 secondArtifact = 0; + + for (uint16 itr = begin; itr != end ; itr = (itr + 1) % _commons->size()) + { + uint16 projectId = _commons->at(itr); + + // Do we have the required SkillLevel ? + if (_projects[projectId].reqSkill > playerSkill) + continue; + + // Did we already complete the artifact ? + if (completedMap->find(projectId) != completedMap->end()) + { + if (!secondArtifact) + secondArtifact = projectId; + + continue; + } + + return _projects[projectId].entry; + } + // looks like we didn't find an eligible common artifact => relax the non repetitive restriction + return secondArtifact; +} + +} // namespace archaeology diff --git a/src/server/game/Skills/Archaeology/ArchaeologyMgr.h b/src/server/game/Skills/Archaeology/ArchaeologyMgr.h new file mode 100644 index 0000000000..41e2869d87 --- /dev/null +++ b/src/server/game/Skills/Archaeology/ArchaeologyMgr.h @@ -0,0 +1,152 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#ifndef _ARCHAEOLOGY_MGR_H +#define _ARCHAEOLOGY_MGR_H + +#include +#include +#include "SitePolygonGraph.h" +#include "Define.h" + +#define CONTINENT_SITES 4 +#define MAX_PROJECTS 16 +#define POINT_PER_POLYGON 12 +#define EPIC_CHANCE 0.05f +#define MAX_RARE_CHANCE 0.2f + +enum Continent +{ + CONT_EASTERN = 0, + CONT_KALIMDOR = 1, + CONT_OUTLAND = 2, + CONT_NORTHREND = 3, + COUNT_CONT = 4, +}; + +enum SkillStart +{ + SKILL_OUTLAND = 300, + SKILL_NORTHREND = 375, + SKILL_CATA = 450, +}; + +enum FindObject +{ + FIND_OBJECT_DRAENEI = 207188, + FIND_OBJECT_DWARF = 204282, + FIND_OBJECT_FOSSIL = 206836, + FIND_OBJECT_NERUBIAN = 203078, + FIND_OBJECT_NIGHT_ELF = 203071, + FIND_OBJECT_ORC = 207187, + FIND_OBJECT_TOL_VIR = 207190, + FIND_OBJECT_TROLL = 202655, + FIND_OBJECT_VRYKUL = 207189, +}; + +struct SiteEntry +{ + uint16 id; + uint32 mapId; + uint16 questPOI; + uint32 reqSkill; + uint32 minLevel; +}; + +struct SiteData +{ + uint16 entry; + uint16 state; + float x; + float y; + float tele_x; + float tele_y; + float tele_angle; +}; + +struct BranchEntry +{ + uint8 fieldId; + float rareChance; + uint32 currencyId; + uint32 keystoneId; +}; + +struct BranchData +{ + uint8 branchId; + uint8 completedRares; + uint32 project; +}; + +struct ProjectEntry +{ + uint16 entry; + uint8 branch; + uint8 rarity; + uint32 reqSkill; + uint32 fragments; + uint8 keystone; + uint32 spell; +}; + +namespace archaeology +{ + +class TC_GAME_API ArchaeologyMgr +{ +public: + static ArchaeologyMgr* instance(); + void LoadData(); + + // Site Functionality + static uint32 GetFindCurrency(uint32 go_entry); + uint16 GetNewSite(Continent continent, SiteData *sites, bool extended, uint32 playerLevel); + bool SetSiteCoords(SiteData &site); + uint32 GetSiteType(uint16 entry); + + // Project Functionality + uint8 Currency2BranchId(uint32 currencyId) { return _currencyMap[currencyId]; } + const BranchEntry *GetBranchEntry(uint8 branchId) { return &_branchMap[branchId]; } + const ProjectEntry *GetProjectEntry(uint16 projectId) { return &_projects[projectId]; } + + bool IsRareProject(uint16 project) { return _projects[project].rarity >= ITEM_QUALITY_RARE; } + uint16 GetNewProject(BranchData *branch, std::map > *completedMap, uint32 PlayerSkill); + +private: + ArchaeologyMgr(); + ~ArchaeologyMgr(); + + bool isSiteValid(SiteData *sites, uint16 entry); + + std::map > _siteMap; + std::map _polygonMap; + std::map _objectMap; + + std::map _currencyMap; // currencyId -> branchId + std::map _branchMap; // branchId -> BranchEntry + + std::map _projects; + std::map > _commonProjects; + std::map > _rareProjects; +}; + +} // namespace archaeology + +#define sArchaeologyMgr archaeology::ArchaeologyMgr::instance() + +#endif diff --git a/src/server/game/Skills/Archaeology/ArchaeologyProjects.cpp b/src/server/game/Skills/Archaeology/ArchaeologyProjects.cpp new file mode 100644 index 0000000000..3a386f5971 --- /dev/null +++ b/src/server/game/Skills/Archaeology/ArchaeologyProjects.cpp @@ -0,0 +1,234 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#include "Archaeology.h" +#include "DBCStore.h" +#include "Player.h" + +void Archaeology::LoadCompletedProjectsFromDB() +{ + QueryResult result; + + result = CharacterDatabase.PQuery("SELECT project, time, count FROM character_archaeology_completed WHERE guid=%u", + _player->GetGUID()); + + if (!result) + return; + + uint32 count = 0; + do { + Field *fields = result->Fetch(); + uint16 project = fields[0].GetUInt16(); + int32 time = fields[1].GetInt32(); + int32 count = fields[2].GetInt32(); + + _completedProjects[project].first = time; + _completedProjects[project].second = count; + + } while (result->NextRow()); +} + +void Archaeology::InitBranches() +{ + for (uint8 i = 0; i < sResearchBranchStore.GetNumRows(); ++i) + if (const ResearchBranchEntry *branch = sResearchBranchStore.LookupEntry(i)) + if (branch->CurrencyID) + { + struct BranchData branchData; + branchData.branchId = branch->ID; + branchData.completedRares = 0; + branchData.project = 0; + _branches[i] = branchData; + } +} + +void Archaeology::LoadCurrentProjectsFromDB() +{ + QueryResult result; + + InitBranches(); + + result = CharacterDatabase.PQuery("SELECT branch, project FROM character_archaeology_projects WHERE guid=%u", + _player->GetGUID()); + + if (!result) + return; + + uint32 count = 0; + do { + Field *fields = result->Fetch(); + uint8 branch = fields[0].GetUInt8(); + uint16 project = fields[1].GetUInt16(); + + if (uint16 old_project = _branches[branch].project) + { + TC_LOG_ERROR("player.skills", "ERROR - Archaeology: Tried to assign project %u to branch %u, which already holds project %u!", + project, branch, old_project); + continue; + } + + _branches[branch].project = project; + } while (result->NextRow()); +} + +void Archaeology::VisualizeBranch(uint8 position, uint16 project) +{ + if (position < MAX_PROJECTS) + _player->SetUInt16Value(PLAYER_FIELD_RESEARCH_PROJECT_1 + (position / 2), position % 2, project); +} + +void Archaeology::RegenerateBranch(uint8 branch) +{ + uint16 project = sArchaeologyMgr->GetNewProject(&_branches[branch], &_completedProjects, _player->GetSkillValue(SKILL_ARCHAEOLOGY)); + _branches[branch].project = project; + + CharacterDatabase.PExecute("REPLACE INTO character_archaeology_projects VALUES (%u, %u, %u)", + _player->GetGUID(), branch, project); +} + +void Archaeology::VerifyProjects() +{ + for (std::map::iterator itr = _branches.begin(); itr != _branches.end(); ++itr) + if (const BranchEntry* branch = sArchaeologyMgr->GetBranchEntry(itr->first)) + { + uint16 project = itr->second.project; + if (project) + { + if (_completedProjects.find(project) != _completedProjects.end() && sArchaeologyMgr->IsRareProject(project)) + RegenerateBranch(itr->first); + } + else if (_player->GetCurrency(branch->currencyId, false)) + RegenerateBranch(itr->first); + else + continue; + + VisualizeBranch(branch->fieldId, _branches[itr->first].project); + } +} + +void Archaeology::CleanProjects() +{ + CharacterDatabase.PExecute("delete from character_archaeology_projects where guid=%u", + _player->GetGUID()); + + for (uint8 i = 0; i < MAX_PROJECTS; ++i) + VisualizeBranch(i, 0); + + _branches.clear(); +} + +void Archaeology::ActivateBranch(uint8 branchId, bool force) +{ + if (_branches[branchId].project && !force) + return; + + RegenerateBranch(branchId); + VisualizeBranch(sArchaeologyMgr->GetBranchEntry(branchId)->fieldId, _branches[branchId].project); +} + +bool Archaeology::ProjectExists(uint16 projectId) +{ + if (const ProjectEntry* project = sArchaeologyMgr->GetProjectEntry(projectId)) + return (_branches[project->branch].project == projectId); + + return false; +} + +bool Archaeology::ProjectCompleteable(uint16 projectId) +{ + if (!archData) + return false; + + if (const ProjectEntry* project = sArchaeologyMgr->GetProjectEntry(projectId)) + if (_branches[project->branch].project == projectId) + { + const BranchEntry* branchEntry = sArchaeologyMgr->GetBranchEntry(project->branch); + + // Check Ids + uint32 currencyId = branchEntry->currencyId; + uint32 keystoneId = branchEntry->keystoneId; + if (currencyId != archData->fragId || (archData->keyId && archData->keyId != keystoneId)) + return false; + + // Project can have keystones ? + if (project->keystone < archData->keyCount) + return false; + + // Check if the given fragment/keystone usage would be enough + if (archData->fragCount + archData->keyCount * 12 != project->fragments) + return false; + + // Check if the player has the keystones + if (_player->GetCurrency(archData->fragId, false) < archData->fragCount) + return false; + + // Check if the player has the fragments + return (!archData->keyId || _player->GetItemCount(archData->keyId) >= archData->keyCount); + } + + return false; +} + +void Archaeology::CompleteProject(uint16 projectId) +{ + const ProjectEntry *project = sArchaeologyMgr->GetProjectEntry(projectId); + ASSERT(project); + ASSERT(_branches[project->branch].project == projectId); + ASSERT(archData); + + _player->ModifyCurrency(archData->fragId, -int32(archData->fragCount), false, true); + _player->DestroyItemCount(archData->keyId, archData->keyCount, true, false); + + if (_completedProjects.find(projectId) == _completedProjects.end()) + { + _completedProjects[projectId].first = time(NULL); + _completedProjects[projectId].second = 1; + + CharacterDatabase.PExecute("REPLACE INTO character_archaeology_completed VALUES (%u, %u, %u, 1);", + _player->GetGUID(), projectId, time(NULL)); + } + else + { + _completedProjects[projectId].second++; + + CharacterDatabase.PExecute("UPDATE character_archaeology_completed SET count=%u WHERE guid=%u and project=%u;", + _completedProjects[projectId].second, _player->GetGUID(), projectId); + } + + _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ARCHAEOLOGY_PROJECTS, projectId, 1); + + delete archData; + ActivateBranch(project->branch, true); +} + +void Archaeology::SendResearchHistory() +{ + uint32 count = _completedProjects.size(); + WorldPacket packet(SMSG_RESEARCH_SETUP_HISTORY, 4 + 4 + (4 + 4 + 4) * count); + packet.WriteBits(count, 22); + + packet.FlushBits(); + + for (std::map >::iterator itr = _completedProjects.begin(); itr != _completedProjects.end(); ++itr) + { + packet << int32(itr->first); + packet << int32(itr->second.second); + packet << uint32(itr->second.first); + } + + _player->GetSession()->SendPacket(&packet); +} diff --git a/src/server/game/Skills/Archaeology/ArchaeologySites.cpp b/src/server/game/Skills/Archaeology/ArchaeologySites.cpp new file mode 100644 index 0000000000..1c12e42cae --- /dev/null +++ b/src/server/game/Skills/Archaeology/ArchaeologySites.cpp @@ -0,0 +1,249 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#include "Archaeology.h" +#include "DBCStore.h" +#include "Player.h" +#include "Language.h" +#include + + +// use squared distances so we don't have to use the squareroot +enum DigSiteDist +{ + DIST_FAR = 10000, // 100 yards + DIST_MED = 900, // 30 yards + DIST_CLOSE = 100, // 10 yards +}; + +enum SurveyBot +{ + FAR_SURVEYBOT = 206590, + MED_SURVEYBOT = 206589, + CLOSE_SURVEYBOT = 204272, +}; + +void Archaeology::LoadSitesFromDB() +{ + QueryResult result = CharacterDatabase.PQuery("SELECT site, type, finds FROM character_archaeology_sites WHERE guid=%u", _player->GetGUID()); + + if (!result) + return; + + uint32 count = 0; + do { + Field *fields = result->Fetch(); + uint32 site = fields[0].GetUInt8(); + uint16 type = fields[1].GetUInt16(); + uint8 finds = fields[2].GetUInt8(); + SetSite(site, type, finds); + count++; + } while (result->NextRow()); +} + +void Archaeology::VerifySites() +{ + uint16 skillValue = _player->GetSkillValue(SKILL_ARCHAEOLOGY); + + if (skillValue >= SKILL_OUTLAND) + _continentState[CONT_OUTLAND] = STATE_USE; + else + _continentState[CONT_OUTLAND] = STATE_NULL; + + if (skillValue >= SKILL_NORTHREND) + _continentState[CONT_NORTHREND] = STATE_USE; + else + _continentState[CONT_NORTHREND] = STATE_NULL; + + if (skillValue >= SKILL_CATA) + { + _continentState[CONT_EASTERN] = STATE_EXT; + _continentState[CONT_KALIMDOR] = STATE_EXT; + } + else + { + _continentState[CONT_EASTERN] = STATE_USE; + _continentState[CONT_KALIMDOR] = STATE_USE; + } + + RegenerateAllSites(); +} + +void Archaeology::UseSite() +{ + float dist; + uint32 position = GetNearestSite(dist); + + if (0 == position) + return; + + position--; + + if (dist > DIST_CLOSE) + { + uint32 surveyGoID; + float angle = std::atan2(_site[position].y - _player->GetPositionY(), _site[position].x - _player->GetPositionX()); + float pi = float(M_PI); + + if (dist < DIST_MED) + { + surveyGoID = CLOSE_SURVEYBOT; // within the med radius -> green light + angle += frand(-pi / 12, pi / 12); + } + else if (dist < DIST_FAR) + { + surveyGoID = MED_SURVEYBOT; // within the far radius -> yellow light + angle += frand(-pi / 6, pi / 6); + } + else + { + surveyGoID = FAR_SURVEYBOT; // outside far radius -> red light + angle += frand(-pi / 3, pi / 3); + } + + float o = _player->GetOrientation(); + float x = _player->GetPositionX() + cos(o) * 2.0f; + float y = _player->GetPositionY() + sin(o) * 2.0f; + float z = _player->GetPositionZ(); + float ground = _player->GetMap()->GetWaterOrGroundLevel(_player->GetPhases(), x, y, z, &ground, _player->IsInWater()); + + G3D::Quat rot = G3D::Matrix3::fromEulerAnglesZYX(angle, 0.f, 0.f); + if (abs(z - ground) < 1.5f) + { + _player->SummonGameObject(surveyGoID, Position(x, y, ground, angle), rot, 4); + return; + } + else + { + _player->GetSession()->SendNotification(LANG_YOU_CANT_USE_THAT_HERE); + return; + } + } + + // Spawn the correct Find Object Here + uint32 goId = sArchaeologyMgr->GetSiteType(_site[position].entry); + float o = _player->GetOrientation(); + float x = _player->GetPositionX() + cos(o) * 2.0f; + float y = _player->GetPositionY() + sin(o) * 2.0f; + float z = _player->GetPositionZ(); + float ground = _player->GetMap()->GetWaterOrGroundLevel(_player->GetPhases(), x, y, z, &ground, _player->IsInWater()); + + // Spawn object + _player->SummonGameObject(goId, Position(x, y, ground, o), G3D::Quat(), 60); + (_site[position].state)++; + + if (_site[position].state >= DIGS_PER_SITE) + RegeneratePosition(position, GetContinent()); + else + { + CharacterDatabase.PExecute("UPDATE character_archaeology_sites SET finds = %u WHERE site= %u AND guid= %u", _site[position].state, position, _player->GetGUID()); + sArchaeologyMgr->SetSiteCoords(_site[position]); + } +} + +Continent Archaeology::GetContinent() +{ + switch (_player->GetMapId()) + { + case 0: + return CONT_EASTERN; + case 1: + return CONT_KALIMDOR; + case 530: + return CONT_OUTLAND; + case 571: + return CONT_NORTHREND; + } + return COUNT_CONT; +} + +uint32 Archaeology::GetNearestSite(float &distance) +{ + uint32 cont = GetContinent(); + + if (COUNT_CONT == cont) + return 0; + + uint32 position = cont * CONTINENT_SITES; + + float pX = _player->GetPositionX(); + float pY = _player->GetPositionY(); + + float dist = (pX - _site[position].x) * (pX - _site[position].x) + (pY - _site[position].y) * (pY - _site[position].y); + + for (uint32 i = cont * CONTINENT_SITES + 1; i < (cont + 1) * CONTINENT_SITES; i++) + { + float dist2 = (pX -_site[i].x) * (pX -_site[i].x) + (pY - _site[i].y) * (pY - _site[i].y); + if (dist2 < dist) + { + position = i; + dist = dist2; + } + } + distance = dist; + + return position + 1; +} + +void Archaeology::SetSite(uint32 position, uint16 entry, uint32 state) +{ + _site[position].entry = entry; + _site[position].state = state; + _player->SetUInt16Value(PLAYER_FIELD_RESEARCH_SITE_1 + (position / 2), position % 2, entry); + + if (entry == 0) + { + _site[position].x = 0.f; + _site[position].y = 0.f; + } + else + sArchaeologyMgr->SetSiteCoords(_site[position]); +} + +void Archaeology::RegeneratePosition(uint32 position, Continent continent) +{ + if (_site[position].entry && sResearchSiteStore.LookupEntry(_site[position].entry) && _site[position].state < DIGS_PER_SITE) + return; + + uint16 entry = sArchaeologyMgr->GetNewSite(continent, _site, _continentState[continent] == STATE_EXT, _player->getLevel()); + SetSite(position, entry); + CharacterDatabase.PExecute("REPLACE INTO character_archaeology_sites values (%u, %u, %u, %u);", _player->GetGUID(), position, entry, _site[position].state); +} + +void Archaeology::RegenerateContinent(Continent continent) +{ + if (_continentState[continent] == STATE_NULL) + { + uint32 position = continent * CONTINENT_SITES; + + for (int i = 0; i < CONTINENT_SITES; i++) + SetSite(position + i, 0); + return; + } + + uint32 position = continent * CONTINENT_SITES; + + for (uint32 i = 0; i < CONTINENT_SITES; i++) + RegeneratePosition(position + i, continent); +} + +void Archaeology::RegenerateAllSites() +{ + for (uint32 i = 0; i < COUNT_CONT; i++) + RegenerateContinent(Continent(i)); +} + diff --git a/src/server/game/Skills/Archaeology/SitePolygonGeometry.cpp b/src/server/game/Skills/Archaeology/SitePolygonGeometry.cpp new file mode 100644 index 0000000000..5e04b0a696 --- /dev/null +++ b/src/server/game/Skills/Archaeology/SitePolygonGeometry.cpp @@ -0,0 +1,565 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#include "SitePolygonGraph.h" + +//===========================================================================// +// RESIZE NODES +//===========================================================================// + +bool SitePolygonGraph::resize_nodes() +{ + SitePolygonGraphNode* tmpNodes = _nodes; + index_type tmpSize = _size_nodes++; + _nodes = 0; + + try + { + if(_size_nodes > 0) _nodes = new SitePolygonGraphNode[_size_nodes]; + else _nodes = 0; + + for(index_type i = 0; i < tmpSize; i++) + _nodes[i].set(tmpNodes[i].get()); + + delete[] tmpNodes; + } + catch(...) + { + _nodes = tmpNodes; + _size_nodes = tmpSize; + + return false; + } + + return true; +} + +//===========================================================================// +// RESIZE ARCHES +//===========================================================================// + +bool SitePolygonGraph::resize_arches() +{ + if(_size_nodes <= 0) + return false; + + SitePolygonGraphArch* tmpArches = _arches; + index_type tmpSizeArches = _size_arches++; + _arches = 0; + + try + { + if(_size_arches > 0) _arches = new SitePolygonGraphArch[_size_arches]; + else _arches = 0; + + for(index_type i = 0; i < tmpSizeArches; i++) + _arches[i].set(tmpArches[i].get()); + + delete[] tmpArches; + } + catch(...) + { + _arches = tmpArches; + _size_arches = tmpSizeArches; + + return false; + } + + return true; +} + +//===========================================================================// +// BOUNDARY GEOMETRY GENERATOR +//===========================================================================// + +void SitePolygonGraph::generate_geometry() +{ + SitePolygonGraphNode last = _nodes[_size_nodes - 1]; + float x = last.getX(); + float y = last.getY(); + + float x_plus = x + 1.0; + float x_minus = x - 1.0; + float y_plus = y + 1.0; + float y_minus = y - 1.0; + + float ax_point = float(); + float ay_point = float(); + float bx_point = float(); + float by_point = float(); + + if(_geometry.isNull()) + { + _geometry.setLeft(x_minus, y_plus, x_minus, y_minus); + _geometry.setBottom(x_minus, y_minus, x_plus, y_minus); + _geometry.setRight(x_plus, y_minus, x_plus, y_plus); + _geometry.setTop(x_plus, y_plus, x_minus, y_plus); + + _geometry.setNull(false); + return; + } + + // LEFT ARCH + ax_point = _geometry.getLeft().getA().getX(); + ax_point = (ax_point >= x - 1.0) ? x_minus : ax_point; + ay_point = _geometry.getLeft().getA().getY(); + ay_point = (ay_point <= y + 1.0) ? y_plus : ay_point; + + bx_point = _geometry.getLeft().getB().getX(); + bx_point = (bx_point >= x - 1.0) ? x_minus : bx_point; + by_point = _geometry.getLeft().getB().getY(); + by_point = (by_point >= y - 1.0) ? y_minus : by_point; + + _geometry.setLeft(ax_point, ay_point, bx_point, by_point); + + // BOTTOM ARCH + ax_point = _geometry.getBottom().getA().getX(); + ax_point = (ax_point >= x - 1.0) ? x_minus : ax_point; + ay_point = _geometry.getBottom().getA().getY(); + ay_point = (ay_point >= y - 1.0) ? y_minus : ay_point; + + bx_point = _geometry.getBottom().getB().getX(); + bx_point = (bx_point <= x + 1.0) ? x_plus : bx_point; + by_point = _geometry.getBottom().getB().getY(); + by_point = (by_point >= y - 1.0) ? y_minus : by_point; + + _geometry.setBottom(ax_point, ay_point, bx_point, by_point); + + // RIGHT ARCH + ax_point = _geometry.getRight().getA().getX(); + ax_point = (ax_point <= x + 1.0) ? x_plus : ax_point; + ay_point = _geometry.getRight().getA().getY(); + ay_point = (ay_point >= y - 1.0) ? y_minus : ay_point; + + bx_point = _geometry.getRight().getB().getX(); + bx_point = (bx_point <= x + 1.0) ? x_plus : bx_point; + by_point = _geometry.getRight().getB().getY(); + by_point = (by_point <= y + 1.0) ? y_plus : by_point; + + _geometry.setRight(ax_point, ay_point, bx_point, by_point); + + // TOP ARCH + ax_point = _geometry.getTop().getA().getX(); + ax_point = (ax_point <= x + 1.0) ? x_plus : ax_point; + ay_point = _geometry.getTop().getA().getY(); + ay_point = (ay_point <= y + 1.0) ? y_plus : ay_point; + + bx_point = _geometry.getTop().getB().getX(); + bx_point = (bx_point >= x - 1.0) ? x_minus : bx_point; + by_point = _geometry.getTop().getB().getY(); + by_point = (by_point <= y + 1.0) ? y_plus : by_point; + + _geometry.setTop(ax_point, ay_point, bx_point, by_point); +} + +//===========================================================================// +// GEOMETRY INSCRIBED GENERATOR +//===========================================================================// + +void SitePolygonGraph::generate_geometry_inscribed() +{ + if(_size_nodes <= 1 && _size_arches <= 0) + return; + + index_type max_value = _size_arches; + + for(index_type i = 0; i < _size_nodes; i++) + { + SitePolygonGraphNode node = _nodes[i]; + + for(index_type n = 0; n < max_value; n++) + { + // Temporary arch + SitePolygonGraphArch arch = _arches[n]; + + // Create temporary arch for check + SitePolygonGraphArch tmp; + tmp.setA(node); + tmp.setB(arch.getMidPoint()); + + // Skip loops on nodes + if(node == arch.getA() || node == arch.getB()) + continue; + + // Check if arches contains tmp + if(arch_exists(tmp)) + continue; + + add_arch(tmp.getA(), tmp.getB()); + } + } +} + +//===========================================================================// +// GEOMETRY INSCRIBED GENERATOR +//===========================================================================// + +bool SitePolygonGraph::generate_geometry_fragmentation(SitePolygonGraphArch &base_arch) +{ + if(_size_arches <= 0) + return false; + + // Number of intersection + index_type N_INTSECD = 0; + index_type N_REACHED = 0; + + // Test node + SitePolygonGraphNode node_test; + SitePolygonGraphNode node_test_boundary; + + // FRAGMENTATION WITH GEOMETRY BOUNDARY + //------------------------------------------------------------------------- + SitePolygonGraphArch geometry_arches[4]; + geometry_arches[0] = _geometry.getLeft(); + geometry_arches[1] = _geometry.getBottom(); + geometry_arches[2] = _geometry.getRight(); + geometry_arches[3] = _geometry.getTop(); + + for(index_type n = 0; n < 4; n++) + { + // Arch from geometry + SitePolygonGraphArch boundary_arch = geometry_arches[n]; + float intersect_X = float(); + float intersect_Y = float(); + bool check_intersect_strict = false; + bool check_intersect = get_xy_intersect(base_arch, boundary_arch, intersect_X, intersect_Y, check_intersect_strict); + bool skip_check = false; + + // Save for current step + SitePolygonGraphNode tmp_check; + tmp_check.setX(intersect_X); + tmp_check.setY(intersect_Y); + + if(n > 0) + { + // Check for duplicate start/end point + skip_check = (node_test_boundary == tmp_check && check_intersect_strict); + } + + // Save for next step + node_test_boundary.setX(intersect_X); + node_test_boundary.setY(intersect_Y); + + if(check_intersect && !skip_check) + N_INTSECD += 1; + } + + // FRAGMENTATION WITH GEOMETRY INSCRIBED + //------------------------------------------------------------------------- + bool is_ab_start = false; + bool is_md_start = false; + //------------------------------------------------------------------------- + SitePolygonGraphNode ab_point; + SitePolygonGraphNode md_point; + + for(index_type n = 0; n < SITE_POLYGON_GRAPH_MAX_VERTEX; n++) + { + // Arch from perimeter + SitePolygonGraphArch boundary_arch = _arches[n]; + float intersect_X = float(); + float intersect_Y = float(); + bool check_intersect_strict = false; + bool check_intersect_line = get_xy_intersect(base_arch, boundary_arch, intersect_X, intersect_Y, check_intersect_strict); + + // Set new values for test + node_test.setX(intersect_X); + node_test.setY(intersect_Y); + + // Pre-Check for possibile skip + bool check_is_ab_point = ((node_test == boundary_arch.getA() || node_test == boundary_arch.getB()) && check_intersect_strict); + bool check_is_md_point = ((node_test == boundary_arch.getMidPoint()) && check_intersect_strict); + + // Check for possibile skip + if(check_is_ab_point && !is_ab_start) + { + is_ab_start = true; + N_REACHED += 1; + + ab_point.setX(intersect_X); + ab_point.setY(intersect_Y); + } + else if(check_is_md_point && !is_md_start) + { + is_md_start = true; + N_REACHED += 1; + + md_point.setX(intersect_X); + md_point.setY(intersect_Y); + } + // SAVE OTHERWISE! + else if(check_intersect_line) + { + // Skip duplicate points on A, B, MD poi! + if((is_ab_start && (ab_point == node_test)) || + (is_md_start && (md_point == node_test))) + continue; + + if(check_intersect_strict) + { + N_REACHED += 1; + } + else + { + N_INTSECD += 1; + } + } + } + + // FIX FRAGMENTATION WITH GEOMETRY + //------------------------------------------------------------------------- + if(N_REACHED == 2 && is_ab_start && is_md_start) + { + if(N_INTSECD == 2) + return true; + } + + // Force FALSE + return false; +} + +//===========================================================================// +// MERGE PERIMETER +//===========================================================================// + +bool SitePolygonGraph::merge_perimeter() +{ + SitePolygonGraphNode prev = _nodes[_size_nodes - 2]; + SitePolygonGraphNode last = _nodes[_size_nodes - 1]; + bool check = true; + + if(_size_nodes > 2) + { + check = (check && add_arch(prev, last, true)); + _arches[0].setA(last); + } + else + { + check = (check && add_arch(last, prev, true)); + check = (check && add_arch(prev, last, true)); + } + + return check; +} + +//===========================================================================// +// CHECK SAVED ARCHES +//===========================================================================// + +bool SitePolygonGraph::arch_exists(const SitePolygonGraphArch &arch) +{ + if(_size_arches <= 0) + return false; + + for(index_type i = 0; i < _size_arches; i++) + if(_arches[i] == arch) + return true; + + return false; +} + +//===========================================================================// +// CALCULATE INTERSECTION FROM TWO ARCHES +//===========================================================================// + +bool SitePolygonGraph::get_xy_intersect(const SitePolygonGraphArch &arch1, const SitePolygonGraphArch &arch2, float &TX, float &TY, bool &DCHECK) +{ + TX = float(); + TY = float(); + DCHECK = false; + + //--------------------------------------------------------------------- + // Arch flag + bool is_m1_null = false; + bool is_m1_inf = false; + bool is_m2_null = false; + bool is_m2_inf = false; + + //--------------------------------------------------------------------- + // Temporary save base_arch values + float P1_X = arch1.getA().getX(); + float P1_Y = arch1.getA().getY(); + float P2_X = arch1.getB().getX(); + float P2_Y = arch1.getB().getY(); + + //--------------------------------------------------------------------- + // Temporary save perimeter_arch values + float P3_X = arch2.getA().getX(); + float P3_Y = arch2.getA().getY(); + float P4_X = arch2.getB().getX(); + float P4_Y = arch2.getB().getY(); + + //--------------------------------------------------------------------- + // Swap values + float PX_1 = P1_X > P2_X ? P2_X : P1_X; + float PY_1 = P1_X > P2_X ? P2_Y : P1_Y; + float PX_2 = P1_X > P2_X ? P1_X : P2_X; + float PY_2 = P1_X > P2_X ? P1_Y : P2_Y; + + float PX_3 = P3_X > P4_X ? P4_X : P3_X; + float PY_3 = P3_X > P4_X ? P4_Y : P3_Y; + float PX_4 = P3_X > P4_X ? P3_X : P4_X; + float PY_4 = P3_X > P4_X ? P3_Y : P4_Y; + + //--------------------------------------------------------------------- + // Calculate line ONE + float DX_1 = PX_2 - PX_1; + float DY_1 = PY_2 - PY_1; + float M_1 = float(); + + if(DX_1 == 0) + { + is_m1_inf = true; + M_1 = 0; + } + else if(DY_1 == 0) + { + is_m1_null = true; + M_1 = 0; + } + else M_1 = DY_1 / DX_1; + + //--------------------------------------------------------------------- + // Calculate intercept for line ONE + float INTCP_1 = PY_1 - M_1 * PX_1; + + //--------------------------------------------------------------------- + // Calculate line TWO + float DX_2 = PX_4 - PX_3; + float DY_2 = PY_4 - PY_3; + float M_2 = float(); + + if(DX_2 == 0) + { + is_m2_inf = true; + M_2 = 0; + } + else if(DY_2 == 0) + { + is_m2_null = true; + M_2 = 0; + } + else M_2 = DY_2 / DX_2; + + //--------------------------------------------------------------------- + // Calculate intercept for line TWO + float INTCP_2 = PY_3 - M_2 * PX_3; + + //--------------------------------------------------------------------- + // Check deltas + if(((M_1 - M_2) != 0) || is_m1_inf || is_m2_inf) + { + // Calculate T parameter from segment equation + float TK_1X = float(); + float TK_1Y = float(); + float TK_2X = float(); + float TK_2Y = float(); + + // Save temporary XY + float IX = float(); + float IY = float(); + + if(M_1 != 0 && M_2 != 0) + { + IX = (INTCP_2 - INTCP_1) / (M_1 - M_2); + IY = (M_1 * IX + INTCP_1); + TK_1X = (IX - PX_1) / (PX_2 - PX_1); + TK_1Y = (IY - PY_1) / (PY_2 - PY_1); + TK_2X = (IX - PX_3) / (PX_4 - PX_3); + TK_2Y = (IY - PY_3) / (PY_4 - PY_3); + } + else if(is_m1_inf && M_2 != 0) + { + IX = PX_1; + IY = M_2 * IX + INTCP_2; + TK_1X = 1; + TK_1Y = (IY - PY_1) / (PY_2 - PY_1); + TK_2X = (IX - PX_3) / (PX_4 - PX_3); + TK_2Y = (IY - PY_3) / (PY_4 - PY_3); + } + else if(is_m1_null && M_2 != 0) + { + IY = PY_1; + IX = (IY - INTCP_2) / M_2; + TK_1Y = 1; + TK_1X = (IX - PX_1) / (PX_2 - PX_1); + TK_2X = (IX - PX_3) / (PX_4 - PX_3); + TK_2Y = (IY - PY_3) / (PY_4 - PY_3); + } + else if(is_m2_inf && M_1 != 0) + { + IX = PX_3; + IY = M_1 * IX + INTCP_1; + TK_1X = (IX - PX_1) / (PX_2 - PX_1); + TK_1Y = (IY - PY_1) / (PY_2 - PY_1); + TK_2Y = (IY - PY_3) / (PY_4 - PY_3); + TK_2X = 1; + } + else if(is_m2_null && M_1 != 0) + { + IY = PY_3; + IX = (IY - INTCP_1) / M_1; + TK_1X = (IX - PX_1) / (PX_2 - PX_1); + TK_1Y = (IY - PY_1) / (PY_2 - PY_1); + TK_2X = (IX - PX_3) / (PX_4 - PX_3); + TK_2Y = 1; + } + else if((is_m1_null && is_m2_inf) || (is_m2_null && is_m1_inf)) + { + IX = PX_1; + IY = PY_3; + + if(is_m1_null && is_m2_inf) + { + TK_1Y = 1; + TK_1X = (IX - PX_1) / (PX_2 - PX_1); + TK_2Y = (IY - PY_3) / (PY_4 - PY_3); + TK_2X = 1; + } + else if(is_m2_null && is_m1_inf) + { + TK_1X = 1; + TK_1Y = (IY - PY_1) / (PY_2 - PY_1); + TK_2X = (IX - PX_3) / (PX_4 - PX_3); + TK_2Y = 1; + } + } + + // Check if T is valid into (0; 1) + bool CKTK_1X = (TK_1X >= 0.0 && TK_1X <= 1.0); + bool CKTK_1Y = (TK_1Y >= 0.0 && TK_1Y <= 1.0); + bool CKTK_2X = (TK_2X >= 0.0 && TK_2X <= 1.0); + bool CKTK_2Y = (TK_2Y >= 0.0 && TK_2Y <= 1.0); + + // Double check + bool T_CHECK = (CKTK_2X && CKTK_2Y); + DCHECK = (CKTK_1X && CKTK_1Y && CKTK_2X && CKTK_2Y); + + // Return value + if(T_CHECK) + { + // Set XY + TX = IX; + TY = IY; + + // Return TRUE + return true; + } + } + + // Force FALSE + return false; +} diff --git a/src/server/game/Skills/Archaeology/SitePolygonGraph.cpp b/src/server/game/Skills/Archaeology/SitePolygonGraph.cpp new file mode 100644 index 0000000000..4879fb8909 --- /dev/null +++ b/src/server/game/Skills/Archaeology/SitePolygonGraph.cpp @@ -0,0 +1,137 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#include "SitePolygonGraph.h" + +//===========================================================================// +// CLASS BASE METHODS +//===========================================================================// + +SitePolygonGraph::SitePolygonGraph() +{ + // Commons + _arches = 0; + _nodes = 0; + _size_nodes = 0; + _size_arches = 0; + _geometry = SitePolygonGraphGeometry(); + + // use current time as seed for random generator + std::srand(std::time(0)); +} + +SitePolygonGraph::~SitePolygonGraph() +{ + // Mem. Leaks free + if(!_nodes) return; + delete[] _nodes; + + // Mem. Leaks free + if(!_arches) return; + delete[] _arches; +} + +//===========================================================================// +// ADD NEW NODE TO POOL +//===========================================================================// + +bool SitePolygonGraph::add_node(const SitePolygonGraphNode &node) +{ + return add_node(node.getX(), node.getY()); +} + +bool SitePolygonGraph::add_node(const float &x, const float &y) +{ + if(!resize_nodes()) + return false; + + _nodes[_size_nodes - 1].setX(x); + _nodes[_size_nodes - 1].setY(y); + + // Generate boundary rectangle + generate_geometry(); + + if(_size_nodes > 1) + return this->merge_perimeter(); + + return true; +} + +//===========================================================================// +// ADD NEW ARCH TO POOL +//===========================================================================// + +bool SitePolygonGraph::add_arch(const SitePolygonGraphNode &nodeA, const SitePolygonGraphNode &nodeB, bool skip_check) +{ + SitePolygonGraphArch tmp; + tmp.setA(nodeA); + tmp.setB(nodeB); + + // Check selector + bool check = skip_check ? true : generate_geometry_fragmentation(tmp); + + // CHECK FOR FRAGMENTATION + if(check) + { + if(!resize_arches()) + return false; + + // SAVE ARCH AND RETURN + _arches[_size_arches - 1].set(tmp); + } + else return false; + + // Force TRUE + return true; +} + +//===========================================================================// +// FINALIZE CREATION OF POLYGON (GEOMETRY INSCRIBED) +//===========================================================================// + +void SitePolygonGraph::finalize_polygon() +{ + // Arches filtering + generate_geometry_inscribed(); +} + +//===========================================================================// +// CALCULATE RANDOM POINT ON RANDOM ARCH FOR SPAWN POI +//===========================================================================// + +SitePolygonGraphNode SitePolygonGraph::randomize_poi() +{ + SitePolygonGraphNode poi_node; + index_type i_poi[SITE_POLYGON_GRAPH_MAX_VERTEX]; + + if(_size_arches <= 0 || _size_arches <= 0) + return poi_node; + + for(index_type i = 0; i < SITE_POLYGON_GRAPH_MAX_VERTEX; i++) + i_poi[i] = std::rand() % _size_arches; + + index_type i_idx = std::rand() % SITE_POLYGON_GRAPH_MAX_VERTEX; + float i_segment = ((float) std::rand() / RAND_MAX); + SitePolygonGraphArch arch = _arches[i_poi[i_idx]]; + float x_segment = arch.getA().getX() + i_segment * (arch.getB().getX() - arch.getA().getX()); + float y_segment = arch.getA().getY() + i_segment * (arch.getB().getY() - arch.getA().getY()); + + poi_node.setX(x_segment); + poi_node.setY(y_segment); + + return poi_node; +} diff --git a/src/server/game/Skills/Archaeology/SitePolygonGraph.h b/src/server/game/Skills/Archaeology/SitePolygonGraph.h new file mode 100644 index 0000000000..eea397d4f1 --- /dev/null +++ b/src/server/game/Skills/Archaeology/SitePolygonGraph.h @@ -0,0 +1,247 @@ +/* +* Copyright (C) 2008-2017 TrinityCore +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see . +*/ + +#ifndef SITE_POLYGON_GRAPH_GENERATOR_H +#define SITE_POLYGON_GRAPH_GENERATOR_H + +#include /* srand, rand */ +#include /* time */ +#include /* math, roundf */ + +//===========================================================================// +// GLOBAL DEFINE +//===========================================================================// + +#define SITE_POLYGON_GRAPH_MAX_VERTEX 12 + +//===========================================================================// +// POLYGON SUB-STRUCT FOR NODES +//===========================================================================// + +struct SitePolygonGraphNode +{ +public: + SitePolygonGraphNode() + { + _pointX = float(); + _pointY = float(); + } + + bool operator==(const SitePolygonGraphNode &other) const + { + float tX = other.getX(); + float tY = other.getY(); + + float cX_m = _pointX - 0.1; + float cY_m = _pointY - 0.1; + float cX_p = _pointX + 0.1; + float cY_p = _pointY + 0.1; + + bool x = (cX_m <= tX && tX <= cX_p); + bool y = (cY_m <= tY && tY <= cY_p); + + if(x && y) return true; + else return false; + } + + void set(const SitePolygonGraphNode &node) { _pointX = node.getX(); _pointY = node.getY(); _round_p(); } + void set(const float &x, const float &y) { _pointX = x; _pointY = y; _round_p(); } + + void setX(const float &x) { _pointX = x; _round_p(); } + void setY(const float &y) { _pointY = y; _round_p(); } + + const float getX() const { return _pointX; } + const float getY() const { return _pointY; } + + const SitePolygonGraphNode get() const { return (*this); } + +private: + void _round_p() + { + _pointX = roundf(_pointX * 10) / 10; + _pointY = roundf(_pointY * 10) / 10; + } + + float _pointX; + float _pointY; +}; + +//===========================================================================// +// POLYGON SUB-STRUCT FOR ARCHES +//===========================================================================// + +struct SitePolygonGraphArch +{ +public: + SitePolygonGraphArch() + { + _pointA = SitePolygonGraphNode(); + _pointB = SitePolygonGraphNode(); + _midpoint = SitePolygonGraphNode(); + } + + void set(const SitePolygonGraphArch &arch) + { + _pointA.set(arch.getA()); + _pointB.set(arch.getB()); + + _calculateMidPoint(); + } + + bool operator==(const SitePolygonGraphArch &other) const + { + bool an = (other.getA() == _pointA); + bool bn = (other.getB() == _pointB); + bool ai = (other.getA() == _pointB); + bool bi = (other.getB() == _pointA); + + if((an && bn) || (ai && bi)) return true; + else return false; + } + + void setA(const SitePolygonGraphNode &node) { _pointA.set(node.get()); _calculateMidPoint(); } + void setB(const SitePolygonGraphNode &node) { _pointB.set(node.get()); _calculateMidPoint(); } + + void setA(const float &x, const float &y) { _pointA.set(x, y); _calculateMidPoint(); } + void setB(const float &x, const float &y) { _pointB.set(x, y); _calculateMidPoint(); } + + const SitePolygonGraphArch get() const { return (*this); } + const SitePolygonGraphNode getA() const { return _pointA; } + const SitePolygonGraphNode getB() const { return _pointB; } + const SitePolygonGraphNode getMidPoint() const { return _midpoint; } + +private: + void _calculateMidPoint() + { + float midX = (_pointB.getX() + _pointA.getX()) / 2; + float midY = (_pointB.getY() + _pointA.getY()) / 2; + + _midpoint.set(midX, midY); + } + + SitePolygonGraphNode _pointA; + SitePolygonGraphNode _pointB; + SitePolygonGraphNode _midpoint; +}; + +//===========================================================================// +// POLYGON SUB-STRUCT FOR GEOMETRY +//===========================================================================// + +struct SitePolygonGraphGeometry +{ +public: + SitePolygonGraphGeometry() + { + _archL = SitePolygonGraphArch(); + _archB = SitePolygonGraphArch(); + _archR = SitePolygonGraphArch(); + _archT = SitePolygonGraphArch(); + _isNull = true; + } + + void set(const SitePolygonGraphGeometry &geo) + { + _archL.set(geo.getLeft()); + _archB.set(geo.getBottom()); + _archR.set(geo.getRight()); + _archT.set(geo.getTop()); + } + + void setLeft(const SitePolygonGraphArch &arch) { _archL.set(arch.get()); } + void setBottom(const SitePolygonGraphArch &arch) { _archB.set(arch.get()); } + void setRight(const SitePolygonGraphArch &arch) { _archR.set(arch.get()); } + void setTop(const SitePolygonGraphArch &arch) { _archT.set(arch.get()); } + void setNull(const bool &val) { _isNull = val; } + + void setLeft(const SitePolygonGraphNode &nodeA, + const SitePolygonGraphNode &nodeB) { _archL.setA(nodeA); _archL.setB(nodeB); } + void setBottom(const SitePolygonGraphNode &nodeA, + const SitePolygonGraphNode &nodeB) { _archB.setA(nodeA); _archB.setB(nodeB); } + void setRight(const SitePolygonGraphNode &nodeA, + const SitePolygonGraphNode &nodeB) { _archR.setA(nodeA); _archR.setB(nodeB); } + void setTop(const SitePolygonGraphNode &nodeA, + const SitePolygonGraphNode &nodeB) { _archT.setA(nodeA); _archT.setB(nodeB); } + + void setLeft(const float &ax, const float &ay, + const float &bx, const float &by) { _archL.setA(ax, ay); _archL.setB(bx, by); } + void setBottom(const float &ax, const float &ay, + const float &bx, const float &by) { _archB.setA(ax, ay); _archB.setB(bx, by); } + void setRight(const float &ax, const float &ay, + const float &bx, const float &by) { _archR.setA(ax, ay); _archR.setB(bx, by); } + void setTop(const float &ax, const float &ay, + const float &bx, const float &by) { _archT.setA(ax, ay); _archT.setB(bx, by); } + + const SitePolygonGraphGeometry get() const { return (*this); } + const SitePolygonGraphArch getLeft() const { return _archL; } + const SitePolygonGraphArch getBottom() const { return _archB; } + const SitePolygonGraphArch getRight() const { return _archR; } + const SitePolygonGraphArch getTop() const { return _archT; } + const bool isNull() const { return _isNull; } + +private: + SitePolygonGraphArch _archL; + SitePolygonGraphArch _archR; + SitePolygonGraphArch _archT; + SitePolygonGraphArch _archB; + bool _isNull; +}; + +//===========================================================================// +// POLYGON GRAPH MASTER CLASS +//===========================================================================// + +class SitePolygonGraph +{ +public: + SitePolygonGraph(); + ~SitePolygonGraph(); + + bool add_node(const SitePolygonGraphNode &node); + bool add_node(const float &x, const float &y); + bool add_arch(const SitePolygonGraphNode &nodeA, const SitePolygonGraphNode &nodeB, bool skip_check = false); + + void finalize_polygon(); + SitePolygonGraphNode randomize_poi(); + +private: + bool resize_nodes(); + bool resize_arches(); + + bool merge_perimeter(); + bool arch_exists(const SitePolygonGraphArch &arch); + + void generate_geometry(); + void generate_geometry_inscribed(); + bool generate_geometry_fragmentation(SitePolygonGraphArch &base_arch); + + bool get_xy_intersect(const SitePolygonGraphArch &arch1, const SitePolygonGraphArch &arch2, float &TX, float &TY, bool &dcheck); + + typedef unsigned int index_type; + typedef float value_type; + + SitePolygonGraphNode* _nodes; + SitePolygonGraphArch* _arches; + SitePolygonGraphGeometry _geometry; + + index_type _size_nodes; + index_type _size_arches; +}; + +//---------------------------------------------------------------------------// + +#endif // SITE_POLYGON_GRAPH_GENERATOR_H diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 8e2082fa33..c2bfcf71c8 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5440,12 +5440,6 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint if (ReqValue > skillValue) return SPELL_FAILED_LOW_CASTLEVEL; - // chance for fail at orange skinning attempt - if ((m_selfContainer && (*m_selfContainer) == this) && - skillValue < sWorld->GetConfigMaxSkillValue() && - (ReqValue < 0 ? 0 : ReqValue) > irand(skillValue - 25, skillValue + 37)) - return SPELL_FAILED_TRY_AGAIN; - break; } case SPELL_EFFECT_OPEN_LOCK: @@ -5504,10 +5498,10 @@ SpellCastResult Spell::CheckCast(bool strict, uint32* param1 /*= nullptr*/, uint // second check prevent fail at rechecks if (skillId != SKILL_NONE && (!m_selfContainer || ((*m_selfContainer) != this))) { - bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING; + bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING && skillId != SKILL_ARCHAEOLOGY; // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill) - if ((canFailAtMax || skillValue < sWorld->GetConfigMaxSkillValue()) && reqSkillValue > irand(skillValue - 25, skillValue + 37)) + if ((canFailAtMax && skillValue < sWorld->GetConfigMaxSkillValue()) && reqSkillValue > irand(skillValue - 25, skillValue + 37)) return SPELL_FAILED_TRY_AGAIN; } break; @@ -7312,7 +7306,6 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk if (skillValue < reqSkillValue) return SPELL_FAILED_LOW_CASTLEVEL; } - return SPELL_CAST_OK; } } diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index 49bdb1b7b9..b03b676131 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -282,6 +282,7 @@ class TC_GAME_API Spell void EffectHealMaxHealth(SpellEffIndex effIndex); void EffectInterruptCast(SpellEffIndex effIndex); void EffectSummonObjectWild(SpellEffIndex effIndex); + void EffectSurvey(SpellEffIndex effIndex); void EffectSummonRaidMarker(SpellEffIndex effIndex); void EffectSummonDynObj(SpellEffIndex effIndex); void EffectScriptEffect(SpellEffIndex effIndex); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index f4cea585df..f08601a642 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -167,7 +167,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectDismissPet, //102 SPELL_EFFECT_DISMISS_PET &Spell::EffectReputation, //103 SPELL_EFFECT_REPUTATION &Spell::EffectSummonObject, //104 SPELL_EFFECT_SUMMON_OBJECT_SLOT1 - &Spell::EffectSummonObject, //105 SPELL_EFFECT_SUMMON_OBJECT_SLOT2 + &Spell::EffectSurvey, //105 SPELL_EFFECT_SUMMON_OBJECT_SLOT2 &Spell::EffectSummonRaidMarker, //106 SPELL_EFFECT_SUMMON_OBJECT_SLOT3 &Spell::EffectSummonObject, //107 SPELL_EFFECT_SUMMON_OBJECT_SLOT4 &Spell::EffectDispelMechanic, //108 SPELL_EFFECT_DISPEL_MECHANIC @@ -1422,6 +1422,14 @@ void Spell::DoCreateItem(uint32 /*i*/, uint32 itemtype) if (num_to_add) { + uint32 projectId = GetSpellInfo()->ResearchProjectId; + + if (projectId && !player->ArchProjectCompleteable(projectId)) + { + player->SendEquipError(EQUIP_ERR_SPELL_FAILED_REAGENTS_GENERIC, NULL, NULL); + return; + } + // create the new item and store it Item* pItem = player->StoreNewItem(dest, newitemid, true, Item::GenerateItemRandomPropertyId(newitemid)); @@ -1432,6 +1440,9 @@ void Spell::DoCreateItem(uint32 /*i*/, uint32 itemtype) return; } + if (projectId) + player->CompleteArchProject(projectId); + // set the "Crafted by ..." property of the item if (pItem->GetTemplate()->Class != ITEM_CLASS_CONSUMABLE && pItem->GetTemplate()->Class != ITEM_CLASS_QUEST && newitemid != 6265 && newitemid != 6948) pItem->SetGuidValue(ITEM_FIELD_CREATOR, player->GetGUID()); @@ -1789,7 +1800,12 @@ void Spell::EffectOpenLock(SpellEffIndex effIndex) } if (gameObjTarget) - SendLoot(guid, LOOT_SKINNING); + { + if (skillId == SKILL_ARCHAEOLOGY) + SendLoot(guid, LOOT_ARCHAEOLOGY); + else + SendLoot(guid, LOOT_SKINNING); + } else if (itemTarget) itemTarget->SetFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_UNLOCKED); @@ -3173,6 +3189,19 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex) } } +void Spell::EffectSurvey(SpellEffIndex effIndex) +{ + if (effectHandleMode != SPELL_EFFECT_HANDLE_LAUNCH) + return; + + if (!m_caster || m_spellInfo->Id != uint32(80451) || effIndex != EFFECT_0) + return; + + if (Player *player = m_caster->ToPlayer()) + if (player->HasSkill(SKILL_ARCHAEOLOGY)) + player->SurveyDigSite(); +} + void Spell::EffectScriptEffect(SpellEffIndex effIndex) { if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 02a9f76944..e0bfc1177b 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -908,6 +908,7 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntry const** effe SpellShapeshiftId = spellEntry->SpellShapeshiftId; SpellTargetRestrictionsId = spellEntry->SpellTargetRestrictionsId; SpellTotemsId = spellEntry->SpellTotemsId; + ResearchProjectId = spellEntry->ResearchProjectId; // SpellDifficultyEntry for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index 67524782fd..2ee08a927b 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -417,6 +417,7 @@ class TC_GAME_API SpellInfo uint32 SpellShapeshiftId; uint32 SpellTargetRestrictionsId; uint32 SpellTotemsId; + uint32 ResearchProjectId; // SpellScalingEntry int32 CastTimeMin; int32 CastTimeMax; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 4893710469..330df3cadd 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1826,6 +1826,9 @@ void World::SetInitialWorldSettings() TC_LOG_INFO("server.loading", "Loading Skill Fishing base level requirements..."); sObjectMgr->LoadFishingBaseSkillLevel(); + TC_LOG_INFO("server.loading", "Loading Archaeology store..."); + sArchaeologyMgr->LoadData(); + TC_LOG_INFO("server.loading", "Loading Achievements..."); sAchievementMgr->LoadAchievementReferenceList(); TC_LOG_INFO("server.loading", "Loading Achievement Criteria Lists...");