diff options
60 files changed, 2947 insertions, 2052 deletions
diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index b1c57a2f5fb..4a2d2951da3 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -1429,9 +1429,11 @@ CREATE TABLE `gm_tickets` ( `closedBy` int(10) NOT NULL DEFAULT '0', `assignedTo` int(10) unsigned NOT NULL DEFAULT '0' COMMENT 'GUID of admin to whom ticket is assigned', `comment` text NOT NULL, + `response` text NOT NULL, `completed` tinyint(3) unsigned NOT NULL DEFAULT '0', `escalated` tinyint(3) unsigned NOT NULL DEFAULT '0', `viewed` tinyint(3) unsigned NOT NULL DEFAULT '0', + `haveTicket` tinyint(3) unsigned NOT NULL DEFAULT '0', PRIMARY KEY (`ticketId`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Player System'; /*!40101 SET character_set_client = @saved_cs_client */; diff --git a/sql/updates/characters/2012_10_17_00_character_gm_tickets.sql b/sql/updates/characters/2012_10_17_00_character_gm_tickets.sql new file mode 100644 index 00000000000..affb23f836c --- /dev/null +++ b/sql/updates/characters/2012_10_17_00_character_gm_tickets.sql @@ -0,0 +1,3 @@ +ALTER TABLE `gm_tickets` + ADD COLUMN `response` text NOT NULL AFTER `comment`, + ADD COLUMN `haveTicket` tinyint(3) unsigned NOT NULL DEFAULT '0' AFTER `viewed`; diff --git a/sql/updates/world/2012_10_16_00_world_spell_area.sql b/sql/updates/world/2012_10_16_00_world_spell_area.sql new file mode 100644 index 00000000000..3dbc29d077f --- /dev/null +++ b/sql/updates/world/2012_10_16_00_world_spell_area.sql @@ -0,0 +1,4 @@ +ALTER TABLE `spell_area` ADD COLUMN `quest_start_status` INT(11) NOT NULL DEFAULT 64; -- default is QUEST_STATUS_REWARDED +ALTER TABLE `spell_area` ADD COLUMN `quest_end_status` INT(11) NOT NULL DEFAULT 11; -- default is QUEST_STATUS_COMPLETE | QUEST_STATUS_NONE | QUEST_STATUS_INCOMPLETE +UPDATE spell_area SET `quest_start_status` = (1 << 6) | (1 << 3) | (1 << 1) WHERE `quest_start_active` = 1; +ALTER TABLE spell_area DROP COLUMN `quest_start_active`;
\ No newline at end of file diff --git a/sql/updates/world/2012_10_20_00_world_transports.sql b/sql/updates/world/2012_10_20_00_world_transports.sql new file mode 100644 index 00000000000..8cc2bae4b47 --- /dev/null +++ b/sql/updates/world/2012_10_20_00_world_transports.sql @@ -0,0 +1,6 @@ +-- Update position for Koltira Deathweaver +UPDATE `creature_transport` SET `TransOffsetX`=45.50927,`TransOffsetY`=6.679555,`TransOffsetZ`=30.17881,`TransOffsetO`=5.445427 WHERE `guid`=36; + +-- Set proper name and period timers for icecrown ships +UPDATE `transports` SET `name`= 'Alliance gunship patrolling above Icecrown (\"The Skybreaker\")',`period`=1051388 WHERE `entry`=192242; +UPDATE `transports` SET `name`= 'Horde gunship patrolling above Icecrown (\"Orgrim''s Hammer\")',`period`=1431158 WHERE `entry`=192241; diff --git a/sql/updates/world/2012_10_20_01_world_spell_script_names.sql b/sql/updates/world/2012_10_20_01_world_spell_script_names.sql new file mode 100644 index 00000000000..d0eefb17943 --- /dev/null +++ b/sql/updates/world/2012_10_20_01_world_spell_script_names.sql @@ -0,0 +1,8 @@ +-- Spellscript and conditions for spells of quest Gordunni Cobalt +DELETE FROM `spell_script_names` WHERE `spell_id`=19395; +INSERT INTO `spell_script_names`(`spell_id`,`ScriptName`) VALUES +(19395,'spell_gordunni_trap'); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=11757; +INSERT INTO `conditions`(`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ConditionTypeOrReference`,`ConditionValue1`,`ConditionValue2`,`Comment`) VALUES +(13,1,11757,31,5,144050,'Digging for Cobalt targets Gordunni Trap'); diff --git a/sql/updates/world/2012_10_20_02_world_creature_text.sql b/sql/updates/world/2012_10_20_02_world_creature_text.sql new file mode 100644 index 00000000000..bb153c6b2f6 --- /dev/null +++ b/sql/updates/world/2012_10_20_02_world_creature_text.sql @@ -0,0 +1,27 @@ +-- Gnomeregan/Blastmaster Emi Shortfuse's event +DELETE FROM `script_texts` WHERE `entry` BETWEEN -1090028 AND -1090000; +DELETE FROM `creature_text` WHERE `entry` IN (7361,7998); +INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `comment`) VALUES +-- Emi Shortfuse +(7998,0,0, 'With your help, I can evaluate these tunnels.',12,0,100,0,0,0, 'SAY_BLASTMASTER_0'), +(7998,1,0, 'Let''s see if we can find out where these Troggs are coming from... and put a stop to the invasion!',12,0,100,0,0,0, 'SAY_BLASTMASTER_1'), +(7998,2,0, 'Such devastation... what horrible mess...',12,0,100,0,0,0, 'SAY_BLASTMASTER_2'), +(7998,3,0, 'It''s quiet here...',12,0,100,0,0,0, 'SAY_BLASTMASTER_3'), +(7998,4,0, '...too quiet.',12,0,100,0,0,0, 'SAY_BLASTMASTER_4'), +(7998,5,0, 'Look! Over there at the tunnel wall!',12,0,100,0,0,0, 'SAY_BLASTMASTER_5'), +(7998,6,0, 'Trogg incrusion! Defend me while I blast the hole closed!',12,0,100,0,0,0, 'SAY_BLASTMASTER_6'), +(7998,7,0, 'I don''t think one charge is going to cut it. Keep fending them off!',12,0,100,0,0,0, 'SAY_BLASTMASTER_7'), +(7998,8,0, 'The charges are set. Get back before they blow!',12,0,100,0,0,0, 'SAY_BLASTMASTER_8'), +(7998,9,0, 'Incoming blast in 10 seconds!',14,0,100,0,0,0, 'SAY_BLASTMASTER_9'), +(7998,10,0, 'Incoming blast in 5 seconds. Clear the tunnel!',14,0,100,0,0,0, 'SAY_BLASTMASTER_10'), +(7998,11,0, 'FIRE IN THE HOLE!',14,0,100,0,0,0, 'SAY_BLASTMASTER_11'), +(7998,12,0, 'Well done! Without your help I would have never been able to thwart that wave of troggs.',12,0,100,0,0,0, 'SAY_BLASTMASTER_12'), +(7998,13,0, 'Did you hear something?',12,0,100,0,0,0, 'SAY_BLASTMASTER_13'), +(7998,14,0, 'I heard something over there.',12,0,100,0,0,0, 'SAY_BLASTMASTER_14'), +(7998,15,0, 'More troggs! Ward them off as I prepare the explosives!',12,0,100,0,0,0, 'SAY_BLASTMASTER_15'), +(7998,16,0, 'The final charge is set. Stand back!',12,0,100,0,0,0, 'SAY_BLASTMASTER_16'), +(7998,17,0, '10 seconds to blast! Stand back!!!',14,0,100,0,0,0, 'SAY_BLASTMASTER_17'), +(7998,18,0, '5 seconds until detonation!!',14,0,100,0,0,0, 'SAY_BLASTMASTER_18'), +(7998,19,0, 'Superb! Because of your help, my people stand a chance of re-taking our belowed city. Three cheers to you!',12,0,100,0,0,0, 'SAY_BLASTMASTER_19'), +-- Grubbis +(7361,0,0, 'We come from below! You can never stop us!',14,0,100,0,0,0, 'SAY_GRUBBIS'); diff --git a/sql/updates/world/2012_10_21_00_world_conditions.sql b/sql/updates/world/2012_10_21_00_world_conditions.sql new file mode 100644 index 00000000000..35b44fec196 --- /dev/null +++ b/sql/updates/world/2012_10_21_00_world_conditions.sql @@ -0,0 +1 @@ +ALTER TABLE `conditions` CHANGE COLUMN `SourceEntry` `SourceEntry` MEDIUMINT(8) NOT NULL DEFAULT 0 diff --git a/sql/updates/world/2012_10_21_01_world_misc.sql b/sql/updates/world/2012_10_21_01_world_misc.sql new file mode 100644 index 00000000000..fe74f8a7a80 --- /dev/null +++ b/sql/updates/world/2012_10_21_01_world_misc.sql @@ -0,0 +1,37 @@ +-- Area conditions for Plant Chieftains Totem +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=17 AND `SourceEntry`=56765; +INSERT INTO `conditions`(`SourceTypeOrReferenceId`,`SourceEntry`,`ConditionTypeOrReference`,`ConditionValue1`,`ConditionValue2`,`ErrorTextId`,`Comment`) VALUES +(17,56765,29,30446,15,64,'Plant Chieftains Totem only useable next to Rift'); + +-- Apply Close Rift to The Chieftain's Totem +DELETE FROM `creature_template_addon` WHERE `entry`=30444; +INSERT INTO `creature_template_addon`(`entry`,`auras`) VALUES +(30444,'56763'); + +-- Assign aura script to Close Rift +DELETE FROM `spell_script_names` WHERE `spell_id`=56763; +INSERT INTO `spell_script_names`(`spell_id`,`ScriptName`) VALUES +(56763,'spell_close_rift'); + +-- Target conditions for spell triggered by Close Rift Periodic +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry` IN (56764,61665); +INSERT INTO `conditions`(`SourceTypeOrReferenceId`,`SourceGroup`,`SourceEntry`,`ConditionTypeOrReference`,`ConditionValue1`,`ConditionValue2`,`Comment`) VALUES +(13,1,56764,31,3,30446,'Close Rift Periodic targets Frostfloe Rift'), +(13,1,61665,31,3,30446,'Despawn Rift targets Frostfloe Rift'); + +-- Assign SmartAI to Frostfloe Rift +UPDATE `creature_template` SET `AIName`='SmartAI',`ScriptName`='' WHERE `entry`=30446; + +-- Create SAI for Frostfloe Rift +DELETE FROM `smart_scripts` WHERE `entryorguid`=30446 AND `source_type`=0; +INSERT INTO `smart_scripts`(`entryorguid`,`id`,`link`,`event_type`,`event_param1`,`action_type`,`action_param1`,`action_param2`,`target_type`,`comment`) VALUES +(30446,1,2,8,61665,45,1,1,7,'Frostfloe Rift - On spell hit - Set data of invoker'), +(30446,2,0,61,0,41,0,0,1,'Frostfloe Rift - On spell hit - Despawn'); + +-- Assign SmartAI to Chieftain's Totem +UPDATE `creature_template` SET `AIName`='SmartAI',`ScriptName`='' WHERE `entry`=30444; + +-- Create SAI for Chieftain's Totem +DELETE FROM `smart_scripts` WHERE `entryorguid`=30444 AND `source_type`=0; +INSERT INTO `smart_scripts`(`entryorguid`,`event_type`,`event_param1`,`event_param2`,`action_type`,`action_param1`,`target_type`,`comment`) VALUES +(30444,38,1,1,33,30444,23,'Chieftains Totem - On data set - Give quest credit'); diff --git a/sql/updates/world/2012_10_23_00_world_trinity_string.sql b/sql/updates/world/2012_10_23_00_world_trinity_string.sql new file mode 100644 index 00000000000..60318667fbc --- /dev/null +++ b/sql/updates/world/2012_10_23_00_world_trinity_string.sql @@ -0,0 +1,22 @@ +DELETE FROM `trinity_string` WHERE `entry` IN (9980, 9981, 9982, 9983, 9984, 9985, 9986, 9987, 9988, 9989, 9990, 9991, 9992, 9993, 9994, 9995, 9996, 9997, 9998, 9999); +INSERT INTO `trinity_string` (`entry`, `content_default`, `content_loc1`, `content_loc2`, `content_loc3`, `content_loc4`, `content_loc5`, `content_loc6`, `content_loc7`, `content_loc8`) VALUES + (9980, 'Player name: %s, State: %s, Dungeons: %u (%s), Roles: %s, Comment: %s', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9981, 'LfgGroup?: %u, State: %s, Dungeon: %u', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9982, 'Not in group', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9983, 'Queues cleared', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9984, 'Lfg options: %u', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9985, 'Lfg options changed', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9986, 'None', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9987, 'Role check', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9988, 'Queued', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9989, 'Proposal', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9990, 'Vote kick', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9991, 'In dungeon', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9992, 'Finished dungeon', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9993, 'Raid browser', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9994, 'Tank', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9995, 'Healer', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9996, 'Dps', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9997, 'Leader', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9998, 'None', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL), + (9999, 'Error', NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); diff --git a/sql/updates/world/2012_10_23_01_world_command.sql b/sql/updates/world/2012_10_23_01_world_command.sql new file mode 100644 index 00000000000..b46882c5949 --- /dev/null +++ b/sql/updates/world/2012_10_23_01_world_command.sql @@ -0,0 +1,8 @@ +DELETE FROM `command` WHERE `name` LIKE 'lfg%'; + +INSERT INTO `command` (`name`, `security`, `help`) VALUES +('lfg player', 2, 'Syntax: .lfg player\n Shows information about player (state, roles, comment, dungeons selected).'), +('lfg group', 2, 'Syntax: .lfg group\n Shows information about all players in the group (state, roles, comment, dungeons selected).'), +('lfg queue', 2, 'Syntax: .lfg queue\n Shows info about current lfg queues.'), +('lfg clean', 3, 'Syntax: .flg clean\n Cleans current queue, only for debugging purposes.'), +('lfg options', 3, 'Syntax: .lfg options [new value]\n Shows current lfg options. New value is set if extra param is present.'); diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index 011210dd95b..90d112a1433 100755 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -86,7 +86,7 @@ extern int main(int argc, char **argv) { if (++c >= argc) { - printf("Runtime-Error: -c option requires an input argument"); + printf("Runtime-Error: -c option requires an input argument\n"); usage(argv[0]); return 1; } @@ -98,8 +98,8 @@ extern int main(int argc, char **argv) if (!ConfigMgr::Load(cfg_file)) { - printf("Invalid or missing configuration file : %s", cfg_file); - printf("Verify that the file exists and has \'[authserver]\' written in the top of the file!"); + printf("Invalid or missing configuration file : %s\n", cfg_file); + printf("Verify that the file exists and has \'[authserver]\' written in the top of the file!\n"); return 1; } diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index 7dc4b57d188..7b609220c30 100755 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -737,7 +737,7 @@ void ConditionMgr::LoadConditions(bool isReload) Condition* cond = new Condition(); int32 iSourceTypeOrReferenceId = fields[0].GetInt32(); cond->SourceGroup = fields[1].GetUInt32(); - cond->SourceEntry = fields[2].GetUInt32(); + cond->SourceEntry = fields[2].GetInt32(); cond->SourceId = fields[3].GetInt32(); cond->ElseGroup = fields[4].GetUInt32(); int32 iConditionTypeOrReference = fields[5].GetInt32(); diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 538a53344e8..20eab098add 100755 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1196,7 +1196,7 @@ struct ItemSetEntry struct LFGDungeonEntry { uint32 ID; // 0 - //char* name[16]; // 1-17 Name lang + char* name[16]; // 1-17 Name lang uint32 minlevel; // 18 uint32 maxlevel; // 19 uint32 reclevel; // 20 diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 39f9009419e..0fe82ea3311 100755 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -19,111 +19,111 @@ #ifndef TRINITY_DBCSFRM_H #define TRINITY_DBCSFRM_H -const char Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; +char const Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii"; const std::string CustomAchievementfmt="pppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaapapaaaaaaaaaaaaaaaaaapp"; const std::string CustomAchievementIndex = "ID"; -const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiiiix"; -const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxiiiiixxx"; -const char AreaGroupEntryfmt[]="niiiiiii"; -const char AreaPOIEntryfmt[]="niiiiiiiiiiifffixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix"; -const char AreaTriggerEntryfmt[]="niffffffff"; -const char AuctionHouseEntryfmt[]="niiixxxxxxxxxxxxxxxxx"; -const char BankBagSlotPricesEntryfmt[]="ni"; -const char BarberShopStyleEntryfmt[]="nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii"; -const char BattlemasterListEntryfmt[]="niiiiiiiiixssssssssssssssssxiixx"; -const char CharStartOutfitEntryfmt[]="diiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; -const char CharTitlesEntryfmt[]="nxssssssssssssssssxxxxxxxxxxxxxxxxxxi"; -const char ChatChannelsEntryfmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxx"; +char const AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiiiix"; +char const AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxiiiiixxx"; +char const AreaGroupEntryfmt[]="niiiiiii"; +char const AreaPOIEntryfmt[]="niiiiiiiiiiifffixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxix"; +char const AreaTriggerEntryfmt[]="niffffffff"; +char const AuctionHouseEntryfmt[]="niiixxxxxxxxxxxxxxxxx"; +char const BankBagSlotPricesEntryfmt[]="ni"; +char const BarberShopStyleEntryfmt[]="nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii"; +char const BattlemasterListEntryfmt[]="niiiiiiiiixssssssssssssssssxiixx"; +char const CharStartOutfitEntryfmt[]="diiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const CharTitlesEntryfmt[]="nxssssssssssssssssxxxxxxxxxxxxxxxxxxi"; +char const ChatChannelsEntryfmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxx"; // ChatChannelsEntryfmt, index not used (more compact store) -const char ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii"; -const char ChrRacesEntryfmt[]="nxixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi"; -const char CinematicSequencesEntryfmt[]="nxxxxxxxxx"; -const char CreatureDisplayInfofmt[]="nixxfxxxxxxxxxxx"; -const char CreatureFamilyfmt[]="nfifiiiiixssssssssssssssssxx"; -const char CreatureModelDatafmt[]="nxxxfxxxxxxxxxxffxxxxxxxxxxx"; -const char CreatureSpellDatafmt[]="niiiixxxx"; -const char CreatureTypefmt[]="nxxxxxxxxxxxxxxxxxx"; -const char CurrencyTypesfmt[]="xnxi"; -const char DestructibleModelDatafmt[]="nxxixxxixxxixxxixxx"; -const char DungeonEncounterfmt[]="niixissssssssssssssssxx"; -const char DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; -const char DurabilityQualityfmt[]="nf"; -const char EmotesEntryfmt[]="nxxiiix"; -const char EmotesTextEntryfmt[]="nxixxxxxxxxxxxxxxxx"; -const char FactionEntryfmt[]="niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx"; -const char FactionTemplateEntryfmt[]="niiiiiiiiiiiii"; -const char GameObjectDisplayInfofmt[]="nsxxxxxxxxxxffffffx"; -const char GemPropertiesEntryfmt[]="nixxi"; -const char GlyphPropertiesfmt[]="niii"; -const char GlyphSlotfmt[]="nii"; -const char GtBarberShopCostBasefmt[]="f"; -const char GtCombatRatingsfmt[]="f"; -const char GtChanceToMeleeCritBasefmt[]="f"; -const char GtChanceToMeleeCritfmt[]="f"; -const char GtChanceToSpellCritBasefmt[]="f"; -const char GtChanceToSpellCritfmt[]="f"; -const char GtOCTClassCombatRatingScalarfmt[]="df"; -const char GtOCTRegenHPfmt[]="f"; -//const char GtOCTRegenMPfmt[]="f"; -const char GtRegenHPPerSptfmt[]="f"; -const char GtRegenMPPerSptfmt[]="f"; -const char Holidaysfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxsiix"; -const char Itemfmt[]="niiiiiii"; -const char ItemBagFamilyfmt[]="nxxxxxxxxxxxxxxxxx"; -//const char ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx"; -//const char ItemCondExtCostsEntryfmt[]="xiii"; -const char ItemExtendedCostEntryfmt[]="niiiiiiiiiiiiiix"; -const char ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; -const char ItemRandomPropertiesfmt[]="nxiiixxssssssssssssssssx"; -const char ItemRandomSuffixfmt[]="nssssssssssssssssxxiiixxiiixx"; -const char ItemSetEntryfmt[]="dssssssssssssssssxiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii"; -const char LFGDungeonEntryfmt[]="nxxxxxxxxxxxxxxxxxiiiiiiiiixxixixxxxxxxxxxxxxxxxx"; -const char LiquidTypefmt[]="nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; -const char LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; -const char MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx"; -const char MapEntryfmt[]="nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxiix"; -const char MapDifficultyEntryfmt[]="diisxxxxxxxxxxxxxxxxiix"; -const char MovieEntryfmt[]="nxx"; -const char OverrideSpellDatafmt[]="niiiiiiiiiix"; -const char QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx"; -const char QuestXPfmt[]="niiiiiiiiii"; -const char QuestFactionRewardfmt[]="niiiiiiiiii"; -const char PvPDifficultyfmt[]="diiiii"; -const char RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii"; -const char ScalingStatDistributionfmt[]="niiiiiiiiiiiiiiiiiiiii"; -const char ScalingStatValuesfmt[]="iniiiiiiiiiiiiiiiiiiiiii"; -const char SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi"; -const char SkillLineAbilityfmt[]="niiiixxiiiiixx"; -const char SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; -const char SpellCastTimefmt[]="nixx"; -const char SpellDifficultyfmt[]="niiii"; +char const ChrClassesEntryfmt[]="nxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii"; +char const ChrRacesEntryfmt[]="nxixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi"; +char const CinematicSequencesEntryfmt[]="nxxxxxxxxx"; +char const CreatureDisplayInfofmt[]="nixxfxxxxxxxxxxx"; +char const CreatureFamilyfmt[]="nfifiiiiixssssssssssssssssxx"; +char const CreatureModelDatafmt[]="nxxxfxxxxxxxxxxffxxxxxxxxxxx"; +char const CreatureSpellDatafmt[]="niiiixxxx"; +char const CreatureTypefmt[]="nxxxxxxxxxxxxxxxxxx"; +char const CurrencyTypesfmt[]="xnxi"; +char const DestructibleModelDatafmt[]="nxxixxxixxxixxxixxx"; +char const DungeonEncounterfmt[]="niixissssssssssssssssxx"; +char const DurabilityCostsfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; +char const DurabilityQualityfmt[]="nf"; +char const EmotesEntryfmt[]="nxxiiix"; +char const EmotesTextEntryfmt[]="nxixxxxxxxxxxxxxxxx"; +char const FactionEntryfmt[]="niiiiiiiiiiiiiiiiiiffixssssssssssssssssxxxxxxxxxxxxxxxxxx"; +char const FactionTemplateEntryfmt[]="niiiiiiiiiiiii"; +char const GameObjectDisplayInfofmt[]="nsxxxxxxxxxxffffffx"; +char const GemPropertiesEntryfmt[]="nixxi"; +char const GlyphPropertiesfmt[]="niii"; +char const GlyphSlotfmt[]="nii"; +char const GtBarberShopCostBasefmt[]="f"; +char const GtCombatRatingsfmt[]="f"; +char const GtChanceToMeleeCritBasefmt[]="f"; +char const GtChanceToMeleeCritfmt[]="f"; +char const GtChanceToSpellCritBasefmt[]="f"; +char const GtChanceToSpellCritfmt[]="f"; +char const GtOCTClassCombatRatingScalarfmt[]="df"; +char const GtOCTRegenHPfmt[]="f"; +//char const GtOCTRegenMPfmt[]="f"; +char const GtRegenHPPerSptfmt[]="f"; +char const GtRegenMPPerSptfmt[]="f"; +char const Holidaysfmt[]="niiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiixxsiix"; +char const Itemfmt[]="niiiiiii"; +char const ItemBagFamilyfmt[]="nxxxxxxxxxxxxxxxxx"; +//char const ItemDisplayTemplateEntryfmt[]="nxxxxxxxxxxixxxxxxxxxxx"; +//char const ItemCondExtCostsEntryfmt[]="xiii"; +char const ItemExtendedCostEntryfmt[]="niiiiiiiiiiiiiix"; +char const ItemLimitCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; +char const ItemRandomPropertiesfmt[]="nxiiixxssssssssssssssssx"; +char const ItemRandomSuffixfmt[]="nssssssssssssssssxxiiixxiiixx"; +char const ItemSetEntryfmt[]="dssssssssssssssssxiiiiiiiiiixxxxxxxiiiiiiiiiiiiiiiiii"; +char const LFGDungeonEntryfmt[]="nssssssssssssssssxiiiiiiiiixxixixxxxxxxxxxxxxxxxx"; +char const LiquidTypefmt[]="nxxixixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const LockEntryfmt[]="niiiiiiiiiiiiiiiiiiiiiiiixxxxxxxx"; +char const MailTemplateEntryfmt[]="nxxxxxxxxxxxxxxxxxssssssssssssssssx"; +char const MapEntryfmt[]="nxixxssssssssssssssssxixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixiffxiix"; +char const MapDifficultyEntryfmt[]="diisxxxxxxxxxxxxxxxxiix"; +char const MovieEntryfmt[]="nxx"; +char const OverrideSpellDatafmt[]="niiiiiiiiiix"; +char const QuestSortEntryfmt[]="nxxxxxxxxxxxxxxxxx"; +char const QuestXPfmt[]="niiiiiiiiii"; +char const QuestFactionRewardfmt[]="niiiiiiiiii"; +char const PvPDifficultyfmt[]="diiiii"; +char const RandomPropertiesPointsfmt[]="niiiiiiiiiiiiiii"; +char const ScalingStatDistributionfmt[]="niiiiiiiiiiiiiiiiiiiii"; +char const ScalingStatValuesfmt[]="iniiiiiiiiiiiiiiiiiiiiii"; +char const SkillLinefmt[]="nixssssssssssssssssxxxxxxxxxxxxxxxxxxixxxxxxxxxxxxxxxxxi"; +char const SkillLineAbilityfmt[]="niiiixxiiiiixx"; +char const SoundEntriesfmt[]="nxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const SpellCastTimefmt[]="nixx"; +char const SpellDifficultyfmt[]="niiii"; const std::string CustomSpellDifficultyfmt="ppppp"; const std::string CustomSpellDifficultyIndex="id"; -const char SpellDurationfmt[]="niii"; -const char SpellEntryfmt[]="niiiiiiiiiiiixixiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffiiiiiiiiiiiiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiiixfffxxxiiiiixxfffxx"; +char const SpellDurationfmt[]="niii"; +char const SpellEntryfmt[]="niiiiiiiiiiiixixiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiifxiiiiiiiiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiiiiiiiifffiiiiiiiiiiiiiiifffiiiiiiiiiiiiixssssssssssssssssxssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiiiiiiiiiiixfffxxxiiiiixxfffxx"; const std::string CustomSpellEntryfmt="papppppppppppapapaaaaaaaaaaapaaapapppppppaaaaapaapaaaaaaaaaaaaaaaaaappppppppppppppppppppppppppppppppppppaaaaaapppppppppaaapppppppppaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaappppppppapppaaaaappaaaaaaa"; const std::string CustomSpellEntryIndex = "Id"; -const char SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx"; -const char SpellItemEnchantmentfmt[]="nxiiiiiixxxiiissssssssssssssssxiiiiiii"; -const char SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX"; -const char SpellRadiusfmt[]="nfxf"; -const char SpellRangefmt[]="nffffixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; -const char SpellRuneCostfmt[]="niiii"; -const char SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixiiixxiiiiiiii"; -const char StableSlotPricesfmt[] = "ni"; -const char SummonPropertiesfmt[] = "niiiii"; -const char TalentEntryfmt[]="niiiiiiiixxxxixxixxxxxx"; -const char TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiiix"; -const char TaxiNodesEntryfmt[]="nifffssssssssssssssssxii"; -const char TaxiPathEntryfmt[]="niii"; -const char TaxiPathNodeEntryfmt[]="diiifffiiii"; -const char TeamContributionPointsfmt[]="df"; -const char TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; -const char VehicleEntryfmt[]="niffffiiiiiiiifffffffffffffffssssfifiixx"; -const char VehicleSeatEntryfmt[]="niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiixxxxxxxxxxxx"; -const char WMOAreaTableEntryfmt[]="niiixxxxxiixxxxxxxxxxxxxxxxx"; -const char WorldMapAreaEntryfmt[]="xinxffffixx"; -const char WorldMapOverlayEntryfmt[]="nxiiiixxxxxxxxxxx"; -const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx"; +char const SpellFocusObjectfmt[]="nxxxxxxxxxxxxxxxxx"; +char const SpellItemEnchantmentfmt[]="nxiiiiiixxxiiissssssssssssssssxiiiiiii"; +char const SpellItemEnchantmentConditionfmt[]="nbbbbbxxxxxbbbbbbbbbbiiiiiXXXXX"; +char const SpellRadiusfmt[]="nfxf"; +char const SpellRangefmt[]="nffffixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; +char const SpellRuneCostfmt[]="niiii"; +char const SpellShapeshiftfmt[]="nxxxxxxxxxxxxxxxxxxiixiiixxiiiiiiii"; +char const StableSlotPricesfmt[] = "ni"; +char const SummonPropertiesfmt[] = "niiiii"; +char const TalentEntryfmt[]="niiiiiiiixxxxixxixxxxxx"; +char const TalentTabEntryfmt[]="nxxxxxxxxxxxxxxxxxxxiiix"; +char const TaxiNodesEntryfmt[]="nifffssssssssssssssssxii"; +char const TaxiPathEntryfmt[]="niii"; +char const TaxiPathNodeEntryfmt[]="diiifffiiii"; +char const TeamContributionPointsfmt[]="df"; +char const TotemCategoryEntryfmt[]="nxxxxxxxxxxxxxxxxxii"; +char const VehicleEntryfmt[]="niffffiiiiiiiifffffffffffffffssssfifiixx"; +char const VehicleSeatEntryfmt[]="niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiiixxxxxxxxxxxx"; +char const WMOAreaTableEntryfmt[]="niiixxxxxiixxxxxxxxxxxxxxxxx"; +char const WorldMapAreaEntryfmt[]="xinxffffixx"; +char const WorldMapOverlayEntryfmt[]="nxiiiixxxxxxxxxxx"; +char const WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx"; #endif diff --git a/src/server/game/DungeonFinding/LFG.h b/src/server/game/DungeonFinding/LFG.h index 28498749af8..abdb0ffa511 100755 --- a/src/server/game/DungeonFinding/LFG.h +++ b/src/server/game/DungeonFinding/LFG.h @@ -20,19 +20,26 @@ #include "Common.h" +enum LFGEnum +{ + LFG_TANKS_NEEDED = 1, + LFG_HEALERS_NEEDED = 1, + LFG_DPS_NEEDED = 3 +}; + enum LfgRoles { - ROLE_NONE = 0x00, - ROLE_LEADER = 0x01, - ROLE_TANK = 0x02, - ROLE_HEALER = 0x04, - ROLE_DAMAGE = 0x08 + PLAYER_ROLE_NONE = 0x00, + PLAYER_ROLE_LEADER = 0x01, + PLAYER_ROLE_TANK = 0x02, + PLAYER_ROLE_HEALER = 0x04, + PLAYER_ROLE_DAMAGE = 0x08 }; enum LfgUpdateType { LFG_UPDATETYPE_DEFAULT = 0, // Internal Use - LFG_UPDATETYPE_LEADER = 1, + LFG_UPDATETYPE_LEADER_UNK1 = 1, // FIXME: At group leave LFG_UPDATETYPE_ROLECHECK_ABORTED = 4, LFG_UPDATETYPE_JOIN_PROPOSAL = 5, LFG_UPDATETYPE_ROLECHECK_FAILED = 6, @@ -44,7 +51,7 @@ enum LfgUpdateType LFG_UPDATETYPE_PROPOSAL_BEGIN = 13, LFG_UPDATETYPE_CLEAR_LOCK_LIST = 14, LFG_UPDATETYPE_GROUP_MEMBER_OFFLINE = 15, - LFG_UPDATETYPE_GROUP_DISBAND = 16 + LFG_UPDATETYPE_GROUP_DISBAND_UNK16 = 16, // FIXME: Sometimes at group disband }; enum LfgState @@ -62,7 +69,6 @@ enum LfgState /// Instance lock types enum LfgLockStatusType { - LFG_LOCKSTATUS_OK = 0, // Internal use only LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION = 1, LFG_LOCKSTATUS_TOO_LOW_LEVEL = 2, LFG_LOCKSTATUS_TOO_HIGH_LEVEL = 3, @@ -73,18 +79,24 @@ enum LfgLockStatusType LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL = 1002, LFG_LOCKSTATUS_QUEST_NOT_COMPLETED = 1022, LFG_LOCKSTATUS_MISSING_ITEM = 1025, - LFG_LOCKSTATUS_NOT_IN_SEASON = 1031 + LFG_LOCKSTATUS_NOT_IN_SEASON = 1031, + LFG_LOCKSTATUS_MISSING_ACHIEVEMENT = 1034 }; -/// Dungeon and reason why player can't join -struct LfgLockStatus +/// Answer state (Also used to check compatibilites) +enum LfgAnswer { - uint32 dungeon; ///< Dungeon Id - LfgLockStatusType lockstatus; ///< Lock type + LFG_ANSWER_PENDING = -1, + LFG_ANSWER_DENY = 0, + LFG_ANSWER_AGREE = 1 }; typedef std::set<uint32> LfgDungeonSet; -typedef std::map<uint32, LfgLockStatusType> LfgLockMap; +typedef std::map<uint32, uint32> LfgLockMap; typedef std::map<uint64, LfgLockMap> LfgLockPartyMap; +typedef std::set<uint64> LfgGuidSet; +typedef std::list<uint64> LfgGuidList; +typedef std::map<uint64, uint8> LfgRolesMap; +typedef std::map<uint64, uint64> LfgGroupsMap; #endif diff --git a/src/server/game/DungeonFinding/LFGGroupData.cpp b/src/server/game/DungeonFinding/LFGGroupData.cpp index 607389c5dbf..712ae5132b0 100644 --- a/src/server/game/DungeonFinding/LFGGroupData.cpp +++ b/src/server/game/DungeonFinding/LFGGroupData.cpp @@ -18,25 +18,27 @@ #include "LFG.h" #include "LFGGroupData.h" -LfgGroupData::LfgGroupData(): -m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), m_Dungeon(0), -m_VotesNeeded(LFG_GROUP_KICK_VOTES_NEEDED), m_KicksLeft(LFG_GROUP_MAX_KICKS) -{ -} +LfgGroupData::LfgGroupData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), + m_Leader(0), m_Dungeon(0), m_KicksLeft(LFG_GROUP_MAX_KICKS) +{ } LfgGroupData::~LfgGroupData() +{ } + +bool LfgGroupData::IsLfgGroup() { + return m_OldState != LFG_STATE_NONE; } void LfgGroupData::SetState(LfgState state) { switch (state) { + case LFG_STATE_FINISHED_DUNGEON: case LFG_STATE_NONE: case LFG_STATE_DUNGEON: - case LFG_STATE_FINISHED_DUNGEON: m_OldState = state; - // No break on purpose + // No break on purpose default: m_State = state; } @@ -47,6 +49,29 @@ void LfgGroupData::RestoreState() m_State = m_OldState; } +void LfgGroupData::AddPlayer(uint64 guid) +{ + m_Players.insert(guid); +} + +uint8 LfgGroupData::RemovePlayer(uint64 guid) +{ + LfgGuidSet::iterator it = m_Players.find(guid); + if (it != m_Players.end()) + m_Players.erase(it); + return uint8(m_Players.size()); +} + +void LfgGroupData::RemoveAllPlayers() +{ + m_Players.clear(); +} + +void LfgGroupData::SetLeader(uint64 guid) +{ + m_Leader = guid; +} + void LfgGroupData::SetDungeon(uint32 dungeon) { m_Dungeon = dungeon; @@ -63,6 +88,21 @@ LfgState LfgGroupData::GetState() const return m_State; } +LfgState LfgGroupData::GetOldState() const +{ + return m_OldState; +} + +const LfgGuidSet &LfgGroupData::GetPlayers() const +{ + return m_Players; +} + +uint64 LfgGroupData::GetLeader() const +{ + return m_Leader; +} + uint32 LfgGroupData::GetDungeon(bool asId /* = true */) const { if (asId) @@ -71,11 +111,6 @@ uint32 LfgGroupData::GetDungeon(bool asId /* = true */) const return m_Dungeon; } -uint8 LfgGroupData::GetVotesNeeded() const -{ - return m_VotesNeeded; -} - uint8 LfgGroupData::GetKicksLeft() const { return m_KicksLeft; diff --git a/src/server/game/DungeonFinding/LFGGroupData.h b/src/server/game/DungeonFinding/LFGGroupData.h index 74570817698..43cd64f97c3 100644 --- a/src/server/game/DungeonFinding/LFGGroupData.h +++ b/src/server/game/DungeonFinding/LFGGroupData.h @@ -23,7 +23,6 @@ enum LfgGroupEnum { LFG_GROUP_MAX_KICKS = 3, - LFG_GROUP_KICK_VOTES_NEEDED = 3 }; /** @@ -35,31 +34,43 @@ class LfgGroupData LfgGroupData(); ~LfgGroupData(); + bool IsLfgGroup(); + // General void SetState(LfgState state); void RestoreState(); + void AddPlayer(uint64 guid); + uint8 RemovePlayer(uint64 guid); + void RemoveAllPlayers(); + void SetLeader(uint64 guid); + // Dungeon void SetDungeon(uint32 dungeon); + // VoteKick - void SetVotesNeeded(uint8 votes); void DecreaseKicksLeft(); // General LfgState GetState() const; + LfgState GetOldState() const; + LfgGuidSet const& GetPlayers() const; + uint64 GetLeader() const; + // Dungeon uint32 GetDungeon(bool asId = true) const; + // VoteKick - uint8 GetVotesNeeded() const; uint8 GetKicksLeft() const; private: // General LfgState m_State; ///< State if group in LFG LfgState m_OldState; ///< Old State + uint64 m_Leader; ///< Leader GUID + LfgGuidSet m_Players; ///< Players in group // Dungeon uint32 m_Dungeon; ///< Dungeon entry // Vote Kick - uint8 m_VotesNeeded; ///< Votes need to kick success uint8 m_KicksLeft; ///< Number of kicks left }; diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index 2b425dffff0..0a2a4b861b8 100755 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -18,60 +18,30 @@ #include "Common.h" #include "SharedDefines.h" #include "DBCStores.h" -#include "Containers.h" #include "DisableMgr.h" #include "ObjectMgr.h" #include "SocialMgr.h" #include "LFGMgr.h" -#include "GroupMgr.h" -#include "GameEventMgr.h" #include "LFGScripts.h" #include "LFGGroupData.h" #include "LFGPlayerData.h" - +#include "LFGQueue.h" #include "Group.h" #include "Player.h" +#include "GroupMgr.h" +#include "GameEventMgr.h" -LFGMgr::LFGMgr(): m_update(true), m_QueueTimer(0), m_lfgProposalId(1), -m_WaitTimeAvg(-1), m_WaitTimeTank(-1), m_WaitTimeHealer(-1), m_WaitTimeDps(-1), -m_NumWaitTimeAvg(0), m_NumWaitTimeTank(0), m_NumWaitTimeHealer(0), m_NumWaitTimeDps(0) +LFGMgr::LFGMgr(): m_QueueTimer(0), m_lfgProposalId(1), + m_options(sWorld->getIntConfig(CONFIG_LFG_OPTIONSMASK)) { - m_update = sWorld->getBoolConfig(CONFIG_DUNGEON_FINDER_ENABLE); - if (m_update) - { - new LFGPlayerScript(); - new LFGGroupScript(); - - // Initialize dungeon cache - for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) - { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i); - if (dungeon && dungeon->type != LFG_TYPE_ZONE) - { - if (dungeon->type != LFG_TYPE_RANDOM) - m_CachedDungeonMap[dungeon->grouptype].insert(dungeon->ID); - m_CachedDungeonMap[0].insert(dungeon->ID); - } - } - } + new LFGPlayerScript(); + new LFGGroupScript(); } LFGMgr::~LFGMgr() { for (LfgRewardMap::iterator itr = m_RewardMap.begin(); itr != m_RewardMap.end(); ++itr) delete itr->second; - - for (LfgQueueInfoMap::iterator it = m_QueueInfoMap.begin(); it != m_QueueInfoMap.end(); ++it) - delete it->second; - - for (LfgProposalMap::iterator it = m_Proposals.begin(); it != m_Proposals.end(); ++it) - delete it->second; - - for (LfgPlayerBootMap::iterator it = m_Boots.begin(); it != m_Boots.end(); ++it) - delete it->second; - - for (LfgRoleCheckMap::iterator it = m_RoleChecks.begin(); it != m_RoleChecks.end(); ++it) - delete it->second; } void LFGMgr::_LoadFromDB(Field* fields, uint64 guid) @@ -122,6 +92,89 @@ void LFGMgr::_SaveToDB(uint64 guid, uint32 db_guid) CharacterDatabase.Execute(stmt); } +std::string LFGMgr::ConcatenateDungeons(LfgDungeonSet const& dungeons) +{ + std::string dungeonstr = ""; + if (!dungeons.empty()) + { + std::ostringstream o; + LfgDungeonSet::const_iterator it = dungeons.begin(); + o << (*it); + for (++it; it != dungeons.end(); ++it) + o << ", " << uint32(*it); + dungeonstr = o.str(); + } + return dungeonstr; +} + +std::string LFGMgr::GetRolesString(uint8 roles) +{ + std::string rolesstr = ""; + + if (roles & PLAYER_ROLE_TANK) + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_TANK)); + + if (roles & PLAYER_ROLE_HEALER) + { + if (!rolesstr.empty()) + rolesstr.append(", "); + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_HEALER)); + } + + if (roles & PLAYER_ROLE_DAMAGE) + { + if (!rolesstr.empty()) + rolesstr.append(", "); + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_DAMAGE)); + } + + if (roles & PLAYER_ROLE_LEADER) + { + if (!rolesstr.empty()) + rolesstr.append(", "); + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_LEADER)); + } + + if (rolesstr.empty()) + rolesstr.append(sObjectMgr->GetTrinityStringForDBCLocale(LANG_LFG_ROLE_NONE)); + + return rolesstr; +} + +char const * LFGMgr::GetStateString(LfgState state) +{ + int32 entry = LANG_LFG_ERROR; + switch (state) + { + case LFG_STATE_NONE: + entry = LANG_LFG_STATE_NONE; + break; + case LFG_STATE_ROLECHECK: + entry = LANG_LFG_STATE_ROLECHECK; + break; + case LFG_STATE_QUEUED: + entry = LANG_LFG_STATE_QUEUED; + break; + case LFG_STATE_PROPOSAL: + entry = LANG_LFG_STATE_PROPOSAL; + break; + case LFG_STATE_DUNGEON: + entry = LANG_LFG_STATE_DUNGEON; + break; + case LFG_STATE_BOOT: + entry = LANG_LFG_STATE_BOOT; + break; + case LFG_STATE_FINISHED_DUNGEON: + entry = LANG_LFG_STATE_FINISHED_DUNGEON; + break; + case LFG_STATE_RAIDBROWSER: + entry = LANG_LFG_STATE_RAIDBROWSER; + break; + } + char const * const str = sObjectMgr->GetTrinityStringForDBCLocale(entry); + return str; +} + /// Load rewards for completing dungeons void LFGMgr::LoadRewards() { @@ -155,7 +208,7 @@ void LFGMgr::LoadRewards() uint32 otherMoneyVar = fields[6].GetUInt32(); uint32 otherXPVar = fields[7].GetUInt32(); - if (!sLFGDungeonStore.LookupEntry(dungeonId)) + if (!GetLFGDungeon(dungeonId)) { sLog->outError(LOG_FILTER_SQL, "Dungeon %u specified in table `lfg_dungeon_rewards` does not exist!", dungeonId); continue; @@ -186,16 +239,50 @@ void LFGMgr::LoadRewards() sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u lfg dungeon rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); } -void LFGMgr::LoadEntrancePositions() +LFGDungeonData const* LFGMgr::GetLFGDungeon(uint32 id) +{ + LFGDungeonMap::const_iterator itr = m_LfgDungeonMap.find(id); + if (itr != m_LfgDungeonMap.end()) + return &(itr->second); + + return NULL; +} + +LFGDungeonMap & LFGMgr::GetLFGDungeonMap() +{ + return m_LfgDungeonMap; +} + +void LFGMgr::LoadLFGDungeons(bool reload /* = false */) { uint32 oldMSTime = getMSTime(); - m_entrancePositions.clear(); + m_LfgDungeonMap.clear(); + + // Initialize Dungeon map with data from dbcs + for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) + { + LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i); + if (!dungeon) + continue; + + switch (dungeon->type) + { + case LFG_TYPE_DUNGEON: + case LFG_TYPE_HEROIC: + case LFG_TYPE_RAID: + case LFG_TYPE_RANDOM: + m_LfgDungeonMap[dungeon->ID] = LFGDungeonData(dungeon); + break; + } + } + + // Fill teleport locations from DB QueryResult result = WorldDatabase.Query("SELECT dungeonId, position_x, position_y, position_z, orientation FROM lfg_entrances"); if (!result) { - sLog->outError(LOG_FILTER_SERVER_LOADING, ">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!"); + sLog->outError(LOG_FILTER_SQL, ">> Loaded 0 lfg entrance positions. DB table `lfg_entrances` is empty!"); return; } @@ -205,49 +292,86 @@ void LFGMgr::LoadEntrancePositions() { Field* fields = result->Fetch(); uint32 dungeonId = fields[0].GetUInt32(); - Position pos; - pos.m_positionX = fields[1].GetFloat(); - pos.m_positionY = fields[2].GetFloat(); - pos.m_positionZ = fields[3].GetFloat(); - pos.m_orientation = fields[4].GetFloat(); - m_entrancePositions[dungeonId] = pos; + LFGDungeonMap::iterator dungeonItr = m_LfgDungeonMap.find(dungeonId); + if (dungeonItr == m_LfgDungeonMap.end()) + { + sLog->outError(LOG_FILTER_SQL, "table `lfg_entrances` contains coordinates for wrong dungeon %u", dungeonId); + continue; + } + + LFGDungeonData& data = dungeonItr->second; + data.x = fields[1].GetFloat(); + data.y = fields[2].GetFloat(); + data.z = fields[3].GetFloat(); + data.o = fields[4].GetFloat(); + ++count; } while (result->NextRow()); sLog->outInfo(LOG_FILTER_SERVER_LOADING, ">> Loaded %u lfg entrance positions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + + // Fill all other teleport coords from areatriggers + for (LFGDungeonMap::iterator itr = m_LfgDungeonMap.begin(); itr != m_LfgDungeonMap.end(); ++itr) + { + LFGDungeonData& dungeon = itr->second; + + // No teleport coords in database, load from areatriggers + if (dungeon.type != LFG_TYPE_RANDOM && dungeon.x == 0.0f && dungeon.y == 0.0f && dungeon.z == 0.0f) + { + AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(dungeon.map); + if (!at) + { + sLog->outError(LOG_FILTER_LFG, "LFGMgr::LoadLFGDungeons: Failed to load dungeon %s, cant find areatrigger for map %u", dungeon.name.c_str(), dungeon.map); + continue; + } + + dungeon.map = at->target_mapId; + dungeon.x = at->target_X; + dungeon.y = at->target_Y; + dungeon.z = at->target_Z; + dungeon.o = at->target_Orientation; + } + + if (dungeon.type != LFG_TYPE_RANDOM) + m_CachedDungeonMap[dungeon.group].insert(dungeon.id); + m_CachedDungeonMap[0].insert(dungeon.id); + } + + if (reload) + { + m_CachedDungeonMap.clear(); + // Recalculate locked dungeons + for (LfgPlayerDataMap::const_iterator it = m_Players.begin(); it != m_Players.end(); ++it) + if (Player* player = ObjectAccessor::FindPlayer(it->first)) + InitializeLockedDungeons(player); + } } void LFGMgr::Update(uint32 diff) { - if (!m_update) + if (!isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) return; - m_update = false; time_t currTime = time(NULL); // Remove obsolete role checks for (LfgRoleCheckMap::iterator it = m_RoleChecks.begin(); it != m_RoleChecks.end();) { LfgRoleCheckMap::iterator itRoleCheck = it++; - LfgRoleCheck* roleCheck = itRoleCheck->second; - if (currTime < roleCheck->cancelTime) + LfgRoleCheck& roleCheck = itRoleCheck->second; + if (currTime < roleCheck.cancelTime) continue; - roleCheck->state = LFG_ROLECHECK_MISSING_ROLE; + roleCheck.state = LFG_ROLECHECK_MISSING_ROLE; - for (LfgRolesMap::const_iterator itRoles = roleCheck->roles.begin(); itRoles != roleCheck->roles.end(); ++itRoles) + for (LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin(); itRoles != roleCheck.roles.end(); ++itRoles) { uint64 guid = itRoles->first; - ClearState(guid); - if (Player* player = ObjectAccessor::FindPlayer(guid)) - { - player->GetSession()->SendLfgRoleCheckUpdate(roleCheck); - - if (itRoles->first == roleCheck->leader) - player->GetSession()->SendLfgJoinResult(LfgJoinResultData(LFG_JOIN_FAILED, LFG_ROLECHECK_MISSING_ROLE)); - } + ClearState(guid, "Remove Obsolete RoleCheck"); + SendLfgRoleCheckUpdate(guid, roleCheck); + if (guid == roleCheck.leader) + SendLfgJoinResult(guid, LfgJoinResultData(LFG_JOIN_FAILED, LFG_ROLECHECK_MISSING_ROLE)); } - delete roleCheck; m_RoleChecks.erase(itRoleCheck); } @@ -255,7 +379,7 @@ void LFGMgr::Update(uint32 diff) for (LfgProposalMap::iterator it = m_Proposals.begin(); it != m_Proposals.end();) { LfgProposalMap::iterator itRemove = it++; - if (itRemove->second->cancelTime < currTime) + if (itRemove->second.cancelTime < currTime) RemoveProposal(itRemove, LFG_UPDATETYPE_PROPOSAL_FAILED); } @@ -263,71 +387,53 @@ void LFGMgr::Update(uint32 diff) for (LfgPlayerBootMap::iterator it = m_Boots.begin(); it != m_Boots.end();) { LfgPlayerBootMap::iterator itBoot = it++; - LfgPlayerBoot* pBoot = itBoot->second; - if (pBoot->cancelTime < currTime) + LfgPlayerBoot& boot = itBoot->second; + if (boot.cancelTime < currTime) { - pBoot->inProgress = false; - for (LfgAnswerMap::const_iterator itVotes = pBoot->votes.begin(); itVotes != pBoot->votes.end(); ++itVotes) - if (Player* plrg = ObjectAccessor::FindPlayer(itVotes->first)) - if (plrg->GetGUID() != pBoot->victim) - plrg->GetSession()->SendLfgBootPlayer(pBoot); - delete pBoot; + boot.inProgress = false; + for (LfgAnswerMap::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes) + { + uint64 pguid = itVotes->first; + if (pguid != boot.victim) + SendLfgBootProposalUpdate(pguid, boot); + SetState(pguid, LFG_STATE_DUNGEON); + } + SetState(itBoot->first, LFG_STATE_DUNGEON); m_Boots.erase(itBoot); } } + uint32 lastProposalId = m_lfgProposalId; // Check if a proposal can be formed with the new groups being added - for (LfgGuidListMap::iterator it = m_newToQueue.begin(); it != m_newToQueue.end(); ++it) + for (LfgQueueMap::iterator it = m_Queues.begin(); it != m_Queues.end(); ++it) + if (uint8 newProposals = it->second.FindGroups()) + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Update: Found %u new groups in queue %u", newProposals, it->first); + + if (lastProposalId != m_lfgProposalId) { - uint8 queueId = it->first; - LfgGuidList& newToQueue = it->second; - LfgGuidList& currentQueue = m_currentQueue[queueId]; - LfgGuidList firstNew; - while (!newToQueue.empty()) + // FIXME lastProposalId ? lastProposalId +1 ? + for (LfgProposalMap::const_iterator itProposal = m_Proposals.find(m_lfgProposalId); itProposal != m_Proposals.end(); ++itProposal) { - uint64 frontguid = newToQueue.front(); - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Update: QueueId %u: checking [" UI64FMTD "] newToQueue(%u), currentQueue(%u)", queueId, frontguid, uint32(newToQueue.size()), uint32(currentQueue.size())); - firstNew.push_back(frontguid); - newToQueue.pop_front(); + uint32 proposalId = itProposal->first; + LfgProposal& proposal = m_Proposals[proposalId]; - LfgGuidList temporalList = currentQueue; - if (LfgProposal* pProposal = FindNewGroups(firstNew, temporalList)) // Group found! + uint64 guid = 0; + for (LfgProposalPlayerMap::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers) { - // Remove groups in the proposal from new and current queues (not from queue map) - for (LfgGuidList::const_iterator itQueue = pProposal->queues.begin(); itQueue != pProposal->queues.end(); ++itQueue) - { - currentQueue.remove(*itQueue); - newToQueue.remove(*itQueue); - } - m_Proposals[++m_lfgProposalId] = pProposal; - - uint64 guid = 0; - for (LfgProposalPlayerMap::const_iterator itPlayers = pProposal->players.begin(); itPlayers != pProposal->players.end(); ++itPlayers) + guid = itPlayers->first; + SetState(guid, LFG_STATE_PROPOSAL); + if (uint64 gguid = GetGroup(guid)) { - guid = itPlayers->first; - SetState(guid, LFG_STATE_PROPOSAL); - if (Player* player = ObjectAccessor::FindPlayer(itPlayers->first)) - { - Group* grp = player->GetGroup(); - if (grp) - { - uint64 gguid = grp->GetGUID(); - SetState(gguid, LFG_STATE_PROPOSAL); - player->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid))); - } - else - player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid))); - player->GetSession()->SendLfgUpdateProposal(m_lfgProposalId, pProposal); - } + SetState(gguid, LFG_STATE_PROPOSAL); + SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid))); } - - if (pProposal->state == LFG_PROPOSAL_SUCCESS) - UpdateProposal(m_lfgProposalId, guid, true); + else + SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_PROPOSAL_BEGIN, GetSelectedDungeons(guid), GetComment(guid))); + SendLfgUpdateProposal(guid, m_lfgProposalId, proposal); } - else - if (std::find(currentQueue.begin(), currentQueue.end(), frontguid) == currentQueue.end()) //already in queue? - currentQueue.push_back(frontguid); // Lfg group not found, add this group to the queue. - firstNew.clear(); + + if (proposal.state == LFG_PROPOSAL_SUCCESS) + UpdateProposal(proposalId, guid, true); } } @@ -335,105 +441,12 @@ void LFGMgr::Update(uint32 diff) if (m_QueueTimer > LFG_QUEUEUPDATE_INTERVAL) { m_QueueTimer = 0; - currTime = time(NULL); - for (LfgQueueInfoMap::const_iterator itQueue = m_QueueInfoMap.begin(); itQueue != m_QueueInfoMap.end(); ++itQueue) - { - LfgQueueInfo* queue = itQueue->second; - if (!queue) - { - sLog->outError(LOG_FILTER_LFG, "LFGMgr::Update: [" UI64FMTD "] queued with null queue info!", itQueue->first); - continue; - } - uint32 dungeonId = (*queue->dungeons.begin()); - uint32 queuedTime = uint32(currTime - queue->joinTime); - uint8 role = ROLE_NONE; - for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) - role |= itPlayer->second; - role &= ~ROLE_LEADER; - - int32 waitTime = -1; - switch (role) - { - case ROLE_NONE: // Should not happen - just in case - waitTime = -1; - break; - case ROLE_TANK: - waitTime = m_WaitTimeTank; - break; - case ROLE_HEALER: - waitTime = m_WaitTimeHealer; - break; - case ROLE_DAMAGE: - waitTime = m_WaitTimeDps; - break; - default: - waitTime = m_WaitTimeAvg; - break; - } - - for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) - if (Player* player = ObjectAccessor::FindPlayer(itPlayer->first)) - player->GetSession()->SendLfgQueueStatus(dungeonId, waitTime, m_WaitTimeAvg, m_WaitTimeTank, m_WaitTimeHealer, m_WaitTimeDps, queuedTime, queue->tanks, queue->healers, queue->dps); - } + time_t currTime = time(NULL); + for (LfgQueueMap::iterator it = m_Queues.begin(); it != m_Queues.end(); ++it) + it->second.UpdateQueueTimers(currTime); } else m_QueueTimer += diff; - m_update = true; -} - -/** - Add a guid to the queue of guids to be added to main queue. It guid its already - in queue does nothing. If this function is called guid is not in the main queue - (No need to check it here) - - @param[in] guid Player or group guid to add to queue - @param[in] queueId Queue Id to add player/group to -*/ -void LFGMgr::AddToQueue(uint64 guid, uint8 queueId) -{ - if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) - queueId = 0; - - LfgGuidList& list = m_newToQueue[queueId]; - if (std::find(list.begin(), list.end(), guid) != list.end()) - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::AddToQueue: [" UI64FMTD "] already in new queue. ignoring", guid); - else - { - list.push_back(guid); - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::AddToQueue: [" UI64FMTD "] added to m_newToQueue (size: %u)", guid, uint32(list.size())); - } -} - -/** - Removes a guid from the main and new queues. - - @param[in] guid Player or group guid to add to queue - @return true if guid was found in main queue. -*/ -bool LFGMgr::RemoveFromQueue(uint64 guid) -{ - for (LfgGuidListMap::iterator it = m_currentQueue.begin(); it != m_currentQueue.end(); ++it) - it->second.remove(guid); - - for (LfgGuidListMap::iterator it = m_newToQueue.begin(); it != m_newToQueue.end(); ++it) - it->second.remove(guid); - - RemoveFromCompatibles(guid); - - LfgQueueInfoMap::iterator it = m_QueueInfoMap.find(guid); - if (it != m_QueueInfoMap.end()) - { - delete it->second; - m_QueueInfoMap.erase(it); - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveFromQueue: [" UI64FMTD "] removed", guid); - return true; - } - else - { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveFromQueue: [" UI64FMTD "] not in queue", guid); - return false; - } - } /** @@ -441,68 +454,61 @@ bool LFGMgr::RemoveFromQueue(uint64 guid) @param[in] player Player we need to initialize the lock status map */ -void LFGMgr::InitializeLockedDungeons(Player* player) +void LFGMgr::InitializeLockedDungeons(Player* player, uint8 level /* = 0 */) { uint64 guid = player->GetGUID(); - uint8 level = player->getLevel(); + if (!level) + level = player->getLevel(); uint8 expansion = player->GetSession()->Expansion(); - LfgDungeonSet dungeons = GetDungeonsByRandom(0); + LfgDungeonSet const& dungeons = GetDungeonsByRandom(0); LfgLockMap lock; for (LfgDungeonSet::const_iterator it = dungeons.begin(); it != dungeons.end(); ++it) { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(*it); + LFGDungeonData const* dungeon = GetLFGDungeon(*it); if (!dungeon) // should never happen - We provide a list from sLFGDungeonStore continue; - AccessRequirement const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty)); - - LfgLockStatusType locktype = LFG_LOCKSTATUS_OK; + uint32 lockData = 0; if (dungeon->expansion > expansion) - locktype = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION; + lockData = LFG_LOCKSTATUS_INSUFFICIENT_EXPANSION; else if (DisableMgr::IsDisabledFor(DISABLE_TYPE_MAP, dungeon->map, player)) - locktype = LFG_LOCKSTATUS_RAID_LOCKED; + lockData = LFG_LOCKSTATUS_RAID_LOCKED; else if (dungeon->difficulty > DUNGEON_DIFFICULTY_NORMAL && player->GetBoundInstance(dungeon->map, Difficulty(dungeon->difficulty))) - { - //if (!player->GetGroup() || !player->GetGroup()->isLFGGroup() || GetDungeon(player->GetGroup()->GetGUID(), true) != dungeon->ID || GetState(player->GetGroup()->GetGUID()) != LFG_STATE_DUNGEON) - locktype = LFG_LOCKSTATUS_RAID_LOCKED; - } + lockData = LFG_LOCKSTATUS_RAID_LOCKED; else if (dungeon->minlevel > level) - locktype = LFG_LOCKSTATUS_TOO_LOW_LEVEL; + lockData = LFG_LOCKSTATUS_TOO_LOW_LEVEL; else if (dungeon->maxlevel < level) - locktype = LFG_LOCKSTATUS_TOO_HIGH_LEVEL; - else if (dungeon->flags & LFG_FLAG_SEASONAL) - { - if (HolidayIds holiday = sLFGMgr->GetDungeonSeason(dungeon->ID)) - if (!IsHolidayActive(holiday)) - locktype = LFG_LOCKSTATUS_NOT_IN_SEASON; - } - else if (locktype == LFG_LOCKSTATUS_OK && ar) + lockData = LFG_LOCKSTATUS_TOO_HIGH_LEVEL; + else if (dungeon->seasonal && !IsSeasonActive(dungeon->id)) + lockData = LFG_LOCKSTATUS_NOT_IN_SEASON; + else if (AccessRequirement const* ar = sObjectMgr->GetAccessRequirement(dungeon->map, Difficulty(dungeon->difficulty))) { if (ar->achievement && !player->HasAchieved(ar->achievement)) - locktype = LFG_LOCKSTATUS_RAID_LOCKED; // FIXME: Check the correct lock value + lockData = LFG_LOCKSTATUS_MISSING_ACHIEVEMENT; else if (player->GetTeam() == ALLIANCE && ar->quest_A && !player->GetQuestRewardStatus(ar->quest_A)) - locktype = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; + lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; else if (player->GetTeam() == HORDE && ar->quest_H && !player->GetQuestRewardStatus(ar->quest_H)) - locktype = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; + lockData = LFG_LOCKSTATUS_QUEST_NOT_COMPLETED; else if (ar->item) { if (!player->HasItemCount(ar->item, 1) && (!ar->item2 || !player->HasItemCount(ar->item2, 1))) - locktype = LFG_LOCKSTATUS_MISSING_ITEM; + lockData = LFG_LOCKSTATUS_MISSING_ITEM; } else if (ar->item2 && !player->HasItemCount(ar->item2, 1)) - locktype = LFG_LOCKSTATUS_MISSING_ITEM; + lockData = LFG_LOCKSTATUS_MISSING_ITEM; } + /* TODO VoA closed if WG is not under team control (LFG_LOCKSTATUS_RAID_LOCKED) - locktype = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE; - locktype = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE; - locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL; - locktype = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL; + lockData = LFG_LOCKSTATUS_TOO_LOW_GEAR_SCORE; + lockData = LFG_LOCKSTATUS_TOO_HIGH_GEAR_SCORE; + lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_LOW_LEVEL; + lockData = LFG_LOCKSTATUS_ATTUNEMENT_TOO_HIGH_LEVEL; */ - if (locktype != LFG_LOCKSTATUS_OK) - lock[dungeon->Entry()] = locktype; + if (lockData) + lock[dungeon->Entry()] = lockData; } SetLockedDungeons(guid, lock); } @@ -517,19 +523,18 @@ void LFGMgr::InitializeLockedDungeons(Player* player) @param[in] dungeons Dungeons the player/group is applying for @param[in] comment Player selected comment */ -void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDungeons, const std::string& comment) +void LFGMgr::JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, const std::string& comment) { - if (!player || !player->GetSession() || selectedDungeons.empty()) - return; + if (!player || !player->GetSession() || dungeons.empty()) + return; Group* grp = player->GetGroup(); uint64 guid = player->GetGUID(); uint64 gguid = grp ? grp->GetGUID() : guid; LfgJoinResultData joinData; - PlayerSet players; + LfgGuidSet players; uint32 rDungeonId = 0; bool isContinue = grp && grp->isLFGGroup() && GetState(gguid) != LFG_STATE_FINISHED_DUNGEON; - LfgDungeonSet dungeons = selectedDungeons; // Do not allow to change dungeon in the middle of a current dungeon if (isContinue) @@ -539,10 +544,10 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung } // Already in queue? - LfgQueueInfoMap::iterator itQueue = m_QueueInfoMap.find(gguid); - if (itQueue != m_QueueInfoMap.end()) + LfgState state = GetState(gguid); + if (state == LFG_STATE_QUEUED) { - LfgDungeonSet playerDungeons = GetSelectedDungeons(guid); + LfgDungeonSet const& playerDungeons = GetSelectedDungeons(guid); if (playerDungeons == dungeons) // Joining the same dungeons -- Send OK { LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, comment); @@ -556,7 +561,10 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung return; } else // Remove from queue and rejoin - RemoveFromQueue(gguid); + { + LfgQueue& queue = GetQueue(gguid); + queue.RemoveFromQueue(gguid); + } } // Check player or group member restrictions @@ -586,15 +594,16 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung else if (plrg->InBattleground() || plrg->InArena() || plrg->InBattlegroundQueue()) joinData.result = LFG_JOIN_USING_BG_SYSTEM; ++memberCount; - players.insert(plrg); + players.insert(plrg->GetGUID()); } } - if (memberCount != grp->GetMembersCount()) + + if (joinData.result == LFG_JOIN_OK && memberCount != grp->GetMembersCount()) joinData.result = LFG_JOIN_DISCONNECTED; } } else - players.insert(player); + players.insert(player->GetGUID()); // Check if all dungeons are valid bool isRaid = false; @@ -652,25 +661,24 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung return; } - // FIXME - Raid browser not supported yet + SetComment(guid, comment); + if (isRaid) { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Join: [" UI64FMTD "] trying to join raid browser and it's disabled.", guid); return; } - SetComment(guid, comment); - + std::string debugNames = ""; if (grp) // Begin rolecheck { // Create new rolecheck - LfgRoleCheck* roleCheck = new LfgRoleCheck(); - roleCheck->cancelTime = time_t(time(NULL)) + LFG_TIME_ROLECHECK; - roleCheck->state = LFG_ROLECHECK_INITIALITING; - roleCheck->leader = guid; - roleCheck->dungeons = dungeons; - roleCheck->rDungeonId = rDungeonId; - m_RoleChecks[gguid] = roleCheck; + LfgRoleCheck& roleCheck = m_RoleChecks[gguid]; + roleCheck.cancelTime = time_t(time(NULL)) + LFG_TIME_ROLECHECK; + roleCheck.state = LFG_ROLECHECK_INITIALITING; + roleCheck.leader = guid; + roleCheck.dungeons = dungeons; + roleCheck.rDungeonId = rDungeonId; if (rDungeonId) { @@ -690,7 +698,10 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung SetState(pguid, LFG_STATE_ROLECHECK); if (!isContinue) SetSelectedDungeons(pguid, dungeons); - roleCheck->roles[pguid] = 0; + roleCheck.roles[pguid] = 0; + if (!debugNames.empty()) + debugNames.append(", "); + debugNames.append(plrg->GetName()); } } // Update leader role @@ -698,24 +709,11 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung } else // Add player to queue { - // Queue player - LfgQueueInfo* pqInfo = new LfgQueueInfo(); - pqInfo->joinTime = time_t(time(NULL)); - pqInfo->roles[player->GetGUID()] = roles; - pqInfo->dungeons = dungeons; - if (roles & ROLE_TANK) - --pqInfo->tanks; - else if (roles & ROLE_HEALER) - --pqInfo->healers; - else - --pqInfo->dps; - m_QueueInfoMap[guid] = pqInfo; + LfgRolesMap rolesMap; + rolesMap[guid] = roles; + LfgQueue& queue = GetQueue(gguid); + queue.AddQueueData(guid, time_t(time(NULL)), dungeons, rolesMap); - // Send update to player - player->GetSession()->SendLfgJoinResult(joinData); - player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_JOIN_PROPOSAL, dungeons, comment)); - SetState(gguid, LFG_STATE_QUEUED); - SetRoles(guid, roles); if (!isContinue) { if (rDungeonId) @@ -725,66 +723,75 @@ void LFGMgr::Join(Player* player, uint8 roles, const LfgDungeonSet& selectedDung } SetSelectedDungeons(guid, dungeons); } - AddToQueue(guid, uint8(player->GetTeam())); + // Send update to player + player->GetSession()->SendLfgJoinResult(joinData); + player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_JOIN_PROPOSAL, dungeons, comment)); + SetState(gguid, LFG_STATE_QUEUED); + SetRoles(guid, roles); + debugNames.append(player->GetName()); + } + + if (sLog->ShouldLog(LOG_FILTER_LFG, LOG_LEVEL_DEBUG)) + { + std::ostringstream o; + o << "LFGMgr::Join: [" << guid << "] joined (" << (grp ? "group" : "player") << ") Members: " << debugNames.c_str() + << ". Dungeons (" << uint32(dungeons.size()) << "): " << ConcatenateDungeons(dungeons); + sLog->outDebug(LOG_FILTER_LFG, "%s", o.str().c_str()); } - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Join: [" UI64FMTD "] joined with %u members. dungeons: %u", guid, grp ? grp->GetMembersCount() : 1, uint8(dungeons.size())); } /** Leaves Dungeon System. Player/Group is removed from queue, rolechecks, proposals or votekicks. Player or group needs to be not NULL and using Dungeon System - @param[in] player Player trying to leave (can be NULL) - @param[in] grp Group trying to leave (default NULL) + @param[in] guid Player or group guid */ -void LFGMgr::Leave(Player* player, Group* grp /* = NULL*/) +void LFGMgr::LeaveLfg(uint64 guid) { - if (!player && !grp) - return; - - uint64 guid = grp ? grp->GetGUID() : player->GetGUID(); LfgState state = GetState(guid); + uint64 gguid = IS_GROUP(guid) ? guid : GetGroup(guid); sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::Leave: [" UI64FMTD "]", guid); switch (state) { case LFG_STATE_QUEUED: + if (gguid) { - RemoveFromQueue(guid); - LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); - if (grp) + LfgQueue& queue = GetQueue(gguid); + queue.RemoveFromQueue(gguid); + RestoreState(gguid, "Leave queue"); + const LfgGuidSet& players = GetPlayers(gguid); + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it) { - RestoreState(guid); - for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - if (Player* plrg = itr->getSource()) - { - plrg->GetSession()->SendLfgUpdateParty(updateData); - uint64 pguid = plrg->GetGUID(); - ClearState(pguid); - } - } - else - { - player->GetSession()->SendLfgUpdatePlayer(updateData); - ClearState(guid); + uint64 guid = (*it); + ClearState(guid, "Leave queue"); + SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE)); } } + else + { + LfgQueue& queue = GetQueue(guid); + queue.RemoveFromQueue(guid); + SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE)); + ClearState(guid, "Leave queue"); + } break; case LFG_STATE_ROLECHECK: - if (grp) - UpdateRoleCheck(guid); // No player to update role = LFG_ROLECHECK_ABORTED + if (gguid) + UpdateRoleCheck(gguid); // No player to update role = LFG_ROLECHECK_ABORTED break; case LFG_STATE_PROPOSAL: { // Remove from Proposals LfgProposalMap::iterator it = m_Proposals.begin(); + uint64 pguid = gguid == guid ? GetLeader(gguid) : guid; while (it != m_Proposals.end()) { - LfgProposalPlayerMap::iterator itPlayer = it->second->players.find(player ? player->GetGUID() : grp->GetLeaderGUID()); - if (itPlayer != it->second->players.end()) + LfgProposalPlayerMap::iterator itPlayer = it->second.players.find(pguid); + if (itPlayer != it->second.players.end()) { // Mark the player/leader of group who left as didn't accept the proposal - itPlayer->second->accept = LFG_ANSWER_DENY; + itPlayer->second.accept = LFG_ANSWER_DENY; break; } ++it; @@ -801,312 +808,13 @@ void LFGMgr::Leave(Player* player, Group* grp /* = NULL*/) } /** - Sends the leader of a group the offer to continue popup - - @param[in] grp Group to send offer to -*/ -void LFGMgr::OfferContinue(Group* grp) -{ - if (grp) - { - uint64 gguid = grp->GetGUID(); - if (Player* leader = ObjectAccessor::FindPlayer(grp->GetLeaderGUID())) - leader->GetSession()->SendLfgOfferContinue(GetDungeon(gguid, false)); - } -} - -/** - Checks que main queue to try to form a Lfg group. Returns first match found (if any) - - @param[in] check List of guids trying to match with other groups - @param[in] all List of all other guids in main queue to match against - @return Pointer to proposal, if match is found -*/ -LfgProposal* LFGMgr::FindNewGroups(LfgGuidList& check, LfgGuidList& all) -{ - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::FindNewGroup: (%s) - all(%s)", ConcatenateGuids(check).c_str(), ConcatenateGuids(all).c_str()); - - LfgProposal* pProposal = NULL; - if (check.empty() || check.size() > MAXGROUPSIZE || !CheckCompatibility(check, pProposal)) - return NULL; - - // Try to match with queued groups - while (!pProposal && !all.empty()) - { - check.push_back(all.front()); - all.pop_front(); - pProposal = FindNewGroups(check, all); - check.pop_back(); - } - return pProposal; -} - -/** - Check compatibilities between groups - - @param[in] check List of guids to check compatibilities - @param[out] pProposal Proposal found if groups are compatibles and Match - @return true if group are compatibles -*/ -bool LFGMgr::CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal) -{ - if (pProposal) // Do not check anything if we already have a proposal - return false; - - std::string strGuids = ConcatenateGuids(check); - - if (check.size() > MAXGROUPSIZE || check.empty()) - { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s): Size wrong - Not compatibles", strGuids.c_str()); - return false; - } - - if (check.size() == 1 && IS_PLAYER_GUID(check.front())) // Player joining dungeon... compatible - return true; - - // Previously cached? - LfgAnswer answer = GetCompatibles(strGuids); - if (answer != LFG_ANSWER_PENDING) - { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) compatibles (cached): %d", strGuids.c_str(), answer); - return bool(answer); - } - - // Check all but new compatiblitity - if (check.size() > 2) - { - uint64 frontGuid = check.front(); - check.pop_front(); - - // Check all-but-new compatibilities (New, A, B, C, D) --> check(A, B, C, D) - if (!CheckCompatibility(check, pProposal)) // Group not compatible - { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) not compatibles (%s not compatibles)", strGuids.c_str(), ConcatenateGuids(check).c_str()); - SetCompatibles(strGuids, false); - return false; - } - check.push_front(frontGuid); - // all-but-new compatibles, now check with new - } - - uint8 numPlayers = 0; - uint8 numLfgGroups = 0; - uint32 groupLowGuid = 0; - LfgQueueInfoMap pqInfoMap; - for (LfgGuidList::const_iterator it = check.begin(); it != check.end() && numLfgGroups < 2 && numPlayers <= MAXGROUPSIZE; ++it) - { - uint64 guid = (*it); - LfgQueueInfoMap::iterator itQueue = m_QueueInfoMap.find(guid); - if (itQueue == m_QueueInfoMap.end() || GetState(guid) != LFG_STATE_QUEUED) - { - sLog->outError(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: [" UI64FMTD "] is not queued but listed as queued!", (*it)); - RemoveFromQueue(guid); - return false; - } - pqInfoMap[guid] = itQueue->second; - numPlayers += itQueue->second->roles.size(); - - if (IS_GROUP(guid)) - { - uint32 lowGuid = GUID_LOPART(guid); - if (Group* grp = sGroupMgr->GetGroupByGUID(lowGuid)) - if (grp->isLFGGroup()) - { - if (!numLfgGroups) - groupLowGuid = lowGuid; - ++numLfgGroups; - } - } - } - - if (check.size() == 1 && numPlayers != MAXGROUPSIZE) // Single group with less than MAXGROUPSIZE - Compatibles - return true; - - // Do not match - groups already in a lfgDungeon or too much players - if (numLfgGroups > 1 || numPlayers > MAXGROUPSIZE) - { - SetCompatibles(strGuids, false); - if (numLfgGroups > 1) - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) More than one Lfggroup (%u)", strGuids.c_str(), numLfgGroups); - else - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Too much players (%u)", strGuids.c_str(), numPlayers); - return false; - } - - // ----- Player checks ----- - LfgRolesMap rolesMap; - uint64 leader = 0; - for (LfgQueueInfoMap::const_iterator it = pqInfoMap.begin(); it != pqInfoMap.end(); ++it) - { - for (LfgRolesMap::const_iterator itRoles = it->second->roles.begin(); itRoles != it->second->roles.end(); ++itRoles) - { - // Assign new leader - if (itRoles->second & ROLE_LEADER && (!leader || urand(0, 1))) - leader = itRoles->first; - - rolesMap[itRoles->first] = itRoles->second; - } - } - - if (rolesMap.size() != numPlayers) // Player in multiples queues! - return false; - - PlayerSet players; - for (LfgRolesMap::const_iterator it = rolesMap.begin(); it != rolesMap.end(); ++it) - { - Player* player = ObjectAccessor::FindPlayer(it->first); - if (!player) - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Warning! [" UI64FMTD "] offline! Marking as not compatibles!", strGuids.c_str(), it->first); - else - { - for (PlayerSet::const_iterator itPlayer = players.begin(); itPlayer != players.end() && player; ++itPlayer) - { - // Do not form a group with ignoring candidates - if (player->GetSocial()->HasIgnore((*itPlayer)->GetGUIDLow()) || (*itPlayer)->GetSocial()->HasIgnore(player->GetGUIDLow())) - { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Players [" UI64FMTD "] and [" UI64FMTD "] ignoring", strGuids.c_str(), (*itPlayer)->GetGUID(), player->GetGUID()); - player = NULL; - } - } - if (player) - players.insert(player); - } - } - - // if we dont have the same ammount of players then we have self ignoring candidates or different faction groups - // otherwise check if roles are compatible - if (players.size() != numPlayers || !CheckGroupRoles(rolesMap)) - { - if (players.size() == numPlayers) - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Roles not compatible", strGuids.c_str()); - SetCompatibles(strGuids, false); - return false; - } - - // ----- Selected Dungeon checks ----- - // Check if there are any compatible dungeon from the selected dungeons - LfgDungeonSet compatibleDungeons; - - LfgQueueInfoMap::const_iterator itFirst = pqInfoMap.begin(); - for (LfgDungeonSet::const_iterator itDungeon = itFirst->second->dungeons.begin(); itDungeon != itFirst->second->dungeons.end(); ++itDungeon) - { - LfgQueueInfoMap::const_iterator itOther = itFirst; - ++itOther; - while (itOther != pqInfoMap.end() && itOther->second->dungeons.find(*itDungeon) != itOther->second->dungeons.end()) - ++itOther; - - if (itOther == pqInfoMap.end()) - compatibleDungeons.insert(*itDungeon); - } - LfgLockPartyMap lockMap; - GetCompatibleDungeons(compatibleDungeons, players, lockMap); - - if (compatibleDungeons.empty()) - { - SetCompatibles(strGuids, false); - return false; - } - SetCompatibles(strGuids, true); - - // ----- Group is compatible, if we have MAXGROUPSIZE members then match is found - if (numPlayers != MAXGROUPSIZE) - { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) Compatibles but not match. Players(%u)", strGuids.c_str(), numPlayers); - uint8 Tanks_Needed = LFG_TANKS_NEEDED; - uint8 Healers_Needed = LFG_HEALERS_NEEDED; - uint8 Dps_Needed = LFG_DPS_NEEDED; - for (LfgQueueInfoMap::const_iterator itQueue = pqInfoMap.begin(); itQueue != pqInfoMap.end(); ++itQueue) - { - LfgQueueInfo* queue = itQueue->second; - for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) - { - uint8 roles = itPlayer->second; - if ((roles & ROLE_TANK) && Tanks_Needed > 0) - --Tanks_Needed; - else if ((roles & ROLE_HEALER) && Healers_Needed > 0) - --Healers_Needed; - else if ((roles & ROLE_DAMAGE) && Dps_Needed > 0) - --Dps_Needed; - } - } - for (PlayerSet::const_iterator itPlayers = players.begin(); itPlayers != players.end(); ++itPlayers) - { - for (LfgQueueInfoMap::const_iterator itQueue = pqInfoMap.begin(); itQueue != pqInfoMap.end(); ++itQueue) - { - LfgQueueInfo* queue = itQueue->second; - if (!queue) - continue; - - for (LfgRolesMap::const_iterator itPlayer = queue->roles.begin(); itPlayer != queue->roles.end(); ++itPlayer) - { - if (*itPlayers == ObjectAccessor::FindPlayer(itPlayer->first)) - { - queue->tanks = Tanks_Needed; - queue->healers = Healers_Needed; - queue->dps = Dps_Needed; - } - } - } - } - return true; - } - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::CheckCompatibility: (%s) MATCH! Group formed", strGuids.c_str()); - - // GROUP FORMED! - // TODO - Improve algorithm to select proper group based on Item Level - // Do not match bad tank and bad healer on same group - - // Select a random dungeon from the compatible list - // TODO - Select the dungeon based on group item Level, not just random - // Create a new proposal - pProposal = new LfgProposal(Trinity::Containers::SelectRandomContainerElement(compatibleDungeons)); - pProposal->cancelTime = time_t(time(NULL)) + LFG_TIME_PROPOSAL; - pProposal->state = LFG_PROPOSAL_INITIATING; - pProposal->queues = check; - pProposal->groupLowGuid = groupLowGuid; - - // Assign new roles to players and assign new leader - PlayerSet::const_iterator itPlayers = players.begin(); - if (!leader) - { - uint8 pos = uint8(urand(0, players.size() - 1)); - for (uint8 i = 0; i < pos; ++i) - ++itPlayers; - leader = (*itPlayers)->GetGUID(); - } - pProposal->leader = leader; - - uint8 numAccept = 0; - for (itPlayers = players.begin(); itPlayers != players.end(); ++itPlayers) - { - uint64 guid = (*itPlayers)->GetGUID(); - LfgProposalPlayer* ppPlayer = new LfgProposalPlayer(); - if (Group* grp = (*itPlayers)->GetGroup()) - { - ppPlayer->groupLowGuid = grp->GetLowGUID(); - if (grp->isLFGGroup()) // Player from existing group, autoaccept - { - ppPlayer->accept = LFG_ANSWER_AGREE; - ++numAccept; - } - } - ppPlayer->role = rolesMap[guid]; - pProposal->players[guid] = ppPlayer; - } - if (numAccept == MAXGROUPSIZE) - pProposal->state = LFG_PROPOSAL_SUCCESS; - - return true; -} - -/** Update the Role check info with the player selected role. @param[in] grp Group guid to update rolecheck @param[in] guid Player guid (0 = rolecheck failed) @param[in] roles Player selected roles */ -void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* = ROLE_NONE */) +void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* = PLAYER_ROLE_NONE */) { if (!gguid) return; @@ -1116,171 +824,91 @@ void LFGMgr::UpdateRoleCheck(uint64 gguid, uint64 guid /* = 0 */, uint8 roles /* if (itRoleCheck == m_RoleChecks.end()) return; - LfgRoleCheck* roleCheck = itRoleCheck->second; - bool sendRoleChosen = roleCheck->state != LFG_ROLECHECK_DEFAULT && guid; + LfgRoleCheck& roleCheck = itRoleCheck->second; + bool sendRoleChosen = roleCheck.state != LFG_ROLECHECK_DEFAULT && guid; if (!guid) - roleCheck->state = LFG_ROLECHECK_ABORTED; - else if (roles < ROLE_TANK) // Player selected no role. - roleCheck->state = LFG_ROLECHECK_NO_ROLE; + roleCheck.state = LFG_ROLECHECK_ABORTED; + else if (roles < PLAYER_ROLE_TANK) // Player selected no role. + roleCheck.state = LFG_ROLECHECK_NO_ROLE; else { - roleCheck->roles[guid] = roles; + roleCheck.roles[guid] = roles; // Check if all players have selected a role - LfgRolesMap::const_iterator itRoles = roleCheck->roles.begin(); - while (itRoles != roleCheck->roles.end() && itRoles->second != ROLE_NONE) + LfgRolesMap::const_iterator itRoles = roleCheck.roles.begin(); + while (itRoles != roleCheck.roles.end() && itRoles->second != PLAYER_ROLE_NONE) ++itRoles; - if (itRoles == roleCheck->roles.end()) + if (itRoles == roleCheck.roles.end()) { // use temporal var to check roles, CheckGroupRoles modifies the roles - check_roles = roleCheck->roles; - roleCheck->state = CheckGroupRoles(check_roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES; + check_roles = roleCheck.roles; + roleCheck.state = CheckGroupRoles(check_roles) ? LFG_ROLECHECK_FINISHED : LFG_ROLECHECK_WRONG_ROLES; } } - uint8 team = 0; LfgDungeonSet dungeons; - if (roleCheck->rDungeonId) - dungeons.insert(roleCheck->rDungeonId); + if (roleCheck.rDungeonId) + dungeons.insert(roleCheck.rDungeonId); else - dungeons = roleCheck->dungeons; + dungeons = roleCheck.dungeons; - LfgJoinResultData joinData = LfgJoinResultData(LFG_JOIN_FAILED, roleCheck->state); - for (LfgRolesMap::const_iterator it = roleCheck->roles.begin(); it != roleCheck->roles.end(); ++it) + LfgJoinResultData joinData = LfgJoinResultData(LFG_JOIN_FAILED, roleCheck.state); + for (LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it) { uint64 pguid = it->first; - Player* plrg = ObjectAccessor::FindPlayer(pguid); - if (!plrg) - { - if (roleCheck->state == LFG_ROLECHECK_FINISHED) - SetState(pguid, LFG_STATE_QUEUED); - else if (roleCheck->state != LFG_ROLECHECK_INITIALITING) - ClearState(pguid); - continue; - } - team = uint8(plrg->GetTeam()); if (!sendRoleChosen) - plrg->GetSession()->SendLfgRoleChosen(guid, roles); - plrg->GetSession()->SendLfgRoleCheckUpdate(roleCheck); - switch (roleCheck->state) + SendLfgRoleChosen(pguid, guid, roles); + + SendLfgRoleCheckUpdate(pguid, roleCheck); + switch (roleCheck.state) { case LFG_ROLECHECK_INITIALITING: continue; case LFG_ROLECHECK_FINISHED: SetState(pguid, LFG_STATE_QUEUED); - plrg->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, GetComment(pguid))); + SetRoles(pguid, it->second); + SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, dungeons, GetComment(pguid))); break; default: - if (roleCheck->leader == pguid) - plrg->GetSession()->SendLfgJoinResult(joinData); - plrg->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_ROLECHECK_FAILED)); - ClearState(pguid); + if (roleCheck.leader == pguid) + SendLfgJoinResult(pguid, joinData); + SendLfgUpdateParty(pguid, LfgUpdateData(LFG_UPDATETYPE_ROLECHECK_FAILED)); + ClearState(pguid, "Role check Failed"); break; } } - if (roleCheck->state == LFG_ROLECHECK_FINISHED) + if (roleCheck.state == LFG_ROLECHECK_FINISHED) { SetState(gguid, LFG_STATE_QUEUED); - LfgQueueInfo* pqInfo = new LfgQueueInfo(); - pqInfo->joinTime = time_t(time(NULL)); - pqInfo->roles = roleCheck->roles; - pqInfo->dungeons = roleCheck->dungeons; - - // Set queue roles needed - As we are using check_roles will not have more that 1 tank, 1 healer, 3 dps - for (LfgRolesMap::const_iterator it = check_roles.begin(); it != check_roles.end(); ++it) - { - uint8 roles2 = it->second; - if (roles2 & ROLE_TANK) - --pqInfo->tanks; - else if (roles2 & ROLE_HEALER) - --pqInfo->healers; - else - --pqInfo->dps; - } - - m_QueueInfoMap[gguid] = pqInfo; - if (GetState(gguid) != LFG_STATE_NONE) - { - LfgGuidList& currentQueue = m_currentQueue[team]; - currentQueue.push_front(gguid); - } - AddToQueue(gguid, team); - } - - if (roleCheck->state != LFG_ROLECHECK_INITIALITING) - { - if (roleCheck->state != LFG_ROLECHECK_FINISHED) - RestoreState(gguid); - delete roleCheck; + LfgQueue& queue = GetQueue(gguid); + queue.AddQueueData(gguid, time_t(time(NULL)), roleCheck.dungeons, roleCheck.roles); m_RoleChecks.erase(itRoleCheck); } -} - -/** - Remove from cached compatible dungeons any entry that contains the given guid - - @param[in] guid Guid to remove from compatible cache -*/ -void LFGMgr::RemoveFromCompatibles(uint64 guid) -{ - std::stringstream out; - out << guid; - std::string strGuid = out.str(); - - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveFromCompatibles: Removing [" UI64FMTD "]", guid); - for (LfgCompatibleMap::iterator itNext = m_CompatibleMap.begin(); itNext != m_CompatibleMap.end();) + else if (roleCheck.state != LFG_ROLECHECK_INITIALITING) { - LfgCompatibleMap::iterator it = itNext++; - if (it->first.find(strGuid) != std::string::npos) // Found, remove it - m_CompatibleMap.erase(it); + RestoreState(gguid, "Rolecheck Failed"); + m_RoleChecks.erase(itRoleCheck); } } /** - Stores the compatibility of a list of guids - - @param[in] key String concatenation of guids (| used as separator) - @param[in] compatibles Compatibles or not -*/ -void LFGMgr::SetCompatibles(std::string key, bool compatibles) -{ - m_CompatibleMap[key] = LfgAnswer(compatibles); -} - -/** - Get the compatibility of a group of guids - - @param[in] key String concatenation of guids (| used as separator) - @return 1 (Compatibles), 0 (Not compatibles), -1 (Not set) -*/ -LfgAnswer LFGMgr::GetCompatibles(std::string key) -{ - LfgAnswer answer = LFG_ANSWER_PENDING; - LfgCompatibleMap::iterator it = m_CompatibleMap.find(key); - if (it != m_CompatibleMap.end()) - answer = it->second; - - return answer; -} - -/** Given a list of dungeons remove the dungeons players have restrictions. @param[in, out] dungeons Dungeons to check restrictions @param[in] players Set of players to check their dungeon restrictions @param[out] lockMap Map of players Lock status info of given dungeons (Empty if dungeons is not empty) */ -void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, const PlayerSet& players, LfgLockPartyMap& lockMap) +void LFGMgr::GetCompatibleDungeons(LfgDungeonSet& dungeons, const LfgGuidSet& players, LfgLockPartyMap& lockMap) { lockMap.clear(); - for (PlayerSet::const_iterator it = players.begin(); it != players.end() && !dungeons.empty(); ++it) + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end() && !dungeons.empty(); ++it) { - uint64 guid = (*it)->GetGUID(); - LfgLockMap cachedLockMap = GetLockedDungeons(guid); + uint64 guid = (*it); + LfgLockMap const& cachedLockMap = GetLockedDungeons(guid); for (LfgLockMap::const_iterator it2 = cachedLockMap.begin(); it2 != cachedLockMap.end() && !dungeons.empty(); ++it2) { uint32 dungeonId = (it2->first & 0x00FFFFFF); // Compare dungeon ids @@ -1314,21 +942,21 @@ bool LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true if (removeLeaderFlag) for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it) - it->second &= ~ROLE_LEADER; + it->second &= ~PLAYER_ROLE_LEADER; for (LfgRolesMap::iterator it = groles.begin(); it != groles.end(); ++it) { - if (it->second == ROLE_NONE) + if (it->second == PLAYER_ROLE_NONE) return false; - if (it->second & ROLE_TANK) + if (it->second & PLAYER_ROLE_TANK) { - if (it->second != ROLE_TANK) + if (it->second != PLAYER_ROLE_TANK) { - it->second -= ROLE_TANK; + it->second -= PLAYER_ROLE_TANK; if (CheckGroupRoles(groles, false)) return true; - it->second += ROLE_TANK; + it->second += PLAYER_ROLE_TANK; } else if (tank == LFG_TANKS_NEEDED) return false; @@ -1336,14 +964,14 @@ bool LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true tank++; } - if (it->second & ROLE_HEALER) + if (it->second & PLAYER_ROLE_HEALER) { - if (it->second != ROLE_HEALER) + if (it->second != PLAYER_ROLE_HEALER) { - it->second -= ROLE_HEALER; + it->second -= PLAYER_ROLE_HEALER; if (CheckGroupRoles(groles, false)) return true; - it->second += ROLE_HEALER; + it->second += PLAYER_ROLE_HEALER; } else if (healer == LFG_HEALERS_NEEDED) return false; @@ -1351,14 +979,14 @@ bool LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true healer++; } - if (it->second & ROLE_DAMAGE) + if (it->second & PLAYER_ROLE_DAMAGE) { - if (it->second != ROLE_DAMAGE) + if (it->second != PLAYER_ROLE_DAMAGE) { - it->second -= ROLE_DAMAGE; + it->second -= PLAYER_ROLE_DAMAGE; if (CheckGroupRoles(groles, false)) return true; - it->second += ROLE_DAMAGE; + it->second += PLAYER_ROLE_DAMAGE; } else if (damage == LFG_DPS_NEEDED) return false; @@ -1370,6 +998,84 @@ bool LFGMgr::CheckGroupRoles(LfgRolesMap& groles, bool removeLeaderFlag /*= true } /** + Makes a new group given a proposal + @param[in] proposal Proposal to get info from +*/ +void LFGMgr::MakeNewGroup(const LfgProposal& proposal) +{ + LfgGuidList players; + LfgGuidList playersToTeleport; + + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + { + uint64 guid = it->first; + if (guid == proposal.leader) + players.push_front(guid); + else + players.push_back(guid); + + if (proposal.isNew || GetGroup(guid) != proposal.group) + playersToTeleport.push_back(guid); + } + + // Set the dungeon difficulty + LFGDungeonData const* dungeon = GetLFGDungeon(proposal.dungeonId); + ASSERT(dungeon); + + Group* grp = proposal.group ? sGroupMgr->GetGroupByGUID(GUID_LOPART(proposal.group)) : NULL; + for (LfgGuidList::const_iterator it = players.begin(); it != players.end(); ++it) + { + uint64 pguid = (*it); + Player* player = ObjectAccessor::FindPlayer(pguid); + if (!player) + continue; + + Group* group = player->GetGroup(); + if (group && group != grp) + player->RemoveFromGroup(); + + if (!grp) + { + grp = new Group(); + grp->ConvertToLFG(); + grp->Create(player); + uint64 gguid = grp->GetGUID(); + SetState(gguid, LFG_STATE_PROPOSAL); + sGroupMgr->AddGroup(grp); + } + else if (group != grp) + grp->AddMember(player); + + grp->SetLfgRoles(pguid, proposal.players.find(pguid)->second.role); + + // Add the cooldown spell if queued for a random dungeon + if (dungeon->type == LFG_TYPE_RANDOM) + player->CastSpell(player, LFG_SPELL_DUNGEON_COOLDOWN, false); + } + + grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty)); + uint64 gguid = grp->GetGUID(); + SetDungeon(gguid, dungeon->Entry()); + SetState(gguid, LFG_STATE_DUNGEON); + + _SaveToDB(gguid, grp->GetDbStoreId()); + + // Teleport Player + for (LfgGuidList::const_iterator it = playersToTeleport.begin(); it != playersToTeleport.end(); ++it) + if (Player* player = ObjectAccessor::FindPlayer(*it)) + TeleportPlayer(player, false); + + // Update group info + grp->SendUpdate(); +} + +uint32 LFGMgr::AddProposal(LfgProposal const& proposal) +{ + m_Proposals[++m_lfgProposalId] = proposal; + return m_lfgProposalId; +} + +/** Update Proposal info with player answer @param[in] proposalId Proposal id to be updated @@ -1382,15 +1088,17 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) LfgProposalMap::iterator itProposal = m_Proposals.find(proposalId); if (itProposal == m_Proposals.end()) return; - LfgProposal* pProposal = itProposal->second; + + LfgProposal& proposal = itProposal->second; // Check if proposal have the current player - LfgProposalPlayerMap::iterator itProposalPlayer = pProposal->players.find(guid); - if (itProposalPlayer == pProposal->players.end()) + LfgProposalPlayerMap::iterator itProposalPlayer = proposal.players.find(guid); + if (itProposalPlayer == proposal.players.end()) return; - LfgProposalPlayer* ppPlayer = itProposalPlayer->second; - ppPlayer->accept = LfgAnswer(accept); + LfgProposalPlayer& player = itProposalPlayer->second; + player.accept = LfgAnswer(accept); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::UpdateProposal: Player [" UI64FMTD "] of proposal %u selected: %u", guid, proposalId, accept); if (!accept) { @@ -1398,157 +1106,80 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) return; } - LfgPlayerList players; - LfgPlayerList playersToTeleport; - // check if all have answered and reorder players (leader first) bool allAnswered = true; - for (LfgProposalPlayerMap::const_iterator itPlayers = pProposal->players.begin(); itPlayers != pProposal->players.end(); ++itPlayers) - { - if (Player* player = ObjectAccessor::FindPlayer(itPlayers->first)) - { - if (itPlayers->first == pProposal->leader) - players.push_front(player); - else - players.push_back(player); - - // Only teleport new players - Group* grp = player->GetGroup(); - uint64 gguid = grp ? grp->GetGUID() : 0; - if (!gguid || !grp->isLFGGroup() || GetState(gguid) == LFG_STATE_FINISHED_DUNGEON) - playersToTeleport.push_back(player); - } - - if (itPlayers->second->accept != LFG_ANSWER_AGREE) // No answer (-1) or not accepted (0) + for (LfgProposalPlayerMap::const_iterator itPlayers = proposal.players.begin(); itPlayers != proposal.players.end(); ++itPlayers) + if (itPlayers->second.accept != LFG_ANSWER_AGREE) // No answer (-1) or not accepted (0) allAnswered = false; - } if (!allAnswered) { - for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) - (*it)->GetSession()->SendLfgUpdateProposal(proposalId, pProposal); - } - else - { - bool sendUpdate = pProposal->state != LFG_PROPOSAL_SUCCESS; - pProposal->state = LFG_PROPOSAL_SUCCESS; - time_t joinTime = time_t(time(NULL)); - std::map<uint64, int32> waitTimesMap; - // Save wait times before redoing groups - for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { - LfgProposalPlayer* player = pProposal->players[(*it)->GetGUID()]; - uint32 lowgroupguid = (*it)->GetGroup() ? (*it)->GetGroup()->GetLowGUID() : 0; - if (player->groupLowGuid != lowgroupguid) - sLog->outError(LOG_FILTER_LFG, "LFGMgr::UpdateProposal: [" UI64FMTD "] group mismatch: actual (%u) - queued (%u)", (*it)->GetGUID(), lowgroupguid, player->groupLowGuid); - - uint64 guid2 = player->groupLowGuid ? MAKE_NEW_GUID(player->groupLowGuid, 0, HIGHGUID_GROUP) : (*it)->GetGUID(); - LfgQueueInfoMap::iterator itQueue = m_QueueInfoMap.find(guid2); - if (itQueue == m_QueueInfoMap.end()) - { - sLog->outError(LOG_FILTER_LFG, "LFGMgr::UpdateProposal: Queue info for guid [" UI64FMTD "] not found!", guid); - waitTimesMap[(*it)->GetGUID()] = -1; - } - else - waitTimesMap[(*it)->GetGUID()] = int32(joinTime - itQueue->second->joinTime); + uint64 guid = it->first; + SendLfgUpdateProposal(guid, proposalId, proposal); } + return; + } - // Set the dungeon difficulty - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(pProposal->dungeonId); - ASSERT(dungeon); - - // Create a new group (if needed) - LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_GROUP_FOUND); - Group* grp = pProposal->groupLowGuid ? sGroupMgr->GetGroupByGUID(pProposal->groupLowGuid) : NULL; - for (LfgPlayerList::const_iterator it = players.begin(); it != players.end(); ++it) - { - Player* player = (*it); - uint64 pguid = player->GetGUID(); - Group* group = player->GetGroup(); - if (sendUpdate) - player->GetSession()->SendLfgUpdateProposal(proposalId, pProposal); - - if (group) - { - player->GetSession()->SendLfgUpdateParty(updateData); - if (group != grp) - player->RemoveFromGroup(); - } - else - player->GetSession()->SendLfgUpdatePlayer(updateData); - - if (!grp) - { - grp = new Group(); - grp->Create(player); - grp->ConvertToLFG(); - uint64 gguid = grp->GetGUID(); - SetState(gguid, LFG_STATE_PROPOSAL); - sGroupMgr->AddGroup(grp); - } - else if (group != grp) - grp->AddMember(player); + bool sendUpdate = proposal.state != LFG_PROPOSAL_SUCCESS; + proposal.state = LFG_PROPOSAL_SUCCESS; + time_t joinTime = time_t(time(NULL)); - // Update timers - uint8 role = GetRoles(pguid); - role &= ~ROLE_LEADER; - switch (role) - { - case ROLE_DAMAGE: - { - uint32 old_number = m_NumWaitTimeDps++; - m_WaitTimeDps = int32((m_WaitTimeDps * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeDps); - break; - } - case ROLE_HEALER: - { - uint32 old_number = m_NumWaitTimeHealer++; - m_WaitTimeHealer = int32((m_WaitTimeHealer * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeHealer); - break; - } - case ROLE_TANK: - { - uint32 old_number = m_NumWaitTimeTank++; - m_WaitTimeTank = int32((m_WaitTimeTank * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeTank); - break; - } - default: - { - uint32 old_number = m_NumWaitTimeAvg++; - m_WaitTimeAvg = int32((m_WaitTimeAvg * old_number + waitTimesMap[player->GetGUID()]) / m_NumWaitTimeAvg); - break; - } - } - - m_teleport.push_back(pguid); - grp->SetLfgRoles(pguid, pProposal->players[pguid]->role); - SetState(pguid, LFG_STATE_DUNGEON); + LfgQueue& queue = GetQueue(guid); + LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_GROUP_FOUND); + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + { + uint64 pguid = it->first; + uint64 gguid = it->second.group; + uint32 dungeonId = (*GetSelectedDungeons(pguid).begin()); + int32 waitTime = -1; + if (sendUpdate) + SendLfgUpdateProposal(pguid, proposalId, proposal); - // Add the cooldown spell if queued for a random dungeon - if (dungeon->type == LFG_TYPE_RANDOM) - player->CastSpell(player, LFG_SPELL_DUNGEON_COOLDOWN, false); + if (gguid) + { + waitTime = int32((joinTime - queue.GetJoinTime(gguid)) / IN_MILLISECONDS); + SendLfgUpdateParty(pguid, updateData); + } + else + { + waitTime = int32((joinTime - queue.GetJoinTime(pguid)) / IN_MILLISECONDS); + SendLfgUpdatePlayer(pguid, updateData); + } + updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE; + SendLfgUpdatePlayer(pguid, updateData); + SendLfgUpdateParty(pguid, updateData); + + // Update timers + uint8 role = GetRoles(pguid); + role &= ~PLAYER_ROLE_LEADER; + switch (role) + { + case PLAYER_ROLE_DAMAGE: + queue.UpdateWaitTimeDps(waitTime, dungeonId); + break; + case PLAYER_ROLE_HEALER: + queue.UpdateWaitTimeHealer(waitTime, dungeonId); + break; + case PLAYER_ROLE_TANK: + queue.UpdateWaitTimeTank(waitTime, dungeonId); + break; + default: + queue.UpdateWaitTimeAvg(waitTime, dungeonId); + break; } - grp->SetDungeonDifficulty(Difficulty(dungeon->difficulty)); - uint64 gguid = grp->GetGUID(); - SetDungeon(gguid, dungeon->Entry()); - SetState(gguid, LFG_STATE_DUNGEON); - _SaveToDB(gguid, grp->GetDbStoreId()); - - // Remove players/groups from Queue - for (LfgGuidList::const_iterator it = pProposal->queues.begin(); it != pProposal->queues.end(); ++it) - RemoveFromQueue(*it); - - // Teleport Player - for (LfgPlayerList::const_iterator it = playersToTeleport.begin(); it != playersToTeleport.end(); ++it) - TeleportPlayer(*it, false); + m_teleport.push_back(pguid); + SetState(pguid, LFG_STATE_DUNGEON); + } - // Update group info - grp->SendUpdate(); + // Remove players/groups from Queue + for (LfgGuidList::const_iterator it = proposal.queues.begin(); it != proposal.queues.end(); ++it) + queue.RemoveFromQueue(*it); - delete pProposal; - m_Proposals.erase(itProposal); - } + MakeNewGroup(proposal); + m_Proposals.erase(itProposal); } /** @@ -1559,51 +1190,44 @@ void LFGMgr::UpdateProposal(uint32 proposalId, uint64 guid, bool accept) */ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType type) { - LfgProposal* pProposal = itProposal->second; - pProposal->state = LFG_PROPOSAL_FAILED; + LfgProposal& proposal = itProposal->second; + proposal.state = LFG_PROPOSAL_FAILED; sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: Proposal %u, state FAILED, UpdateType %u", itProposal->first, type); // Mark all people that didn't answered as no accept if (type == LFG_UPDATETYPE_PROPOSAL_FAILED) - for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) - if (it->second->accept == LFG_ANSWER_PENDING) - it->second->accept = LFG_ANSWER_DENY; + for (LfgProposalPlayerMap::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) + if (it->second.accept == LFG_ANSWER_PENDING) + it->second.accept = LFG_ANSWER_DENY; // Mark players/groups to be removed LfgGuidSet toRemove; - for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) + for (LfgProposalPlayerMap::iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { - if (it->second->accept == LFG_ANSWER_AGREE) + if (it->second.accept == LFG_ANSWER_AGREE) continue; - uint64 guid = it->second->groupLowGuid ? MAKE_NEW_GUID(it->second->groupLowGuid, 0, HIGHGUID_GROUP) : it->first; + uint64 guid = it->second.group ? it->second.group : it->first; // Player didn't accept or still pending when no secs left - if (it->second->accept == LFG_ANSWER_DENY || type == LFG_UPDATETYPE_PROPOSAL_FAILED) + if (it->second.accept == LFG_ANSWER_DENY || type == LFG_UPDATETYPE_PROPOSAL_FAILED) { - it->second->accept = LFG_ANSWER_DENY; + it->second.accept = LFG_ANSWER_DENY; toRemove.insert(guid); } } - uint8 team = 0; // Notify players - for (LfgProposalPlayerMap::const_iterator it = pProposal->players.begin(); it != pProposal->players.end(); ++it) + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { - Player* player = ObjectAccessor::FindPlayer(it->first); - if (!player) - continue; - - team = uint8(player->GetTeam()); - player->GetSession()->SendLfgUpdateProposal(itProposal->first, pProposal); + uint64 guid = it->first; + uint64 gguid = it->second.group ? it->second.group : guid; - Group* grp = player->GetGroup(); - uint64 guid = player->GetGUID(); - uint64 gguid = it->second->groupLowGuid ? MAKE_NEW_GUID(it->second->groupLowGuid, 0, HIGHGUID_GROUP) : guid; + SendLfgUpdateProposal(guid, itProposal->first, proposal); if (toRemove.find(gguid) != toRemove.end()) // Didn't accept or in same group that someone that didn't accept { LfgUpdateData updateData; - if (it->second->accept == LFG_ANSWER_DENY) + if (it->second.accept == LFG_ANSWER_DENY) { updateData.updateType = type; sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: [" UI64FMTD "] didn't accept. Removing from queue and compatible cache", guid); @@ -1613,126 +1237,110 @@ void LFGMgr::RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType t updateData.updateType = LFG_UPDATETYPE_REMOVED_FROM_QUEUE; sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: [" UI64FMTD "] in same group that someone that didn't accept. Removing from queue and compatible cache", guid); } - ClearState(guid); - if (grp) + ClearState(guid, "Proposal Fail (didn't accepted or in group with someone that didn't accept"); + if (gguid != guid) { - RestoreState(gguid); - player->GetSession()->SendLfgUpdateParty(updateData); + RestoreState(gguid, "Proposal Fail (someone in group didn't accepted)"); + SendLfgUpdateParty(guid, updateData); } else - player->GetSession()->SendLfgUpdatePlayer(updateData); + SendLfgUpdatePlayer(guid, updateData); } else { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RemoveProposal: Readding [" UI64FMTD "] to queue.", guid); SetState(guid, LFG_STATE_QUEUED); - if (grp) + if (gguid != guid) { SetState(gguid, LFG_STATE_QUEUED); - player->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); + SendLfgUpdateParty(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); } else - player->GetSession()->SendLfgUpdatePlayer(LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); + SendLfgUpdatePlayer(guid, LfgUpdateData(LFG_UPDATETYPE_ADDED_TO_QUEUE, GetSelectedDungeons(guid), GetComment(guid))); } } + LfgQueue& queue = GetQueue(proposal.players.begin()->first); // Remove players/groups from queue for (LfgGuidSet::const_iterator it = toRemove.begin(); it != toRemove.end(); ++it) { uint64 guid = *it; - RemoveFromQueue(guid); - pProposal->queues.remove(guid); + queue.RemoveFromQueue(guid); + proposal.queues.remove(guid); } // Readd to queue - for (LfgGuidList::const_iterator it = pProposal->queues.begin(); it != pProposal->queues.end(); ++it) + for (LfgGuidList::const_iterator it = proposal.queues.begin(); it != proposal.queues.end(); ++it) { uint64 guid = *it; - LfgGuidList& currentQueue = m_currentQueue[team]; - currentQueue.push_front(guid); //Add GUID for high priority - AddToQueue(guid, team); //We have to add each GUID in newQueue to check for a new groups + queue.AddToQueue(guid); } - delete pProposal; m_Proposals.erase(itProposal); } /** Initialize a boot kick vote - @param[in] grp Group the vote kicks belongs to + @param[in] gguid Group the vote kicks belongs to @param[in] kicker Kicker guid @param[in] victim Victim guid @param[in] reason Kick reason */ -void LFGMgr::InitBoot(Group* grp, uint64 kicker, uint64 victim, std::string reason) +void LFGMgr::InitBoot(uint64 gguid, uint64 kicker, uint64 victim, std::string const& reason) { - if (!grp) - return; - - uint64 gguid = grp->GetGUID(); SetState(gguid, LFG_STATE_BOOT); - LfgPlayerBoot* pBoot = new LfgPlayerBoot(); - pBoot->inProgress = true; - pBoot->cancelTime = time_t(time(NULL)) + LFG_TIME_BOOT; - pBoot->reason = reason; - pBoot->victim = victim; - pBoot->votedNeeded = GetVotesNeeded(gguid); + LfgPlayerBoot& boot = m_Boots[gguid]; + boot.inProgress = true; + boot.cancelTime = time_t(time(NULL)) + LFG_TIME_BOOT; + boot.reason = reason; + boot.victim = victim; + + LfgGuidSet const& players = GetPlayers(gguid); // Set votes - for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + for (LfgGuidSet::const_iterator itr = players.begin(); itr != players.end(); ++itr) { - if (Player* plrg = itr->getSource()) - { - uint64 guid = plrg->GetGUID(); - SetState(guid, LFG_STATE_BOOT); - if (guid == victim) - pBoot->votes[victim] = LFG_ANSWER_DENY; // Victim auto vote NO - else if (guid == kicker) - pBoot->votes[kicker] = LFG_ANSWER_AGREE; // Kicker auto vote YES - else - { - pBoot->votes[guid] = LFG_ANSWER_PENDING; // Other members need to vote - plrg->GetSession()->SendLfgBootPlayer(pBoot); - } - } + uint64 guid = (*itr); + SetState(guid, LFG_STATE_BOOT); + boot.votes[guid] = LFG_ANSWER_PENDING; } - m_Boots[grp->GetLowGUID()] = pBoot; + boot.votes[victim] = LFG_ANSWER_DENY; // Victim auto vote NO + boot.votes[kicker] = LFG_ANSWER_AGREE; // Kicker auto vote YES + + // Notify players + for (LfgGuidSet::const_iterator it = players.begin(); it != players.end(); ++it) + SendLfgBootProposalUpdate(*it, boot); } /** Update Boot info with player answer - @param[in] player Player who has answered + @param[in] guid Player who has answered @param[in] accept player answer */ -void LFGMgr::UpdateBoot(Player* player, bool accept) +void LFGMgr::UpdateBoot(uint64 guid, bool accept) { - Group* grp = player ? player->GetGroup() : NULL; - if (!grp) + uint64 gguid = GetGroup(guid); + if (!gguid) return; - uint32 bootId = grp->GetLowGUID(); - uint64 guid = player->GetGUID(); - - LfgPlayerBootMap::iterator itBoot = m_Boots.find(bootId); + LfgPlayerBootMap::iterator itBoot = m_Boots.find(gguid); if (itBoot == m_Boots.end()) return; - LfgPlayerBoot* pBoot = itBoot->second; - if (!pBoot) - return; + LfgPlayerBoot& boot = itBoot->second; - if (pBoot->votes[guid] != LFG_ANSWER_PENDING) // Cheat check: Player can't vote twice + if (boot.votes[guid] != LFG_ANSWER_PENDING) // Cheat check: Player can't vote twice return; - pBoot->votes[guid] = LfgAnswer(accept); + boot.votes[guid] = LfgAnswer(accept); uint8 votesNum = 0; uint8 agreeNum = 0; - for (LfgAnswerMap::const_iterator itVotes = pBoot->votes.begin(); itVotes != pBoot->votes.end(); ++itVotes) + for (LfgAnswerMap::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes) { if (itVotes->second != LFG_ANSWER_PENDING) { @@ -1742,39 +1350,30 @@ void LFGMgr::UpdateBoot(Player* player, bool accept) } } - if (agreeNum == pBoot->votedNeeded || // Vote passed - votesNum == pBoot->votes.size() || // All voted but not passed - (pBoot->votes.size() - votesNum + agreeNum) < pBoot->votedNeeded) // Vote didnt passed + // if we don't have enough votes (agree or deny) do nothing + if (agreeNum < LFG_GROUP_KICK_VOTES_NEEDED && (votesNum - agreeNum) < LFG_GROUP_KICK_VOTES_NEEDED) + return; + + // Send update info to all players + boot.inProgress = false; + for (LfgAnswerMap::const_iterator itVotes = boot.votes.begin(); itVotes != boot.votes.end(); ++itVotes) { - // Send update info to all players - pBoot->inProgress = false; - for (LfgAnswerMap::const_iterator itVotes = pBoot->votes.begin(); itVotes != pBoot->votes.end(); ++itVotes) + uint64 pguid = itVotes->first; + if (pguid != boot.victim) { - uint64 pguid = itVotes->first; - if (pguid != pBoot->victim) - { - SetState(pguid, LFG_STATE_DUNGEON); - if (Player* plrg = ObjectAccessor::FindPlayer(pguid)) - plrg->GetSession()->SendLfgBootPlayer(pBoot); - } + SetState(pguid, LFG_STATE_DUNGEON); + SendLfgBootProposalUpdate(pguid, boot); } + } - uint64 gguid = grp->GetGUID(); - SetState(gguid, LFG_STATE_DUNGEON); - if (agreeNum == pBoot->votedNeeded) // Vote passed - Kick player - { - Player::RemoveFromGroup(grp, pBoot->victim); - if (Player* victim = ObjectAccessor::FindPlayer(pBoot->victim)) - { - TeleportPlayer(victim, true, false); - SetState(pBoot->victim, LFG_STATE_NONE); - } - OfferContinue(grp); - DecreaseKicksLeft(gguid); - } - delete pBoot; - m_Boots.erase(itBoot); + SetState(gguid, LFG_STATE_DUNGEON); + if (agreeNum == LFG_GROUP_KICK_VOTES_NEEDED) // Vote passed - Kick player + { + if (Group* group = sGroupMgr->GetGroupByGUID(GUID_LOPART(gguid))) + Player::RemoveFromGroup(group, boot.victim, GROUP_REMOVEMETHOD_KICK_LFG); + DecreaseKicksLeft(gguid); } + m_Boots.erase(itBoot); } /** @@ -1786,10 +1385,26 @@ void LFGMgr::UpdateBoot(Player* player, bool accept) */ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false*/) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::TeleportPlayer: [" UI64FMTD "] is being teleported %s", player->GetGUID(), out ? "out" : "in"); + Group* grp = player->GetGroup(); + uint64 gguid = grp->GetGUID(); + LFGDungeonData const* dungeon = GetLFGDungeon(GetDungeon(gguid)); + if (!dungeon) + return; + + if (out) + { + sLog->outDebug(LOG_FILTER_LFG, "TeleportPlayer: Player %s is being teleported out. Current Map %u - Expected Map %u", + player->GetName(), player->GetMapId(), uint32(dungeon->map)); + if (player->GetMapId() == uint32(dungeon->map)) + { + player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW); + player->TeleportToBGEntryPoint(); + } + + return; + } LfgTeleportError error = LFG_TELEPORTERROR_OK; - Group* grp = player->GetGroup(); if (!grp || !grp->isLFGGroup()) // should never happen, but just in case... error = LFG_TELEPORTERROR_INVALID_LOCATION; @@ -1799,102 +1414,59 @@ void LFGMgr::TeleportPlayer(Player* player, bool out, bool fromOpcode /*= false* error = LFG_TELEPORTERROR_FALLING; else if (player->IsMirrorTimerActive(FATIGUE_TIMER)) error = LFG_TELEPORTERROR_FATIGUE; - else - { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(GetDungeon(grp->GetGUID())); - - if (out) + else if (player->GetVehicle()) + error = LFG_TELEPORTERROR_IN_VEHICLE; + else if (player->GetCharmGUID()) + error = LFG_TELEPORTERROR_CHARMING; + else if (player->GetMapId() != uint32(dungeon->map)) // Do not teleport players in dungeon to the entrance + { + uint32 mapid = dungeon->map; + float x = dungeon->x; + float y = dungeon->y; + float z = dungeon->z; + float orientation = dungeon->o; + + if (!fromOpcode) { - // Player needs to be inside the LFG dungeon to be able to teleport out - if (dungeon && player->GetMapId() == uint32(dungeon->map)) + // Select a player inside to be teleported to + for (GroupReference* itr = grp->GetFirstMember(); itr != NULL && !mapid; itr = itr->next()) { - player->RemoveAurasDueToSpell(LFG_SPELL_LUCK_OF_THE_DRAW); - player->TeleportToBGEntryPoint(); + Player* plrg = itr->getSource(); + if (plrg && plrg != player && plrg->GetMapId() == uint32(dungeon->map)) + { + mapid = plrg->GetMapId(); + x = plrg->GetPositionX(); + y = plrg->GetPositionY(); + z = plrg->GetPositionZ(); + orientation = plrg->GetOrientation(); + } } - else - player->GetSession()->SendLfgTeleportError(LFG_TELEPORTERROR_DONT_REPORT); // Not sure which error message to send - - return; } - if (!dungeon) - error = LFG_TELEPORTERROR_INVALID_LOCATION; - else if (player->GetMapId() != uint32(dungeon->map)) // Do not teleport players in dungeon to the entrance + if (error == LFG_TELEPORTERROR_OK) { - uint32 mapid = 0; - float x = 0; - float y = 0; - float z = 0; - float orientation = 0; - - if (!fromOpcode) - { - // Select a player inside to be teleported to - for (GroupReference* itr = grp->GetFirstMember(); itr != NULL && !mapid; itr = itr->next()) - { - Player* plrg = itr->getSource(); - if (plrg && plrg != player && plrg->GetMapId() == uint32(dungeon->map)) - { - mapid = plrg->GetMapId(); - x = plrg->GetPositionX(); - y = plrg->GetPositionY(); - z = plrg->GetPositionZ(); - orientation = plrg->GetOrientation(); - } - } - } + if (!player->GetMap()->IsDungeon()) + player->SetBattlegroundEntryPoint(); - if (!mapid) + if (player->isInFlight()) { - LfgEntrancePositionMap::const_iterator itr = m_entrancePositions.find(dungeon->ID); - if (itr != m_entrancePositions.end()) - { - mapid = dungeon->map; - x = itr->second.GetPositionX(); - y = itr->second.GetPositionY(); - z = itr->second.GetPositionZ(); - orientation = itr->second.GetOrientation(); - } - else if (AreaTrigger const* at = sObjectMgr->GetMapEntranceTrigger(dungeon->map)) - { - mapid = at->target_mapId; - x = at->target_X; - y = at->target_Y; - z = at->target_Z; - orientation = at->target_Orientation; - } - else - { - sLog->outError(LOG_FILTER_LFG, "LfgMgr::TeleportPlayer: Failed to teleport [" UI64FMTD "]: No areatrigger found for map: %u difficulty: %u", player->GetGUID(), dungeon->map, dungeon->difficulty); - error = LFG_TELEPORTERROR_INVALID_LOCATION; - } + player->GetMotionMaster()->MovementExpired(); + player->CleanupAfterTaxiFlight(); } - if (error == LFG_TELEPORTERROR_OK) + if (!player->TeleportTo(mapid, x, y, z, orientation)) { - if (!player->GetMap()->IsDungeon()) - player->SetBattlegroundEntryPoint(); - - if (player->isInFlight()) - { - player->GetMotionMaster()->MovementExpired(); - player->CleanupAfterTaxiFlight(); - } - - if (player->TeleportTo(mapid, x, y, z, orientation)) - // FIXME - HACK - this should be done by teleport, when teleporting far - player->RemoveAurasByType(SPELL_AURA_MOUNTED); - else - { - error = LFG_TELEPORTERROR_INVALID_LOCATION; - sLog->outError(LOG_FILTER_LFG, "LfgMgr::TeleportPlayer: Failed to teleport [" UI64FMTD "] to map %u: ", player->GetGUID(), mapid); - } + error = LFG_TELEPORTERROR_INVALID_LOCATION; + sLog->outError(LOG_FILTER_LFG, "TeleportPlayer: Failed to teleport [" UI64FMTD "] to map %u (x: %f, y: %f, z: %f)", player->GetGUID(), mapid, x, y, z); } } } if (error != LFG_TELEPORTERROR_OK) player->GetSession()->SendLfgTeleportError(uint8(error)); + + sLog->outDebug(LOG_FILTER_LFG, "TeleportPlayer: Player %s is being teleported in. Result: %u", + player->GetName(), error); } /** @@ -1932,12 +1504,12 @@ void LFGMgr::RewardDungeonDoneFor(const uint32 dungeonId, Player* player) // Clear player related lfg stuff uint32 rDungeonId = (*GetSelectedDungeons(guid).begin()); - ClearState(guid); + ClearState(guid, "Dungeon Finished"); SetState(guid, LFG_STATE_FINISHED_DUNGEON); // Give rewards only if its a random or seasonal dungeon - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(rDungeonId); - if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM && !(dungeon->flags & LFG_FLAG_SEASONAL))) + LFGDungeonData const* dungeon = GetLFGDungeon(rDungeonId); + if (!dungeon || (dungeon->type != LFG_TYPE_RANDOM && !dungeon->seasonal)) { sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RewardDungeonDoneFor: [" UI64FMTD "] dungeon %u is not random nor seasonal", guid, rDungeonId); return; @@ -1986,9 +1558,9 @@ void LFGMgr::RewardDungeonDoneFor(const uint32 dungeonId, Player* player) */ const LfgDungeonSet& LFGMgr::GetDungeonsByRandom(uint32 randomdungeon) { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(randomdungeon); - uint32 groupType = dungeon ? dungeon->grouptype : 0; - return m_CachedDungeonMap[groupType]; + LFGDungeonData const* dungeon = GetLFGDungeon(randomdungeon); + uint32 group = dungeon ? dungeon->group : 0; + return m_CachedDungeonMap[group]; } /** @@ -2021,81 +1593,42 @@ LfgReward const* LFGMgr::GetRandomDungeonReward(uint32 dungeon, uint8 level) */ LfgType LFGMgr::GetDungeonType(uint32 dungeonId) { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId); + LFGDungeonData const* dungeon = GetLFGDungeon(dungeonId); if (!dungeon) return LFG_TYPE_NONE; return LfgType(dungeon->type); } -/** - Given a list of guids returns the concatenation using | as delimiter - - @param[in] check list of guids - @returns Concatenated string -*/ -std::string LFGMgr::ConcatenateGuids(LfgGuidList check) -{ - if (check.empty()) - return ""; - - std::ostringstream o; - LfgGuidList::const_iterator it = check.begin(); - o << (*it); - for (++it; it != check.end(); ++it) - o << '|' << (*it); - return o.str(); -} - -HolidayIds LFGMgr::GetDungeonSeason(uint32 dungeonId) -{ - HolidayIds holiday = HOLIDAY_NONE; - - switch (dungeonId) - { - case 285: - holiday = HOLIDAY_HALLOWS_END; - break; - case 286: - holiday = HOLIDAY_FIRE_FESTIVAL; - break; - case 287: - holiday = HOLIDAY_BREWFEST; - break; - case 288: - holiday = HOLIDAY_LOVE_IS_IN_THE_AIR; - break; - default: - break; - } - - return holiday; -} - LfgState LFGMgr::GetState(uint64 guid) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetState: [" UI64FMTD "]", guid); + LfgState state; if (IS_GROUP(guid)) - return m_Groups[guid].GetState(); + state = m_Groups[guid].GetState(); else - return m_Players[guid].GetState(); + state = m_Players[guid].GetState(); + + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetState: [" UI64FMTD "] = %u", guid, state); + return state; } -uint32 LFGMgr::GetDungeon(uint64 guid, bool asId /*= true*/) +uint32 LFGMgr::GetDungeon(uint64 guid, bool asId /*= true */) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetDungeon: [" UI64FMTD "] asId: %u", guid, asId); - return m_Groups[guid].GetDungeon(asId); + uint32 dungeon = m_Groups[guid].GetDungeon(asId); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetDungeon: [" UI64FMTD "] asId: %u = %u", guid, asId, dungeon); + return dungeon; } uint8 LFGMgr::GetRoles(uint64 guid) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetRoles: [" UI64FMTD "]", guid); - return m_Players[guid].GetRoles(); + uint8 roles = m_Players[guid].GetRoles(); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetRoles: [" UI64FMTD "] = %u", guid, roles); + return roles; } const std::string& LFGMgr::GetComment(uint64 guid) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetComment: [" UI64FMTD "]", guid); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetComment: [" UI64FMTD "] = %s", guid, m_Players[guid].GetComment().c_str()); return m_Players[guid].GetComment(); } @@ -2123,35 +1656,49 @@ const LfgLockMap& LFGMgr::GetLockedDungeons(uint64 guid) uint8 LFGMgr::GetKicksLeft(uint64 guid) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetKicksLeft: [" UI64FMTD "]", guid); - return m_Groups[guid].GetKicksLeft(); -} - -uint8 LFGMgr::GetVotesNeeded(uint64 guid) -{ - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetVotesNeeded: [" UI64FMTD "]", guid); - return m_Groups[guid].GetVotesNeeded(); + uint8 kicks = m_Groups[guid].GetKicksLeft(); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::GetKicksLeft: [" UI64FMTD "] = %u", guid, kicks); + return kicks; } -void LFGMgr::RestoreState(uint64 guid) +void LFGMgr::RestoreState(uint64 guid, char const *debugMsg) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RestoreState: [" UI64FMTD "]", guid); - m_Groups[guid].RestoreState(); + LfgGroupData& data = m_Groups[guid]; + char const * const ps = GetStateString(data.GetState()); + char const * const os = GetStateString(data.GetOldState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::RestoreState: Group: [" UI64FMTD "] (%s), State: %s, oldState: %s", guid, debugMsg, ps, os); + data.RestoreState(); } -void LFGMgr::ClearState(uint64 guid) +void LFGMgr::ClearState(uint64 guid, char const *debugMsg) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::ClearState: [" UI64FMTD "]", guid); - m_Players[guid].ClearState(); + LfgPlayerData& data = m_Players[guid]; + char const * const ps = GetStateString(data.GetState()); + char const * const os = GetStateString(data.GetOldState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::ClearState: Player: [" UI64FMTD "] (%s) State: %s, oldState: %s", guid, debugMsg, ps, os); + data.ClearState(); } void LFGMgr::SetState(uint64 guid, LfgState state) { - sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: [" UI64FMTD "] state %u", guid, state); if (IS_GROUP(guid)) - m_Groups[guid].SetState(state); + { + LfgGroupData& data = m_Groups[guid]; + char const * const ns = GetStateString(state); + char const * const ps = GetStateString(data.GetState()); + char const * const os = GetStateString(data.GetOldState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Group: [" UI64FMTD "] newState: %s, previous: %s, oldState: %s", guid, ns, ps, os); + data.SetState(state); + } else - m_Players[guid].SetState(state); + { + LfgPlayerData& data = m_Players[guid]; + char const * const ns = GetStateString(state); + char const * const ps = GetStateString(data.GetState()); + char const * const os = GetStateString(data.GetOldState()); + sLog->outDebug(LOG_FILTER_LFG, "LFGMgr::SetState: Player: [" UI64FMTD "] newState: %s, previous: %s, oldState: %s", guid, ns, ps, os); + data.SetState(state); + } } void LFGMgr::SetDungeon(uint64 guid, uint32 dungeon) @@ -2205,3 +1752,198 @@ void LFGMgr::RemoveGroupData(uint64 guid) if (it != m_Groups.end()) m_Groups.erase(it); } + +uint8 LFGMgr::GetTeam(uint64 guid) +{ + return m_Players[guid].GetTeam(); +} + +uint8 LFGMgr::RemovePlayerFromGroup(uint64 gguid, uint64 guid) +{ + return m_Groups[gguid].RemovePlayer(guid); +} + +void LFGMgr::AddPlayerToGroup(uint64 gguid, uint64 guid) +{ + m_Groups[gguid].AddPlayer(guid); +} + +void LFGMgr::SetLeader(uint64 gguid, uint64 leader) +{ + m_Groups[gguid].SetLeader(leader); +} + +void LFGMgr::SetTeam(uint64 guid, uint8 team) +{ + if (sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GROUP)) + team = 0; + + m_Players[guid].SetTeam(team); +} + +uint64 LFGMgr::GetGroup(uint64 guid) +{ + return m_Players[guid].GetGroup(); +} + +void LFGMgr::SetGroup(uint64 guid, uint64 group) +{ + m_Players[guid].SetGroup(group); +} + +const LfgGuidSet& LFGMgr::GetPlayers(uint64 guid) +{ + return m_Groups[guid].GetPlayers(); +} + +uint64 LFGMgr::GetLeader(uint64 guid) +{ + return m_Groups[guid].GetLeader(); +} + +bool LFGMgr::HasIgnore(uint64 guid1, uint64 guid2) +{ + Player* plr1 = ObjectAccessor::FindPlayer(guid1); + Player* plr2 = ObjectAccessor::FindPlayer(guid2); + uint32 low1 = GUID_LOPART(guid1); + uint32 low2 = GUID_LOPART(guid2); + return plr1 && plr2 && (plr1->GetSocial()->HasIgnore(low2) || plr2->GetSocial()->HasIgnore(low1)); +} + +void LFGMgr::SendLfgRoleChosen(uint64 guid, uint64 pguid, uint8 roles) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgRoleChosen(pguid, roles); +} + +void LFGMgr::SendLfgRoleCheckUpdate(uint64 guid, const LfgRoleCheck& roleCheck) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgRoleCheckUpdate(roleCheck); +} + +void LFGMgr::SendLfgUpdatePlayer(uint64 guid, const LfgUpdateData& data) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgUpdatePlayer(data); +} + +void LFGMgr::SendLfgUpdateParty(uint64 guid, const LfgUpdateData& data) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgUpdateParty(data); +} + +void LFGMgr::SendLfgJoinResult(uint64 guid, const LfgJoinResultData& data) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgJoinResult(data); +} + +void LFGMgr::SendLfgBootProposalUpdate(uint64 guid, const LfgPlayerBoot& boot) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgBootProposalUpdate(boot); +} + +void LFGMgr::SendLfgUpdateProposal(uint64 guid, uint32 proposalId, const LfgProposal& proposal) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgUpdateProposal(proposalId, proposal); +} + +void LFGMgr::SendLfgQueueStatus(uint64 guid, const LfgQueueStatusData& data) +{ + if (Player* player = ObjectAccessor::FindPlayer(guid)) + player->GetSession()->SendLfgQueueStatus(data); +} + +bool LFGMgr::IsLfgGroup(uint64 guid) +{ + return guid && IS_GROUP(guid) && m_Groups[guid].IsLfgGroup(); +} + +LfgQueue& LFGMgr::GetQueue(uint64 guid) +{ + uint8 queueId = 0; + if (IS_GROUP(guid)) + { + const LfgGuidSet& players = GetPlayers(guid); + uint64 pguid = players.empty() ? 0 : (*players.begin()); + if (pguid) + queueId = GetTeam(pguid); + } + else + queueId = GetTeam(guid); + return m_Queues[queueId]; +} + +bool LFGMgr::AllQueued(const LfgGuidList& check) +{ + if (check.empty()) + return false; + + for (LfgGuidList::const_iterator it = check.begin(); it != check.end(); ++it) + if (GetState(*it) != LFG_STATE_QUEUED) + return false; + return true; +} + +// Only for debugging purposes +void LFGMgr::Clean() +{ + m_Queues.clear(); +} + +bool LFGMgr::isOptionEnabled(uint32 option) +{ + return m_options & option; +} + +uint32 LFGMgr::GetOptions() +{ + return m_options; +} + +void LFGMgr::SetOptions(uint32 options) +{ + m_options = options; +} + +bool LFGMgr::IsSeasonActive(uint32 dungeonId) +{ + switch (dungeonId) + { + case 285: // The Headless Horseman + return IsHolidayActive(HOLIDAY_HALLOWS_END); + case 286: // The Frost Lord Ahune + return IsHolidayActive(HOLIDAY_FIRE_FESTIVAL); + case 287: // Coren Direbrew + return IsHolidayActive(HOLIDAY_BREWFEST); + case 288: // The Crown Chemical Co. + return IsHolidayActive(HOLIDAY_LOVE_IS_IN_THE_AIR); + } + return false; +} + +std::string LFGMgr::DumpQueueInfo(bool /*full*/) +{ + uint32 size = uint32(m_Queues.size()); + std::ostringstream o; + + o << "Number of Queues: " << size << "\n"; + for (LfgQueueMap::const_iterator itr = m_Queues.begin(); itr != m_Queues.end(); ++itr) + { + std::string const& queued = itr->second.DumpQueueInfo(); + std::string const& compatibles = itr->second.DumpCompatibleInfo(); + o << queued << compatibles; + /* + if (full) + { + LfgCompatibleMap const& compatibles = itr->second.GetCompatibleMap(); + } + */ + } + + return o.str(); +} diff --git a/src/server/game/DungeonFinding/LFGMgr.h b/src/server/game/DungeonFinding/LFGMgr.h index 9937759741b..5df6f2445e7 100755 --- a/src/server/game/DungeonFinding/LFGMgr.h +++ b/src/server/game/DungeonFinding/LFGMgr.h @@ -21,24 +21,29 @@ #include "Common.h" #include <ace/Singleton.h> #include "LFG.h" +#include "LFGQueue.h" +#include "LFGGroupData.h" +#include "LFGPlayerData.h" -class LfgGroupData; -class LfgPlayerData; class Group; class Player; -enum LFGenum +enum LfgOptions { - LFG_TIME_ROLECHECK = 40*IN_MILLISECONDS, - LFG_TIME_BOOT = 2*MINUTE, - LFG_TIME_PROPOSAL = 2*MINUTE, - LFG_TANKS_NEEDED = 1, - LFG_HEALERS_NEEDED = 1, - LFG_DPS_NEEDED = 3, - LFG_QUEUEUPDATE_INTERVAL = 15*IN_MILLISECONDS, + LFG_OPTION_ENABLE_DUNGEON_FINDER = 0x01, + LFG_OPTION_ENABLE_RAID_BROWSER = 0x02, +}; + +enum LFGMgrEnum +{ + LFG_TIME_ROLECHECK = 40 * IN_MILLISECONDS, + LFG_TIME_BOOT = 120, + LFG_TIME_PROPOSAL = 120, + LFG_QUEUEUPDATE_INTERVAL = 15 * IN_MILLISECONDS, LFG_SPELL_DUNGEON_COOLDOWN = 71328, LFG_SPELL_DUNGEON_DESERTER = 71041, - LFG_SPELL_LUCK_OF_THE_DRAW = 72221 + LFG_SPELL_LUCK_OF_THE_DRAW = 72221, + LFG_GROUP_KICK_VOTES_NEEDED = 3 }; enum LfgFlags @@ -52,11 +57,9 @@ enum LfgFlags /// Determines the type of instance enum LfgType { - LFG_TYPE_NONE = 0, // Internal use only + LFG_TYPE_NONE = 0, LFG_TYPE_DUNGEON = 1, LFG_TYPE_RAID = 2, - LFG_TYPE_QUEST = 3, - LFG_TYPE_ZONE = 4, LFG_TYPE_HEROIC = 5, LFG_TYPE_RANDOM = 6 }; @@ -72,13 +75,14 @@ enum LfgProposalState /// Teleport errors enum LfgTeleportError { - // 3, 7, 8 = "You can't do that right now" | 5 = No client reaction + // 7 = "You can't do that right now" | 5 = No client reaction LFG_TELEPORTERROR_OK = 0, // Internal use LFG_TELEPORTERROR_PLAYER_DEAD = 1, LFG_TELEPORTERROR_FALLING = 2, - LFG_TELEPORTERROR_DONT_REPORT = 3, + LFG_TELEPORTERROR_IN_VEHICLE = 3, LFG_TELEPORTERROR_FATIGUE = 4, - LFG_TELEPORTERROR_INVALID_LOCATION = 6 + LFG_TELEPORTERROR_INVALID_LOCATION = 6, + LFG_TELEPORTERROR_CHARMING = 8 // FIXME - It can be 7 or 8 (Need proper data) }; /// Queue join results @@ -116,42 +120,27 @@ enum LfgRoleCheckState LFG_ROLECHECK_NO_ROLE = 6 // Someone selected no role }; -/// Answer state (Also used to check compatibilites) -enum LfgAnswer -{ - LFG_ANSWER_PENDING = -1, - LFG_ANSWER_DENY = 0, - LFG_ANSWER_AGREE = 1 -}; - // Forward declaration (just to have all typedef together) +struct LFGDungeonData; struct LfgReward; -struct LfgLockStatus; struct LfgQueueInfo; struct LfgRoleCheck; struct LfgProposal; struct LfgProposalPlayer; struct LfgPlayerBoot; -typedef std::set<uint64> LfgGuidSet; -typedef std::list<uint64> LfgGuidList; -typedef std::map<uint8, LfgGuidList> LfgGuidListMap; -typedef std::set<Player*> PlayerSet; -typedef std::list<Player*> LfgPlayerList; +typedef std::map<uint8, LfgQueue> LfgQueueMap; typedef std::multimap<uint32, LfgReward const*> LfgRewardMap; typedef std::pair<LfgRewardMap::const_iterator, LfgRewardMap::const_iterator> LfgRewardMapBounds; -typedef std::map<std::string, LfgAnswer> LfgCompatibleMap; -typedef std::map<uint64, LfgDungeonSet> LfgDungeonMap; -typedef std::map<uint64, uint8> LfgRolesMap; +typedef std::map<uint8, LfgDungeonSet> LfgCachedDungeonMap; typedef std::map<uint64, LfgAnswer> LfgAnswerMap; -typedef std::map<uint64, LfgRoleCheck*> LfgRoleCheckMap; -typedef std::map<uint64, LfgQueueInfo*> LfgQueueInfoMap; -typedef std::map<uint32, LfgProposal*> LfgProposalMap; -typedef std::map<uint64, LfgProposalPlayer*> LfgProposalPlayerMap; -typedef std::map<uint32, LfgPlayerBoot*> LfgPlayerBootMap; +typedef std::map<uint64, LfgRoleCheck> LfgRoleCheckMap; +typedef std::map<uint32, LfgProposal> LfgProposalMap; +typedef std::map<uint64, LfgProposalPlayer> LfgProposalPlayerMap; +typedef std::map<uint64, LfgPlayerBoot> LfgPlayerBootMap; typedef std::map<uint64, LfgGroupData> LfgGroupDataMap; typedef std::map<uint64, LfgPlayerData> LfgPlayerDataMap; -typedef std::map<uint32, Position> LfgEntrancePositionMap; +typedef UNORDERED_MAP<uint32, LFGDungeonData> LFGDungeonMap; // Data needed by SMSG_LFG_JOIN_RESULT struct LfgJoinResultData @@ -167,7 +156,7 @@ struct LfgJoinResultData struct LfgUpdateData { LfgUpdateData(LfgUpdateType _type = LFG_UPDATETYPE_DEFAULT): updateType(_type), comment("") {} - LfgUpdateData(LfgUpdateType _type, const LfgDungeonSet& _dungeons, std::string _comment): + LfgUpdateData(LfgUpdateType _type, LfgDungeonSet const& _dungeons, std::string _comment): updateType(_type), dungeons(_dungeons), comment(_comment) {} LfgUpdateType updateType; @@ -175,6 +164,26 @@ struct LfgUpdateData std::string comment; }; +// Data needed by SMSG_LFG_QUEUE_STATUS +struct LfgQueueStatusData +{ + LfgQueueStatusData(uint32 _dungeonId = 0, int32 _waitTime = -1, int32 _waitTimeAvg = -1, int32 _waitTimeTank = -1, int32 _waitTimeHealer = -1, + int32 _waitTimeDps = -1, uint32 _queuedTime = 0, uint8 _tanks = 0, uint8 _healers = 0, uint8 _dps = 0) : + dungeonId(_dungeonId), waitTime(_waitTime), waitTimeAvg(_waitTimeAvg), waitTimeTank(_waitTimeTank), waitTimeHealer(_waitTimeHealer), + waitTimeDps(_waitTimeDps), queuedTime(_queuedTime), tanks(_tanks), healers(_healers), dps(_dps) {} + + uint32 dungeonId; + int32 waitTime; + int32 waitTimeAvg; + int32 waitTimeTank; + int32 waitTimeHealer; + int32 waitTimeDps; + uint32 queuedTime; + uint8 tanks; + uint8 healers; + uint8 dps; +}; + /// Reward info struct LfgReward { @@ -198,45 +207,31 @@ struct LfgReward } }; -/// Stores player or group queue info -struct LfgQueueInfo -{ - LfgQueueInfo(): joinTime(0), tanks(LFG_TANKS_NEEDED), healers(LFG_HEALERS_NEEDED), dps(LFG_DPS_NEEDED) {}; - time_t joinTime; ///< Player queue join time (to calculate wait times) - uint8 tanks; ///< Tanks needed - uint8 healers; ///< Healers needed - uint8 dps; ///< Dps needed - LfgDungeonSet dungeons; ///< Selected Player/Group Dungeon/s - LfgRolesMap roles; ///< Selected Player Role/s -}; - /// Stores player data related to proposal to join struct LfgProposalPlayer { - LfgProposalPlayer(): role(0), accept(LFG_ANSWER_PENDING), groupLowGuid(0) {}; + LfgProposalPlayer(): role(0), accept(LFG_ANSWER_PENDING), group(0) { } uint8 role; ///< Proposed role LfgAnswer accept; ///< Accept status (-1 not answer | 0 Not agree | 1 agree) - uint32 groupLowGuid; ///< Original group guid (Low guid) 0 if no original group + uint64 group; ///< Original group guid. 0 if no original group }; /// Stores group data related to proposal to join struct LfgProposal { - LfgProposal(uint32 dungeon = 0): dungeonId(dungeon), state(LFG_PROPOSAL_INITIATING), groupLowGuid(0), leader(0), cancelTime(0) {} + LfgProposal(uint32 dungeon = 0): dungeonId(dungeon), state(LFG_PROPOSAL_INITIATING), + group(0), leader(0), cancelTime(0), encounters(0), isNew(true) + { } - ~LfgProposal() - { - for (LfgProposalPlayerMap::iterator it = players.begin(); it != players.end(); ++it) - delete it->second; - }; uint32 dungeonId; ///< Dungeon to join LfgProposalState state; ///< State of the proposal - uint32 groupLowGuid; ///< Proposal group (0 if new) + uint64 group; ///< Proposal group (0 if new) uint64 leader; ///< Leader guid. time_t cancelTime; ///< Time when we will cancel this proposal + uint32 encounters; ///< Dungeon Encounters + bool isNew; ///< Determines if it's new group or not LfgGuidList queues; ///< Queue Ids to remove/readd LfgProposalPlayerMap players; ///< Players data - }; /// Stores all rolecheck info of a group that wants to join @@ -257,10 +252,36 @@ struct LfgPlayerBoot bool inProgress; ///< Vote in progress LfgAnswerMap votes; ///< Player votes (-1 not answer | 0 Not agree | 1 agree) uint64 victim; ///< Player guid to be kicked (can't vote) - uint8 votedNeeded; ///< Votes needed to kick the player std::string reason; ///< kick reason }; +struct LFGDungeonData +{ + LFGDungeonData(): id(0), name(""), map(0), type(0), expansion(0), group(0), minlevel(0), + maxlevel(0), difficulty(REGULAR_DIFFICULTY), seasonal(false), x(0.0f), y(0.0f), z(0.0f), o(0.0f) + { } + LFGDungeonData(LFGDungeonEntry const* dbc): id(dbc->ID), name(dbc->name[0]), map(dbc->map), + type(dbc->type), expansion(dbc->expansion), group(dbc->grouptype), + minlevel(dbc->minlevel), maxlevel(dbc->maxlevel), difficulty(Difficulty(dbc->difficulty)), + seasonal(dbc->flags & LFG_FLAG_SEASONAL), x(0.0f), y(0.0f), z(0.0f), o(0.0f) + { } + + uint32 id; + std::string name; + uint16 map; + uint8 type; + uint8 expansion; + uint8 group; + uint8 minlevel; + uint8 maxlevel; + Difficulty difficulty; + bool seasonal; + float x, y, z, o; + + // Helpers + uint32 Entry() const { return id + (type << 24); } +}; + class LFGMgr { friend class ACE_Singleton<LFGMgr, ACE_Null_Mutex>; @@ -274,108 +295,125 @@ class LFGMgr // Reward void LoadRewards(); - void RewardDungeonDoneFor(const uint32 dungeonId, Player* player); + void RewardDungeonDoneFor(uint32 const dungeonId, Player* player); LfgReward const* GetRandomDungeonReward(uint32 dungeon, uint8 level); // Queue - void Join(Player* player, uint8 roles, const LfgDungeonSet& dungeons, const std::string& comment); - void Leave(Player* player, Group* grp = NULL); + void JoinLfg(Player* player, uint8 roles, LfgDungeonSet& dungeons, std::string const& comment); + void LeaveLfg(uint64 guid); // Role Check - void UpdateRoleCheck(uint64 gguid, uint64 guid = 0, uint8 roles = ROLE_NONE); + void UpdateRoleCheck(uint64 gguid, uint64 guid = 0, uint8 roles = PLAYER_ROLE_NONE); + + // Group Matching + static bool CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag = true); + void GetCompatibleDungeons(LfgDungeonSet& dungeons, LfgGuidSet const& players, LfgLockPartyMap& lockMap); // Proposals + uint32 AddProposal(LfgProposal const& proposal); void UpdateProposal(uint32 proposalId, uint64 guid, bool accept); // Teleportation - void LoadEntrancePositions(); void TeleportPlayer(Player* player, bool out, bool fromOpcode = false); // Vote kick - void InitBoot(Group* grp, uint64 kguid, uint64 vguid, std::string reason); - void UpdateBoot(Player* player, bool accept); - void OfferContinue(Group* grp); + void InitBoot(uint64 gguid, uint64 kguid, uint64 vguid, std::string const& reason); + void UpdateBoot(uint64 guid, bool accept); - HolidayIds GetDungeonSeason(uint32 dungeonId); + void InitializeLockedDungeons(Player* player, uint8 level = 0); - void InitializeLockedDungeons(Player* player); + void SetRoles(uint64 guid, uint8 roles); + void SetComment(uint64 guid, std::string const& comment); + void SetTeam(uint64 guid, uint8 team); + void SetGroup(uint64 guid, uint64 group); + void SetLeader(uint64 gguid, uint64 leader); + void SetState(uint64 guid, LfgState state); + void SetSelectedDungeons(uint64 guid, LfgDungeonSet const& dungeons); void _LoadFromDB(Field* fields, uint64 guid); void _SaveToDB(uint64 guid, uint32 db_guid); - void SetComment(uint64 guid, const std::string& comment); - const LfgLockMap& GetLockedDungeons(uint64 guid); - LfgState GetState(uint64 guid); - const LfgDungeonSet& GetSelectedDungeons(uint64 guid); - uint32 GetDungeon(uint64 guid, bool asId = true); - void SetState(uint64 guid, LfgState state); - void ClearState(uint64 guid); void RemovePlayerData(uint64 guid); void RemoveGroupData(uint64 guid); + uint8 RemovePlayerFromGroup(uint64 gguid, uint64 guid); + void AddPlayerToGroup(uint64 gguid, uint64 guid); + + LfgLockMap const& GetLockedDungeons(uint64 guid); + LfgDungeonSet const& GetSelectedDungeons(uint64 guid); + uint32 GetDungeon(uint64 guid, bool asId = true); + LfgState GetState(uint64 guid); uint8 GetKicksLeft(uint64 gguid); - uint8 GetVotesNeeded(uint64 gguid); - bool IsTeleported(uint64 pguid); - void SetRoles(uint64 guid, uint8 roles); - void SetSelectedDungeons(uint64 guid, const LfgDungeonSet& dungeons); + uint64 GetLeader(uint64 guid); + bool IsLfgGroup(uint64 guid); + uint8 GetRoles(uint64 guid); + std::string const& GetComment(uint64 gguid); + LfgGuidSet const& GetPlayers(uint64 guid); + + bool IsTeleported(uint64 guid); + + bool AllQueued(LfgGuidList const& check); + void Clean(); + + static bool HasIgnore(uint64 guid1, uint64 guid2); + static void SendLfgQueueStatus(uint64 guid, LfgQueueStatusData const& data); + + bool isOptionEnabled(uint32 option); + uint32 GetOptions(); + void SetOptions(uint32 options); + bool IsSeasonActive(uint32 dungeonId); + std::string DumpQueueInfo(bool full = false); + static std::string ConcatenateDungeons(LfgDungeonSet const& dungeons); + static std::string GetRolesString(uint8 roles); + static char const * GetStateString(LfgState state); + + void LoadLFGDungeons(bool reload = false); + LFGDungeonData const* GetLFGDungeon(uint32 id); + LFGDungeonMap& GetLFGDungeonMap(); private: - uint8 GetRoles(uint64 guid); - const std::string& GetComment(uint64 gguid); - void RestoreState(uint64 guid); + uint8 GetTeam(uint64 guid); + uint64 GetGroup(uint64 guid); + void RestoreState(uint64 guid, char const *debugMsg); + void ClearState(uint64 guid, char const *debugMsg); void SetDungeon(uint64 guid, uint32 dungeon); - void SetLockedDungeons(uint64 guid, const LfgLockMap& lock); + void SetLockedDungeons(uint64 guid, LfgLockMap const& lock); void DecreaseKicksLeft(uint64 guid); - // Queue - void AddToQueue(uint64 guid, uint8 queueId); - bool RemoveFromQueue(uint64 guid); - // Proposals void RemoveProposal(LfgProposalMap::iterator itProposal, LfgUpdateType type); - - // Group Matching - LfgProposal* FindNewGroups(LfgGuidList& check, LfgGuidList& all); - bool CheckGroupRoles(LfgRolesMap &groles, bool removeLeaderFlag = true); - bool CheckCompatibility(LfgGuidList check, LfgProposal*& pProposal); - void GetCompatibleDungeons(LfgDungeonSet& dungeons, const PlayerSet& players, LfgLockPartyMap& lockMap); - void SetCompatibles(std::string concatenatedGuids, bool compatibles); - LfgAnswer GetCompatibles(std::string concatenatedGuids); - void RemoveFromCompatibles(uint64 guid); + void MakeNewGroup(LfgProposal const& proposal); // Generic - const LfgDungeonSet& GetDungeonsByRandom(uint32 randomdungeon); + LfgQueue &GetQueue(uint64 guid); + LfgDungeonSet const& GetDungeonsByRandom(uint32 randomdungeon); LfgType GetDungeonType(uint32 dungeon); - std::string ConcatenateGuids(LfgGuidList check); + + void SendLfgBootProposalUpdate(uint64 guid, LfgPlayerBoot const& boot); + void SendLfgJoinResult(uint64 guid, LfgJoinResultData const& data); + void SendLfgRoleChosen(uint64 guid, uint64 pguid, uint8 roles); + void SendLfgRoleCheckUpdate(uint64 guid, LfgRoleCheck const& roleCheck); + void SendLfgUpdateParty(uint64 guid, LfgUpdateData const& data); + void SendLfgUpdatePlayer(uint64 guid, LfgUpdateData const& data); + void SendLfgUpdateProposal(uint64 guid, uint32 proposalId, LfgProposal const& proposal); // General variables - bool m_update; ///< Doing an update? uint32 m_QueueTimer; ///< used to check interval of update uint32 m_lfgProposalId; ///< used as internal counter for proposals - int32 m_WaitTimeAvg; ///< Average wait time to find a group queuing as multiple roles - int32 m_WaitTimeTank; ///< Average wait time to find a group queuing as tank - int32 m_WaitTimeHealer; ///< Average wait time to find a group queuing as healer - int32 m_WaitTimeDps; ///< Average wait time to find a group queuing as dps - uint32 m_NumWaitTimeAvg; ///< Num of players used to calc avs wait time - uint32 m_NumWaitTimeTank; ///< Num of players used to calc tank wait time - uint32 m_NumWaitTimeHealer; ///< Num of players used to calc healers wait time - uint32 m_NumWaitTimeDps; ///< Num of players used to calc dps wait time - LfgDungeonMap m_CachedDungeonMap; ///< Stores all dungeons by groupType - LfgEntrancePositionMap m_entrancePositions; ///< Stores special entrance positions + uint32 m_options; ///< Stores config options + + LfgQueueMap m_Queues; ///< Queues + LfgCachedDungeonMap m_CachedDungeonMap; ///< Stores all dungeons by groupType // Reward System LfgRewardMap m_RewardMap; ///< Stores rewards for random dungeons - // Queue - LfgQueueInfoMap m_QueueInfoMap; ///< Queued groups - LfgGuidListMap m_currentQueue; ///< Ordered list. Used to find groups - LfgGuidListMap m_newToQueue; ///< New groups to add to queue - LfgCompatibleMap m_CompatibleMap; ///< Compatible dungeons - LfgGuidList m_teleport; ///< Players being teleported + LFGDungeonMap m_LfgDungeonMap; // Rolecheck - Proposal - Vote Kicks LfgRoleCheckMap m_RoleChecks; ///< Current Role checks LfgProposalMap m_Proposals; ///< Current Proposals LfgPlayerBootMap m_Boots; ///< Current player kicks LfgPlayerDataMap m_Players; ///< Player data LfgGroupDataMap m_Groups; ///< Group data + LfgGuidList m_teleport; ///< Players being teleported }; #define sLFGMgr ACE_Singleton<LFGMgr, ACE_Null_Mutex>::instance() diff --git a/src/server/game/DungeonFinding/LFGPlayerData.cpp b/src/server/game/DungeonFinding/LFGPlayerData.cpp index 4a6e86ab966..e5645f0f0aa 100644 --- a/src/server/game/DungeonFinding/LFGPlayerData.cpp +++ b/src/server/game/DungeonFinding/LFGPlayerData.cpp @@ -17,8 +17,8 @@ #include "LFGPlayerData.h" -LfgPlayerData::LfgPlayerData(): -m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), m_Roles(0), m_Comment("") +LfgPlayerData::LfgPlayerData(): m_State(LFG_STATE_NONE), m_OldState(LFG_STATE_NONE), + m_Team(0), m_Group(0), m_Roles(0), m_Comment("") {} LfgPlayerData::~LfgPlayerData() @@ -30,10 +30,13 @@ void LfgPlayerData::SetState(LfgState state) switch (state) { case LFG_STATE_NONE: - case LFG_STATE_DUNGEON: case LFG_STATE_FINISHED_DUNGEON: + m_Roles = 0; + m_SelectedDungeons.clear(); + // No break on purpose + case LFG_STATE_DUNGEON: m_OldState = state; - // No break on purpose + // No break on purpose default: m_State = state; } @@ -51,6 +54,16 @@ void LfgPlayerData::SetLockedDungeons(const LfgLockMap& lockStatus) m_LockedDungeons = lockStatus; } +void LfgPlayerData::SetTeam(uint8 team) +{ + m_Team = team; +} + +void LfgPlayerData::SetGroup(uint64 group) +{ + m_Group = group; +} + void LfgPlayerData::SetRoles(uint8 roles) { m_Roles = roles; @@ -66,21 +79,31 @@ void LfgPlayerData::SetSelectedDungeons(const LfgDungeonSet& dungeons) m_SelectedDungeons = dungeons; } -void LfgPlayerData::ClearSelectedDungeons() +LfgState LfgPlayerData::GetState() const { - m_SelectedDungeons.clear(); + return m_State; } -LfgState LfgPlayerData::GetState() const +LfgState LfgPlayerData::GetOldState() const { - return m_State; + return m_OldState; } -const LfgLockMap & LfgPlayerData::GetLockedDungeons() const +const LfgLockMap& LfgPlayerData::GetLockedDungeons() const { return m_LockedDungeons; } +uint8 LfgPlayerData::GetTeam() const +{ + return m_Team; +} + +uint64 LfgPlayerData::GetGroup() const +{ + return m_Group; +} + uint8 LfgPlayerData::GetRoles() const { return m_Roles; diff --git a/src/server/game/DungeonFinding/LFGPlayerData.h b/src/server/game/DungeonFinding/LFGPlayerData.h index e8bae1c5a0f..0682ad66698 100644 --- a/src/server/game/DungeonFinding/LFGPlayerData.h +++ b/src/server/game/DungeonFinding/LFGPlayerData.h @@ -18,7 +18,6 @@ #ifndef _LFGPLAYERDATA_H #define _LFGPLAYERDATA_H -#include "Common.h" #include "LFG.h" /** @@ -33,20 +32,26 @@ class LfgPlayerData // General void SetState(LfgState state); void ClearState(); - void SetLockedDungeons(const LfgLockMap& lock); + void SetLockedDungeons(LfgLockMap const& lock); + void SetTeam(uint8 team); + void SetGroup(uint64 group); + // Queue void SetRoles(uint8 roles); - void SetComment(const std::string& comment); + void SetComment(std::string const& comment); void SetSelectedDungeons(const LfgDungeonSet& dungeons); - void ClearSelectedDungeons(); // General LfgState GetState() const; - const LfgLockMap& GetLockedDungeons() const; + LfgState GetOldState() const; + LfgLockMap const& GetLockedDungeons() const; + uint8 GetTeam() const; + uint64 GetGroup() const; + // Queue uint8 GetRoles() const; - const std::string& GetComment() const; - const LfgDungeonSet& GetSelectedDungeons() const; + std::string const& GetComment() const; + LfgDungeonSet const& GetSelectedDungeons() const; private: // General @@ -54,6 +59,9 @@ class LfgPlayerData LfgState m_OldState; ///< Old State // Player LfgLockMap m_LockedDungeons; ///< Dungeons player can't do and reason + uint8 m_Team; ///< Player team - determines the queue to join + uint64 m_Group; ///< Original group of player when joined LFG + // Queue uint8 m_Roles; ///< Roles the player selected when joined LFG std::string m_Comment; ///< Player comment used when joined LFG diff --git a/src/server/game/DungeonFinding/LFGQueue.cpp b/src/server/game/DungeonFinding/LFGQueue.cpp new file mode 100644 index 00000000000..131a3e7a992 --- /dev/null +++ b/src/server/game/DungeonFinding/LFGQueue.cpp @@ -0,0 +1,581 @@ +/* + * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "ObjectDefines.h" +#include "Containers.h" +#include "DBCStructure.h" +#include "DBCStores.h" +#include "Group.h" +#include "LFGQueue.h" +#include "LFGMgr.h" +#include "Log.h" +#include "ObjectMgr.h" +#include "World.h" +#include "GroupMgr.h" + +/** + Given a list of guids returns the concatenation using | as delimiter + + @param[in] check list of guids + @returns Concatenated string +*/ +std::string ConcatenateGuids(LfgGuidList const& check) +{ + if (check.empty()) + return ""; + + // need the guids in order to avoid duplicates + LfgGuidSet guids(check.begin(), check.end()); + + std::ostringstream o; + + LfgGuidSet::const_iterator it = guids.begin(); + o << (*it); + for (++it; it != guids.end(); ++it) + o << '|' << (*it); + + return o.str(); +} + +char const * GetCompatibleString(LfgCompatibility compatibles) +{ + switch (compatibles) + { + case LFG_COMPATIBILITY_PENDING: + return "Pending"; + case LFG_COMPATIBLES_BAD_STATES: + return "Compatibles (Bad States)"; + case LFG_COMPATIBLES_MATCH: + return "Match"; + case LFG_COMPATIBLES_WITH_LESS_PLAYERS: + return "Compatibles (Not enough players)"; + case LFG_INCOMPATIBLES_HAS_IGNORES: + return "Has ignores"; + case LFG_INCOMPATIBLES_MULTIPLE_LFG_GROUPS: + return "Multiple Lfg Groups"; + case LFG_INCOMPATIBLES_NO_DUNGEONS: + return "Incompatible dungeons"; + case LFG_INCOMPATIBLES_NO_ROLES: + return "Incompatible roles"; + case LFG_INCOMPATIBLES_TOO_MUCH_PLAYERS: + return "Too much players"; + case LFG_INCOMPATIBLES_WRONG_GROUP_SIZE: + return "Wrong group size"; + default: + return "Unknown"; + } +} + +void LfgQueue::AddToQueue(uint64 guid) +{ + LfgQueueDataMap::iterator itQueue = m_QueueDataMap.find(guid); + if (itQueue == m_QueueDataMap.end()) + { + sLog->outError(LOG_FILTER_LFG, "LfgQueue::AddToQueue: Queue data not found for [" UI64FMTD "]", guid); + return; + } + + AddToNewQueue(guid); +} + +void LfgQueue::RemoveFromQueue(uint64 guid) +{ + RemoveFromNewQueue(guid); + RemoveFromCurrentQueue(guid); + RemoveFromCompatibles(guid); + RemoveQueueData(guid); +} + +void LfgQueue::AddToNewQueue(uint64 guid) +{ + m_newToQueue.push_back(guid); +} + +void LfgQueue::RemoveFromNewQueue(uint64 guid) +{ + m_newToQueue.remove(guid); +} + +void LfgQueue::AddToCurrentQueue(uint64 guid) +{ + m_currentQueue.push_back(guid); +} + +void LfgQueue::RemoveFromCurrentQueue(uint64 guid) +{ + m_currentQueue.remove(guid); +} + +void LfgQueue::AddQueueData(uint64 guid, time_t joinTime, const LfgDungeonSet &dungeons, const LfgRolesMap &rolesMap) +{ + m_QueueDataMap[guid] = LfgQueueData(joinTime, dungeons, rolesMap); + AddToQueue(guid); +} + +void LfgQueue::RemoveQueueData(uint64 guid) +{ + LfgQueueDataMap::iterator it = m_QueueDataMap.find(guid); + if (it != m_QueueDataMap.end()) + m_QueueDataMap.erase(it); +} + +void LfgQueue::UpdateWaitTimeAvg(int32 waitTime, uint32 dungeonId) +{ + LfgWaitTime &wt = m_waitTimesAvg[dungeonId]; + uint32 old_number = wt.number++; + wt.time = int32((wt.time * old_number + waitTime) / wt.number); +} + +void LfgQueue::UpdateWaitTimeTank(int32 waitTime, uint32 dungeonId) +{ + LfgWaitTime &wt = m_waitTimesTank[dungeonId]; + uint32 old_number = wt.number++; + wt.time = int32((wt.time * old_number + waitTime) / wt.number); +} + +void LfgQueue::UpdateWaitTimeHealer(int32 waitTime, uint32 dungeonId) +{ + LfgWaitTime &wt = m_waitTimesHealer[dungeonId]; + uint32 old_number = wt.number++; + wt.time = int32((wt.time * old_number + waitTime) / wt.number); +} + +void LfgQueue::UpdateWaitTimeDps(int32 waitTime, uint32 dungeonId) +{ + LfgWaitTime &wt = m_waitTimesDps[dungeonId]; + uint32 old_number = wt.number++; + wt.time = int32((wt.time * old_number + waitTime) / wt.number); +} + +/** + Remove from cached compatible dungeons any entry that contains the given guid + + @param[in] guid Guid to remove from compatible cache +*/ +void LfgQueue::RemoveFromCompatibles(uint64 guid) +{ + std::stringstream out; + out << guid; + std::string strGuid = out.str(); + + sLog->outDebug(LOG_FILTER_LFG, "LfgQueue::RemoveFromCompatibles: Removing [" UI64FMTD "]", guid); + for (LfgCompatibleMap::iterator itNext = m_CompatibleMap.begin(); itNext != m_CompatibleMap.end();) + { + LfgCompatibleMap::iterator it = itNext++; + if (it->first.find(strGuid) != std::string::npos) // Found, remove it + m_CompatibleMap.erase(it); + } +} + + +/** + Stores the compatibility of a list of guids + + @param[in] key String concatenation of guids (| used as separator) + @param[in] compatibles type of compatibility +*/ +void LfgQueue::SetCompatibles(const std::string &key, LfgCompatibility compatibles) +{ + m_CompatibleMap[key] = compatibles; +} + +/** + Get the compatibility of a group of guids + + @param[in] key String concatenation of guids (| used as separator) + @return LfgCompatibility type of compatibility +*/ +LfgCompatibility LfgQueue::GetCompatibles(std::string const& key) +{ + LfgCompatibleMap::iterator it = m_CompatibleMap.find(key); + if (it != m_CompatibleMap.end()) + return it->second; + + return LFG_COMPATIBILITY_PENDING; +} + +uint8 LfgQueue::FindGroups() +{ + uint8 proposals = 0; + LfgGuidList firstNew; + while (!m_newToQueue.empty()) + { + uint64 frontguid = m_newToQueue.front(); + sLog->outDebug(LOG_FILTER_LFG, "LfgQueue::FindGroups: checking [" UI64FMTD "] newToQueue(%u), currentQueue(%u)", frontguid, uint32(m_newToQueue.size()), uint32(m_currentQueue.size())); + firstNew.clear(); + firstNew.push_back(frontguid); + RemoveFromNewQueue(frontguid); + + LfgGuidList temporalList = m_currentQueue; + LfgCompatibility compatibles = FindNewGroups(firstNew, temporalList); + + if (compatibles == LFG_COMPATIBLES_MATCH) + ++proposals; + else + AddToCurrentQueue(frontguid); // Lfg group not found, add this group to the queue. + } + return proposals; +} + +/** + Checks que main queue to try to form a Lfg group. Returns first match found (if any) + + @param[in] check List of guids trying to match with other groups + @param[in] all List of all other guids in main queue to match against + @return LfgCompatibility type of compatibility between groups +*/ +LfgCompatibility LfgQueue::FindNewGroups(LfgGuidList& check, LfgGuidList& all) +{ + std::string strGuids = ConcatenateGuids(check); + LfgCompatibility compatibles = GetCompatibles(strGuids); + + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::FindNewGroup: (%s): %s - all(%s)", strGuids.c_str(), GetCompatibleString(compatibles), ConcatenateGuids(all).c_str()); + if (compatibles == LFG_COMPATIBILITY_PENDING || compatibles == LFG_COMPATIBLES_BAD_STATES) // Not previously cached, calculate + compatibles = CheckCompatibility(check); + + if (compatibles != LFG_COMPATIBLES_WITH_LESS_PLAYERS) + return compatibles; + + // Try to match with queued groups + while (!all.empty()) + { + check.push_back(all.front()); + all.pop_front(); + LfgCompatibility subcompatibility = FindNewGroups(check, all); + if (subcompatibility == LFG_COMPATIBLES_MATCH) + return LFG_COMPATIBLES_MATCH; + check.pop_back(); + } + return compatibles; +} + +/** + Check compatibilities between groups. If group is Matched proposal will be created + + @param[in] check List of guids to check compatibilities + @return LfgCompatibility type of compatibility +*/ +LfgCompatibility LfgQueue::CheckCompatibility(LfgGuidList check) +{ + std::string strGuids = ConcatenateGuids(check); + LfgProposal proposal; + LfgDungeonSet proposalDungeons; + LfgGroupsMap proposalGroups; + LfgRolesMap proposalRoles; + + // Check for correct size + if (check.size() > MAXGROUPSIZE || check.empty()) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s): Size wrong - Not compatibles", strGuids.c_str()); + return LFG_INCOMPATIBLES_WRONG_GROUP_SIZE; + } + + // Player joining alone always compatible + if (check.size() == 1 && IS_PLAYER_GUID(check.front())) + return LFG_COMPATIBLES_WITH_LESS_PLAYERS; + + // Check all-but-new compatiblitity + if (check.size() > 2) + { + uint64 frontGuid = check.front(); + check.pop_front(); + + // Check all-but-new compatibilities (New, A, B, C, D) --> check(A, B, C, D) + LfgCompatibility child_compatibles = CheckCompatibility(check); + if (child_compatibles < LFG_COMPATIBLES_WITH_LESS_PLAYERS) // Group not compatible + { + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) child %s not compatibles", strGuids.c_str(), ConcatenateGuids(check).c_str()); + SetCompatibles(strGuids, child_compatibles); + return child_compatibles; + } + check.push_front(frontGuid); + } + + // Check if more than one LFG group and number of players joining + uint8 numPlayers = 0; + uint8 numLfgGroups = 0; + for (LfgGuidList::const_iterator it = check.begin(); it != check.end() && numLfgGroups < 2 && numPlayers <= MAXGROUPSIZE; ++it) + { + uint64 guid = (*it); + LfgQueueDataMap::iterator itQueue = m_QueueDataMap.find(guid); + if (itQueue == m_QueueDataMap.end()) + { + sLog->outError(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: [" UI64FMTD "] is not queued but listed as queued!", guid); + RemoveFromQueue(guid); + return LFG_COMPATIBILITY_PENDING; + } + + // Store group so we don't need to call Mgr to get it later (if it's player group will be 0 otherwise would have joined as group) + for (LfgRolesMap::const_iterator it2 = itQueue->second.roles.begin(); it2 != itQueue->second.roles.end(); ++it2) + proposalGroups[it2->first] = IS_GROUP(itQueue->first) ? itQueue->first : 0; + + numPlayers += itQueue->second.roles.size(); + + if (sLFGMgr->IsLfgGroup(guid)) + { + if (!numLfgGroups) + proposal.group = guid; + ++numLfgGroups; + } + } + + // Group with less that MAXGROUPSIZE members always compatible + if (check.size() == 1 && numPlayers != MAXGROUPSIZE) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) sigle group. Compatibles", strGuids.c_str()); + return LFG_COMPATIBLES_WITH_LESS_PLAYERS; + } + + if (numLfgGroups > 1) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) More than one Lfggroup (%u)", strGuids.c_str(), numLfgGroups); + SetCompatibles(strGuids, LFG_INCOMPATIBLES_MULTIPLE_LFG_GROUPS); + return LFG_INCOMPATIBLES_MULTIPLE_LFG_GROUPS; + } + + if (numPlayers > MAXGROUPSIZE) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) Too much players (%u)", strGuids.c_str(), numPlayers); + SetCompatibles(strGuids, LFG_INCOMPATIBLES_TOO_MUCH_PLAYERS); + return LFG_INCOMPATIBLES_TOO_MUCH_PLAYERS; + } + + // If it's single group no need to check for duplicate players, ignores, bad roles or bad dungeons as it's been checked before joining + if (check.size() > 1) + { + for (LfgGuidList::const_iterator it = check.begin(); it != check.end(); ++it) + { + const LfgRolesMap &roles = m_QueueDataMap[(*it)].roles; + for (LfgRolesMap::const_iterator itRoles = roles.begin(); itRoles != roles.end(); ++itRoles) + { + LfgRolesMap::const_iterator itPlayer; + for (itPlayer = proposalRoles.begin(); itPlayer != proposalRoles.end(); ++itPlayer) + { + if (itRoles->first == itPlayer->first) + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: ERROR! Player multiple times in queue! [" UI64FMTD "]", itRoles->first); + else if (sLFGMgr->HasIgnore(itRoles->first, itPlayer->first)) + break; + } + if (itPlayer == proposalRoles.end()) + proposalRoles[itRoles->first] = itRoles->second; + } + } + + if (uint8 playersize = numPlayers - proposalRoles.size()) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) not compatible, %u players are ignoring each other", strGuids.c_str(), playersize); + SetCompatibles(strGuids, LFG_INCOMPATIBLES_HAS_IGNORES); + return LFG_INCOMPATIBLES_HAS_IGNORES; + } + + LfgRolesMap debugRoles = proposalRoles; // DEBUG + if (!LFGMgr::CheckGroupRoles(proposalRoles)) + { + std::ostringstream o; + for (LfgRolesMap::const_iterator it = debugRoles.begin(); it != debugRoles.end(); ++it) + o << ", " << it->first << ": " << sLFGMgr->GetRolesString(it->second); + + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) Roles not compatible%s", strGuids.c_str(), o.str().c_str()); + SetCompatibles(strGuids, LFG_INCOMPATIBLES_NO_ROLES); + return LFG_INCOMPATIBLES_NO_ROLES; + } + + LfgGuidList::iterator itguid = check.begin(); + proposalDungeons = m_QueueDataMap[*itguid].dungeons; + std::ostringstream o; + o << ", " << *itguid << ": (" << sLFGMgr->ConcatenateDungeons(proposalDungeons) << ")"; + for (++itguid; itguid != check.end(); ++itguid) + { + LfgDungeonSet temporal; + LfgDungeonSet &dungeons = m_QueueDataMap[*itguid].dungeons; + o << ", " << *itguid << ": (" << sLFGMgr->ConcatenateDungeons(dungeons) << ")"; + std::set_intersection(proposalDungeons.begin(), proposalDungeons.end(), dungeons.begin(), dungeons.end(), std::inserter(temporal, temporal.begin())); + proposalDungeons = temporal; + } + + if (proposalDungeons.empty()) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) No compatible dungeons%s", strGuids.c_str(), o.str().c_str()); + SetCompatibles(strGuids, LFG_INCOMPATIBLES_NO_DUNGEONS); + return LFG_INCOMPATIBLES_NO_DUNGEONS; + } + } + else + { + uint64 gguid = *check.begin(); + const LfgQueueData &queue = m_QueueDataMap[gguid]; + proposalDungeons = queue.dungeons; + proposalRoles = queue.roles; + LFGMgr::CheckGroupRoles(proposalRoles); // assing new roles + } + + // Enough players? + if (numPlayers != MAXGROUPSIZE) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) Compatibles but not enough players(%u)", strGuids.c_str(), numPlayers); + SetCompatibles(strGuids, LFG_COMPATIBLES_WITH_LESS_PLAYERS); + return LFG_COMPATIBLES_WITH_LESS_PLAYERS; + } + + proposal.queues = check; + if (check.size() == 1) + { + for (LfgGroupsMap::const_iterator it = proposalGroups.begin(); it != proposalGroups.end(); ++it) + if (proposal.group && it->second != proposal.group) + proposal.isNew = false; + } + + if (!sLFGMgr->AllQueued(check)) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) Group MATCH but can't create proposal!", strGuids.c_str()); + SetCompatibles(strGuids, LFG_COMPATIBLES_BAD_STATES); + return LFG_COMPATIBLES_BAD_STATES; + } + + // Create a new proposal + proposal.cancelTime = time(NULL) + LFG_TIME_PROPOSAL; + proposal.state = LFG_PROPOSAL_INITIATING; + proposal.leader = 0; + proposal.dungeonId = Trinity::Containers::SelectRandomContainerElement(proposalDungeons); + + bool leader = false; + for (LfgRolesMap::const_iterator itRoles = proposalRoles.begin(); itRoles != proposalRoles.end(); ++itRoles) + { + // Assing new leader + if (itRoles->second & PLAYER_ROLE_LEADER) + { + if (!leader || !proposal.leader || urand(0, 1)) + proposal.leader = itRoles->first; + leader = true; + } + else if (!leader && (!proposal.leader || urand(0, 1))) + proposal.leader = itRoles->first; + + // Assing player data and roles + LfgProposalPlayer &data = proposal.players[itRoles->first]; + data.role = itRoles->second; + data.group = proposalGroups.find(itRoles->first)->second; + if (!proposal.isNew && data.group && data.group == proposal.group) // Player from existing group, autoaccept + data.accept = LFG_ANSWER_AGREE; + } + + // Mark proposal members as not queued (but not remove queue data) + for (LfgGuidList::const_iterator itQueue = proposal.queues.begin(); itQueue != proposal.queues.end(); ++itQueue) + { + uint64 guid = (*itQueue); + RemoveFromNewQueue(guid); + RemoveFromCurrentQueue(guid); + } + + sLFGMgr->AddProposal(proposal); + + sLog->outDebug(LOG_FILTER_LFG, "LFGQueue::CheckCompatibility: (%s) MATCH! Group formed", strGuids.c_str()); + SetCompatibles(strGuids, LFG_COMPATIBLES_MATCH); + return LFG_COMPATIBLES_MATCH; +} + +void LfgQueue::UpdateQueueTimers(time_t currTime) +{ + for (LfgQueueDataMap::const_iterator itQueue = m_QueueDataMap.begin(); itQueue != m_QueueDataMap.end(); ++itQueue) + { + const LfgQueueData &queueinfo = itQueue->second; + uint32 dungeonId = (*queueinfo.dungeons.begin()); + uint32 queuedTime = uint32(currTime - queueinfo.joinTime); + uint8 role = PLAYER_ROLE_NONE; + int32 waitTime = -1; + int32 wtTank = m_waitTimesTank[dungeonId].time; + int32 wtHealer = m_waitTimesHealer[dungeonId].time; + int32 wtDps = m_waitTimesDps[dungeonId].time; + int32 wtAvg = m_waitTimesAvg[dungeonId].time; + + for (LfgRolesMap::const_iterator itPlayer = queueinfo.roles.begin(); itPlayer != queueinfo.roles.end(); ++itPlayer) + role |= itPlayer->second; + role &= ~PLAYER_ROLE_LEADER; + + switch (role) + { + case PLAYER_ROLE_NONE: // Should not happen - just in case + waitTime = -1; + break; + case PLAYER_ROLE_TANK: + waitTime = wtTank; + break; + case PLAYER_ROLE_HEALER: + waitTime = wtHealer; + break; + case PLAYER_ROLE_DAMAGE: + waitTime = wtDps; + break; + default: + waitTime = wtAvg; + break; + } + + LfgQueueStatusData queueData(dungeonId, waitTime, wtAvg, wtTank, wtHealer, wtDps, queuedTime, queueinfo.tanks, queueinfo.healers, queueinfo.dps); + for (LfgRolesMap::const_iterator itPlayer = queueinfo.roles.begin(); itPlayer != queueinfo.roles.end(); ++itPlayer) + { + uint64 pguid = itPlayer->first; + LFGMgr::SendLfgQueueStatus(pguid, queueData); + } + } +} + +time_t LfgQueue::GetJoinTime(uint64 guid) +{ + return m_QueueDataMap[guid].joinTime; +} + +LfgCompatibleMap const& LfgQueue::GetCompatibleMap() +{ + return m_CompatibleMap; +} + +std::string LfgQueue::DumpQueueInfo() const +{ + uint32 players = 0; + uint32 groups = 0; + uint32 playersInGroup = 0; + + for (uint8 i = 0; i < 2; ++i) + { + LfgGuidList const& queue = i ? m_newToQueue : m_currentQueue; + for (LfgGuidList::const_iterator it = queue.begin(); it != queue.end(); ++it) + { + uint64 guid = *it; + if (IS_GROUP(guid)) + { + groups++; + if (Group const* group = sGroupMgr->GetGroupByGUID(GUID_LOPART(guid))) + playersInGroup += group->GetMembersCount(); + else + playersInGroup += 2; // Shouldn't happen but just in case + } + else + players++; + } + } + std::ostringstream o; + o << "Queued Players: " << players << "(in group: " << playersInGroup << ") Groups: " << groups << "\n"; + return o.str(); +} + +std::string LfgQueue::DumpCompatibleInfo() const +{ + std::ostringstream o; + o << "Compatible Map size: " << m_CompatibleMap.size() << "\n"; + return o.str(); +} diff --git a/src/server/game/DungeonFinding/LFGQueue.h b/src/server/game/DungeonFinding/LFGQueue.h new file mode 100644 index 00000000000..f08199d725a --- /dev/null +++ b/src/server/game/DungeonFinding/LFGQueue.h @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef _LFGQUEUE_H +#define _LFGQUEUE_H + +#include "LFG.h" + +enum LfgCompatibility +{ + LFG_COMPATIBILITY_PENDING, + LFG_INCOMPATIBLES_WRONG_GROUP_SIZE, + LFG_INCOMPATIBLES_TOO_MUCH_PLAYERS, + LFG_INCOMPATIBLES_MULTIPLE_LFG_GROUPS, + LFG_INCOMPATIBLES_HAS_IGNORES, + LFG_INCOMPATIBLES_NO_ROLES, + LFG_INCOMPATIBLES_NO_DUNGEONS, + LFG_COMPATIBLES_WITH_LESS_PLAYERS, // Values under this = not compatible (do not modify order) + LFG_COMPATIBLES_BAD_STATES, + LFG_COMPATIBLES_MATCH // Must be the last one +}; + +/// Stores player or group queue info +struct LfgQueueData +{ + LfgQueueData(): joinTime(time_t(time(NULL))), tanks(LFG_TANKS_NEEDED), + healers(LFG_HEALERS_NEEDED), dps(LFG_DPS_NEEDED) + { } + + LfgQueueData(time_t _joinTime, const LfgDungeonSet &_dungeons, const LfgRolesMap &_roles) + { + joinTime = _joinTime; + dungeons = _dungeons; + roles = _roles; + tanks = LFG_TANKS_NEEDED; + healers = LFG_HEALERS_NEEDED; + dps = LFG_DPS_NEEDED; + + for (LfgRolesMap::const_iterator it = roles.begin(); it != roles.end(); ++it) + { + uint8 role = it->second; + if (role & PLAYER_ROLE_TANK) + --tanks; + else if (role & PLAYER_ROLE_HEALER) + --healers; + else + --dps; + } + } + + time_t joinTime; ///< Player queue join time (to calculate wait times) + uint8 tanks; ///< Tanks needed + uint8 healers; ///< Healers needed + uint8 dps; ///< Dps needed + LfgDungeonSet dungeons; ///< Selected Player/Group Dungeon/s + LfgRolesMap roles; ///< Selected Player Role/s +}; + +struct LfgWaitTime +{ + LfgWaitTime(): time(-1), number(0) {} + int32 time; ///< Wait time + uint32 number; ///< Number of people used to get that wait time +}; + +typedef std::map<uint32, LfgWaitTime> LfgWaitTimesMap; +typedef std::map<std::string, LfgCompatibility> LfgCompatibleMap; +typedef std::map<uint64, LfgQueueData> LfgQueueDataMap; + +/** + Stores all data related to queue +*/ +class LfgQueue +{ + public: + + // Add/Remove from queue + void AddToQueue(uint64 guid); + void RemoveFromQueue(uint64 guid); + void AddQueueData(uint64 guid, time_t joinTime, const LfgDungeonSet &dungeons, const LfgRolesMap &rolesMap); + void RemoveQueueData(uint64 guid); + + // Update Timers (when proposal success) + void UpdateWaitTimeAvg(int32 waitTime, uint32 dungeonId); + void UpdateWaitTimeTank(int32 waitTime, uint32 dungeonId); + void UpdateWaitTimeHealer(int32 waitTime, uint32 dungeonId); + void UpdateWaitTimeDps(int32 waitTime, uint32 dungeonId); + + // Update Queue timers + void UpdateQueueTimers(time_t currTime); + time_t GetJoinTime(uint64 guid); + + // Find new group + uint8 FindGroups(); + + // Just for debugging purposes + LfgCompatibleMap const& GetCompatibleMap(); + std::string DumpQueueInfo() const; + std::string DumpCompatibleInfo() const; + private: + void AddToNewQueue(uint64 guid); + void AddToCurrentQueue(uint64 guid); + void RemoveFromNewQueue(uint64 guid); + void RemoveFromCurrentQueue(uint64 guid); + + void SetCompatibles(std::string const& key, LfgCompatibility compatibles); + LfgCompatibility GetCompatibles(std::string const& key); + void RemoveFromCompatibles(uint64 guid); + + LfgCompatibility FindNewGroups(LfgGuidList& check, LfgGuidList& all); + LfgCompatibility CheckCompatibility(LfgGuidList check); + + // Queue + LfgQueueDataMap m_QueueDataMap; ///< Queued groups + LfgCompatibleMap m_CompatibleMap; ///< Compatible dungeons + + LfgWaitTimesMap m_waitTimesAvg; ///< Average wait time to find a group queuing as multiple roles + LfgWaitTimesMap m_waitTimesTank; ///< Average wait time to find a group queuing as tank + LfgWaitTimesMap m_waitTimesHealer; ///< Average wait time to find a group queuing as healer + LfgWaitTimesMap m_waitTimesDps; ///< Average wait time to find a group queuing as dps + LfgGuidList m_currentQueue; ///< Ordered list. Used to find groups + LfgGuidList m_newToQueue; ///< New groups to add to queue +}; + +#endif diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp index 36f04b3020b..522ded70550 100644 --- a/src/server/game/DungeonFinding/LFGScripts.cpp +++ b/src/server/game/DungeonFinding/LFGScripts.cpp @@ -34,24 +34,34 @@ LFGPlayerScript::LFGPlayerScript() : PlayerScript("LFGPlayerScript") void LFGPlayerScript::OnLevelChanged(Player* player, uint8 /*oldLevel*/) { + if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) + return; + sLFGMgr->InitializeLockedDungeons(player); } void LFGPlayerScript::OnLogout(Player* player) { - sLFGMgr->Leave(player); + if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) + return; + + uint64 guid = player->GetGUID(); + sLFGMgr->LeaveLfg(guid); LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_REMOVED_FROM_QUEUE); player->GetSession()->SendLfgUpdateParty(updateData); player->GetSession()->SendLfgUpdatePlayer(updateData); - player->GetSession()->SendLfgUpdateSearch(false); - uint64 guid = player->GetGUID(); + player->GetSession()->SendLfgLfrList(false); // TODO - Do not remove, add timer before deleting sLFGMgr->RemovePlayerData(guid); } void LFGPlayerScript::OnLogin(Player* player) { + if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) + return; + sLFGMgr->InitializeLockedDungeons(player); + sLFGMgr->SetTeam(player->GetGUID(), player->GetTeam()); // TODO - Restore LfgPlayerData and send proper status to player if it was in a group } @@ -68,80 +78,103 @@ LFGGroupScript::LFGGroupScript() : GroupScript("LFGGroupScript") void LFGGroupScript::OnAddMember(Group* group, uint64 guid) { - uint64 gguid = group->GetGUID(); - if (!gguid) + if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) return; - sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnAddMember [" UI64FMTD "]: added [" UI64FMTD "]", gguid, guid); - LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_CLEAR_LOCK_LIST); - for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) + uint64 gguid = group->GetGUID(); + uint64 leader = group->GetLeaderGUID(); + + if (leader == guid) + { + sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnAddMember [" UI64FMTD "]: added [" UI64FMTD "] leader " UI64FMTD "]", gguid, guid, leader); + sLFGMgr->SetLeader(gguid, guid); + } + else { - if (Player* plrg = itr->getSource()) + LfgState gstate = sLFGMgr->GetState(gguid); + LfgState state = sLFGMgr->GetState(guid); + sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnAddMember [" UI64FMTD "]: added [" UI64FMTD "] leader " UI64FMTD "] gstate: %u, state: %u", gguid, guid, leader, gstate, state); + LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_CLEAR_LOCK_LIST); + for (GroupReference* itr = group->GetFirstMember(); itr != NULL; itr = itr->next()) { - plrg->GetSession()->SendLfgUpdatePlayer(updateData); - plrg->GetSession()->SendLfgUpdateParty(updateData); + if (Player* plrg = itr->getSource()) + { + plrg->GetSession()->SendLfgUpdatePlayer(updateData); + plrg->GetSession()->SendLfgUpdateParty(updateData); + } } - } - // TODO - if group is queued and new player is added convert to rolecheck without notify the current players queued - if (sLFGMgr->GetState(gguid) == LFG_STATE_QUEUED) - sLFGMgr->Leave(NULL, group); + if (state == LFG_STATE_QUEUED) + sLFGMgr->LeaveLfg(guid); - if (sLFGMgr->GetState(guid) == LFG_STATE_QUEUED) - if (Player* player = ObjectAccessor::FindPlayer(guid)) - sLFGMgr->Leave(player); + // TODO - if group is queued and new player is added convert to rolecheck without notify the current players queued + if (gstate == LFG_STATE_QUEUED) + sLFGMgr->LeaveLfg(gguid); + } + + sLFGMgr->SetGroup(guid, gguid); + sLFGMgr->AddPlayerToGroup(gguid, guid); } void LFGGroupScript::OnRemoveMember(Group* group, uint64 guid, RemoveMethod method, uint64 kicker, char const* reason) { - uint64 gguid = group->GetGUID(); - if (!gguid || method == GROUP_REMOVEMETHOD_DEFAULT) + if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) return; + uint64 gguid = group->GetGUID(); sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnRemoveMember [" UI64FMTD "]: remove [" UI64FMTD "] Method: %d Kicker: [" UI64FMTD "] Reason: %s", gguid, guid, method, kicker, (reason ? reason : "")); - if (sLFGMgr->GetState(gguid) == LFG_STATE_QUEUED) - { - // TODO - Do not remove, just remove the one leaving and rejoin queue with all other data - sLFGMgr->Leave(NULL, group); - } - if (!group->isLFGGroup()) - return; + bool isLFG = group->isLFGGroup(); - if (method == GROUP_REMOVEMETHOD_KICK) // Player have been kicked + if (isLFG && method == GROUP_REMOVEMETHOD_KICK) // Player have been kicked { // TODO - Update internal kick cooldown of kicker std::string str_reason = ""; if (reason) str_reason = std::string(reason); - sLFGMgr->InitBoot(group, kicker, guid, str_reason); + sLFGMgr->InitBoot(gguid, kicker, guid, str_reason); + return; + } + + LfgState state = sLFGMgr->GetState(gguid); + + // If group is being formed after proposal success do nothing more + if (state == LFG_STATE_PROPOSAL && method == GROUP_REMOVEMETHOD_DEFAULT) + { + // LfgData: Remove player from group + sLFGMgr->SetGroup(guid, 0); + sLFGMgr->RemovePlayerFromGroup(gguid, guid); return; } - uint32 state = sLFGMgr->GetState(gguid); - sLFGMgr->ClearState(guid); + sLFGMgr->LeaveLfg(guid); sLFGMgr->SetState(guid, LFG_STATE_NONE); + sLFGMgr->SetGroup(guid, 0); + uint8 players = sLFGMgr->RemovePlayerFromGroup(gguid, guid); + if (Player* player = ObjectAccessor::FindPlayer(guid)) { - if (method == GROUP_REMOVEMETHOD_LEAVE && sLFGMgr->GetState(gguid) != LFG_STATE_FINISHED_DUNGEON && sLFGMgr->GetDungeon(gguid, false)) + if (method == GROUP_REMOVEMETHOD_LEAVE && state == LFG_STATE_DUNGEON && + players >= LFG_GROUP_KICK_VOTES_NEEDED) player->CastSpell(player, LFG_SPELL_DUNGEON_DESERTER, true); - /* - else if (group->isLfgKickActive()) + //else if (state == LFG_STATE_BOOT) // Update internal kick cooldown of kicked - */ - LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_LEADER); - player->GetSession()->SendLfgUpdateParty(updateData); - if (player->GetMap()->IsDungeon()) // Teleport player out the dungeon + player->GetSession()->SendLfgUpdateParty(LfgUpdateData(LFG_UPDATETYPE_LEADER_UNK1)); + if (isLFG && player->GetMap()->IsDungeon()) // Teleport player out the dungeon sLFGMgr->TeleportPlayer(player, true); } - if (state != LFG_STATE_FINISHED_DUNGEON)// Need more players to finish the dungeon - sLFGMgr->OfferContinue(group); + if (isLFG && state != LFG_STATE_FINISHED_DUNGEON) // Need more players to finish the dungeon + if (Player* leader = ObjectAccessor::FindPlayer(sLFGMgr->GetLeader(gguid))) + leader->GetSession()->SendLfgOfferContinue(sLFGMgr->GetDungeon(gguid, false)); } void LFGGroupScript::OnDisband(Group* group) { + if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) + return; + uint64 gguid = group->GetGUID(); sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnDisband [" UI64FMTD "]", gguid); @@ -150,31 +183,26 @@ void LFGGroupScript::OnDisband(Group* group) void LFGGroupScript::OnChangeLeader(Group* group, uint64 newLeaderGuid, uint64 oldLeaderGuid) { - uint64 gguid = group->GetGUID(); - if (!gguid) + if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) return; - sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnChangeLeader [" UI64FMTD "]: old [" UI64FMTD "] new [" UI64FMTD "]", gguid, newLeaderGuid, oldLeaderGuid); - Player* player = ObjectAccessor::FindPlayer(newLeaderGuid); - - LfgUpdateData updateData = LfgUpdateData(LFG_UPDATETYPE_LEADER); - if (player) - player->GetSession()->SendLfgUpdateParty(updateData); + uint64 gguid = group->GetGUID(); - player = ObjectAccessor::FindPlayer(oldLeaderGuid); - if (player) - { - updateData.updateType = LFG_UPDATETYPE_GROUP_DISBAND; - player->GetSession()->SendLfgUpdateParty(updateData); - } + sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnChangeLeader [" UI64FMTD "]: old [" UI64FMTD "] new [" UI64FMTD "]", gguid, newLeaderGuid, oldLeaderGuid); + sLFGMgr->SetLeader(gguid, newLeaderGuid); } void LFGGroupScript::OnInviteMember(Group* group, uint64 guid) { - uint64 gguid = group->GetGUID(); - if (!gguid) + if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER)) return; - sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnInviteMember [" UI64FMTD "]: invite [" UI64FMTD "] leader [" UI64FMTD "]", gguid, guid, group->GetLeaderGUID()); - sLFGMgr->Leave(NULL, group); + uint64 gguid = group->GetGUID(); + uint64 leader = group->GetLeaderGUID(); + sLog->outDebug(LOG_FILTER_LFG, "LFGScripts::OnInviteMember [" UI64FMTD "]: invite [" UI64FMTD "] leader [" UI64FMTD "]", gguid, guid, leader); + // No gguid == new group being formed + // No leader == after group creation first invite is new leader + // leader and no gguid == first invite after leader is added to new group (this is the real invite) + if (leader && !gguid) + sLFGMgr->LeaveLfg(leader); } diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index da6e4ef8407..ee85cbbc301 100755 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1115,6 +1115,19 @@ void GameObject::Use(Unit* user) player->SendPreparedGossip(this); return; } + case GAMEOBJECT_TYPE_TRAP: //6 + { + GameObjectTemplate const* goInfo = GetGOInfo(); + if (goInfo->trap.spellId) + CastSpell(user, goInfo->trap.spellId); + + m_cooldownTime = time(NULL) + (goInfo->trap.cooldown ? goInfo->trap.cooldown : uint32(4)); // template or 4 seconds + + if (goInfo->trap.type == 1) // Deactivate after trigger + SetLootState(GO_JUST_DEACTIVATED); + + return; + } //Sitting: Wooden bench, chairs enzz case GAMEOBJECT_TYPE_CHAIR: //7 { @@ -1522,14 +1535,18 @@ void GameObject::Use(Unit* user) Player* player = user->ToPlayer(); - if (player->CanUseBattlegroundObject()) + if (player->CanUseBattlegroundObject(this)) { // in battleground check Battleground* bg = player->GetBattleground(); if (!bg) return; + if (player->GetVehicle()) return; + + player->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); + player->RemoveAurasByType(SPELL_AURA_MOD_INVISIBILITY); // BG flag click // AB: // 15001 @@ -1562,14 +1579,18 @@ void GameObject::Use(Unit* user) Player* player = user->ToPlayer(); - if (player->CanUseBattlegroundObject()) + if (player->CanUseBattlegroundObject(this)) { // in battleground check Battleground* bg = player->GetBattleground(); if (!bg) return; + if (player->GetVehicle()) return; + + player->RemoveAurasByType(SPELL_AURA_MOD_STEALTH); + player->RemoveAurasByType(SPELL_AURA_MOD_INVISIBILITY); // BG flag dropped // WS: // 179785 - Silverwing Flag diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 0d31c40f3a1..e06463bf0aa 100755 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -9053,7 +9053,6 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) { // data depends on zoneid/mapid... Battleground* bg = GetBattleground(); - uint16 NumberOfFields = 0; uint32 mapid = GetMapId(); OutdoorPvP* pvp = sOutdoorPvPMgr->GetOutdoorPvPToZoneId(zoneid); InstanceScript* instance = GetInstanceScript(); @@ -9061,94 +9060,12 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) sLog->outDebug(LOG_FILTER_NETWORKIO, "Sending SMSG_INIT_WORLD_STATES to Map: %u, Zone: %u", mapid, zoneid); - // may be exist better way to do this... - switch (zoneid) - { - case 0: - case 1: - case 4: - case 8: - case 10: - case 11: - case 12: - case 36: - case 38: - case 40: - case 41: - case 51: - case 267: - case 1519: - case 1537: - case 2257: - case 2918: - NumberOfFields = 8; - break; - case 139: - NumberOfFields = 41; - break; - case 1377: - NumberOfFields = 15; - break; - case 2597: - NumberOfFields = 83; - break; - case 3277: - NumberOfFields = 18; - break; - case 3358: - case 3820: - NumberOfFields = 40; - break; - case 3483: - NumberOfFields = 27; - break; - case 3518: - NumberOfFields = 39; - break; - case 3519: - NumberOfFields = 38; - break; - case 3521: - NumberOfFields = 37; - break; - case 3698: - case 3702: - case 3968: - case 4378: - case 3703: - NumberOfFields = 11; - break; - case 4384: - NumberOfFields = 30; - break; - case 4710: - NumberOfFields = 28; - break; - case 4812: // Icecrown Citadel - case 4100: // The Culling of Stratholme - NumberOfFields = 13; - break; - case 4987: // The Ruby Sanctum - NumberOfFields = 3; - break; - case 4273: // Ulduar - NumberOfFields = 10; - break; - case 4197: // Wintergrasp - /// Use the max here, and fill with zeros if missing. - NumberOfFields = 10 + WG_MAX_OBJ + WG_MAX_WORKSHOP; - break; - default: - NumberOfFields = 12; - break; - } - - WorldPacket data(SMSG_INIT_WORLD_STATES, (4+4+4+2+(NumberOfFields*8))); + WorldPacket data(SMSG_INIT_WORLD_STATES, (4+4+4+2+(12*8))); data << uint32(mapid); // mapid data << uint32(zoneid); // zone id data << uint32(areaid); // area id, new 2.1.0 size_t countPos = data.wpos(); - data << uint16(NumberOfFields); // count of uint64 blocks + data << uint16(0); // count of uint64 blocks data << uint32(0x8d8) << uint32(0x0); // 1 data << uint32(0x8d7) << uint32(0x0); // 2 data << uint32(0x8d6) << uint32(0x0); // 3 @@ -9711,8 +9628,6 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) bf->FillInitialWorldStates(data); break; } - else - data.put<uint16>(countPos, 12); // No break here, intended. default: data << uint32(0x914) << uint32(0x0); // 7 @@ -9721,6 +9636,10 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid) data << uint32(0x915) << uint32(0x0); // 10 break; } + + uint16 length = (data.wpos() - countPos) / 8; + data.put<uint16>(countPos, length); + GetSession()->SendPacket(&data); SendBGWeekendWorldStates(); SendBattlefieldWorldStates(); @@ -11982,7 +11901,7 @@ InventoryResult Player::CanRollForItemInLFG(ItemTemplate const* proto, WorldObje bool lootedObjectInDungeon = false; Map const* map = lootedObject->GetMap(); if (uint32 dungeonId = sLFGMgr->GetDungeon(GetGroup()->GetGUID(), true)) - if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId)) + if (LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId)) if (uint32(dungeon->map) == map->GetId() && dungeon->difficulty == uint32(map->GetDifficulty())) lootedObjectInDungeon = true; @@ -15061,19 +14980,6 @@ void Player::AddQuest(Quest const* quest, Object* questGiver) if (questGiver && quest->GetQuestStartScript() != 0) GetMap()->ScriptsStart(sQuestStartScripts, quest->GetQuestStartScript(), questGiver, this); - // Some spells applied at quest activation - SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(quest_id, true); - if (saBounds.first != saBounds.second) - { - uint32 zone, area; - GetZoneAndAreaId(zone, area); - - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - if (itr->second->autocast && itr->second->IsFitToRequirements(this, zone, area)) - if (!HasAura(itr->second->spellId)) - CastSpell(this, itr->second->spellId, true); - } - UpdateForQuestWorldObjects(); } @@ -15262,33 +15168,6 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT); UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, quest->GetQuestId()); - uint32 zone = 0; - uint32 area = 0; - - // remove auras from spells with quest reward state limitations - SpellAreaForQuestMapBounds saEndBounds = sSpellMgr->GetSpellAreaForQuestEndMapBounds(quest_id); - if (saEndBounds.first != saEndBounds.second) - { - GetZoneAndAreaId(zone, area); - - for (SpellAreaForAreaMap::const_iterator itr = saEndBounds.first; itr != saEndBounds.second; ++itr) - if (!itr->second->IsFitToRequirements(this, zone, area)) - RemoveAurasDueToSpell(itr->second->spellId); - } - - // Some spells applied at quest reward - SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(quest_id, false); - if (saBounds.first != saBounds.second) - { - if (!zone || !area) - GetZoneAndAreaId(zone, area); - - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - if (itr->second->autocast && itr->second->IsFitToRequirements(this, zone, area)) - if (!HasAura(itr->second->spellId)) - CastSpell(this, itr->second->spellId, true); - } - //lets remove flag for delayed teleports SetCanDelayTeleport(false); } @@ -15831,6 +15710,30 @@ void Player::SetQuestStatus(uint32 quest_id, QuestStatus status) m_QuestStatusSave[quest_id] = true; } + uint32 zone = 0, area = 0; + + SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(quest_id); + if (saBounds.first != saBounds.second) + { + GetZoneAndAreaId(zone, area); + + for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if (itr->second->autocast && itr->second->IsFitToRequirements(this, zone, area)) + if (!HasAura(itr->second->spellId)) + CastSpell(this, itr->second->spellId, true); + } + + saBounds = sSpellMgr->GetSpellAreaForQuestEndMapBounds(quest_id); + if (saBounds.first != saBounds.second) + { + if (!zone || !area) + GetZoneAndAreaId(zone, area); + + for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + if (!itr->second->IsFitToRequirements(this, zone, area)) + RemoveAurasDueToSpell(itr->second->spellId); + } + UpdateForQuestWorldObjects(); } @@ -23358,7 +23261,7 @@ PartyResult Player::CanUninviteFromGroup() const if (state == LFG_STATE_BOOT) return ERR_PARTY_LFG_BOOT_IN_PROGRESS; - if (grp->GetMembersCount() <= sLFGMgr->GetVotesNeeded(gguid)) + if (grp->GetMembersCount() <= LFG_GROUP_KICK_VOTES_NEEDED) return ERR_PARTY_LFG_BOOT_TOO_FEW_PLAYERS; if (state == LFG_STATE_FINISHED_DUNGEON) @@ -23391,8 +23294,22 @@ PartyResult Player::CanUninviteFromGroup() const bool Player::isUsingLfg() { - uint64 guid = GetGUID(); - return sLFGMgr->GetState(guid) != LFG_STATE_NONE; + return sLFGMgr->GetState(GetGUID()) != LFG_STATE_NONE; +} + +bool Player::inRandomLfgDungeon() +{ + if (isUsingLfg()) + { + const LfgDungeonSet& dungeons = sLFGMgr->GetSelectedDungeons(GetGUID()); + if (!dungeons.empty()) + { + LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(*dungeons.begin()); + if (dungeon && (dungeon->type == LFG_TYPE_RANDOM || dungeon->seasonal)) + return true; + } + } + return false; } void Player::SetBattlegroundOrBattlefieldRaid(Group* group, int8 subgroup) @@ -23585,26 +23502,24 @@ WorldObject* Player::GetViewpoint() const return NULL; } -bool Player::CanUseBattlegroundObject() +bool Player::CanUseBattlegroundObject(GameObject* gameobject) { - // TODO : some spells gives player ForceReaction to one faction (ReputationMgr::ApplyForceReaction) - // maybe gameobject code should handle that ForceReaction usage + FactionTemplateEntry const* playerFaction = getFactionTemplateEntry(); + FactionTemplateEntry const* faction = sFactionTemplateStore.LookupEntry(gameobject->GetUInt32Value(GAMEOBJECT_FACTION)); + + if (playerFaction && faction && !playerFaction->IsFriendlyTo(*faction)) + return false; + // BUG: sometimes when player clicks on flag in AB - client won't send gameobject_use, only gameobject_report_use packet - return (//InBattleground() && // in battleground - not need, check in other cases - //!IsMounted() && - not correct, player is dismounted when he clicks on flag - //player cannot use object when he is invulnerable (immune) - !isTotalImmune() && // not totally immune - //i'm not sure if these two are correct, because invisible players should get visible when they click on flag - !HasStealthAura() && // not stealthed - !HasInvisibilityAura() && // not invisible - !HasAura(SPELL_RECENTLY_DROPPED_FLAG) && // can't pickup - isAlive() // live player -); + // Note: Mount, stealth and invisibility will be removed when used + return (!isTotalImmune() && // Damage immune + !HasAura(SPELL_RECENTLY_DROPPED_FLAG) && // Still has recently held flag debuff + isAlive()); // Alive } bool Player::CanCaptureTowerPoint() { - return (!HasStealthAura() && // not stealthed + return (!HasStealthAura() && // not stealthed !HasInvisibilityAura() && // not invisible isAlive() // live player ); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 5670ca05a85..9d80a17bc5e 100755 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -2237,7 +2237,7 @@ class Player : public Unit, public GridObject<Player> bool GetBGAccessByLevel(BattlegroundTypeId bgTypeId) const; bool isTotalImmunity(); - bool CanUseBattlegroundObject(); + bool CanUseBattlegroundObject(GameObject* gameobject); bool isTotalImmune(); bool CanCaptureTowerPoint(); @@ -2356,6 +2356,7 @@ class Player : public Unit, public GridObject<Player> void RemoveAtLoginFlag(AtLoginFlags flags, bool persist = false); bool isUsingLfg(); + bool inRandomLfgDungeon(); typedef std::set<uint32> DFQuestsDoneList; DFQuestsDoneList m_DFQuests; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index c1f7a5a1fe5..47449c2bf88 100755 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -6637,7 +6637,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere if (GetTypeId() != TYPEID_PLAYER) { beaconTarget = triggeredByAura->GetBase()->GetCaster(); - if (beaconTarget == this || !(beaconTarget->GetAura(53563, victim->GetGUID()))) + if (!beaconTarget || beaconTarget == this || !(beaconTarget->GetAura(53563, victim->GetGUID()))) return false; basepoints0 = int32(damage); triggered_spell_id = procSpell->IsRankOf(sSpellMgr->GetSpellInfo(635)) ? 53652 : 53654; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index b884a369eeb..17982a58061 100755 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -45,6 +45,7 @@ #include "ScriptMgr.h" #include "SpellScript.h" #include "PoolMgr.h" +#include "LFGMgr.h" ScriptMapMap sQuestEndScripts; ScriptMapMap sQuestStartScripts; @@ -5028,7 +5029,7 @@ void ObjectMgr::LoadInstanceEncounters() continue; } - if (lastEncounterDungeon && !sLFGDungeonStore.LookupEntry(lastEncounterDungeon)) + if (lastEncounterDungeon && !sLFGMgr->GetLFGDungeon(lastEncounterDungeon)) { sLog->outError(LOG_FILTER_SQL, "Table `instance_encounters` has an encounter %u (%s) marked as final for invalid dungeon id %u, skipped!", entry, dungeonEncounter->encounterName[0], lastEncounterDungeon); continue; diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 19ea3ff8174..11d9f502bab 100755 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -523,7 +523,7 @@ bool Group::RemoveMember(uint64 guid, const RemoveMethod &method /*= GROUP_REMOV WorldPacket data; - if (method == GROUP_REMOVEMETHOD_KICK) + if (method == GROUP_REMOVEMETHOD_KICK || method == GROUP_REMOVEMETHOD_KICK_LFG) { data.Initialize(SMSG_GROUP_UNINVITE, 0); player->GetSession()->SendPacket(&data); @@ -600,7 +600,7 @@ bool Group::RemoveMember(uint64 guid, const RemoveMethod &method /*= GROUP_REMOV if (isLFGGroup() && GetMembersCount() == 1) { Player* Leader = ObjectAccessor::FindPlayer(GetLeaderGUID()); - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(sLFGMgr->GetDungeon(GetGUID())); + LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(sLFGMgr->GetDungeon(GetGUID())); if ((Leader && dungeon && Leader->isAlive() && Leader->GetMapId() != uint32(dungeon->map)) || !dungeon) { Disband(); diff --git a/src/server/game/Handlers/LFGHandler.cpp b/src/server/game/Handlers/LFGHandler.cpp index f5d5a0b67e2..96d21097e71 100755 --- a/src/server/game/Handlers/LFGHandler.cpp +++ b/src/server/game/Handlers/LFGHandler.cpp @@ -23,8 +23,6 @@ #include "LFGMgr.h" #include "ObjectMgr.h" #include "GroupMgr.h" -#include "GameEventMgr.h" -#include "InstanceScript.h" void BuildPlayerLockDungeonBlock(WorldPacket& data, const LfgLockMap& lock) { @@ -48,7 +46,7 @@ void BuildPartyLockDungeonBlock(WorldPacket& data, const LfgLockPartyMap& lockMa void WorldSession::HandleLfgJoinOpcode(WorldPacket& recv_data) { - if (!sWorld->getBoolConfig(CONFIG_DUNGEON_FINDER_ENABLE) || + if (!sLFGMgr->isOptionEnabled(LFG_OPTION_ENABLE_DUNGEON_FINDER | LFG_OPTION_ENABLE_RAID_BROWSER) || (GetPlayer()->GetGroup() && GetPlayer()->GetGroup()->GetLeaderGUID() != GetPlayer()->GetGUID() && (GetPlayer()->GetGroup()->GetMembersCount() == MAXGROUPSIZE || !GetPlayer()->GetGroup()->isLFGGroup()))) { @@ -82,18 +80,20 @@ void WorldSession::HandleLfgJoinOpcode(WorldPacket& recv_data) std::string comment; recv_data >> comment; sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_JOIN [" UI64FMTD "] roles: %u, Dungeons: %u, Comment: %s", GetPlayer()->GetGUID(), roles, uint8(newDungeons.size()), comment.c_str()); - sLFGMgr->Join(GetPlayer(), uint8(roles), newDungeons, comment); + sLFGMgr->JoinLfg(GetPlayer(), uint8(roles), newDungeons, comment); } void WorldSession::HandleLfgLeaveOpcode(WorldPacket& /*recv_data*/) { Group* grp = GetPlayer()->GetGroup(); + uint64 guid = GetPlayer()->GetGUID(); + uint64 gguid = grp ? grp->GetGUID() : guid; - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LEAVE [" UI64FMTD "] in group: %u", GetPlayer()->GetGUID(), grp ? 1 : 0); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LEAVE [" UI64FMTD "] in group: %u", guid, grp ? 1 : 0); // Check cheating - only leader can leave the queue if (!grp || grp->GetLeaderGUID() == GetPlayer()->GetGUID()) - sLFGMgr->Leave(GetPlayer(), grp); + sLFGMgr->LeaveLfg(gguid); } void WorldSession::HandleLfgProposalResultOpcode(WorldPacket& recv_data) @@ -128,7 +128,7 @@ void WorldSession::HandleLfgSetCommentOpcode(WorldPacket& recv_data) std::string comment; recv_data >> comment; uint64 guid = GetPlayer()->GetGUID(); - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SET_LFG_COMMENT [" UI64FMTD "] comment: %s", guid, comment.c_str()); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_COMMENT [" UI64FMTD "] comment: %s", guid, comment.c_str()); sLFGMgr->SetComment(guid, comment); } @@ -138,8 +138,9 @@ void WorldSession::HandleLfgSetBootVoteOpcode(WorldPacket& recv_data) bool agree; // Agree to kick player recv_data >> agree; - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_BOOT_VOTE [" UI64FMTD "] agree: %u", GetPlayer()->GetGUID(), agree ? 1 : 0); - sLFGMgr->UpdateBoot(GetPlayer(), agree); + uint64 guid = GetPlayer()->GetGUID(); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_SET_BOOT_VOTE [" UI64FMTD "] agree: %u", guid, agree ? 1 : 0); + sLFGMgr->UpdateBoot(guid, agree); } void WorldSession::HandleLfgTeleportOpcode(WorldPacket& recv_data) @@ -154,32 +155,24 @@ void WorldSession::HandleLfgTeleportOpcode(WorldPacket& recv_data) void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recv_data*/) { uint64 guid = GetPlayer()->GetGUID(); - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFD_PLAYER_LOCK_INFO_REQUEST [" UI64FMTD "]", guid); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_PLAYER_LOCK_INFO_REQUEST [" UI64FMTD "]", guid); // Get Random dungeons that can be done at a certain level and expansion LfgDungeonSet randomDungeons; uint8 level = GetPlayer()->getLevel(); uint8 expansion = GetPlayer()->GetSession()->Expansion(); - for (uint32 i = 0; i < sLFGDungeonStore.GetNumRows(); ++i) - { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(i); - if (dungeon && dungeon->expansion <= expansion && dungeon->minlevel <= level && level <= dungeon->maxlevel) - { - if (dungeon->flags & LFG_FLAG_SEASONAL) - { - if (HolidayIds holiday = sLFGMgr->GetDungeonSeason(dungeon->ID)) - if (!IsHolidayActive(holiday)) - continue; - } - else if (dungeon->type != LFG_TYPE_RANDOM) - continue; - randomDungeons.insert(dungeon->Entry()); - } + LFGDungeonMap& LfgDungeons = sLFGMgr->GetLFGDungeonMap(); + for (LFGDungeonMap::const_iterator itr = LfgDungeons.begin(); itr != LfgDungeons.end(); ++itr) + { + LFGDungeonData const& dungeon = itr->second; + if ((dungeon.type == LFG_TYPE_RANDOM || (dungeon.seasonal && sLFGMgr->IsSeasonActive(dungeon.id))) + && dungeon.expansion <= expansion && dungeon.minlevel <= level && level <= dungeon.maxlevel) + randomDungeons.insert(dungeon.Entry()); } // Get player locked Dungeons - LfgLockMap lock = sLFGMgr->GetLockedDungeons(guid); + LfgLockMap const& lock = sLFGMgr->GetLockedDungeons(guid); uint32 rsize = uint32(randomDungeons.size()); uint32 lsize = uint32(lock.size()); @@ -191,40 +184,37 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recv_data { data << uint32(*it); // Dungeon Entry (id + type) LfgReward const* reward = sLFGMgr->GetRandomDungeonReward(*it, level); - Quest const* qRew = NULL; + Quest const* quest = NULL; uint8 done = 0; if (reward) { - qRew = sObjectMgr->GetQuestTemplate(reward->reward[0].questId); - if (qRew) + quest = sObjectMgr->GetQuestTemplate(reward->reward[0].questId); + if (quest) { - done = !GetPlayer()->CanRewardQuest(qRew, false); + done = !GetPlayer()->CanRewardQuest(quest, false); if (done) - qRew = sObjectMgr->GetQuestTemplate(reward->reward[1].questId); + quest = sObjectMgr->GetQuestTemplate(reward->reward[1].questId); } } - if (qRew) + + if (quest) { data << uint8(done); - data << uint32(qRew->GetRewOrReqMoney()); - data << uint32(qRew->XPValue(GetPlayer())); + data << uint32(quest->GetRewOrReqMoney()); + data << uint32(quest->XPValue(GetPlayer())); data << uint32(reward->reward[done].variableMoney); data << uint32(reward->reward[done].variableXP); - data << uint8(qRew->GetRewItemsCount()); - if (qRew->GetRewItemsCount()) + data << uint8(quest->GetRewItemsCount()); + if (quest->GetRewItemsCount()) { - ItemTemplate const* iProto = NULL; for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i) - { - if (!qRew->RewardItemId[i]) - continue; - - iProto = sObjectMgr->GetItemTemplate(qRew->RewardItemId[i]); - - data << uint32(qRew->RewardItemId[i]); - data << uint32(iProto ? iProto->DisplayInfoID : 0); - data << uint32(qRew->RewardItemIdCount[i]); - } + if (uint32 itemId = quest->RewardItemId[i]) + { + ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId); + data << uint32(itemId); + data << uint32(item ? item->DisplayInfoID : 0); + data << uint32(quest->RewardItemIdCount[i]); + } } } else @@ -244,7 +234,7 @@ void WorldSession::HandleLfgPlayerLockInfoRequestOpcode(WorldPacket& /*recv_data void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket& /*recv_data*/) { uint64 guid = GetPlayer()->GetGUID(); - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFD_PARTY_LOCK_INFO_REQUEST [" UI64FMTD "]", guid); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_PARTY_LOCK_INFO_REQUEST [" UI64FMTD "]", guid); Group* grp = GetPlayer()->GetGroup(); if (!grp) @@ -275,11 +265,11 @@ void WorldSession::HandleLfgPartyLockInfoRequestOpcode(WorldPacket& /*recv_data SendPacket(&data); } -void WorldSession::HandleLfrSearchOpcode(WorldPacket& recv_data) +void WorldSession::HandleLfrJoinOpcode(WorldPacket& recv_data) { uint32 entry; // Raid id to search recv_data >> entry; - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SEARCH_LFG_JOIN [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), entry); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LFR_JOIN [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), entry); //SendLfrUpdateListOpcode(entry); } @@ -287,7 +277,7 @@ void WorldSession::HandleLfrLeaveOpcode(WorldPacket& recv_data) { uint32 dungeonId; // Raid id queue to leave recv_data >> dungeonId; - sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_SEARCH_LFG_LEAVE [" UI64FMTD "] dungeonId: %u", GetPlayer()->GetGUID(), dungeonId); + sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_LFG_LFR_LEAVE [" UI64FMTD "] dungeonId: %u", GetPlayer()->GetGUID(), dungeonId); //sLFGMgr->LeaveLfr(GetPlayer(), dungeonId); } @@ -295,6 +285,8 @@ void WorldSession::SendLfgUpdatePlayer(const LfgUpdateData& updateData) { bool queued = false; bool extrainfo = false; + uint64 guid = GetPlayer()->GetGUID(); + uint8 size = uint8(updateData.dungeons.size()); switch (updateData.updateType) { @@ -311,9 +303,6 @@ void WorldSession::SendLfgUpdatePlayer(const LfgUpdateData& updateData) break; } - uint64 guid = GetPlayer()->GetGUID(); - uint8 size = uint8(updateData.dungeons.size()); - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_PLAYER [" UI64FMTD "] updatetype: %u", guid, updateData.updateType); WorldPacket data(SMSG_LFG_UPDATE_PLAYER, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + size * 4 + updateData.comment.length())); data << uint8(updateData.updateType); // Lfg Update type @@ -338,6 +327,8 @@ void WorldSession::SendLfgUpdateParty(const LfgUpdateData& updateData) bool join = false; bool extrainfo = false; bool queued = false; + uint64 guid = GetPlayer()->GetGUID(); + uint8 size = uint8(updateData.dungeons.size()); switch (updateData.updateType) { @@ -361,9 +352,6 @@ void WorldSession::SendLfgUpdateParty(const LfgUpdateData& updateData) break; } - uint64 guid = GetPlayer()->GetGUID(); - uint8 size = uint8(updateData.dungeons.size()); - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_PARTY [" UI64FMTD "] updatetype: %u", guid, updateData.updateType); WorldPacket data(SMSG_LFG_UPDATE_PARTY, 1 + 1 + (extrainfo ? 1 : 0) * (1 + 1 + 1 + 1 + 1 + size * 4 + updateData.comment.length())); data << uint8(updateData.updateType); // Lfg Update type @@ -397,45 +385,44 @@ void WorldSession::SendLfgRoleChosen(uint64 guid, uint8 roles) SendPacket(&data); } -void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck* pRoleCheck) +void WorldSession::SendLfgRoleCheckUpdate(const LfgRoleCheck& roleCheck) { - ASSERT(pRoleCheck); LfgDungeonSet dungeons; - if (pRoleCheck->rDungeonId) - dungeons.insert(pRoleCheck->rDungeonId); + if (roleCheck.rDungeonId) + dungeons.insert(roleCheck.rDungeonId); else - dungeons = pRoleCheck->dungeons; + dungeons = roleCheck.dungeons; sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_ROLE_CHECK_UPDATE [" UI64FMTD "]", GetPlayer()->GetGUID()); - WorldPacket data(SMSG_LFG_ROLE_CHECK_UPDATE, 4 + 1 + 1 + dungeons.size() * 4 + 1 + pRoleCheck->roles.size() * (8 + 1 + 4 + 1)); + WorldPacket data(SMSG_LFG_ROLE_CHECK_UPDATE, 4 + 1 + 1 + dungeons.size() * 4 + 1 + roleCheck.roles.size() * (8 + 1 + 4 + 1)); - data << uint32(pRoleCheck->state); // Check result - data << uint8(pRoleCheck->state == LFG_ROLECHECK_INITIALITING); + data << uint32(roleCheck.state); // Check result + data << uint8(roleCheck.state == LFG_ROLECHECK_INITIALITING); data << uint8(dungeons.size()); // Number of dungeons if (!dungeons.empty()) { for (LfgDungeonSet::iterator it = dungeons.begin(); it != dungeons.end(); ++it) { - LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(*it); + LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(*it); data << uint32(dungeon ? dungeon->Entry() : 0); // Dungeon } } - data << uint8(pRoleCheck->roles.size()); // Players in group - if (!pRoleCheck->roles.empty()) + data << uint8(roleCheck.roles.size()); // Players in group + if (!roleCheck.roles.empty()) { // Leader info MUST be sent 1st :S - uint64 guid = pRoleCheck->leader; - uint8 roles = pRoleCheck->roles.find(guid)->second; + uint64 guid = roleCheck.leader; + uint8 roles = roleCheck.roles.find(guid)->second; data << uint64(guid); // Guid data << uint8(roles > 0); // Ready data << uint32(roles); // Roles Player* player = ObjectAccessor::FindPlayer(guid); data << uint8(player ? player->getLevel() : 0); // Level - for (LfgRolesMap::const_iterator it = pRoleCheck->roles.begin(); it != pRoleCheck->roles.end(); ++it) + for (LfgRolesMap::const_iterator it = roleCheck.roles.begin(); it != roleCheck.roles.end(); ++it) { - if (it->first == pRoleCheck->leader) + if (it->first == roleCheck.leader) continue; guid = it->first; @@ -465,30 +452,31 @@ void WorldSession::SendLfgJoinResult(const LfgJoinResultData& joinData) SendPacket(&data); } -void WorldSession::SendLfgQueueStatus(uint32 dungeon, int32 waitTime, int32 avgWaitTime, int32 waitTimeTanks, int32 waitTimeHealer, int32 waitTimeDps, uint32 queuedTime, uint8 tanks, uint8 healers, uint8 dps) +void WorldSession::SendLfgQueueStatus(const LfgQueueStatusData& queueData) { - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_QUEUE_STATUS [" UI64FMTD "] dungeon: %u - waitTime: %d - avgWaitTime: %d - waitTimeTanks: %d - waitTimeHealer: %d - waitTimeDps: %d - queuedTime: %u - tanks: %u - healers: %u - dps: %u", GetPlayer()->GetGUID(), dungeon, waitTime, avgWaitTime, waitTimeTanks, waitTimeHealer, waitTimeDps, queuedTime, tanks, healers, dps); + sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_QUEUE_STATUS [" UI64FMTD "] dungeon: %u - waitTime: %d - avgWaitTime: %d - waitTimeTanks: %d - waitTimeHealer: %d - waitTimeDps: %d - queuedTime: %u - tanks: %u - healers: %u - dps: %u", + GetPlayer()->GetGUID(), queueData.dungeonId, queueData.waitTime, queueData.waitTimeAvg, queueData.waitTimeTank, queueData.waitTimeHealer, queueData.waitTimeDps, queueData.queuedTime, queueData.tanks, queueData.healers, queueData.dps); WorldPacket data(SMSG_LFG_QUEUE_STATUS, 4 + 4 + 4 + 4 + 4 +4 + 1 + 1 + 1 + 4); - data << uint32(dungeon); // Dungeon - data << int32(avgWaitTime); // Average Wait time - data << int32(waitTime); // Wait Time - data << int32(waitTimeTanks); // Wait Tanks - data << int32(waitTimeHealer); // Wait Healers - data << int32(waitTimeDps); // Wait Dps - data << uint8(tanks); // Tanks needed - data << uint8(healers); // Healers needed - data << uint8(dps); // Dps needed - data << uint32(queuedTime); // Player wait time in queue + data << uint32(queueData.dungeonId); // Dungeon + data << int32(queueData.waitTimeAvg); // Average Wait time + data << int32(queueData.waitTime); // Wait Time + data << int32(queueData.waitTimeTank); // Wait Tanks + data << int32(queueData.waitTimeHealer); // Wait Healers + data << int32(queueData.waitTimeDps); // Wait Dps + data << uint8(queueData.tanks); // Tanks needed + data << uint8(queueData.healers); // Healers needed + data << uint8(queueData.dps); // Dps needed + data << uint32(queueData.queuedTime); // Player wait time in queue SendPacket(&data); } -void WorldSession::SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntry, uint8 done, const LfgReward* reward, const Quest* qRew) +void WorldSession::SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntry, uint8 done, const LfgReward* reward, const Quest* quest) { - if (!rdungeonEntry || !sdungeonEntry || !qRew) + if (!rdungeonEntry || !sdungeonEntry || !quest) return; - uint8 itemNum = uint8(qRew ? qRew->GetRewItemsCount() : 0); + uint8 itemNum = uint8(quest ? quest->GetRewItemsCount() : 0); sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PLAYER_REWARD [" UI64FMTD "] rdungeonEntry: %u - sdungeonEntry: %u - done: %u", GetPlayer()->GetGUID(), rdungeonEntry, sdungeonEntry, done); WorldPacket data(SMSG_LFG_PLAYER_REWARD, 4 + 4 + 1 + 4 + 4 + 4 + 4 + 4 + 1 + itemNum * (4 + 4 + 4)); @@ -496,37 +484,33 @@ void WorldSession::SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntr data << uint32(sdungeonEntry); // Dungeon Finished data << uint8(done); data << uint32(1); - data << uint32(qRew->GetRewOrReqMoney()); - data << uint32(qRew->XPValue(GetPlayer())); + data << uint32(quest->GetRewOrReqMoney()); + data << uint32(quest->XPValue(GetPlayer())); data << uint32(reward->reward[done].variableMoney); data << uint32(reward->reward[done].variableXP); data << uint8(itemNum); if (itemNum) { - ItemTemplate const* iProto = NULL; for (uint8 i = 0; i < QUEST_REWARDS_COUNT; ++i) - { - if (!qRew->RewardItemId[i]) - continue; - - iProto = sObjectMgr->GetItemTemplate(qRew->RewardItemId[i]); - - data << uint32(qRew->RewardItemId[i]); - data << uint32(iProto ? iProto->DisplayInfoID : 0); - data << uint32(qRew->RewardItemIdCount[i]); - } + if (uint32 itemId = quest->RewardItemId[i]) + { + ItemTemplate const* item = sObjectMgr->GetItemTemplate(itemId); + data << uint32(itemId); + data << uint32(item ? item->DisplayInfoID : 0); + data << uint32(quest->RewardItemIdCount[i]); + } } SendPacket(&data); } -void WorldSession::SendLfgBootPlayer(const LfgPlayerBoot* pBoot) +void WorldSession::SendLfgBootProposalUpdate(const LfgPlayerBoot& boot) { uint64 guid = GetPlayer()->GetGUID(); - LfgAnswer playerVote = pBoot->votes.find(guid)->second; + LfgAnswer playerVote = boot.votes.find(guid)->second; uint8 votesNum = 0; uint8 agreeNum = 0; - uint32 secsleft = uint8((pBoot->cancelTime - time(NULL)) / 1000); - for (LfgAnswerMap::const_iterator it = pBoot->votes.begin(); it != pBoot->votes.end(); ++it) + uint32 secsleft = uint8((boot.cancelTime - time(NULL)) / 1000); + for (LfgAnswerMap::const_iterator it = boot.votes.begin(); it != boot.votes.end(); ++it) { if (it->second != LFG_ANSWER_PENDING) { @@ -536,106 +520,72 @@ void WorldSession::SendLfgBootPlayer(const LfgPlayerBoot* pBoot) } } sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_BOOT_PROPOSAL_UPDATE [" UI64FMTD "] inProgress: %u - didVote: %u - agree: %u - victim: [" UI64FMTD "] votes: %u - agrees: %u - left: %u - needed: %u - reason %s", - guid, uint8(pBoot->inProgress), uint8(playerVote != LFG_ANSWER_PENDING), uint8(playerVote == LFG_ANSWER_AGREE), pBoot->victim, votesNum, agreeNum, secsleft, pBoot->votedNeeded, pBoot->reason.c_str()); - WorldPacket data(SMSG_LFG_BOOT_PROPOSAL_UPDATE, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + pBoot->reason.length()); - data << uint8(pBoot->inProgress); // Vote in progress + guid, uint8(boot.inProgress), uint8(playerVote != LFG_ANSWER_PENDING), uint8(playerVote == LFG_ANSWER_AGREE), boot.victim, votesNum, agreeNum, secsleft, LFG_GROUP_KICK_VOTES_NEEDED, boot.reason.c_str()); + WorldPacket data(SMSG_LFG_BOOT_PROPOSAL_UPDATE, 1 + 1 + 1 + 8 + 4 + 4 + 4 + 4 + boot.reason.length()); + data << uint8(boot.inProgress); // Vote in progress data << uint8(playerVote != LFG_ANSWER_PENDING); // Did Vote data << uint8(playerVote == LFG_ANSWER_AGREE); // Agree - data << uint64(pBoot->victim); // Victim GUID + data << uint64(boot.victim); // Victim GUID data << uint32(votesNum); // Total Votes data << uint32(agreeNum); // Agree Count data << uint32(secsleft); // Time Left - data << uint32(pBoot->votedNeeded); // Needed Votes - data << pBoot->reason.c_str(); // Kick reason + data << uint32(LFG_GROUP_KICK_VOTES_NEEDED); // Needed Votes + data << boot.reason.c_str(); // Kick reason SendPacket(&data); } -void WorldSession::SendLfgUpdateProposal(uint32 proposalId, const LfgProposal* pProp) +void WorldSession::SendLfgUpdateProposal(uint32 proposalId, LfgProposal const& proposal) { - if (!pProp) - return; - uint64 guid = GetPlayer()->GetGUID(); - LfgProposalPlayerMap::const_iterator itPlayer = pProp->players.find(guid); - if (itPlayer == pProp->players.end()) // Player MUST be in the proposal - return; + uint64 gguid = proposal.players.find(guid)->second.group; + bool silent = !proposal.isNew && gguid == proposal.group; + uint32 dungeonEntry = proposal.dungeonId; - LfgProposalPlayer* ppPlayer = itPlayer->second; - uint32 pLowGroupGuid = ppPlayer->groupLowGuid; - uint32 dLowGuid = pProp->groupLowGuid; - uint32 dungeonId = pProp->dungeonId; - bool isSameDungeon = false; - bool isContinue = false; - Group* grp = dLowGuid ? sGroupMgr->GetGroupByGUID(dLowGuid) : NULL; - uint32 completedEncounters = 0; - if (grp) - { - uint64 gguid = grp->GetGUID(); - isContinue = grp->isLFGGroup() && sLFGMgr->GetState(gguid) != LFG_STATE_FINISHED_DUNGEON; - isSameDungeon = GetPlayer()->GetGroup() == grp && isContinue; - } - - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PROPOSAL_UPDATE [" UI64FMTD "] state: %u", GetPlayer()->GetGUID(), pProp->state); - WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + pProp->players.size() * (4 + 1 + 1 + 1 + 1 +1)); + sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_PROPOSAL_UPDATE [" UI64FMTD "] state: %u", guid, proposal.state); + WorldPacket data(SMSG_LFG_PROPOSAL_UPDATE, 4 + 1 + 4 + 4 + 1 + 1 + proposal.players.size() * (4 + 1 + 1 + 1 + 1 +1)); - if (!isContinue) // Only show proposal dungeon if it's continue + // show random dungeon if player selected random dungeon and it's not lfg group + if (!silent) { - LfgDungeonSet playerDungeons = sLFGMgr->GetSelectedDungeons(guid); - if (playerDungeons.size() == 1) - dungeonId = (*playerDungeons.begin()); + LfgDungeonSet const& playerDungeons = sLFGMgr->GetSelectedDungeons(guid); + if (playerDungeons.find(proposal.dungeonId) == playerDungeons.end()) + dungeonEntry = (*playerDungeons.begin()); } - if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId)) - { - dungeonId = dungeon->Entry(); - - // Select a player inside to be get completed encounters from - if (grp) - { - for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) - { - Player* groupMember = itr->getSource(); - if (groupMember && groupMember->GetMapId() == uint32(dungeon->map)) - { - if (InstanceScript* instance = groupMember->GetInstanceScript()) - completedEncounters = instance->GetCompletedEncounterMask(); - break; - } - } - } - } + if (LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(dungeonEntry)) + dungeonEntry = dungeon->Entry(); - data << uint32(dungeonId); // Dungeon - data << uint8(pProp->state); // Result state - data << uint32(proposalId); // Internal Proposal ID - data << uint32(completedEncounters); // Bosses killed - data << uint8(isSameDungeon); // Silent (show client window) - data << uint8(pProp->players.size()); // Group size + data << uint32(dungeonEntry); // Dungeon + data << uint8(proposal.state); // Proposal state + data << uint32(proposalId); // Proposal ID + data << uint32(proposal.encounters); // encounters done + data << uint8(silent); // Show proposal window + data << uint8(proposal.players.size()); // Group size - for (itPlayer = pProp->players.begin(); itPlayer != pProp->players.end(); ++itPlayer) + for (LfgProposalPlayerMap::const_iterator it = proposal.players.begin(); it != proposal.players.end(); ++it) { - ppPlayer = itPlayer->second; - data << uint32(ppPlayer->role); // Role - data << uint8(itPlayer->first == guid); // Self player - if (!ppPlayer->groupLowGuid) // Player not it a group + LfgProposalPlayer const& player = it->second; + data << uint32(player.role); // Role + data << uint8(it->first == guid); // Self player + if (!player.group) // Player not it a group { data << uint8(0); // Not in dungeon data << uint8(0); // Not same group } else { - data << uint8(ppPlayer->groupLowGuid == dLowGuid); // In dungeon (silent) - data << uint8(ppPlayer->groupLowGuid == pLowGroupGuid); // Same Group than player + data << uint8(player.group == proposal.group); // In dungeon (silent) + data << uint8(player.group == gguid); // Same Group than player } - data << uint8(ppPlayer->accept != LFG_ANSWER_PENDING); // Answered - data << uint8(ppPlayer->accept == LFG_ANSWER_AGREE); // Accepted + data << uint8(player.accept != LFG_ANSWER_PENDING); // Answered + data << uint8(player.accept == LFG_ANSWER_AGREE); // Accepted } SendPacket(&data); } -void WorldSession::SendLfgUpdateSearch(bool update) +void WorldSession::SendLfgLfrList(bool update) { - sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_UPDATE_SEARCH [" UI64FMTD "] update: %u", GetPlayer()->GetGUID(), update ? 1 : 0); + sLog->outDebug(LOG_FILTER_NETWORKIO, "SMSG_LFG_LFR_LIST [" UI64FMTD "] update: %u", GetPlayer()->GetGUID(), update ? 1 : 0); WorldPacket data(SMSG_LFG_UPDATE_SEARCH, 1); data << uint8(update); // In Lfg Queue? SendPacket(&data); @@ -667,8 +617,8 @@ void WorldSession::SendLfgTeleportError(uint8 err) /* void WorldSession::SendLfrUpdateListOpcode(uint32 dungeonEntry) { - sLog->outDebug(LOG_FILTER_PACKETIO, "SMSG_UPDATE_LFG_LIST [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), dungeonEntry); - WorldPacket data(SMSG_UPDATE_LFG_LIST); + sLog->outDebug(LOG_FILTER_PACKETIO, "SMSG_LFG_UPDATE_LIST [" UI64FMTD "] dungeon entry: %u", GetPlayer()->GetGUID(), dungeonEntry); + WorldPacket data(SMSG_LFG_UPDATE_LIST); SendPacket(&data); } */ diff --git a/src/server/game/Handlers/TicketHandler.cpp b/src/server/game/Handlers/TicketHandler.cpp index d6675188f6e..7fcfbbbc23e 100755 --- a/src/server/game/Handlers/TicketHandler.cpp +++ b/src/server/game/Handlers/TicketHandler.cpp @@ -16,6 +16,7 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ +#include "zlib.h" #include "Language.h" #include "WorldPacket.h" #include "Common.h" @@ -26,7 +27,7 @@ #include "WorldSession.h" #include "Util.h" -void WorldSession::HandleGMTicketCreateOpcode(WorldPacket & recv_data) +void WorldSession::HandleGMTicketCreateOpcode(WorldPacket& recvData) { // Don't accept tickets if the ticket queue is disabled. (Ticket UI is greyed out but not fully dependable) if (sTicketMgr->GetStatus() == GMTICKET_QUEUE_STATUS_DISABLED) @@ -42,7 +43,46 @@ void WorldSession::HandleGMTicketCreateOpcode(WorldPacket & recv_data) // Player must not have ticket if (!sTicketMgr->GetTicketByPlayer(GetPlayer()->GetGUID())) { - GmTicket* ticket = new GmTicket(GetPlayer(), recv_data); + GmTicket* ticket = new GmTicket(GetPlayer(), recvData); + + uint32 count; + std::list<uint32> times; + uint32 decompressedSize; + std::string chatLog; + + recvData >> count; + + for (uint32 i = 0; i < count; i++) + { + uint32 time; + recvData >> time; + times.push_back(time); + } + + recvData >> decompressedSize; + + if (count && decompressedSize && decompressedSize < 0xFFFF) + { + uint32 pos = recvData.rpos(); + ByteBuffer dest; + dest.resize(decompressedSize); + + uLongf realSize = decompressedSize; + if (uncompress(const_cast<uint8*>(dest.contents()), &realSize, const_cast<uint8*>(recvData.contents() + pos), recvData.size() - pos) == Z_OK) + { + dest >> chatLog; + ticket->SetChatLog(times, chatLog); + } + else + { + sLog->outError(LOG_FILTER_NETWORKIO, "CMSG_GMTICKET_CREATE possibly corrupt. Uncompression failed."); + recvData.rfinish(); + return; + } + + recvData.rfinish(); // Will still have compressed data in buffer. + } + sTicketMgr->AddTicket(ticket); sTicketMgr->UpdateLastChange(); diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index bc524c720f7..51435d6df9b 100755 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -2468,8 +2468,8 @@ bool InstanceMap::AddPlayerToMap(Player* player) if (group && group->isLFGGroup()) if (uint32 dungeonId = sLFGMgr->GetDungeon(group->GetGUID(), true)) - if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId)) - if (LFGDungeonEntry const* randomDungeon = sLFGDungeonStore.LookupEntry(*(sLFGMgr->GetSelectedDungeons(player->GetGUID()).begin()))) + if (LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId)) + if (LFGDungeonData const* randomDungeon = sLFGMgr->GetLFGDungeon(*(sLFGMgr->GetSelectedDungeons(player->GetGUID()).begin()))) if (uint32(dungeon->map) == GetId() && dungeon->difficulty == uint32(GetDifficulty()) && randomDungeon->type == uint32(LFG_TYPE_RANDOM)) player->CastSpell(player, LFG_SPELL_LUCK_OF_THE_DRAW, true); } diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index 5893bbd6564..07bfd284a7d 100755 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -990,6 +990,27 @@ enum TrinityStrings LANG_WORLD_CLOSED = 7523, LANG_WORLD_OPENED = 7524, + LANG_LFG_PLAYER_INFO = 9980, + LANG_LFG_GROUP_INFO = 9981, + LANG_LFG_NOT_IN_GROUP = 9982, + LANG_LFG_CLEAN = 9983, + LANG_LFG_OPTIONS = 9984, + LANG_LFG_OPTIONS_CHANGED = 9985, + LANG_LFG_STATE_NONE = 9986, + LANG_LFG_STATE_ROLECHECK = 9987, + LANG_LFG_STATE_QUEUED = 9988, + LANG_LFG_STATE_PROPOSAL = 9989, + LANG_LFG_STATE_BOOT = 9990, + LANG_LFG_STATE_DUNGEON = 9991, + LANG_LFG_STATE_FINISHED_DUNGEON = 9992, + LANG_LFG_STATE_RAIDBROWSER = 9993, + LANG_LFG_ROLE_TANK = 9994, + LANG_LFG_ROLE_HEALER = 9995, + LANG_LFG_ROLE_DAMAGE = 9996, + LANG_LFG_ROLE_LEADER = 9997, + LANG_LFG_ROLE_NONE = 9998, + LANG_LFG_ERROR = 9999, + // Use for not-in-offcial-sources patches // 10000-10999 // opvp hp @@ -1076,11 +1097,10 @@ enum TrinityStrings // Use for custom patches 11000-11999 LANG_AUTO_BROADCAST = 11000, - LANG_INVALID_REALMID = 11001 + LANG_INVALID_REALMID = 11001, // NOT RESERVED IDS 12000-1999999999 // `db_script_string` table index 2000000000-2000009999 (MIN_DB_SCRIPT_STRING_ID-MAX_DB_SCRIPT_STRING_ID) // For other tables maybe 2000010000-2147483647 (max index) }; #endif - diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 4f6ad917423..422385f406f 100755 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -3399,9 +3399,10 @@ enum XPColorChar enum RemoveMethod { - GROUP_REMOVEMETHOD_DEFAULT = 0, - GROUP_REMOVEMETHOD_KICK = 1, - GROUP_REMOVEMETHOD_LEAVE = 2 + GROUP_REMOVEMETHOD_DEFAULT = 0, + GROUP_REMOVEMETHOD_KICK = 1, + GROUP_REMOVEMETHOD_LEAVE = 2, + GROUP_REMOVEMETHOD_KICK_LFG = 3 }; enum ActivateTaxiReply diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index a229d3ed536..af2ff2c6d86 100755 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -888,7 +888,7 @@ OpcodeHandler opcodeTable[NUM_MSG_TYPES] = /*0x35B*/ { "SMSG_ARENA_TEAM_STATS", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x35C*/ { "CMSG_LFG_JOIN", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgJoinOpcode }, /*0x35D*/ { "CMSG_LFG_LEAVE", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfgLeaveOpcode }, - /*0x35E*/ { "CMSG_SEARCH_LFG_JOIN", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfrSearchOpcode }, + /*0x35E*/ { "CMSG_SEARCH_LFG_JOIN", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfrJoinOpcode }, /*0x35F*/ { "CMSG_SEARCH_LFG_LEAVE", STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleLfrLeaveOpcode }, /*0x360*/ { "SMSG_UPDATE_LFG_LIST", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, /*0x361*/ { "SMSG_LFG_PROPOSAL_UPDATE", STATUS_NEVER, PROCESS_INPLACE, &WorldSession::Handle_ServerSide }, diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index bc9e6ac4298..3ff45bff3b1 100755 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -54,6 +54,7 @@ struct LfgJoinResultData; struct LfgLockStatus; struct LfgPlayerBoot; struct LfgProposal; +struct LfgQueueStatusData; struct LfgReward; struct LfgRoleCheck; struct LfgUpdateData; @@ -815,19 +816,19 @@ class WorldSession void HandleLfgProposalResultOpcode(WorldPacket& recv_data); void HandleLfgSetBootVoteOpcode(WorldPacket& recv_data); void HandleLfgTeleportOpcode(WorldPacket& recv_data); - void HandleLfrSearchOpcode(WorldPacket& recv_data); + void HandleLfrJoinOpcode(WorldPacket& recv_data); void HandleLfrLeaveOpcode(WorldPacket& recv_data); void SendLfgUpdatePlayer(const LfgUpdateData& updateData); void SendLfgUpdateParty(const LfgUpdateData& updateData); void SendLfgRoleChosen(uint64 guid, uint8 roles); - void SendLfgRoleCheckUpdate(const LfgRoleCheck* pRoleCheck); - void SendLfgUpdateSearch(bool update); + void SendLfgRoleCheckUpdate(const LfgRoleCheck& pRoleCheck); + void SendLfgLfrList(bool update); void SendLfgJoinResult(const LfgJoinResultData& joinData); - void SendLfgQueueStatus(uint32 dungeon, int32 waitTime, int32 avgWaitTime, int32 waitTimeTanks, int32 waitTimeHealer, int32 waitTimeDps, uint32 queuedTime, uint8 tanks, uint8 healers, uint8 dps); + void SendLfgQueueStatus(const LfgQueueStatusData& queueData); void SendLfgPlayerReward(uint32 rdungeonEntry, uint32 sdungeonEntry, uint8 done, const LfgReward* reward, const Quest *qRew); - void SendLfgBootPlayer(const LfgPlayerBoot* pBoot); - void SendLfgUpdateProposal(uint32 proposalId, const LfgProposal *pProp); + void SendLfgBootProposalUpdate(const LfgPlayerBoot& boot); + void SendLfgUpdateProposal(uint32 proposalId, const LfgProposal& proposal); void SendLfgDisabled(); void SendLfgOfferContinue(uint32 dungeonEntry); void SendLfgTeleportError(uint8 err); diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 85a1d0d3c7f..295c8216b7f 100755 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -5210,7 +5210,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_spellInfo->Id != 1842 || (m_targets.GetGOTarget() && m_targets.GetGOTarget()->GetGOInfo()->type != GAMEOBJECT_TYPE_TRAP)) if (m_caster->ToPlayer()->InBattleground() && // In Battleground players can use only flags and banners - !m_caster->ToPlayer()->CanUseBattlegroundObject()) + !m_caster->ToPlayer()->CanUseBattlegroundObject(m_targets.GetGOTarget())) return SPELL_FAILED_TRY_AGAIN; // get the lock entry diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 13841aed7ed..4ffbab6cb63 100755 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -1061,12 +1061,9 @@ SpellAreaMapBounds SpellMgr::GetSpellAreaMapBounds(uint32 spell_id) const return SpellAreaMapBounds(mSpellAreaMap.lower_bound(spell_id), mSpellAreaMap.upper_bound(spell_id)); } -SpellAreaForQuestMapBounds SpellMgr::GetSpellAreaForQuestMapBounds(uint32 quest_id, bool active) const +SpellAreaForQuestMapBounds SpellMgr::GetSpellAreaForQuestMapBounds(uint32 quest_id) const { - if (active) - return SpellAreaForQuestMapBounds(mSpellAreaForActiveQuestMap.lower_bound(quest_id), mSpellAreaForActiveQuestMap.upper_bound(quest_id)); - else - return SpellAreaForQuestMapBounds(mSpellAreaForQuestMap.lower_bound(quest_id), mSpellAreaForQuestMap.upper_bound(quest_id)); + return SpellAreaForQuestMapBounds(mSpellAreaForQuestMap.lower_bound(quest_id), mSpellAreaForQuestMap.upper_bound(quest_id)); } SpellAreaForQuestMapBounds SpellMgr::GetSpellAreaForQuestEndMapBounds(uint32 quest_id) const @@ -1099,11 +1096,11 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 return false; if (questStart) // not in expected required quest state - if (!player || ((!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart))) + if (!player || (((1 << player->GetQuestStatus(questStart)) & questStartStatus) == 0)) return false; if (questEnd) // not in expected forbidden quest state - if (!player || player->GetQuestRewardStatus(questEnd)) + if (!player || (((1 << player->GetQuestStatus(questEnd)) & questEndStatus) == 0)) return false; if (auraSpell) // not have expected aura @@ -2433,12 +2430,11 @@ void SpellMgr::LoadSpellAreas() mSpellAreaMap.clear(); // need for reload case mSpellAreaForQuestMap.clear(); - mSpellAreaForActiveQuestMap.clear(); mSpellAreaForQuestEndMap.clear(); mSpellAreaForAuraMap.clear(); - // 0 1 2 3 4 5 6 7 8 - QueryResult result = WorldDatabase.Query("SELECT spell, area, quest_start, quest_start_active, quest_end, aura_spell, racemask, gender, autocast FROM spell_area"); + // 0 1 2 3 4 5 6 7 8 9 + QueryResult result = WorldDatabase.Query("SELECT spell, area, quest_start, quest_start_status, quest_end_status, quest_end, aura_spell, racemask, gender, autocast FROM spell_area"); if (!result) { @@ -2457,12 +2453,13 @@ void SpellMgr::LoadSpellAreas() spellArea.spellId = spell; spellArea.areaId = fields[1].GetUInt32(); spellArea.questStart = fields[2].GetUInt32(); - spellArea.questStartCanActive = fields[3].GetBool(); - spellArea.questEnd = fields[4].GetUInt32(); - spellArea.auraSpell = fields[5].GetInt32(); - spellArea.raceMask = fields[6].GetUInt32(); - spellArea.gender = Gender(fields[7].GetUInt8()); - spellArea.autocast = fields[8].GetBool(); + spellArea.questStartStatus = fields[3].GetUInt32(); + spellArea.questEndStatus = fields[4].GetUInt32(); + spellArea.questEnd = fields[5].GetUInt32(); + spellArea.auraSpell = fields[6].GetInt32(); + spellArea.raceMask = fields[7].GetUInt32(); + spellArea.gender = Gender(fields[8].GetUInt8()); + spellArea.autocast = fields[9].GetBool(); if (SpellInfo const* spellInfo = GetSpellInfo(spell)) { @@ -2494,7 +2491,7 @@ void SpellMgr::LoadSpellAreas() continue; // duplicate by requirements - ok =false; + ok = false; break; } @@ -2524,12 +2521,6 @@ void SpellMgr::LoadSpellAreas() sLog->outError(LOG_FILTER_SQL, "Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell, spellArea.questEnd); continue; } - - if (spellArea.questEnd == spellArea.questStart && !spellArea.questStartCanActive) - { - sLog->outError(LOG_FILTER_SQL, "Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell, spellArea.questEnd); - continue; - } } if (spellArea.auraSpell) @@ -2605,12 +2596,7 @@ void SpellMgr::LoadSpellAreas() // for search at quest start/reward if (spellArea.questStart) - { - if (spellArea.questStartCanActive) - mSpellAreaForActiveQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart, sa)); - else - mSpellAreaForQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart, sa)); - } + mSpellAreaForQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart, sa)); // for search at quest start/reward if (spellArea.questEnd) @@ -3488,7 +3474,7 @@ void SpellMgr::LoadDbcDataCorrections() break; case 72015: // Frostbolt Volley (only heroic) case 72016: // Frostbolt Volley (only heroic) - spellInfo->EffectRadiusIndex[2] = 23; // 40 yards + spellInfo->EffectRadiusIndex[2] = EFFECT_RADIUS_40_YARDS; break; case 70936: // Summon Suppressor (needs target selection script) spellInfo->EffectImplicitTargetA[0] = TARGET_UNIT_TARGET_ANY; diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index 9646bc9dabf..cec9d4650f5 100755 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -495,7 +495,8 @@ struct SpellArea int32 auraSpell; // spell aura must be applied for spell apply)if possitive) and it must not be applied in other case uint32 raceMask; // can be applied only to races Gender gender; // can be applied only to gender - bool questStartCanActive; // if true then quest start can be active (not only rewarded) + uint32 questStartStatus; // QuestStatus that quest_start must have in order to keep the spell + uint32 questEndStatus; // QuestStatus that the quest_end must have in order to keep the spell (if the quest_end's status is different than this, the spell will be dropped) bool autocast; // if true then auto applied at area enter, in other case just allowed to cast // helpers @@ -681,7 +682,7 @@ class SpellMgr // Spell area SpellAreaMapBounds GetSpellAreaMapBounds(uint32 spell_id) const; - SpellAreaForQuestMapBounds GetSpellAreaForQuestMapBounds(uint32 quest_id, bool active) const; + SpellAreaForQuestMapBounds GetSpellAreaForQuestMapBounds(uint32 quest_id) const; SpellAreaForQuestMapBounds GetSpellAreaForQuestEndMapBounds(uint32 quest_id) const; SpellAreaForAuraMapBounds GetSpellAreaForAuraMapBounds(uint32 spell_id) const; SpellAreaForAreaMapBounds GetSpellAreaForAreaMapBounds(uint32 area_id) const; @@ -740,7 +741,6 @@ class SpellMgr EnchantCustomAttribute mEnchantCustomAttr; SpellAreaMap mSpellAreaMap; SpellAreaForQuestMap mSpellAreaForQuestMap; - SpellAreaForQuestMap mSpellAreaForActiveQuestMap; SpellAreaForQuestMap mSpellAreaForQuestEndMap; SpellAreaForAuraMap mSpellAreaForAuraMap; SpellAreaForAreaMap mSpellAreaForAreaMap; diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index c8126f8aaf3..b1a2097a8f9 100755 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -586,7 +586,7 @@ class AuraScript : public _SpellScript uint8 _currentScriptState; bool _defaultActionPrevented; ScriptStateStore(uint8 currentScriptState, AuraApplication const* auraApplication, bool defaultActionPrevented) - : _currentScriptState(currentScriptState), _auraApplication(auraApplication), _defaultActionPrevented(defaultActionPrevented) + : _auraApplication(auraApplication), _currentScriptState(currentScriptState), _defaultActionPrevented(defaultActionPrevented) {} }; typedef std::stack<ScriptStateStore> ScriptStateStack; diff --git a/src/server/game/Tickets/TicketMgr.cpp b/src/server/game/Tickets/TicketMgr.cpp index 0a4682db759..51e17cb9076 100755 --- a/src/server/game/Tickets/TicketMgr.cpp +++ b/src/server/game/Tickets/TicketMgr.cpp @@ -31,45 +31,32 @@ inline float GetAge(uint64 t) { return float(time(NULL) - t) / DAY; } // GM ticket GmTicket::GmTicket() { } -GmTicket::GmTicket(Player* player, WorldPacket& recv_data) : _createTime(time(NULL)), _lastModifiedTime(time(NULL)), _closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED) +GmTicket::GmTicket(Player* player, WorldPacket& recvData) : _createTime(time(NULL)), _lastModifiedTime(time(NULL)), _closedBy(0), _assignedTo(0), _completed(false), _escalatedStatus(TICKET_UNASSIGNED), _haveTicket(false) { _id = sTicketMgr->GenerateTicketId(); _playerName = player->GetName(); _playerGuid = player->GetGUID(); uint32 mapId; - recv_data >> mapId; // Map is sent as UInt32! + recvData >> mapId; // Map is sent as UInt32! _mapId = mapId; - recv_data >> _posX; - recv_data >> _posY; - recv_data >> _posZ; - recv_data >> _message; + recvData >> _posX; + recvData >> _posY; + recvData >> _posZ; + recvData >> _message; uint32 needResponse; - recv_data >> needResponse; - _needResponse = (needResponse == 17); // Requires GM response. 17 = true, 1 = false (17 is default) - uint8 unk1; - recv_data >> unk1; // Requests further GM interaction on a ticket to which a GM has already responded - - recv_data.rfinish(); - /* - recv_data >> uint32(count); // text lines - for (int i = 0; i < count; i++) - recv_data >> uint32(); - - if (something) - recv_data >> uint32(); - else - compressed uint32 + string; - */ + recvData >> needResponse; + _needResponse = (needResponse == 17); // Requires GM response. 17 = true, 1 = false (17 is default) + recvData >> _haveTicket; // Requests further GM interaction on a ticket to which a GM has already responded. Basically means "has a new ticket" } GmTicket::~GmTicket() { } bool GmTicket::LoadFromDB(Field* fields) { - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - // ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, completed, escalated, viewed + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + // ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket uint8 index = 0; _id = fields[ index].GetUInt32(); _playerGuid = MAKE_NEW_GUID(fields[++index].GetUInt32(), 0, HIGHGUID_PLAYER); @@ -84,9 +71,11 @@ bool GmTicket::LoadFromDB(Field* fields) _closedBy = fields[++index].GetInt32(); _assignedTo = MAKE_NEW_GUID(fields[++index].GetUInt32(), 0, HIGHGUID_PLAYER); _comment = fields[++index].GetString(); + _response = fields[++index].GetString(); _completed = fields[++index].GetBool(); _escalatedStatus = GMTicketEscalationStatus(fields[++index].GetUInt8()); _viewed = fields[++index].GetBool(); + _haveTicket = fields[++index].GetBool(); return true; } @@ -109,9 +98,11 @@ void GmTicket::SaveToDB(SQLTransaction& trans) const stmt->setInt32 (++index, GUID_LOPART(_closedBy)); stmt->setUInt32(++index, GUID_LOPART(_assignedTo)); stmt->setString(++index, _comment); + stmt->setString(++index, _response); stmt->setBool (++index, _completed); stmt->setUInt8 (++index, uint8(_escalatedStatus)); stmt->setBool (++index, _viewed); + stmt->setBool (++index, _haveTicket); CharacterDatabase.ExecuteOrAppend(trans, stmt); } @@ -125,6 +116,10 @@ void GmTicket::DeleteFromDB() void GmTicket::WritePacket(WorldPacket& data) const { + data << uint32(GMTICKET_STATUS_HASTEXT); + data << uint32(_id); + data << _message; + data << uint8(_haveTicket); data << GetAge(_lastModifiedTime); if (GmTicket* ticket = sTicketMgr->GetOldestOpenTicket()) data << GetAge(ticket->GetLastModifiedTime()); @@ -223,6 +218,20 @@ void GmTicket::TeleportTo(Player* player) const player->TeleportTo(_mapId, _posX, _posY, _posZ, 0.0f, 0); } +void GmTicket::SetChatLog(std::list<uint32> time, std::string const& log) +{ + std::stringstream ss(log); + std::stringstream newss; + std::string line; + while (std::getline(ss, line)) + { + newss << secsToTimeString(time.front()) << ": " << line << "\n"; + time.pop_front(); + } + + _chatLog = newss.str(); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// // Ticket manager TicketMgr::TicketMgr() : _status(true), _lastTicketId(0), _lastSurveyId(0), _openTicketCount(0), _lastChange(time(NULL)) { } @@ -365,26 +374,12 @@ void TicketMgr::ShowEscalatedList(ChatHandler& handler) const void TicketMgr::SendTicket(WorldSession* session, GmTicket* ticket) const { - uint32 status = GMTICKET_STATUS_DEFAULT; - std::string message; - if (ticket) - { - message = ticket->GetMessage(); - status = GMTICKET_STATUS_HASTEXT; - } - - WorldPacket data(SMSG_GMTICKET_GETTICKET, (4 + 4 + (ticket ? message.length() + 1 + 4 + 4 + 4 + 1 + 1 : 0))); - data << uint32(status); // standard 0x0A, 0x06 if text present - data << uint32(ticket ? ticket->GetId() : 0); // ticketID + WorldPacket data(SMSG_GMTICKET_GETTICKET, (ticket ? (4 + 4 + 1 + 4 + 4 + 4 + 1 + 1) : 4)); if (ticket) - { - data << message.c_str(); // ticket text - data << uint8(0x7); // ticket category; why is this hardcoded? does it make a diff re: client? - - // we've got the easy stuff done by now. - // Now we need to go through the client logic for displaying various levels of ticket load ticket->WritePacket(data); - } + else + data << uint32(GMTICKET_STATUS_DEFAULT); + session->SendPacket(&data); } diff --git a/src/server/game/Tickets/TicketMgr.h b/src/server/game/Tickets/TicketMgr.h index ecf315c059c..63837e39452 100755 --- a/src/server/game/Tickets/TicketMgr.h +++ b/src/server/game/Tickets/TicketMgr.h @@ -82,7 +82,7 @@ class GmTicket { public: GmTicket(); - explicit GmTicket(Player* player, WorldPacket& recv_data); + explicit GmTicket(Player* player, WorldPacket& recvData); ~GmTicket(); bool IsClosed() const { return _closedBy; } @@ -142,6 +142,9 @@ public: std::string FormatMessageString(ChatHandler& handler, bool detailed = false) const; std::string FormatMessageString(ChatHandler& handler, const char* szClosedName, const char* szAssignedToName, const char* szUnassignedName, const char* szDeletedName) const; + void SetChatLog(std::list<uint32> time, std::string const& log); + std::string GetChatLog() const { return _chatLog; } + private: uint32 _id; uint64 _playerGuid; @@ -160,7 +163,9 @@ private: GMTicketEscalationStatus _escalatedStatus; bool _viewed; bool _needResponse; // TODO: find out the use of this, and then store it in DB + bool _haveTicket; std::string _response; + std::string _chatLog; // No need to store in db, will be refreshed every session client side }; typedef std::map<uint32, GmTicket*> GmTicketList; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 06e8d3eccbe..4949f904a57 100755 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1182,7 +1182,7 @@ void World::LoadConfigSettings(bool reload) m_int_configs[CONFIG_WARDEN_CLIENT_RESPONSE_DELAY] = ConfigMgr::GetIntDefault("Warden.ClientResponseDelay", 600); // Dungeon finder - m_bool_configs[CONFIG_DUNGEON_FINDER_ENABLE] = ConfigMgr::GetBoolDefault("DungeonFinder.Enable", false); + m_int_configs[CONFIG_LFG_OPTIONSMASK] = ConfigMgr::GetIntDefault("DungeonFinder.OptionsMask", 1); // DBC_ItemAttributes m_bool_configs[CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES] = ConfigMgr::GetBoolDefault("DBC.EnforceItemAttributes", true); @@ -1461,15 +1461,6 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Vehicle Accessories..."); sObjectMgr->LoadVehicleAccessories(); // must be after LoadCreatureTemplates() and LoadNPCSpellClickSpells() - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Dungeon boss data..."); - sObjectMgr->LoadInstanceEncounters(); - - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading LFG rewards..."); - sLFGMgr->LoadRewards(); - - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading LFG entrance positions..."); - sLFGMgr->LoadEntrancePositions(); - sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading SpellArea Data..."); // must be after quest load sSpellMgr->LoadSpellAreas(); @@ -1488,6 +1479,15 @@ void World::SetInitialWorldSettings() sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading AreaTrigger script names..."); sObjectMgr->LoadAreaTriggerScripts(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading LFG entrance positions..."); // Must be after areatriggers + sLFGMgr->LoadLFGDungeons(); + + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Dungeon boss data..."); + sObjectMgr->LoadInstanceEncounters(); + + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading LFG rewards..."); + sLFGMgr->LoadRewards(); + sLog->outInfo(LOG_FILTER_SERVER_LOADING, "Loading Graveyard-zone links..."); sObjectMgr->LoadGraveyardZones(); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 66b52549e93..470059eb86b 100755 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -155,7 +155,6 @@ enum WorldBoolConfigs CONFIG_CHATLOG_PUBLIC, CONFIG_CHATLOG_ADDON, CONFIG_CHATLOG_BGROUND, - CONFIG_DUNGEON_FINDER_ENABLE, CONFIG_AUTOBROADCAST, CONFIG_ALLOW_TICKETS, CONFIG_DBC_ENFORCE_ITEM_ATTRIBUTES, @@ -313,6 +312,7 @@ enum WorldIntConfigs CONFIG_DB_PING_INTERVAL, CONFIG_PRESERVE_CUSTOM_CHANNEL_DURATION, CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS, + CONFIG_LFG_OPTIONSMASK, CONFIG_MAX_INSTANCES_PER_HOUR, CONFIG_WARDEN_CLIENT_RESPONSE_DELAY, CONFIG_WARDEN_CLIENT_CHECK_HOLDOFF, diff --git a/src/server/scripts/Commands/CMakeLists.txt b/src/server/scripts/Commands/CMakeLists.txt index 177b3a479f5..a65324a9f42 100644 --- a/src/server/scripts/Commands/CMakeLists.txt +++ b/src/server/scripts/Commands/CMakeLists.txt @@ -27,6 +27,7 @@ set(scripts_STAT_SRCS Commands/cs_honor.cpp Commands/cs_instance.cpp Commands/cs_learn.cpp + Commands/cs_lfg.cpp Commands/cs_list.cpp Commands/cs_lookup.cpp Commands/cs_message.cpp diff --git a/src/server/scripts/Commands/cs_lfg.cpp b/src/server/scripts/Commands/cs_lfg.cpp new file mode 100644 index 00000000000..45ecc4e4721 --- /dev/null +++ b/src/server/scripts/Commands/cs_lfg.cpp @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2008-2012 TrinityCore <http://www.trinitycore.org/> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "ScriptMgr.h" +#include "Chat.h" +#include "LFGMgr.h" +#include "Group.h" + +void GetPlayerInfo(ChatHandler* handler, Player* player) +{ + if (!player) + return; + + uint64 guid = player->GetGUID(); + LfgDungeonSet dungeons = sLFGMgr->GetSelectedDungeons(guid); + + char const * const state = sLFGMgr->GetStateString(sLFGMgr->GetState(guid)); + handler->PSendSysMessage(LANG_LFG_PLAYER_INFO, player->GetName(), + state, uint8(dungeons.size()), sLFGMgr->ConcatenateDungeons(dungeons).c_str(), + sLFGMgr->GetRolesString(sLFGMgr->GetRoles(guid)).c_str(), sLFGMgr->GetComment(guid).c_str()); +} + +class lfg_commandscript : public CommandScript +{ +public: + lfg_commandscript() : CommandScript("lfg_commandscript") { } + + ChatCommand* GetCommands() const + { + static ChatCommand lfgCommandTable[] = + { + { "player", SEC_GAMEMASTER, false, &HandleLfgPlayerInfoCommand, "", NULL }, + { "group", SEC_GAMEMASTER, false, &HandleLfgGroupInfoCommand, "", NULL }, + { "queue", SEC_GAMEMASTER, false, &HandleLfgQueueInfoCommand, "", NULL }, + { "clean", SEC_ADMINISTRATOR, false, &HandleLfgCleanCommand, "", NULL }, + { "options", SEC_ADMINISTRATOR, false, &HandleLfgOptionsCommand, "", NULL }, + { NULL, SEC_PLAYER, false, NULL, "", NULL } + }; + + static ChatCommand commandTable[] = + { + { "lfg", SEC_GAMEMASTER, false, NULL, "", lfgCommandTable }, + { NULL, SEC_PLAYER, false, NULL, "", NULL } + }; + return commandTable; + } + + static bool HandleLfgPlayerInfoCommand(ChatHandler* handler, char const* args) + { + Player* target = NULL; + std::string playerName; + if (!handler->extractPlayerTarget((char*)args, &target, NULL, &playerName)) + return false; + + GetPlayerInfo(handler, target); + return true; + } + + static bool HandleLfgGroupInfoCommand(ChatHandler* handler, char const* args) + { + Player* target = NULL; + std::string playerName; + if (!handler->extractPlayerTarget((char*)args, &target, NULL, &playerName)) + return false; + + Group* grp = target->GetGroup(); + if (!grp) + { + handler->PSendSysMessage(LANG_LFG_NOT_IN_GROUP, playerName.c_str()); + return true; + } + + uint64 guid = grp->GetGUID(); + char const * const state = sLFGMgr->GetStateString(sLFGMgr->GetState(guid)); + handler->PSendSysMessage(LANG_LFG_GROUP_INFO, grp->isLFGGroup(), + state, sLFGMgr->GetDungeon(guid)); + + for (GroupReference* itr = grp->GetFirstMember(); itr != NULL; itr = itr->next()) + GetPlayerInfo(handler, itr->getSource()); + + return true; + } + + static bool HandleLfgOptionsCommand(ChatHandler* handler, char const* args) + { + int32 options = -1; + if (char* str = strtok((char*)args, " ")) + { + int32 tmp = atoi(str); + if (tmp > -1) + options = tmp; + } + + if (options != -1) + { + sLFGMgr->SetOptions(options); + handler->PSendSysMessage(LANG_LFG_OPTIONS_CHANGED); + } + handler->PSendSysMessage(LANG_LFG_OPTIONS, sLFGMgr->GetOptions()); + return true; + } + + static bool HandleLfgQueueInfoCommand(ChatHandler* handler, char const* /*args*/) + { + handler->SendSysMessage(sLFGMgr->DumpQueueInfo().c_str()); + return true; + } + + static bool HandleLfgCleanCommand(ChatHandler* handler, char const* /*args*/) + { + handler->PSendSysMessage(LANG_LFG_CLEAN); + sLFGMgr->Clean(); + return true; + } +}; + +void AddSC_lfg_commandscript() +{ + new lfg_commandscript(); +} diff --git a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp index f1aa3d68bce..8f79c4973f7 100644 --- a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp +++ b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp @@ -30,40 +30,32 @@ Script Data End */ #define GOSSIP_START_EVENT "I am ready to being" -enum eBlastmasterEmiShortfuse +enum BlastmasterEmi { GOSSIP_TEXT_EMI = 1693, - SAY_BLASTMASTER_0 = -1090000, - SAY_BLASTMASTER_1 = -1090001, - SAY_BLASTMASTER_2 = -1090002, - SAY_BLASTMASTER_3 = -1090003, - SAY_BLASTMASTER_4 = -1090004, - SAY_BLASTMASTER_5 = -1090005, - SAY_BLASTMASTER_6 = -1090006, - SAY_BLASTMASTER_7 = -1090007, - SAY_BLASTMASTER_8 = -1090008, - SAY_BLASTMASTER_9 = -1090009, - SAY_BLASTMASTER_10 = -1090010, - SAY_BLASTMASTER_11 = -1090011, - SAY_BLASTMASTER_12 = -1090012, - SAY_BLASTMASTER_13 = -1090013, - SAY_BLASTMASTER_14 = -1090014, - SAY_BLASTMASTER_15 = -1090015, - SAY_BLASTMASTER_16 = -1090016, - SAY_BLASTMASTER_17 = -1090017, - SAY_BLASTMASTER_18 = -1090018, - SAY_BLASTMASTER_19 = -1090019, - SAY_BLASTMASTER_20 = -1090020, - SAY_BLASTMASTER_21 = -1090021, - SAY_BLASTMASTER_22 = -1090022, - SAY_BLASTMASTER_23 = -1090023, - SAY_BLASTMASTER_24 = -1090024, - SAY_BLASTMASTER_25 = -1090025, - SAY_BLASTMASTER_26 = -1090026, - SAY_BLASTMASTER_27 = -1090027, - - SAY_GRUBBIS = -1090028 + SAY_BLASTMASTER_0 = 0, + SAY_BLASTMASTER_1 = 1, + SAY_BLASTMASTER_2 = 2, + SAY_BLASTMASTER_3 = 3, + SAY_BLASTMASTER_4 = 4, + SAY_BLASTMASTER_5 = 5, + SAY_BLASTMASTER_6 = 6, + SAY_BLASTMASTER_7 = 7, + SAY_BLASTMASTER_8 = 8, + SAY_BLASTMASTER_9 = 9, + SAY_BLASTMASTER_10 = 10, + SAY_BLASTMASTER_11 = 11, + SAY_BLASTMASTER_12 = 12, + SAY_BLASTMASTER_13 = 13, + SAY_BLASTMASTER_14 = 14, + SAY_BLASTMASTER_15 = 15, + SAY_BLASTMASTER_16 = 16, + SAY_BLASTMASTER_17 = 17, + SAY_BLASTMASTER_18 = 18, + SAY_BLASTMASTER_19 = 19, + + SAY_GRUBBIS = 0 }; const Position SpawnPosition[] = @@ -78,13 +70,16 @@ const Position SpawnPosition[] = {-552.534f, -110.012f, -153.577f, 0.747f}, {-550.708f, -116.436f, -153.103f, 0.679f}, {-554.030f, -115.983f, -152.635f, 0.695f}, - {-494.595f, -87.516f, 149.116f, 3.344f}, + {-494.595f, -87.516f, -149.116f, 3.344f}, {-493.349f, -90.845f, -148.882f, 3.717f}, {-491.995f, -87.619f, -148.197f, 3.230f}, {-490.732f, -90.739f, -148.091f, 3.230f}, {-490.554f, -89.114f, -148.055f, 3.230f}, {-495.240f, -90.808f, -149.493f, 3.238f}, - {-494.195f, -89.553f, -149.131f, 3.254f} + {-494.195f, -89.553f, -149.131f, 3.254f}, + {-511.3304f, -139.9622f, -152.4761f, 0.7504908f}, + {-510.6754f, -139.4371f, -152.6167f, 3.33359f}, + {-511.8976f, -139.3562f, -152.4785f, 3.961899f} }; class npc_blastmaster_emi_shortfuse : public CreatureScript @@ -300,12 +295,12 @@ public: NextStep(25000, false, 18); break; case 13: - Summon(7); + Summon(6); NextStep(25000, false, 19); break; case 14: SetInFace(false); - DoScriptText(SAY_BLASTMASTER_26, me); + Talk(SAY_BLASTMASTER_17); SetEscortPaused(true); NextStep(5000, false, 20); break; @@ -318,8 +313,8 @@ public: { case 1: SetEscortPaused(true); - DoScriptText(SAY_BLASTMASTER_0, me); - NextStep(1500, true); + Talk(SAY_BLASTMASTER_0); + NextStep(2000, true); break; case 2: if (!instance) @@ -368,7 +363,7 @@ public: me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[1], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[2], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[3], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); - DoScriptText(SAY_BLASTMASTER_19, me); + Talk(SAY_BLASTMASTER_7); break; case 4: if (GameObject* go = me->SummonGameObject(183410, -542.199f, -96.854f, -155.790f, 0, 0, 0, 0, 0, 1000)) @@ -378,38 +373,37 @@ public: } break; case 5: - me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[0], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); - me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[1], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); - me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[2], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); - DoScriptText(SAY_BLASTMASTER_15, me); - break; - case 6: me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[10], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[11], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[12], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[13], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[14], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); break; - case 7: + case 6: if (GameObject* go = me->SummonGameObject(183410, -507.820f, -103.333f, -151.353f, 0, 0, 0, 0, 0, 1000)) { GoSummonList.push_back(go->GetGUID()); go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); //We can't use it! - Summon(6); + Summon(5); } break; - case 8: + case 7: if (GameObject* go = me->SummonGameObject(183410, -511.829f, -86.249f, -151.431f, 0, 0, 0, 0, 0, 1000)) { GoSummonList.push_back(go->GetGUID()); go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); //We can't use it! } break; - case 9: - if (Creature* pGrubbis = me->SummonCreature(NPC_GRUBBIS, SpawnPosition[15], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000)) - DoScriptText(SAY_GRUBBIS, pGrubbis); + case 8: + if (Creature* grubbis = me->SummonCreature(NPC_GRUBBIS, SpawnPosition[15], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000)) + grubbis->AI()->Talk(SAY_GRUBBIS); me->SummonCreature(NPC_CHOMPER, SpawnPosition[16], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); break; + case 9: + me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[17].GetPositionX(), SpawnPosition[17].GetPositionY(), SpawnPosition[17].GetPositionZ(), SpawnPosition[17].GetOrientation(), 0, 0, 0, 0, 7200); + me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[18].GetPositionX(), SpawnPosition[18].GetPositionY(), SpawnPosition[18].GetPositionZ(), SpawnPosition[18].GetOrientation(), 0, 0, 0, 0, 7200); + me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[19].GetPositionX(), SpawnPosition[19].GetPositionY(), SpawnPosition[19].GetPositionZ(), SpawnPosition[19].GetOrientation(), 0, 0, 0, 0, 7200); + break; } } @@ -422,29 +416,29 @@ public: switch (uiPhase) { case 1: - DoScriptText(SAY_BLASTMASTER_1, me); - NextStep(1500, true); + Talk(SAY_BLASTMASTER_1); + NextStep(2000, true); break; case 2: SetEscortPaused(false); NextStep(0, false, 0); break; case 3: - DoScriptText(SAY_BLASTMASTER_2, me); + Talk(SAY_BLASTMASTER_2); SetEscortPaused(false); NextStep(0, false, 0); break; case 4: - DoScriptText(SAY_BLASTMASTER_3, me); + Talk(SAY_BLASTMASTER_3); NextStep(3000, true); break; case 5: - DoScriptText(SAY_BLASTMASTER_4, me); + Talk(SAY_BLASTMASTER_4); NextStep(3000, true); break; case 6: SetInFace(true); - DoScriptText(SAY_BLASTMASTER_5, me); + Talk(SAY_BLASTMASTER_5); Summon(1); if (instance) if (GameObject* go = GameObject::GetGameObject(*me, instance->GetData64(DATA_GO_CAVE_IN_RIGHT))) @@ -452,12 +446,12 @@ public: NextStep(3000, true); break; case 7: - DoScriptText(SAY_BLASTMASTER_6, me); + Talk(SAY_BLASTMASTER_6); SetEscortPaused(false); NextStep(0, false, 0); break; case 8: - me->HandleEmoteCommand(EMOTE_STATE_WORK); + me->HandleEmoteCommand(EMOTE_STATE_USE_STANDING); NextStep(25000, true); break; case 9: @@ -466,31 +460,32 @@ public: break; case 10: Summon(4); + Talk(SAY_BLASTMASTER_8); NextStep(0, false); break; case 11: - DoScriptText(SAY_BLASTMASTER_17, me); + Talk(SAY_BLASTMASTER_9); NextStep(5000, true); break; case 12: - DoScriptText(SAY_BLASTMASTER_18, me); + Talk(SAY_BLASTMASTER_10); NextStep(5000, true); break; case 13: - DoScriptText(SAY_BLASTMASTER_20, me); + Talk(SAY_BLASTMASTER_11); CaveDestruction(true); NextStep(8000, true); break; case 14: - DoScriptText(SAY_BLASTMASTER_21, me); + Talk(SAY_BLASTMASTER_12); NextStep(8500, true); break; case 15: - DoScriptText(SAY_BLASTMASTER_22, me); + Talk(SAY_BLASTMASTER_13); NextStep(2000, true); break; case 16: - DoScriptText(SAY_BLASTMASTER_23, me); + Talk(SAY_BLASTMASTER_14); SetInFace(false); if (instance) if (GameObject* go = GameObject::GetGameObject(*me, instance->GetData64(DATA_GO_CAVE_IN_LEFT))) @@ -499,31 +494,36 @@ public: break; case 17: SetEscortPaused(false); - DoScriptText(SAY_BLASTMASTER_24, me); - Summon(6); + Talk(SAY_BLASTMASTER_15); + Summon(5); NextStep(0, false); break; case 18: - Summon(7); + Summon(6); NextStep(0, false); break; case 19: SetInFace(false); - Summon(8); - DoScriptText(SAY_BLASTMASTER_25, me); + Summon(7); + Talk(SAY_BLASTMASTER_16); NextStep(0, false); break; case 20: - DoScriptText(SAY_BLASTMASTER_27, me); + Talk(SAY_BLASTMASTER_18); NextStep(2000, true); break; case 21: - Summon(9); + Summon(8); NextStep(0, false); break; case 22: CaveDestruction(false); - DoScriptText(SAY_BLASTMASTER_20, me); + Talk(SAY_BLASTMASTER_11); + NextStep(3000, true); + break; + case 23: + Summon(9); + Talk(SAY_BLASTMASTER_19); NextStep(0, false); break; } diff --git a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.h b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.h index a62cf9f9cae..068ba5d26af 100644 --- a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.h +++ b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.h @@ -21,7 +21,8 @@ enum eGameObjects { GO_CAVE_IN_LEFT = 146085, - GO_CAVE_IN_RIGHT = 146086 + GO_CAVE_IN_RIGHT = 146086, + GO_RED_ROCKET = 103820 }; enum eCreatures diff --git a/src/server/scripts/Kalimdor/feralas.cpp b/src/server/scripts/Kalimdor/feralas.cpp index 4fcd20951c9..c1e247044f7 100644 --- a/src/server/scripts/Kalimdor/feralas.cpp +++ b/src/server/scripts/Kalimdor/feralas.cpp @@ -27,6 +27,7 @@ EndScriptData */ #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" #include "ScriptedGossip.h" +#include "SpellScript.h" /*###### ## npc_gregan_brewspewer @@ -202,6 +203,39 @@ public: }; +enum GordunniTrap +{ + GO_GORDUNNI_DIRT_MOUND = 144064, +}; + +class spell_gordunni_trap : public SpellScriptLoader +{ + public: + spell_gordunni_trap() : SpellScriptLoader("spell_gordunni_trap") { } + + class spell_gordunni_trap_SpellScript : public SpellScript + { + PrepareSpellScript(spell_gordunni_trap_SpellScript); + + void HandleDummy() + { + if (Unit* caster = GetCaster()) + if (GameObject* chest = caster->SummonGameObject(GO_GORDUNNI_DIRT_MOUND, caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + chest->SetSpellId(GetSpellInfo()->Id); + } + + void Register() + { + OnCast += SpellCastFn(spell_gordunni_trap_SpellScript::HandleDummy); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_gordunni_trap_SpellScript(); + } +}; + /*###### ## AddSC ######*/ @@ -211,4 +245,5 @@ void AddSC_feralas() new npc_gregan_brewspewer(); new npc_oox22fe(); new npc_screecher_spirit(); + new spell_gordunni_trap(); } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp index b8ff3dfbb46..059f513f12c 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -702,7 +702,7 @@ class npc_halion_controller : public CreatureScript halion->AI()->Talk(SAY_INTRO); break; case EVENT_TWILIGHT_MENDING: - if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION))) + if (ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_HALION))) // Just check if physical Halion is spawned if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetData64(DATA_TWILIGHT_HALION))) twilightHalion->CastSpell((Unit*)NULL, SPELL_TWILIGHT_MENDING, true); break; @@ -1121,7 +1121,7 @@ class npc_combustion_consumption : public CreatureScript struct npc_combustion_consumptionAI : public Scripted_NoMovementAI { npc_combustion_consumptionAI(Creature* creature) : Scripted_NoMovementAI(creature), - _summonerGuid(0), _instance(creature->GetInstanceScript()) + _instance(creature->GetInstanceScript()), _summonerGuid(0) { switch (me->GetEntry()) { diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp index 39fff139b52..28595571a2f 100644 --- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp +++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp @@ -232,7 +232,7 @@ public: { npc_verdisa_beglaristrasz_eternosAI(Creature* creature) : ScriptedAI(creature) { } - void MovementInform(uint32 type, uint32 id) + void MovementInform(uint32 /*type*/, uint32 id) { // When Belgaristraz finish his moving say grateful text if (me->GetEntry() == NPC_BELGARISTRASZ) diff --git a/src/server/scripts/Northrend/storm_peaks.cpp b/src/server/scripts/Northrend/storm_peaks.cpp index 976a6e5dba5..1733af17580 100644 --- a/src/server/scripts/Northrend/storm_peaks.cpp +++ b/src/server/scripts/Northrend/storm_peaks.cpp @@ -19,6 +19,8 @@ #include "ScriptedCreature.h" #include "ScriptedGossip.h" #include "ScriptedEscortAI.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" #include "Vehicle.h" #include "CombatAI.h" @@ -564,6 +566,53 @@ class npc_hyldsmeet_protodrake : public CreatureScript } }; +enum CloseRift +{ + SPELL_DESPAWN_RIFT = 61665 +}; + +class spell_close_rift : public SpellScriptLoader +{ + public: + spell_close_rift() : SpellScriptLoader("spell_close_rift") { } + + class spell_close_rift_AuraScript : public AuraScript + { + PrepareAuraScript(spell_close_rift_AuraScript); + + bool Load() + { + _counter = 0; + return true; + } + + bool Validate(SpellInfo const* /*spell*/) + { + return sSpellMgr->GetSpellInfo(SPELL_DESPAWN_RIFT); + } + + void HandlePeriodic(AuraEffect const* /* aurEff */) + { + if (++_counter == 5) + GetTarget()->CastSpell((Unit*)NULL, SPELL_DESPAWN_RIFT, true); + } + + void Register() + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_close_rift_AuraScript::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + + private: + uint8 _counter; + + }; + + AuraScript* GetAuraScript() const + { + return new spell_close_rift_AuraScript(); + } +}; + void AddSC_storm_peaks() { new npc_agnetta_tyrsdottar(); @@ -574,4 +623,5 @@ void AddSC_storm_peaks() new npc_freed_protodrake(); new npc_icefang(); new npc_hyldsmeet_protodrake(); + new spell_close_rift(); } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 5cc31ad54e8..519d79add6d 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -1581,12 +1581,12 @@ class spell_gen_luck_of_the_draw : public SpellScriptLoader } - LFGDungeonEntry const* randomDungeon = sLFGDungeonStore.LookupEntry(*itr); + LFGDungeonData const* randomDungeon = sLFGMgr->GetLFGDungeon(*itr); if (Group* group = owner->GetGroup()) if (Map const* map = owner->GetMap()) if (group->isLFGGroup()) if (uint32 dungeonId = sLFGMgr->GetDungeon(group->GetGUID(), true)) - if (LFGDungeonEntry const* dungeon = sLFGDungeonStore.LookupEntry(dungeonId)) + if (LFGDungeonData const* dungeon = sLFGMgr->GetLFGDungeon(dungeonId)) if (uint32(dungeon->map) == map->GetId() && dungeon->difficulty == uint32(map->GetDifficulty())) if (randomDungeon && randomDungeon->type == LFG_TYPE_RANDOM) return; // in correct dungeon diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index b8df77d0346..b291b9751b4 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -2115,7 +2115,7 @@ class npc_shadowfiend : public CreatureScript { npc_shadowfiendAI(Creature* creature) : PetAI(creature) {} - void JustDied(Unit* killer) + void JustDied(Unit* /*killer*/) { if (me->isSummon()) if (Unit* owner = me->ToTempSummon()->GetSummoner()) diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index c29d8313c20..d9b517c098f 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -327,8 +327,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PREPARE_STATEMENT(CHAR_DEL_GO_RESPAWN_BY_INSTANCE, "DELETE FROM gameobject_respawn WHERE mapId = ? AND instanceId = ?", CONNECTION_ASYNC) // GM Tickets - PREPARE_STATEMENT(CHAR_SEL_GM_TICKETS, "SELECT ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, completed, escalated, viewed FROM gm_tickets", CONNECTION_SYNCH) - PREPARE_STATEMENT(CHAR_REP_GM_TICKET, "REPLACE INTO gm_tickets (ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, completed, escalated, viewed) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC) + PREPARE_STATEMENT(CHAR_SEL_GM_TICKETS, "SELECT ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket FROM gm_tickets", CONNECTION_SYNCH) + PREPARE_STATEMENT(CHAR_REP_GM_TICKET, "REPLACE INTO gm_tickets (ticketId, guid, name, message, createTime, mapId, posX, posY, posZ, lastModifiedTime, closedBy, assignedTo, comment, response, completed, escalated, viewed, haveTicket) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_DEL_GM_TICKET, "DELETE FROM gm_tickets WHERE ticketId = ?", CONNECTION_ASYNC) PREPARE_STATEMENT(CHAR_DEL_PLAYER_GM_TICKETS, "DELETE FROM gm_tickets WHERE guid = ?", CONNECTION_ASYNC) diff --git a/src/server/shared/Logging/Appender.h b/src/server/shared/Logging/Appender.h index 5c4b009ef67..2d6145b14af 100644 --- a/src/server/shared/Logging/Appender.h +++ b/src/server/shared/Logging/Appender.h @@ -25,48 +25,48 @@ enum LogFilterType { - LOG_FILTER_GENERAL, // This one should only be used inside Log.cpp - LOG_FILTER_UNITS, // Anything related to units that doesn't fit in other categories. ie. creature formations - LOG_FILTER_PETS, - LOG_FILTER_VEHICLES, - LOG_FILTER_TSCR, // C++ AI, instance scripts, etc. - LOG_FILTER_DATABASE_AI, // SmartAI, EventAI, Creature* * AI - LOG_FILTER_MAPSCRIPTS, - LOG_FILTER_NETWORKIO, - LOG_FILTER_SPELLS_AURAS, - LOG_FILTER_ACHIEVEMENTSYS, - LOG_FILTER_CONDITIONSYS, - LOG_FILTER_POOLSYS, - LOG_FILTER_AUCTIONHOUSE, - LOG_FILTER_BATTLEGROUND, - LOG_FILTER_OUTDOORPVP, - LOG_FILTER_CHATSYS, - LOG_FILTER_LFG, - LOG_FILTER_MAPS, - LOG_FILTER_PLAYER, // Any player log that does not fit in other player filters - LOG_FILTER_PLAYER_LOADING, // Debug output from Player::_Load functions - LOG_FILTER_PLAYER_ITEMS, - LOG_FILTER_PLAYER_SKILLS, - LOG_FILTER_PLAYER_CHATLOG, - LOG_FILTER_LOOT, - LOG_FILTER_GUILD, - LOG_FILTER_TRANSPORTS, - LOG_FILTER_SQL, - LOG_FILTER_GMCOMMAND, - LOG_FILTER_REMOTECOMMAND, - LOG_FILTER_WARDEN, - LOG_FILTER_AUTHSERVER, - LOG_FILTER_WORLDSERVER, - LOG_FILTER_GAMEEVENTS, - LOG_FILTER_CALENDAR, - LOG_FILTER_CHARACTER, - LOG_FILTER_ARENAS, - LOG_FILTER_SQL_DRIVER, - LOG_FILTER_SQL_DEV, - LOG_FILTER_PLAYER_DUMP, - LOG_FILTER_BATTLEFIELD, - LOG_FILTER_SERVER_LOADING, - LOG_FILTER_OPCODES + LOG_FILTER_GENERAL = 0, // This one should only be used inside Log.cpp + LOG_FILTER_UNITS = 1, // Anything related to units that doesn't fit in other categories. ie. creature formations + LOG_FILTER_PETS = 2, + LOG_FILTER_VEHICLES = 3, + LOG_FILTER_TSCR = 4, // C++ AI, instance scripts, etc. + LOG_FILTER_DATABASE_AI = 5, // SmartAI, EventAI, Creature* * AI + LOG_FILTER_MAPSCRIPTS = 6, + LOG_FILTER_NETWORKIO = 7, + LOG_FILTER_SPELLS_AURAS = 8, + LOG_FILTER_ACHIEVEMENTSYS = 9, + LOG_FILTER_CONDITIONSYS = 10, + LOG_FILTER_POOLSYS = 11, + LOG_FILTER_AUCTIONHOUSE = 12, + LOG_FILTER_BATTLEGROUND = 13, + LOG_FILTER_OUTDOORPVP = 14, + LOG_FILTER_CHATSYS = 15, + LOG_FILTER_LFG = 16, + LOG_FILTER_MAPS = 17, + LOG_FILTER_PLAYER = 18, // Any player log that does not fit in other player filters + LOG_FILTER_PLAYER_LOADING = 19, // Debug output from Player::_Load functions + LOG_FILTER_PLAYER_ITEMS = 20, + LOG_FILTER_PLAYER_SKILLS = 21, + LOG_FILTER_PLAYER_CHATLOG = 22, + LOG_FILTER_LOOT = 23, + LOG_FILTER_GUILD = 24, + LOG_FILTER_TRANSPORTS = 25, + LOG_FILTER_SQL = 26, + LOG_FILTER_GMCOMMAND = 27, + LOG_FILTER_REMOTECOMMAND = 28, + LOG_FILTER_WARDEN = 29, + LOG_FILTER_AUTHSERVER = 30, + LOG_FILTER_WORLDSERVER = 31, + LOG_FILTER_GAMEEVENTS = 32, + LOG_FILTER_CALENDAR = 33, + LOG_FILTER_CHARACTER = 34, + LOG_FILTER_ARENAS = 35, + LOG_FILTER_SQL_DRIVER = 36, + LOG_FILTER_SQL_DEV = 37, + LOG_FILTER_PLAYER_DUMP = 38, + LOG_FILTER_BATTLEFIELD = 39, + LOG_FILTER_SERVER_LOADING = 40, + LOG_FILTER_OPCODES = 41 }; const uint8 MaxLogFilter = uint8(LOG_FILTER_OPCODES) + 1; @@ -95,12 +95,12 @@ enum AppenderType enum AppenderFlags { - APPENDER_FLAGS_NONE = 0x00, - APPENDER_FLAGS_PREFIX_TIMESTAMP = 0x01, - APPENDER_FLAGS_PREFIX_LOGLEVEL = 0x02, - APPENDER_FLAGS_PREFIX_LOGFILTERTYPE = 0x04, - APPENDER_FLAGS_USE_TIMESTAMP = 0x08, // only used by FileAppender - APPENDER_FLAGS_MAKE_FILE_BACKUP = 0x10 // only used by FileAppender + APPENDER_FLAGS_NONE = 0x00, + APPENDER_FLAGS_PREFIX_TIMESTAMP = 0x01, + APPENDER_FLAGS_PREFIX_LOGLEVEL = 0x02, + APPENDER_FLAGS_PREFIX_LOGFILTERTYPE = 0x04, + APPENDER_FLAGS_USE_TIMESTAMP = 0x08, // only used by FileAppender + APPENDER_FLAGS_MAKE_FILE_BACKUP = 0x10 // only used by FileAppender }; struct LogMessage diff --git a/src/server/shared/Logging/Log.cpp b/src/server/shared/Logging/Log.cpp index 4538fc75c1e..d8914ac8ee0 100755 --- a/src/server/shared/Logging/Log.cpp +++ b/src/server/shared/Logging/Log.cpp @@ -435,10 +435,10 @@ void Log::outCharDump(char const* str, uint32 accountId, uint32 guid, char const << ")\n" << str << "\n== END DUMP ==\n"; LogMessage* msg = new LogMessage(LOG_LEVEL_INFO, LOG_FILTER_PLAYER_DUMP, ss.str()); - ss.clear(); - ss << guid << '_' << name; + std::ostringstream param; + param << guid << '_' << name; - msg->param1 = ss.str(); + msg->param1 = param.str(); write(msg); } diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 7e43cf87dfc..16fbca2597d 100755 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -104,13 +104,13 @@ extern int main(int argc, char **argv) if (strcmp(argv[c], "install") == 0) { if (WinServiceInstall()) - printf("Installing service"); + printf("Installing service\n"); return 1; } else if (strcmp(argv[c], "uninstall") == 0) { if (WinServiceUninstall()) - printf("Uninstalling service"); + printf("Uninstalling service\n"); return 1; } else @@ -131,8 +131,8 @@ extern int main(int argc, char **argv) if (!ConfigMgr::Load(cfg_file)) { - printf("Invalid or missing configuration file : %s", cfg_file); - printf("Verify that the file exists and has \'[worldserver]' written in the top of the file!"); + printf("Invalid or missing configuration file : %s\n", cfg_file); + printf("Verify that the file exists and has \'[worldserver]' written in the top of the file!\n"); return 1; } sLog->outInfo(LOG_FILTER_WORLDSERVER, "Using configuration file %s.", cfg_file); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 55443f4253b..8f79cff8f46 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -1106,12 +1106,15 @@ Command.LookupMaxResults = 0 AllowTickets = 1 # -# DungeonFinder.Enable +# DungeonFinder.OptionsMask # Description: Dungeon and raid finder system. -# Default: 1 - (Enabled) -# 0 - (Disabled) +# Value is a bitmask consisting of: +# LFG_OPTION_ENABLE_DUNGEON_FINDER = 1, Enable the dungeon finder browser +# LFG_OPTION_ENABLE_RAID_BROWSER = 2, Enable the raid browser +# Default: 1 + +DungeonFinder.OptionsMask = 1 -DungeonFinder.Enable = 1 # # DBC.EnforceItemAttributes |