diff --git a/contrib/extractor/System.cpp b/contrib/extractor/System.cpp index 843fe616e..9dffa0048 100644 --- a/contrib/extractor/System.cpp +++ b/contrib/extractor/System.cpp @@ -153,7 +153,12 @@ uint32 ReadMapDBC() { printf("Read Map.dbc file... "); DBCFile dbc("DBFilesClient\\Map.dbc"); - dbc.open(); + + if(!dbc.open()) + { + printf("Fatal error: Invalid Map.dbc file format!\n"); + exit(1); + } size_t map_count = dbc.getRecordCount(); map_ids = new map_id[map_count]; @@ -170,7 +175,12 @@ void ReadAreaTableDBC() { printf("Read AreaTable.dbc file..."); DBCFile dbc("DBFilesClient\\AreaTable.dbc"); - dbc.open(); + + if(!dbc.open()) + { + printf("Fatal error: Invalid AreaTable.dbc file format!\n"); + exit(1); + } size_t area_count = dbc.getRecordCount(); size_t maxid = dbc.getMaxId(); @@ -189,7 +199,12 @@ void ReadLiquidTypeTableDBC() { printf("Read LiquidType.dbc file..."); DBCFile dbc("DBFilesClient\\LiquidType.dbc"); - dbc.open(); + if(!dbc.open()) + { + printf("Fatal error: Invalid LiquidType.dbc file format!\n"); + exit(1); + } + size_t LiqType_count = dbc.getRecordCount(); size_t LiqType_maxid = dbc.getMaxId(); LiqType = new uint16[LiqType_maxid + 1]; diff --git a/contrib/extractor/ad.exe b/contrib/extractor/ad.exe index 8300b697c..208eb2da0 100755 Binary files a/contrib/extractor/ad.exe and b/contrib/extractor/ad.exe differ diff --git a/contrib/extractor/dbcfile.cpp b/contrib/extractor/dbcfile.cpp index acfb35482..9e42f876b 100644 --- a/contrib/extractor/dbcfile.cpp +++ b/contrib/extractor/dbcfile.cpp @@ -9,29 +9,42 @@ DBCFile::DBCFile(const std::string &filename): { } -void DBCFile::open() +bool DBCFile::open() { MPQFile f(filename.c_str()); char header[4]; unsigned int na,nb,es,ss; - f.read(header,4); // Number of records - assert(header[0]=='W' && header[1]=='D' && header[2]=='B' && header[3] == 'C'); - f.read(&na,4); // Number of records - f.read(&nb,4); // Number of fields - f.read(&es,4); // Size of a record - f.read(&ss,4); // String size + if(f.read(header,4)!=4) // Number of records + return false; + + if(header[0]!='W' || header[1]!='D' || header[2]!='B' || header[3]!='C') + return false; + + if(f.read(&na,4)!=4) // Number of records + return false; + if(f.read(&nb,4)!=4) // Number of fields + return false; + if(f.read(&es,4)!=4) // Size of a record + return false; + if(f.read(&ss,4)!=4) // String size + return false; recordSize = es; recordCount = na; fieldCount = nb; stringSize = ss; - assert(fieldCount*4 == recordSize); + if(fieldCount*4 != recordSize) + return false; data = new unsigned char[recordSize*recordCount+stringSize]; stringTable = data + recordSize*recordCount; - f.read(data,recordSize*recordCount+stringSize); + + size_t data_size = recordSize*recordCount+stringSize; + if(f.read(data,data_size)!=data_size) + return false; f.close(); + return true; } DBCFile::~DBCFile() { diff --git a/contrib/extractor/dbcfile.h b/contrib/extractor/dbcfile.h index 521ca6742..4af2606af 100644 --- a/contrib/extractor/dbcfile.h +++ b/contrib/extractor/dbcfile.h @@ -10,7 +10,7 @@ public: ~DBCFile(); // Open database. It must be openened before it can be used. - void open(); + bool open(); // Database exceptions class Exception diff --git a/sql/mangos.sql b/sql/mangos.sql index 9c54def8b..82ab1d7ab 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -22,7 +22,7 @@ DROP TABLE IF EXISTS `db_version`; CREATE TABLE `db_version` ( `version` varchar(120) default NULL, - `required_7439_01_mangos_mangos_string` bit(1) default NULL + `required_7472_01_mangos_mangos_string` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -639,6 +639,8 @@ CREATE TABLE `creature_model_info` ( LOCK TABLES `creature_model_info` WRITE; /*!40000 ALTER TABLE `creature_model_info` DISABLE KEYS */; +INSERT INTO `creature_model_info` VALUES +(10045, 1, 1.5, 2, 0); /*!40000 ALTER TABLE `creature_model_info` ENABLE KEYS */; UNLOCK TABLES; @@ -830,7 +832,7 @@ CREATE TABLE `creature_template` ( LOCK TABLES `creature_template` WRITE; /*!40000 ALTER TABLE `creature_template` DISABLE KEYS */; INSERT INTO `creature_template` VALUES -(1,1,10045,0,10045,0,'Waypoint(Only GM can see it)','Visual',NULL,1,1,64,64,0,0,0,35,35,0,0.91,1,0,14,15,0,100,2000,2200,4096,0,0,0,0,0,0,1.76,2.42,100,8,5242886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,3,1.0,1.0,0,1,0,0,0x82,''); +(1,0,10045,0,10045,0,'Waypoint(Only GM can see it)','Visual',NULL,1,1,64,64,0,0,0,35,35,0,0.91,1,0,14,15,0,100,2000,2200,4096,0,0,0,0,0,0,1.76,2.42,100,8,5242886,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,3,1.0,1.0,0,1,0,0,0x82,''); /*!40000 ALTER TABLE `creature_template` ENABLE KEYS */; UNLOCK TABLES; @@ -1706,7 +1708,7 @@ INSERT INTO `item_template` VALUES (56,4,1,-1,'Apprentice\'s Robe',12647,0,0,1,5,1,20,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,35,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (57,4,1,-1,'Acolyte\'s Robe',12645,0,0,1,5,1,20,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,35,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (59,4,0,-1,'Acolyte\'s Shoes',3261,1,0,1,5,1,8,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(117,0,0,-1,'Tough Jerky',2473,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(117,0,0,-1,'Tough Jerky',2473,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (120,4,1,-1,'Thug Pants',10006,0,0,1,4,1,7,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,25,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (121,4,0,-1,'Thug Boots',10008,1,0,1,4,1,8,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (127,4,0,-1,'Trapper\'s Shirt',9996,1,0,1,1,1,4,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), @@ -1716,13 +1718,13 @@ INSERT INTO `item_template` VALUES (148,4,0,-1,'Rugged Trapper\'s Shirt',9976,1,0,1,1,1,4,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (153,4,2,-1,'Primitive Kilt',10050,0,0,1,5,1,7,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,8,0,0,0,0,0,30,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (154,4,0,-1,'Primitive Mantle',10058,1,0,1,1,1,4,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(159,0,0,-1,'Refreshing Spring Water',18084,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,430,0,-1,0,-1,59,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(159,0,0,-1,'Refreshing Spring Water',18084,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,430,0,-1,0,-1,59,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (1395,4,1,-1,'Apprentice\'s Pants',9924,0,0,1,5,1,7,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,25,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (1396,4,1,-1,'Acolyte\'s Pants',3260,0,0,1,4,1,7,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,25,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(2070,0,0,-1,'Darnassian Bleu',6353,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(2070,0,0,-1,'Darnassian Bleu',6353,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (2092,2,15,-1,'Worn Dagger',6442,1,0,1,35,7,13,2047,255,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1600,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,1,3,0,0,0,0,16,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(2101,1,2,-1,'Light Quiver',21328,1,0,1,4,1,18,2047,255,1,1,0,0,0,0,0,0,0,0,1,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,14824,1,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,'',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(2102,1,3,-1,'Small Ammo Pouch',1816,1,0,1,4,1,18,2047,255,1,1,0,0,0,0,0,0,0,0,1,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,14824,1,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,'',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(2101,11,2,-1,'Light Quiver',21328,1,0,1,4,1,18,2047,255,1,1,0,0,0,0,0,0,0,0,1,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,14824,1,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,'',0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(2102,11,3,-1,'Small Ammo Pouch',1816,1,0,1,4,1,18,2047,255,1,1,0,0,0,0,0,0,0,0,1,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,14824,1,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,'',0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (2105,4,0,-1,'Thug Shirt',10005,1,0,1,5,1,4,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (2361,2,5,-1,'Battleworn Hammer',8690,1,0,1,45,9,17,2047,255,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2900,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,2,1,0,0,0,0,25,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (2362,4,6,-1,'Worn Wooden Shield',18730,0,0,1,7,1,14,32767,511,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,1,4,0,0,1,0,20,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), @@ -1730,11 +1732,11 @@ INSERT INTO `item_template` VALUES (2508,2,3,-1,'Old Blunderbuss',6606,1,0,1,27,5,26,2047,255,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2300,3,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,1,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (2512,6,2,-1,'Rough Arrow',5996,1,0,1,10,0,24,2047,255,5,1,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,2,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (2516,6,3,-1,'Light Shot',5998,1,0,1,10,0,24,2047,255,5,1,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,2,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(2947,2,16,-1,'Small Throwing Knife',16754,1,0,1,15,0,25,2047,255,3,1,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2000,4,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(2947,15,0,-1,'Small Throwing Knife',16754,1,0,1,15,0,0,2047,255,3,1,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2000,4,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (3661,2,10,-1,'Handcrafted Staff',18530,1,0,1,45,9,17,2047,255,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2900,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,2,2,0,0,0,0,25,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(4536,0,0,-1,'Shiny Red Apple',6410,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(4540,0,0,-1,'Tough Hunk of Bread',6399,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(4604,0,0,-1,'Forest Mushroom Cap',15852,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(4536,0,0,-1,'Shiny Red Apple',6410,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,100,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(4540,0,0,-1,'Tough Hunk of Bread',6399,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(4604,0,0,-1,'Forest Mushroom Cap',15852,1,0,6,25,1,0,2047,255,5,1,0,0,0,0,0,0,0,0,20,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,433,0,-1,0,-1,11,1000,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (6096,4,0,-1,'Apprentice\'s Shirt',2163,1,0,1,1,1,4,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (6097,4,0,-1,'Acolyte\'s Shirt',2470,1,0,1,1,1,4,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (6098,4,1,-1,'Neophyte\'s Robe',12679,0,0,1,4,1,20,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,35,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), @@ -1750,14 +1752,14 @@ INSERT INTO `item_template` VALUES (6139,4,1,-1,'Novice\'s Robe',12684,0,0,1,4,1,20,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,35,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (6140,4,1,-1,'Apprentice\'s Robe',12649,0,0,1,4,1,20,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,35,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (6144,4,1,-1,'Neophyte\'s Robe',12680,0,0,1,5,1,20,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,7,0,0,0,0,0,35,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(6948,15,0,-1,'Hearthstone',6418,1,64,1,0,0,0,32767,511,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8690,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,'',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(6948,15,0,-1,'Hearthstone',6418,1,64,1,0,0,0,32767,511,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8690,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,'',0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (12282,2,1,-1,'Worn Battleaxe',22291,1,0,1,43,8,17,2047,255,2,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2900,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,1,1,0,0,0,0,25,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(14646,12,0,-1,'Northshire Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5805,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(14647,12,0,-1,'Coldridge Valley Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5841,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(14648,12,0,-1,'Shadowglen Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5842,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(14649,12,0,-1,'Valley of Trials Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5843,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(14650,12,0,-1,'Camp Narache Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5844,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), -(14651,12,0,-1,'Deathknell Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5847,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(14646,12,0,-1,'Northshire Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5805,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(14647,12,0,-1,'Coldridge Valley Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5841,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(14648,12,0,-1,'Shadowglen Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5842,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(14649,12,0,-1,'Valley of Trials Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5843,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(14650,12,0,-1,'Camp Narache Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5844,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), +(14651,12,0,-1,'Deathknell Gift Voucher',18499,1,0,1,0,0,0,2047,255,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1000,0,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,'',0,0,0,5847,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (25861,2,16,-1,'Crude Throwing Axe',20777,1,0,1,15,0,25,2047,255,3,1,0,0,0,0,0,0,0,0,200,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2000,4,0,0,0,0,0,-1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'',0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,'internalItemHandler',0,0,0,0,0), (34648,4,4,-1,'Acherus Knight\'s Greaves',51496,2,32768,1,51,10,8,-1,-1,60,55,0,0,0,0,0,0,0,0,1,0,3,4,10,7,12,3,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,392,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,-1,0,0,0,0,-1,0,-1,0,0,0,0,-1,0,-1,0,0,0,0,-1,0,-1,0,0,0,0,-1,0,-1,1,'',0,0,0,0,0,6,0,0,0,0,0,55,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,'',0,0,0,0), (34649,4,4,-1,'Acherus Knight\'s Gauntlets',51498,2,32768,1,34,6,10,-1,-1,60,55,0,0,0,0,0,0,0,0,1,0,3,4,15,7,6,32,8,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,356,0,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,-1,0,0,0,0,-1,0,-1,0,0,0,0,-1,0,-1,0,0,0,0,-1,0,-1,0,0,0,0,-1,0,-1,1,'',0,0,0,0,0,6,0,0,0,0,0,40,0,0,0,0,0,0,0,0,0,0,0,0,-1,0,0,0,'',0,0,0,0), @@ -2769,9 +2771,11 @@ INSERT INTO `mangos_string` VALUES (611,'The Horde flag was picked up by $n!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (612,'The Alliance Flag was picked up by $n!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (613,'The flags are now placed at their bases.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(636,'The Battle for Eye of the Storm begins in 1 minute.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(637,'The Battle for Eye of the Storm begins in 30 seconds.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(638,'The Battle for Eye of the Storm has begun!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(614,'The Alliance flag is now placed at its base.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(615,'The Horde flag is now placed at its base.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(636,'The battle begins in 1 minute.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(637,'The battle begins in 30 seconds.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(638,'The battle has begun!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (650,'Alliance',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (651,'Horde',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (652,'stables',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), @@ -2783,12 +2787,33 @@ INSERT INTO `mangos_string` VALUES (658,'$n has defended the %s',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (659,'$n has assaulted the %s',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (660,'$n claims the %s! If left unchallenged, the %s will control it in 1 minute!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(661,'The Battle for Arathi Basin begins in 1 minute.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(662,'The Battle for Arathi Basin begins in 30 seconds. Prepare yourselves!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(663,'The Battle for Arathi Basin has begun!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(661,'The battle for Arathi Basin begins in 1 minute.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(662,'The battle for Arathi Basin begins in 30 seconds. Prepare yourselves!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(663,'The battle for Arathi Basin has begun!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (664,'The Alliance has gathered $1776W resources, and is near victory!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (665,'The Horde has gathered $1777W resources, and is near victory!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (666,'After your recent battle in %s our best attempts to award you a Mark of Honor failed. Enclosed you will find the Mark of Honor we were not able to deliver to you at the time. Thanks for fighting in %s!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(667,'The Alliance has taken control of the Mage Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(668,'The Horde has taken control of the Mage Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(669,'The Alliance has taken control of the Draenei Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(670,'The Horde has taken control of the Draenei Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(671,'The Alliance has taken control of the Blood Elf Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(672,'The Horde has taken control of the Blood Elf Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(673,'The Alliance has taken control of the Fel Reaver Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(674,'The Horde has taken control of the Fel Reaver Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(675,'The Alliance has lost control of the Mage Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(676,'The Horde has lost control of the Mage Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(677,'The Alliance has lost control of the Draenei Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(678,'The Horde has lost control of the Draenei Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(679,'The Alliance has lost control of the Blood Elf Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(680,'The Horde has lost control of the Blood Elf Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(681,'The Alliance has lost control of the Fel Reaver Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(682,'The Horde has lost control of the Fel Reaver Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(683,'%s has taken the flag!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(684,'The Alliance have captured the flag!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(685,'The Horde have captured the flag!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(686,'The flag has been dropped.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(687,'The flag has been reset.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (700,'You must be level %u to form an arena team',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (701,'One minute until the Arena battle begins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (702,'Thirty seconds until the Arena battle begins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), @@ -2839,7 +2864,7 @@ INSERT INTO `mangos_string` VALUES (751,'Not enough players. This game will close in %u seconds.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (753,'The battle for Warsong Gulch begins in 2 minutes.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (754,'The battle for Arathi Basin begins in 2 minutes.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), -(755,'The battle for Eye of the Storm begins in 2 minutes.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(755,'The battle begins in 2 minutes.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (801,'You do not have enough gold',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (802,'You do not have enough free slots',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), (803,'Your partner does not have enough free bag slots',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), diff --git a/sql/updates/7472_01_mangos_mangos_string.sql b/sql/updates/7472_01_mangos_mangos_string.sql new file mode 100644 index 000000000..e25ca96d1 --- /dev/null +++ b/sql/updates/7472_01_mangos_mangos_string.sql @@ -0,0 +1,27 @@ +ALTER TABLE db_version CHANGE COLUMN required_7439_01_mangos_mangos_string required_7472_01_mangos_mangos_string bit; + +DELETE FROM mangos_string WHERE entry >= 667 and entry <= 687 or entry = 614 or entry = 615; +INSERT INTO mangos_string VALUES +(614,'The Alliance flag is now placed at its base.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(615,'The Horde flag is now placed at its base.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(667,'The Alliance has taken control of the Mage Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(668,'The Horde has taken control of the Mage Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(669,'The Alliance has taken control of the Draenei Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(670,'The Horde has taken control of the Draenei Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(671,'The Alliance has taken control of the Blood Elf Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(672,'The Horde has taken control of the Blood Elf Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(673,'The Alliance has taken control of the Fel Reaver Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(674,'The Horde has taken control of the Fel Reaver Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(675,'The Alliance has lost control of the Mage Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(676,'The Horde has lost control of the Mage Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(677,'The Alliance has lost control of the Draenei Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(678,'The Horde has lost control of the Draenei Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(679,'The Alliance has lost control of the Blood Elf Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(680,'The Horde has lost control of the Blood Elf Tower!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(681,'The Alliance has lost control of the Fel Reaver Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(682,'The Horde has lost control of the Fel Reaver Ruins!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(683,'%s has taken the flag!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(684,'The Alliance have captured the flag!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(685,'The Horde have captured the flag!',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(686,'The flag has been dropped.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL), +(687,'The flag has been reset.',NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL); \ No newline at end of file diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 7f44538c8..f816f85d9 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -197,6 +197,7 @@ pkgdata_DATA = \ 7399_01_mangos_mangos_string.sql \ 7422_01_mangos_mangos_string.sql \ 7439_01_mangos_mangos_string.sql \ + 7472_01_mangos_mangos_string.sql \ README ## Additional files to include when running 'make dist' @@ -374,4 +375,5 @@ EXTRA_DIST = \ 7399_01_mangos_mangos_string.sql \ 7422_01_mangos_mangos_string.sql \ 7439_01_mangos_mangos_string.sql \ + 7472_01_mangos_mangos_string.sql \ README diff --git a/src/bindings/universal/ScriptMgr.cpp b/src/bindings/universal/ScriptMgr.cpp index 2045386ac..e607469b9 100644 --- a/src/bindings/universal/ScriptMgr.cpp +++ b/src/bindings/universal/ScriptMgr.cpp @@ -274,6 +274,36 @@ InstanceData* CreateInstanceData(Map *map) return tmpscript->GetInstanceData(map); } +MANGOS_DLL_EXPORT +bool EffectDummyGameObj(Unit *caster, uint32 spellId, uint32 effIndex, GameObject *gameObjTarget ) +{ + Script *tmpscript = m_scripts[gameObjTarget->GetGOInfo()->ScriptId]; + + if (!tmpscript || !tmpscript->pEffectDummyGameObj) return false; + + return tmpscript->pEffectDummyGameObj(caster, spellId,effIndex,gameObjTarget); +} + +MANGOS_DLL_EXPORT +bool EffectDummyCreature(Unit *caster, uint32 spellId, uint32 effIndex, Creature *crTarget ) +{ + Script *tmpscript = m_scripts[crTarget->GetScriptId()]; + + if (!tmpscript || !tmpscript->pEffectDummyCreature) return false; + + return tmpscript->pEffectDummyCreature(caster, spellId,effIndex,crTarget); +} + +MANGOS_DLL_EXPORT +bool EffectDummyItem(Unit *caster, uint32 spellId, uint32 effIndex, Item *itemTarget ) +{ + Script *tmpscript = m_scripts[itemTarget->GetProto()->ScriptId]; + + if (!tmpscript || !tmpscript->pEffectDummyItem) return false; + + return tmpscript->pEffectDummyItem(caster, spellId,effIndex,itemTarget); +} + void ScriptedAI::UpdateAI(const uint32) { //Check if we have a current target diff --git a/src/bindings/universal/ScriptMgr.h b/src/bindings/universal/ScriptMgr.h index 4da14aabd..37b32c79b 100644 --- a/src/bindings/universal/ScriptMgr.h +++ b/src/bindings/universal/ScriptMgr.h @@ -41,7 +41,8 @@ struct Script pGossipHello(NULL), pQuestAccept(NULL), pGossipSelect(NULL), pGossipSelectWithCode(NULL), pQuestSelect(NULL), pQuestComplete(NULL), pNPCDialogStatus(NULL), pGODialogStatus(NULL), pChooseReward(NULL), pItemHello(NULL), pGOHello(NULL), pAreaTrigger(NULL), pItemQuestAccept(NULL), pGOQuestAccept(NULL), - pGOChooseReward(NULL), pReceiveEmote(NULL), pItemUse(NULL), GetAI(NULL) + pGOChooseReward(NULL), pReceiveEmote(NULL), pItemUse(NULL), pEffectDummyGameObj(NULL), pEffectDummyCreature(NULL), + pEffectDummyItem(NULL), GetAI(NULL) {} std::string Name; @@ -64,6 +65,9 @@ struct Script bool (*pGOChooseReward )(Player *player, GameObject *_GO, Quest const*_Quest, uint32 opt ); bool (*pReceiveEmote )(Player *player, Creature *_Creature, uint32 emote ); bool (*pItemUse )(Player *player, Item* _Item, SpellCastTargets const& targets); + bool (*pEffectDummyGameObj )(Unit*, uint32, uint32, GameObject* ); + bool (*pEffectDummyCreature )(Unit*, uint32, uint32, Creature* ); + bool (*pEffectDummyItem )(Unit*, uint32, uint32, Item* ); CreatureAI* (*GetAI)(Creature *_Creature); InstanceData* (*GetInstanceData)(Map*); diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp index 3acf2caa5..59e2c5334 100644 --- a/src/game/AchievementMgr.cpp +++ b/src/game/AchievementMgr.cpp @@ -689,12 +689,15 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL: - if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID)) + // spell always provide and at login spell learning. + if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID) + continue; + if(GetPlayer()->HasSpell(miscvalue1)) SetCriteriaProgress(achievementCriteria, 1); break; case ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM: // speedup for non-login case - if(miscvalue1 && achievementCriteria->own_item.itemID!=miscvalue1) + if(miscvalue1 && achievementCriteria->own_item.itemID != miscvalue1) continue; SetCriteriaProgress(achievementCriteria, GetPlayer()->GetItemCount(achievementCriteria->own_item.itemID, true)); break; @@ -736,6 +739,10 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui break; case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION: { + // skip faction check only at loading + if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID) + continue; + int32 reputation = GetPlayer()->GetReputation(achievementCriteria->gain_reputation.factionID); if (reputation > 0) SetCriteriaProgress(achievementCriteria, reputation); @@ -743,6 +750,10 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui } case ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION: { + // skip faction check only at loading + if (miscvalue1 && GetPlayer()->GetReputationRank(miscvalue1) < REP_EXALTED) + continue; + uint32 counter = 0; const FactionStateList factionStateList = GetPlayer()->GetFactionStateList(); for (FactionStateList::const_iterator iter = factionStateList.begin(); iter!= factionStateList.end(); ++iter) @@ -806,24 +817,43 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); break; } + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: + // miscvalue1 = item_id + if(!miscvalue1) + continue; + if(miscvalue1 != achievementCriteria->equip_item.itemID) + continue; + + SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE); + break; case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS: + { + // spell always provide and at login spell learning. + if(!miscvalue1) + continue; + // rescan only when change possible + SkillLineAbilityMap::const_iterator skillIter0 = spellmgr.GetBeginSkillLineAbilityMap(miscvalue1); + if(skillIter0 == spellmgr.GetEndSkillLineAbilityMap(miscvalue1)) + continue; + if(skillIter0->second->skillId != achievementCriteria->learn_skilline_spell.skillLine) + continue; + + uint32 spellCount = 0; + for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); + spellIter != GetPlayer()->GetSpellMap().end(); + ++spellIter) { - uint32 spellCount = 0; - for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin(); - spellIter != GetPlayer()->GetSpellMap().end(); - ++spellIter) + for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first); + skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first); + ++skillIter) { - for(SkillLineAbilityMap::const_iterator skillIter = spellmgr.GetBeginSkillLineAbilityMap(spellIter->first); - skillIter != spellmgr.GetEndSkillLineAbilityMap(spellIter->first); - ++skillIter) - { - if(skillIter->second->skillId == achievementCriteria->learn_skilline_spell.skillLine) - spellCount++; - } + if(skillIter->second->skillId == achievementCriteria->learn_skilline_spell.skillLine) + spellCount++; } - SetCriteriaProgress(achievementCriteria, spellCount); - break; } + SetCriteriaProgress(achievementCriteria, spellCount); + break; + } case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2: { if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID) @@ -876,7 +906,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE: case ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE: case ACHIEVEMENT_CRITERIA_TYPE_GET_KILLING_BLOWS: - case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS: case ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS: case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT: @@ -1004,6 +1033,8 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve return progress->counter >= achievementCriteria->roll_greed_on_loot.count; case ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE: return progress->counter >= achievementCriteria->do_emote.count; + case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM: + return progress->counter >= achievementCriteria->equip_item.count; case ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD: return progress->counter >= achievementCriteria->quest_reward_money.goldInCopper; case ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY: diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp index 35ad7b262..2dfd9c557 100644 --- a/src/game/BattleGround.cpp +++ b/src/game/BattleGround.cpp @@ -428,9 +428,10 @@ void BattleGround::Update(uint32 diff) if(GetStatus() == STATUS_WAIT_LEAVE) { // remove all players from battleground after 2 minutes - m_EndTime += diff; - if(m_EndTime >= TIME_TO_AUTOREMOVE) // 2 minutes + m_EndTime -= diff; + if( m_EndTime <= 0) { + m_EndTime = 0; BattleGroundPlayerMap::iterator itr, next; for(itr = m_Players.begin(); itr != m_Players.end(); itr = next) { @@ -443,6 +444,8 @@ void BattleGround::Update(uint32 diff) } } + //update start time + m_StartTime += diff; } void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O) @@ -633,7 +636,8 @@ void BattleGround::EndBattleGround(uint32 winner) } SetStatus(STATUS_WAIT_LEAVE); - m_EndTime = 0; + //we must set it this way, because end time is sent in packet! + m_EndTime = TIME_TO_AUTOREMOVE; // arena rating calculation if(isArena() && isRated()) @@ -716,7 +720,7 @@ void BattleGround::EndBattleGround(uint32 winner) plr->GetSession()->SendPacket(&data); BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType()); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime()); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType()); plr->GetSession()->SendPacket(&data); plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1); } @@ -964,7 +968,7 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac if(SendPacket) { WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 0, 0); plr->GetSession()->SendPacket(&data); } @@ -1155,10 +1159,15 @@ void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, if(group->IsMember(plr_guid)) { uint8 subgroup = group->GetMemberGroup(plr_guid); - plr->SetGroup(group, subgroup); + plr->SetBattleGroundRaid(group, subgroup); } else - GetBgRaid(team)->AddMember(plr_guid, plr->GetName()); + { + group->AddMember(plr_guid, plr->GetName()); + if( Group* originalGroup = plr->GetOriginalGroup() ) + if( originalGroup->IsLeader(plr_guid) ) + group->ChangeLeader(plr_guid); + } } } @@ -1191,7 +1200,11 @@ void BattleGround::EventPlayerLoggedOut(Player* player) if( isBattleGround() ) EventPlayerDroppedFlag(player); else - CheckArenaWinConditions(); + { + //1 player is logging out, if it is the last, then end arena! + if( GetAlivePlayersCountByTeam(player->GetTeam()) <= 1 && GetPlayersCountByTeam(GetOtherTeam(player->GetTeam())) ) + EndBattleGround(GetOtherTeam(player->GetTeam())); + } } } @@ -1589,7 +1602,7 @@ void BattleGround::EndNow() { RemoveFromBGFreeSlotQueue(); SetStatus(STATUS_WAIT_LEAVE); - SetEndTime(TIME_TO_AUTOREMOVE); + SetEndTime(0); // inform invited players about the removal sBattleGroundMgr.m_BattleGroundQueues[BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType())].BGEndedRemoveInvites(this); } @@ -1659,8 +1672,9 @@ void BattleGround::HandleKillPlayer( Player *player, Player *killer ) } } - // to be able to remove insignia - player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE ); + // to be able to remove insignia -- ONLY IN BattleGrounds + if( !isArena() ) + player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE ); } // return the player's team based on battlegroundplayer info @@ -1699,7 +1713,7 @@ void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr) sBattleGroundMgr.BuildPvpLogDataPacket(&data, this); plr->GetSession()->SendPacket(&data); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime()); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType()); plr->GetSession()->SendPacket(&data); } diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h index c55a89afc..01cbf2d23 100644 --- a/src/game/BattleGround.h +++ b/src/game/BattleGround.h @@ -530,7 +530,7 @@ class BattleGround BattleGroundStatus m_Status; uint32 m_ClientInstanceID; //the instance-id which is sent to the client and without any other internal use uint32 m_StartTime; - uint32 m_EndTime; + int32 m_EndTime; // it is set to 120000 when bg is ending and it decreases itself uint32 m_LastResurrectTime; BGQueueIdBasedOnLevel m_QueueId; uint8 m_ArenaType; // 2=2v2, 3=3v3, 5=5v5 diff --git a/src/game/BattleGroundAB.cpp b/src/game/BattleGroundAB.cpp index dae2b20de..9922c56ea 100644 --- a/src/game/BattleGroundAB.cpp +++ b/src/game/BattleGroundAB.cpp @@ -48,7 +48,7 @@ void BattleGroundAB::Update(uint32 diff) if( GetStatus() == STATUS_IN_PROGRESS ) { - int team_points[2] = { 0, 0 }; + int team_points[BG_TEAMS_COUNT] = { 0, 0 }; for (int node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node) { @@ -88,24 +88,24 @@ void BattleGroundAB::Update(uint32 diff) { // FIXME: team and node names not localized SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_ALLIANCE,NULL,LANG_BG_AB_ALLY,_GetNodeNameId(node)); - PlaySoundToAll(SOUND_NODE_CAPTURED_ALLIANCE); + PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_ALLIANCE); } else { // FIXME: team and node names not localized SendMessage2ToAll(LANG_BG_AB_NODE_TAKEN,CHAT_MSG_BG_SYSTEM_HORDE,NULL,LANG_BG_AB_HORDE,_GetNodeNameId(node)); - PlaySoundToAll(SOUND_NODE_CAPTURED_HORDE); + PlaySoundToAll(BG_AB_SOUND_NODE_CAPTURED_HORDE); } } } - for (int team = 0; team < 2; ++team) + for (int team = 0; team < BG_TEAMS_COUNT; ++team) if( m_Nodes[node] == team + BG_AB_NODE_TYPE_OCCUPIED ) ++team_points[team]; } // Accumulate points - for (int team = 0; team < 2; ++team) + for (int team = 0; team < BG_TEAMS_COUNT; ++team) { int points = team_points[team]; if( !points ) @@ -127,18 +127,18 @@ void BattleGroundAB::Update(uint32 diff) RewardHonorToTeam(GetBonusHonorFromKill(1), (team == BG_TEAM_ALLIANCE) ? ALLIANCE : HORDE); m_HonorScoreTics[team] -= m_HonorTics; } - if( !m_IsInformedNearVictory && m_TeamScores[team] > 1800 ) + if( !m_IsInformedNearVictory && m_TeamScores[team] > BG_AB_WARNING_NEAR_VICTORY_SCORE ) { if( team == BG_TEAM_ALLIANCE ) SendMessageToAll(LANG_BG_AB_A_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); else SendMessageToAll(LANG_BG_AB_H_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); - PlaySoundToAll(SOUND_NEAR_VICTORY); + PlaySoundToAll(BG_AB_SOUND_NEAR_VICTORY); m_IsInformedNearVictory = true; } - if( m_TeamScores[team] > 2000 ) - m_TeamScores[team] = 2000; + if( m_TeamScores[team] > BG_AB_MAX_TEAM_SCORE ) + m_TeamScores[team] = BG_AB_MAX_TEAM_SCORE; if( team == BG_TEAM_ALLIANCE ) UpdateWorldState(BG_AB_OP_RESOURCES_ALLY, m_TeamScores[team]); if( team == BG_TEAM_HORDE ) @@ -147,9 +147,9 @@ void BattleGroundAB::Update(uint32 diff) } // Test win condition - if( m_TeamScores[BG_TEAM_ALLIANCE] >= 2000 ) + if( m_TeamScores[BG_TEAM_ALLIANCE] >= BG_AB_MAX_TEAM_SCORE ) EndBattleGround(ALLIANCE); - if( m_TeamScores[BG_TEAM_HORDE] >= 2000 ) + if( m_TeamScores[BG_TEAM_HORDE] >= BG_AB_MAX_TEAM_SCORE ) EndBattleGround(HORDE); } } @@ -313,7 +313,7 @@ void BattleGroundAB::FillInitialWorldStates(WorldPacket& data) // Team scores data << uint32(BG_AB_OP_RESOURCES_MAX) << uint32(BG_AB_MAX_TEAM_SCORE); - data << uint32(BG_AB_OP_RESOURCES_WARNING) << uint32(BG_AB_WARNING_SCORE); + data << uint32(BG_AB_OP_RESOURCES_WARNING) << uint32(BG_AB_WARNING_NEAR_VICTORY_SCORE); data << uint32(BG_AB_OP_RESOURCES_ALLY) << uint32(m_TeamScores[BG_TEAM_ALLIANCE]); data << uint32(BG_AB_OP_RESOURCES_HORDE) << uint32(m_TeamScores[BG_TEAM_HORDE]); @@ -440,7 +440,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ else SendMessage2ToAll(LANG_BG_AB_NODE_CLAIMED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node), LANG_BG_AB_HORDE); - sound = SOUND_NODE_CLAIMED; + sound = BG_AB_SOUND_NODE_CLAIMED; } // If node is contested else if( (m_Nodes[node] == BG_AB_NODE_STATUS_ALLY_CONTESTED) || (m_Nodes[node] == BG_AB_NODE_STATUS_HORDE_CONTESTED) ) @@ -484,7 +484,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ else SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); } - sound = (teamIndex == 0) ? SOUND_NODE_ASSAULTED_ALLIANCE : SOUND_NODE_ASSAULTED_HORDE; + sound = (teamIndex == 0) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; } // If node is occupied, change to enemy-contested else @@ -506,7 +506,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ else SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node)); - sound = (teamIndex == 0) ? SOUND_NODE_ASSAULTED_ALLIANCE : SOUND_NODE_ASSAULTED_HORDE; + sound = (teamIndex == 0) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE; } // If node is occupied again, send "X has taken the Y" msg. diff --git a/src/game/BattleGroundAB.h b/src/game/BattleGroundAB.h index 644556d0e..e9a255d10 100644 --- a/src/game/BattleGroundAB.h +++ b/src/game/BattleGroundAB.h @@ -75,17 +75,17 @@ enum BG_AB_NodeObjectId enum BG_AB_ObjectType { // for all 5 node points 8*5=40 objects - BG_AB_OBJECT_BANNER_NEUTRAL = 0, - BG_AB_OBJECT_BANNER_CONT_A = 1, - BG_AB_OBJECT_BANNER_CONT_H = 2, - BG_AB_OBJECT_BANNER_ALLY = 3, - BG_AB_OBJECT_BANNER_HORDE = 4, - BG_AB_OBJECT_AURA_ALLY = 5, - BG_AB_OBJECT_AURA_HORDE = 6, - BG_AB_OBJECT_AURA_CONTESTED = 7, + BG_AB_OBJECT_BANNER_NEUTRAL = 0, + BG_AB_OBJECT_BANNER_CONT_A = 1, + BG_AB_OBJECT_BANNER_CONT_H = 2, + BG_AB_OBJECT_BANNER_ALLY = 3, + BG_AB_OBJECT_BANNER_HORDE = 4, + BG_AB_OBJECT_AURA_ALLY = 5, + BG_AB_OBJECT_AURA_HORDE = 6, + BG_AB_OBJECT_AURA_CONTESTED = 7, //gates - BG_AB_OBJECT_GATE_A = 40, - BG_AB_OBJECT_GATE_H = 41, + BG_AB_OBJECT_GATE_A = 40, + BG_AB_OBJECT_GATE_H = 41, //buffs BG_AB_OBJECT_SPEEDBUFF_STABLES = 42, BG_AB_OBJECT_REGENBUFF_STABLES = 43, @@ -128,8 +128,8 @@ enum BG_AB_Timers enum BG_AB_Score { - BG_AB_MAX_TEAM_SCORE = 2000, - BG_AB_WARNING_SCORE = 1800 + BG_AB_WARNING_NEAR_VICTORY_SCORE = 1800, + BG_AB_MAX_TEAM_SCORE = 2000 }; /* do NOT change the order, else wrong behaviour */ @@ -162,18 +162,18 @@ enum BG_AB_NodeStatus enum BG_AB_Sounds { - SOUND_NODE_CLAIMED = 8192, - SOUND_NODE_CAPTURED_ALLIANCE = 8173, - SOUND_NODE_CAPTURED_HORDE = 8213, - SOUND_NODE_ASSAULTED_ALLIANCE = 8174, - SOUND_NODE_ASSAULTED_HORDE = 8212, - SOUND_NEAR_VICTORY = 8456 + BG_AB_SOUND_NODE_CLAIMED = 8192, + BG_AB_SOUND_NODE_CAPTURED_ALLIANCE = 8173, + BG_AB_SOUND_NODE_CAPTURED_HORDE = 8213, + BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE = 8174, + BG_AB_SOUND_NODE_ASSAULTED_HORDE = 8212, + BG_AB_SOUND_NEAR_VICTORY = 8456 }; -#define BG_AB_NotABBGWeekendHonorTicks 330 -#define BG_AB_ABBGWeekendHonorTicks 200 +#define BG_AB_NotABBGWeekendHonorTicks 330 +#define BG_AB_ABBGWeekendHonorTicks 200 #define BG_AB_NotABBGWeekendReputationTicks 200 -#define BG_AB_ABBGWeekendReputationTicks 150 +#define BG_AB_ABBGWeekendReputationTicks 150 // x, y, z, o const float BG_AB_NodePositions[BG_AB_DYNAMIC_NODES_COUNT][4] = { @@ -283,10 +283,10 @@ class BattleGroundAB : public BattleGround uint8 m_prevNodes[BG_AB_DYNAMIC_NODES_COUNT]; BG_AB_BannerTimer m_BannerTimers[BG_AB_DYNAMIC_NODES_COUNT]; int32 m_NodeTimers[BG_AB_DYNAMIC_NODES_COUNT]; - uint32 m_TeamScores[2]; - uint32 m_lastTick[2]; - uint32 m_HonorScoreTics[2]; - uint32 m_ReputationScoreTics[2]; + uint32 m_TeamScores[BG_TEAMS_COUNT]; + uint32 m_lastTick[BG_TEAMS_COUNT]; + uint32 m_HonorScoreTics[BG_TEAMS_COUNT]; + uint32 m_ReputationScoreTics[BG_TEAMS_COUNT]; bool m_IsInformedNearVictory; uint32 m_HonorTics; uint32 m_ReputationTics; diff --git a/src/game/BattleGroundEY.cpp b/src/game/BattleGroundEY.cpp index 4ba6953fb..acd105fe5 100644 --- a/src/game/BattleGroundEY.cpp +++ b/src/game/BattleGroundEY.cpp @@ -258,9 +258,20 @@ void BattleGroundEY::UpdatePointStatuses() void BattleGroundEY::UpdateTeamScore(uint32 Team) { uint32 score = GetTeamScore(Team); - if(score >= EY_MAX_TEAM_SCORE) + //TODO there should be some sound played when one team is near victory!! - and define variables + /*if( !m_IsInformedNearVictory && score >= BG_EY_WARNING_NEAR_VICTORY_SCORE ) { - score = EY_MAX_TEAM_SCORE; + if( Team == ALLIANCE ) + SendMessageToAll(LANG_BG_EY_A_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + else + SendMessageToAll(LANG_BG_EY_H_NEAR_VICTORY, CHAT_MSG_BG_SYSTEM_NEUTRAL); + PlaySoundToAll(BG_EY_SOUND_NEAR_VICTORY); + m_IsInformedNearVictory = true; + }*/ + + if( score >= BG_EY_MAX_TEAM_SCORE ) + { + score = BG_EY_MAX_TEAM_SCORE; EndBattleGround(Team); } @@ -509,7 +520,7 @@ void BattleGroundEY::Reset() m_PointAddingTimer = 0; m_TowerCapCheckTimer = 0; bool isBGWeekend = false; //TODO FIXME - call sBattleGroundMgr.IsBGWeekend(m_TypeID); - you must also implement that call! - uint32 m_HonorTics = (isBGWeekend) ? BG_EY_EYWeekendHonorTicks : BG_EY_NotEYWeekendHonorTicks; + m_HonorTics = (isBGWeekend) ? BG_EY_EYWeekendHonorTicks : BG_EY_NotEYWeekendHonorTicks; for(uint8 i = 0; i < EY_POINTS_MAX; ++i) { @@ -594,9 +605,9 @@ void BattleGroundEY::EventPlayerDroppedFlag(Player *Source) UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_WAIT_RESPAWN); if(Source->GetTeam() == ALLIANCE) - SendMessageToAll(LANG_BG_EY_DROPPED_FLAG,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL); else - SendMessageToAll(LANG_BG_EY_DROPPED_FLAG,CHAT_MSG_BG_SYSTEM_HORDE, Source); + SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL); } void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj) @@ -626,9 +637,9 @@ void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT); if(Source->GetTeam() == ALLIANCE) - SendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source); + PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, Source->GetName()); else - SendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG,CHAT_MSG_BG_SYSTEM_HORDE, Source); + PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL, Source->GetName()); } void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point) diff --git a/src/game/BattleGroundEY.h b/src/game/BattleGroundEY.h index 17b678e8f..ab44fac4f 100644 --- a/src/game/BattleGroundEY.h +++ b/src/game/BattleGroundEY.h @@ -23,9 +23,8 @@ class BattleGround; -#define EY_MAX_TEAM_SCORE 2000 -#define BG_EY_FLAG_RESPAWN_TIME (10*IN_MILISECONDS) //10 seconds -#define BG_EY_FPOINTS_TICK_TIME (2*IN_MILISECONDS) //2 seconds +#define BG_EY_FLAG_RESPAWN_TIME (10*IN_MILISECONDS) //10 seconds +#define BG_EY_FPOINTS_TICK_TIME (2*IN_MILISECONDS) //2 seconds enum BG_EY_WorldStates { @@ -71,11 +70,11 @@ enum BG_EY_ProgressBarConsts enum BG_EY_Sounds { //strange ids, but sure about them - BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE = 8212, - BG_EY_SOUND_FLAG_CAPTURED_HORDE = 8213, - BG_EY_SOUND_FLAG_PICKED_UP_HORDE = 8174, - BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, - BG_EY_SOUND_FLAG_RESET = 8192 + BG_EY_SOUND_FLAG_PICKED_UP_ALLIANCE = 8212, + BG_EY_SOUND_FLAG_CAPTURED_HORDE = 8213, + BG_EY_SOUND_FLAG_PICKED_UP_HORDE = 8174, + BG_EY_SOUND_FLAG_CAPTURED_ALLIANCE = 8173, + BG_EY_SOUND_FLAG_RESET = 8192 }; enum BG_EY_Spells @@ -86,18 +85,18 @@ enum BG_EY_Spells enum EYBattleGroundObjectEntry { - BG_OBJECT_A_DOOR_EY_ENTRY = 184719, //Alliance door - BG_OBJECT_H_DOOR_EY_ENTRY = 184720, //Horde door - BG_OBJECT_FLAG1_EY_ENTRY = 184493, //Netherstorm flag (generic) - BG_OBJECT_FLAG2_EY_ENTRY = 184141, //Netherstorm flag (flagstand) - BG_OBJECT_FLAG3_EY_ENTRY = 184142, //Netherstorm flag (flagdrop) - BG_OBJECT_A_BANNER_EY_ENTRY = 184381, //Visual Banner (Alliance) - BG_OBJECT_H_BANNER_EY_ENTRY = 184380, //Visual Banner (Horde) - BG_OBJECT_N_BANNER_EY_ENTRY = 184382, //Visual Banner (Neutral) - BG_OBJECT_BE_TOWER_CAP_EY_ENTRY = 184080, //BE Tower Cap Pt - BG_OBJECT_FR_TOWER_CAP_EY_ENTRY = 184081, //Fel Reaver Cap Pt - BG_OBJECT_HU_TOWER_CAP_EY_ENTRY = 184082, //Human Tower Cap Pt - BG_OBJECT_DR_TOWER_CAP_EY_ENTRY = 184083 //Draenei Tower Cap Pt + BG_OBJECT_A_DOOR_EY_ENTRY = 184719, //Alliance door + BG_OBJECT_H_DOOR_EY_ENTRY = 184720, //Horde door + BG_OBJECT_FLAG1_EY_ENTRY = 184493, //Netherstorm flag (generic) + BG_OBJECT_FLAG2_EY_ENTRY = 184141, //Netherstorm flag (flagstand) + BG_OBJECT_FLAG3_EY_ENTRY = 184142, //Netherstorm flag (flagdrop) + BG_OBJECT_A_BANNER_EY_ENTRY = 184381, //Visual Banner (Alliance) + BG_OBJECT_H_BANNER_EY_ENTRY = 184380, //Visual Banner (Horde) + BG_OBJECT_N_BANNER_EY_ENTRY = 184382, //Visual Banner (Neutral) + BG_OBJECT_BE_TOWER_CAP_EY_ENTRY = 184080, //BE Tower Cap Pt + BG_OBJECT_FR_TOWER_CAP_EY_ENTRY = 184081, //Fel Reaver Cap Pt + BG_OBJECT_HU_TOWER_CAP_EY_ENTRY = 184082, //Human Tower Cap Pt + BG_OBJECT_DR_TOWER_CAP_EY_ENTRY = 184083 //Draenei Tower Cap Pt }; enum EYBattleGroundPointsTrigger @@ -129,7 +128,7 @@ enum EYBattleGroundPoints DRAENEI_RUINS = 2, MAGE_TOWER = 3, - EY_PLAYERS_OUT_OF_POINTS = 4, + EY_PLAYERS_OUT_OF_POINTS = 4, EY_POINTS_MAX = 4 }; @@ -210,8 +209,14 @@ enum EYBattleGroundObjectTypes BG_EY_OBJECT_MAX = 59 }; -#define BG_EY_NotEYWeekendHonorTicks 330 -#define BG_EY_EYWeekendHonorTicks 200 +#define BG_EY_NotEYWeekendHonorTicks 330 +#define BG_EY_EYWeekendHonorTicks 200 + +enum BG_EY_Score +{ + BG_EY_WARNING_NEAR_VICTORY_SCORE = 1800, + BG_EY_MAX_TEAM_SCORE = 2000 +}; enum BG_EY_FlagState { diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp index a3c3c1920..65d601384 100644 --- a/src/game/BattleGroundHandler.cpp +++ b/src/game/BattleGroundHandler.cpp @@ -127,7 +127,7 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) if( !_player->CanJoinToBattleground() ) { WorldPacket data(SMSG_GROUP_JOINED_BATTLEGROUND, 4); - data << (uint32) 0xFFFFFFFE; + data << uint32(0xFFFFFFFE); _player->GetSession()->SendPacket(&data); return; } @@ -173,7 +173,7 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType); member->GetSession()->SendPacket(&data); sBattleGroundMgr.BuildGroupJoinedBattlegroundPacket(&data, bgTypeId); member->GetSession()->SendPacket(&data); @@ -191,7 +191,7 @@ void WorldSession::HandleBattleGroundJoinOpcode( WorldPacket & recv_data ) WorldPacket data; // send status packet (in queue) - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType); SendPacket(&data); sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo); @@ -302,25 +302,25 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) recv_data >> type >> unk2 >> bgTypeId_ >> unk >> action; - if(!sBattlemasterListStore.LookupEntry(bgTypeId_)) + if( !sBattlemasterListStore.LookupEntry(bgTypeId_) ) { - sLog.outError("Battleground: invalid bgtype (%u) received.",bgTypeId_); + sLog.outError("Battleground: invalid bgtype (%u) received.", bgTypeId_); // update battleground slots for the player to fix his UI and sent data. // this is a HACK, I don't know why the client starts sending invalid packets in the first place. // it usually happens with extremely high latency (if debugging / stepping in the code for example) - if(_player->InBattleGroundQueue()) + if( _player->InBattleGroundQueue() ) { // update all queues, send invitation info if player is invited, queue info if queued for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i); - if(!bgQueueTypeId) + if( !bgQueueTypeId ) continue; BattleGroundTypeId bgTypeId = BattleGroundMgr::BGTemplateId(bgQueueTypeId); BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = qpMap.find(_player->GetGUID()); // if the player is not in queue, continue or no group information - this should never happen - if(itrPlayerStatus == qpMap.end() || !itrPlayerStatus->second.GroupInfo) + if( itrPlayerStatus == qpMap.end() || !itrPlayerStatus->second.GroupInfo ) continue; BattleGround * bg = NULL; @@ -329,7 +329,7 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) uint8 israted = itrPlayerStatus->second.GroupInfo->IsRated; uint8 status = 0; - if(!itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID) + if( !itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID ) { // not invited to bg, get template bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); @@ -342,12 +342,8 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) status = STATUS_WAIT_JOIN; } - // if bg not found, then continue - if(!bg) - continue; - - // don't invite if already in the instance - if(_player->InBattleGround() && _player->GetBattleGround() && _player->GetBattleGround()->GetInstanceID() == bg->GetInstanceID()) + // if bg not found, then continue, don't invite if already in the instance + if( !bg || (_player->InBattleGround() && _player->GetBattleGround() && _player->GetBattleGround()->GetInstanceID() == bg->GetInstanceID()) ) continue; // re - invite player with proper data @@ -359,22 +355,20 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) return; } + //get GroupQueueInfo from BattleGroundQueue BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_); - - BattleGroundQueueTypeId bgQueueTypeId = BATTLEGROUND_QUEUE_NONE; - // get the bg what we were invited to - bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, type); + BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, type); BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = qpMap.find(_player->GetGUID()); - if(itrPlayerStatus == qpMap.end()) + if( itrPlayerStatus == qpMap.end() ) { sLog.outError("Battleground: itrplayerstatus not found."); return; } - instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID; - // if action == 1, then instanceId is _required_ - if(!instanceId && action == 1) + instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID; + // if action == 1, then instanceId is required + if( !instanceId && action == 1 ) { sLog.outError("Battleground: instance not found."); return; @@ -383,56 +377,52 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId, bgTypeId); // bg template might and must be used in case of leaving queue, when instance is not created yet - if(!bg && action == 0) + if( !bg && action == 0 ) bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - - if(!bg) + if( !bg ) { - sLog.outError("Battleground: bg not found for type id %u.",bgTypeId); + sLog.outError("Battleground: bg_template not found for type id %u.", bgTypeId); return; } - bgTypeId = bg->GetTypeID(); - - if(_player->InBattleGroundQueue()) + if( _player->InBattleGroundQueue() ) { - uint32 queueSlot = 0; - uint32 team = 0; - uint32 arenatype = 0; - uint32 israted = 0; - uint32 rating = 0; - uint32 opponentsRating = 0; - // get the team info from the queue + //we must use temporary variables, because GroupQueueInfo pointer can be deleted in BattleGroundQueue::RemovePlayer() function! + uint32 team = itrPlayerStatus->second.GroupInfo->Team; + uint32 arenaType = itrPlayerStatus->second.GroupInfo->ArenaType; + uint32 isRated = itrPlayerStatus->second.GroupInfo->IsRated; + uint32 rating = itrPlayerStatus->second.GroupInfo->ArenaTeamRating; + uint32 opponentsRating = itrPlayerStatus->second.GroupInfo->OpponentsTeamRating; - BattleGroundQueue::QueuedPlayersMap& qpMap2 = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; - BattleGroundQueue::QueuedPlayersMap::iterator pitr = qpMap2.find(_player->GetGUID()); - if (pitr !=qpMap2.end() && pitr->second.GroupInfo) + //some checks if player isn't cheating - it is not exactly cheating, but we cannot allow it + if( action == 1 && arenaType == 0) { - team = pitr->second.GroupInfo->Team; - arenatype = pitr->second.GroupInfo->ArenaType; - israted = pitr->second.GroupInfo->IsRated; - rating = pitr->second.GroupInfo->ArenaTeamRating; - opponentsRating = pitr->second.GroupInfo->OpponentsTeamRating; - } - else - { - sLog.outError("Battleground: Invalid player queue info!"); - return; - } - //if player is trying to enter battleground (not arena!) and he has deserter debuff, we must just remove him from queue - if( arenatype == 0 && !_player->CanJoinToBattleground() ) - { - sLog.outDebug("Battleground: player %s (%u) has a deserter debuff, do not port him to battleground!", _player->GetName(), _player->GetGUIDLow()); - action = 0; + //if player is trying to enter battleground (not arena!) and he has deserter debuff, we must just remove him from queue + if( !_player->CanJoinToBattleground() ) + { + //send bg command result to show nice message + WorldPacket data2(SMSG_GROUP_JOINED_BATTLEGROUND, 4); + data2 << uint32(0xFFFFFFFE); + _player->GetSession()->SendPacket(&data2); + action = 0; + sLog.outDebug("Battleground: player %s (%u) has a deserter debuff, do not port him to battleground!", _player->GetName(), _player->GetGUIDLow()); + } + //if player don't match battleground max level, then do not allow him to enter! (this might happen when player leveled up during his waiting in queue + if( _player->getLevel() > bg->GetMaxLevel() ) + { + sLog.outError("Battleground: Player %s (%u) has level higher than maxlevel of battleground! Do not port him to battleground!", _player->GetName(), _player->GetGUIDLow()); + action = 0; + } } + uint32 queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); WorldPacket data; - switch(action) + switch( action ) { - case 1: // port to battleground - if(!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId)) - return; // cheating? + case 1: // port to battleground + if( !_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId) ) + return; // cheating? // resurrect the player - if(!_player->isAlive()) + if( !_player->isAlive() ) { _player->ResurrectPlayer(1.0f); _player->SpawnCorpseBones(); @@ -443,14 +433,13 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) _player->GetMotionMaster()->MovementExpired(); _player->m_taxi.ClearTaxiDestinations(); } - _player->RemoveFromGroup(); - queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); + + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType()); _player->GetSession()->SendPacket(&data); // remove battleground queue status from BGmgr sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), false); // this is still needed here if battleground "jumping" shouldn't add deserter debuff - // also this required to prevent stuck at old battleground after SetBattleGroundId set to new + // also this is required to prevent stuck at old battleground after SetBattleGroundId set to new if( BattleGround *currentBg = _player->GetBattleGround() ) currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true); @@ -462,30 +451,28 @@ void WorldSession::HandleBattleGroundPlayerPortOpcode( WorldPacket &recv_data ) sBattleGroundMgr.SendToBattleGround(_player, instanceId, bgTypeId); // add only in HandleMoveWorldPortAck() // bg->AddPlayer(_player,team); - sLog.outDebug("Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetInstanceID(),bg->GetTypeID(),bgQueueTypeId); + sLog.outDebug("Battleground: player %s (%u) joined battle for bg %u, bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetInstanceID(), bg->GetTypeID(), bgQueueTypeId); break; case 0: // leave queue - queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId); - /* - if player leaves rated arena match before match start, it is counted as he played but he lost - */ - if (israted) + // if player leaves rated arena match before match start, it is counted as he played but he lost + if( isRated ) { ArenaTeam * at = objmgr.GetArenaTeamById(team); - if (at) + if( at ) { sLog.outDebug("UPDATING memberLost's personal arena rating for %u by opponents rating: %u, because he has left queue!", GUID_LOPART(_player->GetGUID()), opponentsRating); at->MemberLost(_player, opponentsRating); at->SaveToDB(); } } - _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0); + _player->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to queue->removeplayer, it causes bugs + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(_player->GetGUID(), true); - // player left queue, we should update it, maybe now his group fits in - sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId,_player->GetBattleGroundQueueIdFromLevel(bgTypeId),arenatype,israted,rating); + // player left queue, we should update it - do not update Arena Queue + if( !arenaType ) + sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(bgTypeId), arenaType, isRated, rating); SendPacket(&data); - sLog.outDebug("Battleground: player %s (%u) left queue for bgtype %u, queue type %u.",_player->GetName(),_player->GetGUIDLow(),bg->GetTypeID(),bgQueueTypeId); + sLog.outDebug("Battleground: player %s (%u) left queue for bgtype %u, queue type %u.", _player->GetName(), _player->GetGUIDLow(), bg->GetTypeID(), bgQueueTypeId); break; default: sLog.outError("Battleground port: unknown action %u", action); @@ -524,40 +511,55 @@ void WorldSession::HandleBattlefieldStatusOpcode( WorldPacket & /*recv_data*/ ) sLog.outDebug( "WORLD: Battleground status" ); WorldPacket data; - uint32 queueSlot = PLAYER_MAX_BATTLEGROUND_QUEUES; - - if(_player->InBattleGround()) - { - BattleGround *bg = _player->GetBattleGround(); - if(!bg) - return; - BattleGroundQueueTypeId bgQueueTypeId_tmp = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType()); - queueSlot = _player->GetBattleGroundQueueIndex(bgQueueTypeId_tmp); - if((bg->GetStatus() <= STATUS_IN_PROGRESS)) - { - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime()); - SendPacket(&data); - } - } - // we should update all queues? .. i'm not sure if this code is correct - for (uint32 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) + // we must update all queues here + BattleGround *bg = NULL; + for (uint8 i = 0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; i++) { BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i); - if(!bgQueueTypeId || i == queueSlot) //queueslot check in case we already send it in the above code + if( !bgQueueTypeId ) continue; BattleGroundTypeId bgTypeId = BattleGroundMgr::BGTemplateId(bgQueueTypeId); - uint8 arenatype = BattleGroundMgr::BGArenaType(bgQueueTypeId); - BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); - if(!bg) - continue; + uint8 arenaType = BattleGroundMgr::BGArenaType(bgQueueTypeId); + if( bgTypeId == _player->GetBattleGroundTypeId() ) + { + bg = _player->GetBattleGround(); + //i cannot check any variable from player class because player class doesn't know if player is in 2v2 / 3v3 or 5v5 arena + //so i must use bg pointer to get that information + if( bg && bg->GetArenaType() == arenaType ) + { + // this line is checked, i only don't know if GetStartTime is changing itself after bg end! + // send status in BattleGround + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_IN_PROGRESS, bg->GetEndTime(), bg->GetStartTime(), arenaType); + SendPacket(&data); + continue; + } + } + //we are sending update to player about queue - he can be invited there! + //get GroupQueueInfo for queue status BattleGroundQueue::QueuedPlayersMap& qpMap = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].m_QueuedPlayers; BattleGroundQueue::QueuedPlayersMap::iterator itrPlayerStatus = qpMap.find(_player->GetGUID()); - if(itrPlayerStatus == qpMap.end() || !itrPlayerStatus->second.GroupInfo) + if( itrPlayerStatus == qpMap.end() ) continue; - arenatype = itrPlayerStatus->second.GroupInfo->ArenaType; - uint32 avgTime = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].GetAverageQueueWaitTime(itrPlayerStatus->second.GroupInfo, _player->GetBattleGroundQueueIdFromLevel(bgTypeId)); - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_WAIT_QUEUE, avgTime, getMSTime()-itrPlayerStatus->second.GroupInfo->JoinTime, arenatype); - SendPacket(&data); + if( itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID ) + { + bg = sBattleGroundMgr.GetBattleGround(itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID, bgTypeId); + if( !bg ) + continue; + uint32 remainingTime = getMSTimeDiff(getMSTime(), itrPlayerStatus->second.GroupInfo->RemoveInviteTime); + // send status invited to BattleGround + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_WAIT_JOIN, remainingTime, 0, arenaType); + SendPacket(&data); + } + else + { + BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId); + if( !bg ) + continue; + uint32 avgTime = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].GetAverageQueueWaitTime(itrPlayerStatus->second.GroupInfo, _player->GetBattleGroundQueueIdFromLevel(bgTypeId)); + // send status in BattleGround Queue + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, STATUS_WAIT_QUEUE, avgTime, getMSTimeDiff(itrPlayerStatus->second.GroupInfo->JoinTime, getMSTime()), arenaType); + SendPacket(&data); + } } } diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp index 2ca465b48..a375fd7c7 100644 --- a/src/game/BattleGroundMgr.cpp +++ b/src/game/BattleGroundMgr.cpp @@ -159,7 +159,8 @@ GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, BattleGroundTypeId ginfo->ArenaTeamId = arenateamid; ginfo->IsRated = isRated; ginfo->IsInvitedToBGInstanceGUID = 0; - ginfo->JoinTime = sWorld.GetGameTime() * IN_MILISECONDS; + ginfo->JoinTime = getMSTime(); + ginfo->RemoveInviteTime = 0; ginfo->Team = leader->GetTeam(); ginfo->ArenaTeamRating = arenaRating; ginfo->OpponentsTeamRating = 0; @@ -194,7 +195,7 @@ void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo) void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id) { - uint32 timeInQueue = (sWorld.GetGameTime() * IN_MILISECONDS) - ginfo->JoinTime; + uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, getMSTime()); uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas! if( !ginfo->ArenaType ) { @@ -353,7 +354,7 @@ void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCou plr2->RemoveBattleGroundQueueId(bgQueueTypeId); // must be called this way, because if you move this call to // queue->removeplayer, it causes bugs WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); plr2->GetSession()->SendPacket(&data); } // then actually delete, this may delete the group as well! @@ -442,6 +443,7 @@ bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * b // invite the player PlayerInvitedToBGUpdateAverageWaitTime(ginfo, queue_id); + ginfo->RemoveInviteTime = getMSTime() + INVITE_ACCEPT_WAIT_TIME; sBattleGroundMgr.InvitePlayer(plr, bg->GetInstanceID(), bg->GetTypeID(), ginfo->Team); WorldPacket data; @@ -451,7 +453,7 @@ bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * b sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID()); // send status packet - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType); plr->GetSession()->SendPacket(&data); } return true; @@ -508,7 +510,7 @@ void BattleGroundQueue::BGEndedRemoveInvites(BattleGround *bg) // remove player from queue, this might delete the ginfo as well! don't use that pointer after this! RemovePlayer(itr2->first, true); WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); plr->GetSession()->SendPacket(&data); } } @@ -1067,7 +1069,8 @@ bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) if (qItr != qpMap.end() && qItr->second.GroupInfo->IsInvitedToBGInstanceGUID == m_BgInstanceGUID) { WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITATION_REMIND_TIME, 0); + //here must be remaining time + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME - INVITATION_REMIND_TIME, 0, qItr->second.GroupInfo->ArenaType); plr->GetSession()->SendPacket(&data); } } @@ -1106,7 +1109,7 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].RemovePlayer(m_PlayerGuid, true); sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bg->GetTypeID(), bg->GetQueueId()); WorldPacket data; - sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0); + sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0); plr->GetSession()->SendPacket(&data); } } @@ -1226,7 +1229,7 @@ void BattleGroundMgr::Update(uint32 diff) } } -void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype) +void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype) { // we can be in 3 queues in same time... if(StatusID == 0) @@ -1240,11 +1243,11 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro data->Initialize(SMSG_BATTLEFIELD_STATUS, (4+1+1+4+2+4+1+4+4+4)); *data << uint32(QueueSlot); // queue id (0...2) - player can be in 3 queues in time // uint64 in client - *data << uint64( uint64(arenatype ? arenatype : bg->GetArenaType()) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); + *data << uint64( uint64(arenatype) | (uint64(0x0D) << 8) | (uint64(bg->GetTypeID()) << 16) | (uint64(0x1F90) << 48) ); *data << uint32(bg->GetClientInstanceID()); // alliance/horde for BG and skirmish/rated for Arenas // following displays the minimap-icon 0 = faction icon 1 = arenaicon - *data << uint8(bg->isArena()); + *data << uint8(bg->isRated()); /* *data << uint8(arenatype ? arenatype : bg->GetArenaType()); // team type (0=BG, 2=2x2, 3=3x3, 5=5x5), for arenas // NOT PROPER VALUE IF ARENA ISN'T RUNNING YET!!!! switch(bg->GetTypeID()) // value depends on bg id { @@ -1301,7 +1304,7 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro { case STATUS_WAIT_QUEUE: // status_in_queue *data << uint32(Time1); // average wait time, milliseconds - *data << uint32(Time2); // time in queue, updated every minute? + *data << uint32(Time2); // time in queue, updated every minute!, milliseconds break; case STATUS_WAIT_JOIN: // status_invite *data << uint32(bg->GetMapId()); // map id @@ -1309,7 +1312,7 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro break; case STATUS_IN_PROGRESS: // status_in_progress *data << uint32(bg->GetMapId()); // map id - *data << uint32(Time1); // 0 at bg start, 120000 after bg end, time to bg auto leave, milliseconds + *data << uint32(Time1); // time to bg auto leave, 0 at bg start, 120000 after bg end, milliseconds *data << uint32(Time2); // time from bg start, milliseconds *data << uint8(0x1); // unk sometimes 0x0! break; diff --git a/src/game/BattleGroundMgr.h b/src/game/BattleGroundMgr.h index 6e6dd3141..b706ba955 100644 --- a/src/game/BattleGroundMgr.h +++ b/src/game/BattleGroundMgr.h @@ -48,6 +48,7 @@ struct GroupQueueInfo // stores informatio uint8 ArenaType; // 2v2, 3v3, 5v5 or 0 when BG uint32 ArenaTeamId; // team id if rated match uint32 JoinTime; // time when group was added + uint32 RemoveInviteTime; // time when we will remove invite for players in group uint32 IsInvitedToBGInstanceGUID; // was invited to certain BG uint32 ArenaTeamRating; // if rated match, inited to the rating of the team uint32 OpponentsTeamRating; // for rated arena matches @@ -184,7 +185,7 @@ class BattleGroundMgr void BuildGroupJoinedBattlegroundPacket(WorldPacket *data, BattleGroundTypeId bgTypeId); void BuildUpdateWorldStatePacket(WorldPacket *data, uint32 field, uint32 value); void BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg); - void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint32 arenatype = 0); + void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype); void BuildPlaySoundPacket(WorldPacket *data, uint32 soundid); void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid); diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp index 1bde9d37d..f6774dc01 100644 --- a/src/game/ChatHandler.cpp +++ b/src/game/ChatHandler.cpp @@ -34,6 +34,8 @@ #include "SpellAuras.h" #include "Language.h" #include "Util.h" +#include "GridNotifiersImpl.h" +#include "CellImpl.h" void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) { @@ -232,13 +234,15 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; - Group *group = GetPlayer()->GetGroup(); - if(!group) + // if player is in battleground, he cannot say to battleground members by /p + Group *group = GetPlayer()->GetOriginalGroup(); + // so if player hasn't OriginalGroup and his player->GetGroup() is BG raid, then return + if( !group && (!(group = GetPlayer()->GetGroup()) || group->isBGGroup()) ) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_PARTY, lang, NULL, 0, msg.c_str(),NULL); - group->BroadcastPacket(&data, group->GetMemberGroup(GetPlayer()->GetGUID())); + group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID())); } break; case CHAT_MSG_GUILD: @@ -312,13 +316,15 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; - Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup()) + // if player is in battleground, he cannot say to battleground members by /ra + Group *group = GetPlayer()->GetOriginalGroup(); + // so if player hasn't OriginalGroup and his player->GetGroup() is BG raid or his group isn't raid, then return + if( !group && !(group = GetPlayer()->GetGroup()) || group->isBGGroup() || !group->isRaidGroup() ) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); } break; case CHAT_MSG_RAID_LEADER: { @@ -338,13 +344,14 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; - Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID())) + // if player is in battleground, he cannot say to battleground members by /ra + Group *group = GetPlayer()->GetOriginalGroup(); + if( !group && !(group = GetPlayer()->GetGroup()) || group->isBGGroup() || !group->isRaidGroup() ) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_LEADER, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); } break; case CHAT_MSG_RAID_WARNING: { @@ -363,8 +370,9 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) return; WorldPacket data; + //in battleground, raid warning is sent only to players in battleground - code is ok ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); } break; case CHAT_MSG_BATTLEGROUND: @@ -379,13 +387,14 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + //battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup()) + if(!group || !group->isBGGroup()) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); } break; case CHAT_MSG_BATTLEGROUND_LEADER: @@ -400,13 +409,14 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data ) if(msg.empty()) break; + //battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() Group *group = GetPlayer()->GetGroup(); - if(!group || !group->isRaidGroup() || !group->IsLeader(GetPlayer()->GetGUID())) + if(!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID())) return; WorldPacket data; ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(),NULL); - group->BroadcastPacket(&data); + group->BroadcastPacket(&data, false); } break; case CHAT_MSG_CHANNEL: @@ -488,6 +498,38 @@ void WorldSession::HandleEmoteOpcode( WorldPacket & recv_data ) GetPlayer()->HandleEmoteCommand(emote); } +namespace MaNGOS +{ + class EmoteChatBuilder + { + public: + EmoteChatBuilder(Player const& pl, uint32 text_emote, uint32 emote_num, Unit const* target) + : i_player(pl), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {} + + void operator()(WorldPacket& data, int32 loc_idx) + { + char const* nam = i_target ? i_target->GetNameForLocaleIdx(loc_idx) : NULL; + uint32 namlen = (nam ? strlen(nam) : 0) + 1; + + data.Initialize(SMSG_TEXT_EMOTE, (20+namlen)); + data << i_player.GetGUID(); + data << (uint32)i_text_emote; + data << i_emote_num; + data << (uint32)namlen; + if( namlen > 1 ) + data.append(nam, namlen); + else + data << (uint8)0x00; + } + + private: + Player const& i_player; + uint32 i_text_emote; + uint32 i_emote_num; + Unit const* i_target; + }; +} // namespace MaNGOS + void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data ) { if(!GetPlayer()->isAlive()) @@ -509,27 +551,12 @@ void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data ) recv_data >> emoteNum; recv_data >> guid; - const char *nam = 0; - uint32 namlen = 1; - - Unit* unit = ObjectAccessor::GetUnit(*_player, guid); - Creature *pCreature = dynamic_cast(unit); - if(unit) - { - nam = unit->GetName(); - namlen = (nam ? strlen(nam) : 0) + 1; - } - EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote); if (!em) return; - GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit); - uint32 emote_anim = em->textid; - WorldPacket data; - switch(emote_anim) { case EMOTE_STATE_SLEEP: @@ -542,21 +569,26 @@ void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data ) break; } - data.Initialize(SMSG_TEXT_EMOTE, (20+namlen)); - data << GetPlayer()->GetGUID(); - data << (uint32)text_emote; - data << emoteNum; - data << (uint32)namlen; - if( namlen > 1 ) - data.append(nam, namlen); - else - data << (uint8)0x00; + Unit* unit = ObjectAccessor::GetUnit(*_player, guid); - GetPlayer()->SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true); + CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY()); + + Cell cell(p); + cell.data.Part.reserved = ALL_DISTRICT; + cell.SetNoCreate(); + + MaNGOS::EmoteChatBuilder emote_builder(*GetPlayer(), text_emote, emoteNum, unit); + MaNGOS::LocalizedPacketDo emote_do(emote_builder); + MaNGOS::PlayerDistWorker > emote_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),emote_do); + TypeContainerVisitor >, WorldTypeMapContainer > message(emote_worker); + CellLock cell_lock(cell, p); + cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap()); + + GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit); //Send scripted event call - if (pCreature && Script) - Script->ReceiveEmote(GetPlayer(),pCreature,text_emote); + if (unit && unit->GetTypeId()==TYPEID_UNIT && Script) + Script->ReceiveEmote(GetPlayer(),(Creature*)unit,text_emote); } void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data ) diff --git a/src/game/Group.cpp b/src/game/Group.cpp index 21695b754..e6e01aa13 100644 --- a/src/game/Group.cpp +++ b/src/game/Group.cpp @@ -201,7 +201,8 @@ void Group::ConvertToRaid() _initRaidSubGroupsCounter(); - if(!isBGGroup()) CharacterDatabase.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid)); + if(!isBGGroup()) + CharacterDatabase.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid)); SendUpdate(); // update quest related GO states (quest activity dependent from raid membership) @@ -212,7 +213,12 @@ void Group::ConvertToRaid() bool Group::AddInvite(Player *player) { - if(!player || player->GetGroupInvite() || player->GetGroup()) + if( !player || player->GetGroupInvite() ) + return false; + Group* group = player->GetGroup(); + if( group && group->isBGGroup() ) + group = player->GetOriginalGroup(); + if( group ) return false; RemoveInvite(player); @@ -323,9 +329,17 @@ uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method) player->GetSession()->SendPacket( &data ); } - data.Initialize(SMSG_GROUP_LIST, 24); - data << uint64(0) << uint64(0) << uint64(0); - player->GetSession()->SendPacket(&data); + //we already removed player from group and in player->GetGroup() is his original group! + if( Group* group = player->GetGroup() ) + { + group->SendUpdate(); + } + else + { + data.Initialize(SMSG_GROUP_LIST, 24); + data << uint64(0) << uint64(0) << uint64(0); + player->GetSession()->SendPacket(&data); + } _homebindIfInstance(player); } @@ -334,7 +348,7 @@ uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method) { WorldPacket data(SMSG_GROUP_SET_LEADER, (m_memberSlots.front().name.size()+1)); data << m_memberSlots.front().name; - BroadcastPacket(&data); + BroadcastPacket(&data, true); } SendUpdate(); @@ -357,7 +371,7 @@ void Group::ChangeLeader(const uint64 &guid) WorldPacket data(SMSG_GROUP_SET_LEADER, slot->name.size()+1); data << slot->name; - BroadcastPacket(&data); + BroadcastPacket(&data, true); SendUpdate(); } @@ -371,13 +385,23 @@ void Group::Disband(bool hideDestroy) if(!player) continue; - player->SetGroup(NULL); + //we cannot call _removeMember because it would invalidate member iterator + //if we are removing player from battleground raid + if( isBGGroup() ) + player->RemoveFromBattleGroundRaid(); + else + { + //we can remove player who is in battleground from his original group + if( player->GetOriginalGroup() == this ) + player->SetOriginalGroup(NULL); + else + player->SetGroup(NULL); + } // quest related GO state dependent from raid membership if(isRaidGroup()) player->UpdateForQuestsGO(); - if(!player->GetSession()) continue; @@ -388,9 +412,17 @@ void Group::Disband(bool hideDestroy) player->GetSession()->SendPacket(&data); } - data.Initialize(SMSG_GROUP_LIST, 24); - data << uint64(0) << uint64(0) << uint64(0); - player->GetSession()->SendPacket(&data); + //we already removed player from group and in player->GetGroup() is his original group, send update + if( Group* group = player->GetGroup() ) + { + group->SendUpdate(); + } + else + { + data.Initialize(SMSG_GROUP_LIST, 24); + data << uint64(0) << uint64(0) << uint64(0); + player->GetSession()->SendPacket(&data); + } _homebindIfInstance(player); } @@ -838,7 +870,7 @@ void Group::SetTargetIcon(uint8 id, uint64 guid) data << (uint8)0; data << id; data << guid; - BroadcastPacket(&data); + BroadcastPacket(&data, true); } void Group::GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_level, Player* & member_with_max_level, Player* & not_gray_member_with_max_level) @@ -891,7 +923,7 @@ void Group::SendUpdate() for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr) { player = objmgr.GetPlayer(citr->guid); - if(!player || !player->GetSession()) + if(!player || !player->GetSession() || player->GetGroup() != this ) continue; // guess size WorldPacket data(SMSG_GROUP_LIST, (1+1+1+1+8+4+GetMembersCount()*20)); @@ -905,11 +937,14 @@ void Group::SendUpdate() { if(citr->guid == citr2->guid) continue; + Player* member = objmgr.GetPlayer(citr2->guid); + uint8 onlineState = (member) ? MEMBER_STATUS_ONLINE : MEMBER_STATUS_OFFLINE; + onlineState = onlineState | ((isBGGroup()) ? MEMBER_STATUS_PVP : 0); data << citr2->name; data << (uint64)citr2->guid; // online-state - data << (uint8)(objmgr.GetPlayer(citr2->guid) ? 1 : 0); + data << (uint8)(onlineState); data << (uint8)(citr2->group); // groupid data << (uint8)(citr2->assistant?0x01:0); // 0x2 main assist, 0x4 main tank } @@ -943,12 +978,12 @@ void Group::UpdatePlayerOutOfRange(Player* pPlayer) } } -void Group::BroadcastPacket(WorldPacket *packet, int group, uint64 ignore) +void Group::BroadcastPacket(WorldPacket *packet, bool ignorePlayersInBGRaid, int group, uint64 ignore) { for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next()) { Player *pl = itr->getSource(); - if(!pl || (ignore != 0 && pl->GetGUID() == ignore)) + if(!pl || (ignore != 0 && pl->GetGUID() == ignore) || (ignorePlayersInBGRaid && pl->GetGroup() != this) ) continue; if (pl->GetSession() && (group==-1 || itr->getSubGroup()==group)) @@ -1027,7 +1062,15 @@ bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant, u if(player) { player->SetGroupInvite(NULL); - player->SetGroup(this, group); + //if player is in group and he is being added to BG raid group, then call SetBattleGroundRaid() + if( player->GetGroup() && isBGGroup() ) + player->SetBattleGroundRaid(this, group); + //if player is in bg raid and we are adding him to normal group, then call SetOriginalGroup() + else if ( player->GetGroup() ) + player->SetOriginalGroup(this, group); + //if player is not in group, then call set group + else + player->SetGroup(this, group); // if the same group invites the player back, cancel the homebind timer InstanceGroupBind *bind = GetBoundInstance(player->GetMapId(), player->GetDifficulty()); if(bind && bind->save->GetInstanceId() == player->GetInstanceId()) @@ -1054,7 +1097,17 @@ bool Group::_removeMember(const uint64 &guid) Player *player = objmgr.GetPlayer(guid); if (player) { - player->SetGroup(NULL); + //if we are removing player from battleground raid + if( isBGGroup() ) + player->RemoveFromBattleGroundRaid(); + else + { + //we can remove player who is in battleground from his original group + if( player->GetOriginalGroup() == this ) + player->SetOriginalGroup(NULL); + else + player->SetGroup(NULL); + } } _removeRolls(guid); @@ -1246,12 +1299,17 @@ void Group::ChangeMembersGroup(Player *player, const uint8 &group) return; if(_setMembersGroup(player->GetGUID(), group)) { - uint8 prevSubGroup; - prevSubGroup = player->GetSubGroup(); - + uint8 prevSubGroup = player->GetSubGroup(); + if( player->GetGroup() == this ) + player->GetGroupRef().setSubGroup(group); + //if player is in BG raid, it is possible that he is also in normal raid - and that normal raid is stored in m_originalGroup reference + else + { + prevSubGroup = player->GetOriginalSubGroup(); + player->GetOriginalGroupRef().setSubGroup(group); + } SubGroupCounterDecrease(prevSubGroup); - player->GetGroupRef().setSubGroup(group); SendUpdate(); } } diff --git a/src/game/Group.h b/src/game/Group.h index 0fb107735..478b7c5cb 100644 --- a/src/game/Group.h +++ b/src/game/Group.h @@ -291,7 +291,7 @@ class MANGOS_DLL_SPEC Group void SendUpdate(); void UpdatePlayerOutOfRange(Player* pPlayer); // ignore: GUID of player that will be ignored - void BroadcastPacket(WorldPacket *packet, int group=-1, uint64 ignore=0); + void BroadcastPacket(WorldPacket *packet, bool ignorePlayersInBGRaid, int group=-1, uint64 ignore=0); void BroadcastReadyCheck(WorldPacket *packet); void OfflineReadyCheck(); diff --git a/src/game/GroupHandler.cpp b/src/game/GroupHandler.cpp index 44fa9c290..d1de13ad3 100644 --- a/src/game/GroupHandler.cpp +++ b/src/game/GroupHandler.cpp @@ -55,12 +55,6 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data ) std::string membername; recv_data >> membername; - if(_player->InBattleGround()) - { - SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_INVITE_RESTRICTED); - return; - } - // attempt add selected player // cheating @@ -97,15 +91,20 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data ) return; } + Group *group = GetPlayer()->GetGroup(); + if( group && group->isBGGroup() ) + group = GetPlayer()->GetOriginalGroup(); + + Group *group2 = player->GetGroup(); + if( group2 && group2->isBGGroup() ) + group2 = player->GetOriginalGroup(); // player already in another group or invited - if(player->GetGroup() || player->GetGroupInvite() ) + if( group2 || player->GetGroupInvite() ) { SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_ALREADY_IN_GROUP); return; } - Group *group = GetPlayer()->GetGroup(); - if(group) { // not have permissions for invite @@ -114,7 +113,6 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data ) SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_YOU_NOT_LEADER); return; } - // not have place if(group->IsFull()) { @@ -185,27 +183,20 @@ void WorldSession::HandleGroupAcceptOpcode( WorldPacket & /*recv_data*/ ) Player* leader = objmgr.GetPlayer(group->GetLeaderGUID()); - if(leader && leader->InBattleGround()) - { - SendPartyResult(PARTY_OP_INVITE, "", PARTY_RESULT_INVITE_RESTRICTED); - return; - } - // forming a new group, create it if(!group->IsCreated()) { - if(leader) group->RemoveInvite(leader); + if( leader ) + group->RemoveInvite(leader); group->Create(group->GetLeaderGUID(), group->GetLeaderName()); objmgr.AddGroup(group); } - // everything's fine, do it + // everything's fine, do it, PLAYER'S GROUP IS SET IN ADDMEMBER!!! if(!group->AddMember(GetPlayer()->GetGUID(), GetPlayer()->GetName())) return; uint8 subgroup = group->GetMemberGroup(GetPlayer()->GetGUID()); - - GetPlayer()->SetGroup(group, subgroup); } void WorldSession::HandleGroupDeclineOpcode( WorldPacket & /*recv_data*/ ) @@ -424,7 +415,7 @@ void WorldSession::HandleMinimapPingOpcode(WorldPacket& recv_data) data << GetPlayer()->GetGUID(); data << x; data << y; - GetPlayer()->GetGroup()->BroadcastPacket(&data, -1, GetPlayer()->GetGUID()); + GetPlayer()->GetGroup()->BroadcastPacket(&data, true, -1, GetPlayer()->GetGUID()); } void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data) @@ -451,7 +442,7 @@ void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data) data << roll; data << GetPlayer()->GetGUID(); if(GetPlayer()->GetGroup()) - GetPlayer()->GetGroup()->BroadcastPacket(&data); + GetPlayer()->GetGroup()->BroadcastPacket(&data, false); else SendPacket(&data); } @@ -512,6 +503,7 @@ void WorldSession::HandleGroupChangeSubGroupOpcode( WorldPacket & recv_data ) { CHECK_PACKET_SIZE(recv_data,1+1); + // we will get correct pointer for group here, so we don't have to check if group is BG raid Group *group = GetPlayer()->GetGroup(); if(!group) return; @@ -604,7 +596,7 @@ void WorldSession::HandleRaidReadyCheckOpcode( WorldPacket & recv_data ) // everything's fine, do it WorldPacket data(MSG_RAID_READY_CHECK, 8); data << GetPlayer()->GetGUID(); - group->BroadcastPacket(&data, -1); + group->BroadcastPacket(&data, false, -1); group->OfflineReadyCheck(); } diff --git a/src/game/Item.h b/src/game/Item.h index b3afa1124..2f7376aae 100644 --- a/src/game/Item.h +++ b/src/game/Item.h @@ -285,13 +285,10 @@ class MANGOS_DLL_SPEC Item : public Object uState = state; } - bool hasQuest(uint32 quest_id) const - { - ItemPrototype const *itemProto = GetProto(); - return itemProto && itemProto->StartQuest == quest_id; - } + bool hasQuest(uint32 quest_id) const { return GetProto()->StartQuest == quest_id; } bool hasInvolvedQuest(uint32 /*quest_id*/) const { return false; } - + bool IsPotion() const { return GetProto()->IsPotion(); } + bool IsConjuredConsumable() const { return GetProto()->IsConjuredConsumable(); } private: uint8 m_slot; Bag *m_container; diff --git a/src/game/ItemPrototype.h b/src/game/ItemPrototype.h index cb512ae8d..8d018f17a 100644 --- a/src/game/ItemPrototype.h +++ b/src/game/ItemPrototype.h @@ -656,6 +656,9 @@ struct ItemPrototype } return 0; } + + bool IsPotion() const { return Class==ITEM_CLASS_CONSUMABLE && SubClass==ITEM_SUBCLASS_POTION; } + bool IsConjuredConsumable() const { return Class == ITEM_CLASS_CONSUMABLE && (Flags & ITEM_FLAGS_CONJURED); } }; struct ItemLocale diff --git a/src/game/Language.h b/src/game/Language.h index c25d1e6f4..3d8727d14 100644 --- a/src/game/Language.h +++ b/src/game/Language.h @@ -692,9 +692,9 @@ enum MangosStrings LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING = 750, // "Not enough players. This game will close in %u mins." LANG_BATTLEGROUND_PREMATURE_FINISH_WARNING_SECS = 751, // "Not enough players. This game will close in %u seconds." // = 752, not used -// LANG_BG_WS_START_TWO_MINUTES = 753, -// LANG_BG_AB_START_TWO_MINUTES = 754, -// LANG_BG_EY_START_TWO_MINUTES = 755, +// LANG_BG_WS_START_TWO_MINUTES = 753, - defined above +// LANG_BG_AB_START_TWO_MINUTES = 754, - defined above +// LANG_BG_EY_START_TWO_MINUTES = 755, - defined above // Room for batleground/arena strings 756-799 not used // in game strings diff --git a/src/game/Map.cpp b/src/game/Map.cpp index f78871d8b..f7d5ab2d6 100644 --- a/src/game/Map.cpp +++ b/src/game/Map.cpp @@ -65,7 +65,8 @@ bool Map::ExistMap(uint32 mapid,int x,int y) map_fileheader header; fread(&header, sizeof(header), 1, pf); - if (header.mapMagic != MAP_MAGIC || header.versionMagic != MAP_VERSION_MAGIC) + if (header.mapMagic != uint32(MAP_MAGIC) || + header.versionMagic != uint32(MAP_VERSION_MAGIC)) { sLog.outError("Map file '%s' is non-compatible version (outdated?). Please, create new using ad.exe program.",tmp); delete [] tmp; @@ -1081,7 +1082,8 @@ bool GridMap::loadData(char *filename) if (!in) return true; fread(&header, sizeof(header),1,in); - if (header.mapMagic == MAP_MAGIC && header.versionMagic == MAP_VERSION_MAGIC) + if (header.mapMagic == uint32(MAP_MAGIC) && + header.versionMagic == uint32(MAP_VERSION_MAGIC)) { // loadup area data if (header.areaMapOffset && !loadAreaData(in, header.areaMapOffset, header.areaMapSize)) @@ -1132,11 +1134,11 @@ bool GridMap::loadAreaData(FILE *in, uint32 offset, uint32 size) map_areaHeader header; fseek(in, offset, SEEK_SET); fread(&header, sizeof(header), 1, in); - if (header.fourcc != MAP_AREA_MAGIC) + if (header.fourcc != uint32(MAP_AREA_MAGIC)) return false; m_gridArea = header.gridArea; - if (!(header.flags&MAP_AREA_NO_AREA)) + if (!(header.flags & MAP_AREA_NO_AREA)) { m_area_map = new uint16 [16*16]; fread(m_area_map, sizeof(uint16), 16*16, in); @@ -1149,13 +1151,13 @@ bool GridMap::loadHeihgtData(FILE *in, uint32 offset, uint32 size) map_heightHeader header; fseek(in, offset, SEEK_SET); fread(&header, sizeof(header), 1, in); - if (header.fourcc != MAP_HEIGTH_MAGIC) + if (header.fourcc != uint32(MAP_HEIGTH_MAGIC)) return false; m_gridHeight = header.gridHeight; - if (!(header.flags&MAP_HEIGHT_NO_HIGHT)) + if (!(header.flags & MAP_HEIGHT_NO_HIGHT)) { - if ((header.flags&MAP_HEIGHT_AS_INT16)) + if ((header.flags & MAP_HEIGHT_AS_INT16)) { m_uint16_V9 = new uint16 [129*129]; m_uint16_V8 = new uint16 [128*128]; @@ -1164,7 +1166,7 @@ bool GridMap::loadHeihgtData(FILE *in, uint32 offset, uint32 size) m_gridIntHeightMultiplier = (header.gridMaxHeight - header.gridHeight) / 65535; m_gridGetHeight = &GridMap::getHeightFromUint16; } - else if ((header.flags&MAP_HEIGHT_AS_INT8)) + else if ((header.flags & MAP_HEIGHT_AS_INT8)) { m_uint8_V9 = new uint8 [129*129]; m_uint8_V8 = new uint8 [128*128]; @@ -1192,7 +1194,7 @@ bool GridMap::loadLiquidData(FILE *in, uint32 offset, uint32 size) map_liquidHeader header; fseek(in, offset, SEEK_SET); fread(&header, sizeof(header), 1, in); - if (header.fourcc != MAP_LIQUID_MAGIC) + if (header.fourcc != uint32(MAP_LIQUID_MAGIC)) return false; m_liquidType = header.liquidType; @@ -1529,7 +1531,7 @@ inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 R } // For speed check as int values - int delta = (liquid_level - z) * 10; + int delta = int((liquid_level - z) * 10); // Get position delta if (delta > 20) // Under water diff --git a/src/game/MotionMaster.cpp b/src/game/MotionMaster.cpp index b2f99f968..ce20d5cd7 100644 --- a/src/game/MotionMaster.cpp +++ b/src/game/MotionMaster.cpp @@ -43,8 +43,8 @@ MotionMaster::Initialize() while(!empty()) { MovementGenerator *curr = top(); - curr->Finalize(*i_owner); pop(); + curr->Finalize(*i_owner); if( !isStatic( curr ) ) delete curr; } @@ -66,8 +66,8 @@ MotionMaster::~MotionMaster() while(!empty()) { MovementGenerator *curr = top(); - curr->Finalize(*i_owner); pop(); + curr->Finalize(*i_owner); if( !isStatic( curr ) ) delete curr; } @@ -117,8 +117,8 @@ MotionMaster::DirectClean(bool reset) while( !empty() && size() > 1 ) { MovementGenerator *curr = top(); - curr->Finalize(*i_owner); pop(); + curr->Finalize(*i_owner); if( !isStatic( curr ) ) delete curr; } @@ -142,8 +142,8 @@ MotionMaster::DelayedClean() while( !empty() && size() > 1 ) { MovementGenerator *curr = top(); - curr->Finalize(*i_owner); pop(); + curr->Finalize(*i_owner); if( !isStatic( curr ) ) m_expList->push_back(curr); } @@ -156,23 +156,26 @@ MotionMaster::DirectExpire(bool reset) return; MovementGenerator *curr = top(); - curr->Finalize(*i_owner); pop(); + // also drop stored under top() targeted motions + while( !empty() && top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE ) + { + MovementGenerator *temp = top(); + pop(); + temp ->Finalize(*i_owner); + delete temp; + } + + // it can add another motions instead + curr->Finalize(*i_owner); + if( !isStatic(curr) ) delete curr; - assert( !empty() ); - while( !empty() && top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE ) - { - // Should check if target is still valid? If not valid it will crash. - curr = top(); - curr->Finalize(*i_owner); - pop(); - delete curr; - } if( empty() ) Initialize(); + if (reset) top()->Reset(*i_owner); } @@ -183,23 +186,24 @@ MotionMaster::DelayedExpire() return; MovementGenerator *curr = top(); - curr->Finalize(*i_owner); pop(); if(!m_expList) m_expList = new ExpireList(); - if( !isStatic(curr) ) - m_expList->push_back(curr); - + // also drop stored under top() targeted motions while( !empty() && top()->GetMovementGeneratorType() == TARGETED_MOTION_TYPE ) { - // Should check if target is still valid? If not valid it will crash. - curr = top(); - curr->Finalize(*i_owner); + MovementGenerator *temp = top(); pop(); - m_expList->push_back(curr); + temp ->Finalize(*i_owner); + m_expList->push_back(temp ); } + + curr->Finalize(*i_owner); + + if( !isStatic(curr) ) + m_expList->push_back(curr); } void MotionMaster::MoveIdle() diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp index 7ae39ebb3..0b2c31e77 100644 --- a/src/game/Pet.cpp +++ b/src/game/Pet.cpp @@ -294,7 +294,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool AIM_Initialize(); map->Add((Creature*)this); - // Spells should be loaded after pet is added to map, because in CanCast is check on it + // Spells should be loaded after pet is added to map, because in CheckCast is check on it _LoadSpells(); _LoadSpellCooldowns(); diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp index 02f371c35..9d77a44d2 100644 --- a/src/game/PetHandler.cpp +++ b/src/game/PetHandler.cpp @@ -189,7 +189,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) Spell *spell = new Spell(pet, spellInfo, false); - int16 result = spell->PetCanCast(unit_target); + SpellCastResult result = spell->CheckPetCast(unit_target); //auto turn to target unless possessed if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->HasAuraType(SPELL_AURA_MOD_POSSESS)) @@ -200,10 +200,10 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data ) if(Unit* powner = pet->GetCharmerOrOwner()) if(powner->GetTypeId() == TYPEID_PLAYER) pet->SendUpdateToPlayer((Player*)powner); - result = -1; + result = SPELL_CAST_OK; } - if(result == -1) + if(result == SPELL_CAST_OK) { ((Creature*)pet)->AddCreatureSpellCooldown(spellid); if (((Creature*)pet)->isPet()) @@ -611,8 +611,8 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket ) spell->m_cast_count = cast_count; // probably pending spell cast spell->m_targets = targets; - int16 result = spell->PetCanCast(NULL); - if(result == -1) + SpellCastResult result = spell->CheckPetCast(NULL); + if(result == SPELL_CAST_OK) { pet->AddCreatureSpellCooldown(spellid); if(pet->isPet()) diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 54e0df5f4..01404bd7c 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -750,9 +750,9 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 } // if this is ammo then use it - uint8 msg = CanUseAmmo( pItem->GetProto()->ItemId ); + uint8 msg = CanUseAmmo( pItem->GetEntry() ); if( msg == EQUIP_ERR_OK ) - SetAmmo( pItem->GetProto()->ItemId ); + SetAmmo( pItem->GetEntry() ); } } } @@ -2891,8 +2891,8 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen continue; if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || - // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 ) + // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL + (pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0 ) { switch(GetSkillRangeType(pSkill,_spell_idx->second->racemask!=0)) { @@ -2929,8 +2929,8 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen if(IsInWorld()) { - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL,spell_id); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS,spell_id); } // return true (for send learn packet) only if spell active (in case ranked spells) and not replace old spell @@ -3101,8 +3101,8 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool update_action_bar_ continue; if(_spell_idx->second->learnOnGetSkill == ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL || - // lockpicking special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL - pSkill->id==SKILL_LOCKPICKING && _spell_idx->second->max_value==0 ) + // lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL + (pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0 ) { // not reset skills for professions and racial abilities if( (pSkill->categoryId==SKILL_CATEGORY_SECONDARY || pSkill->categoryId==SKILL_CATEGORY_PROFESSION) && @@ -4029,7 +4029,7 @@ void Player::CreateCorpse() flags |= CORPSE_FLAG_HIDE_HELM; if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK)) flags |= CORPSE_FLAG_HIDE_CLOAK; - if(InBattleGround()) + if(InBattleGround() && !InArena()) flags |= CORPSE_FLAG_LOOTABLE; // to be able to remove insignia corpse->SetUInt32Value( CORPSE_FIELD_FLAGS, flags ); @@ -5344,12 +5344,12 @@ void Player::SendInitialActionButtons() sLog.outDetail( "Action Buttons for '%u' Initialized", GetGUIDLow() ); } -void Player::addActionButton(const uint8 button, const uint16 action, const uint8 type, const uint8 misc) +bool Player::addActionButton(const uint8 button, const uint16 action, const uint8 type, const uint8 misc) { if(button >= MAX_ACTION_BUTTONS) { sLog.outError( "Action %u not added into button %u for player %s: button must be < 132", action, button, GetName() ); - return; + return false; } // check cheating with adding non-known spells to action bar @@ -5358,13 +5358,13 @@ void Player::addActionButton(const uint8 button, const uint16 action, const uint if(!sSpellStore.LookupEntry(action)) { sLog.outError( "Action %u not added into button %u for player %s: spell not exist", action, button, GetName() ); - return; + return false; } if(!HasSpell(action)) { sLog.outError( "Action %u not added into button %u for player %s: player don't known this spell", action, button, GetName() ); - return; + return false; } } @@ -5382,6 +5382,7 @@ void Player::addActionButton(const uint8 button, const uint16 action, const uint }; sLog.outDetail( "Player '%u' Added Action '%u' to Button '%u'", GetGUIDLow(), action, button ); + return true; } void Player::removeActionButton(uint8 button) @@ -5934,8 +5935,8 @@ bool Player::ModifyOneFactionReputation(FactionEntry const* factionEntry, int32 } } } - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION,factionEntry->ID); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION,factionEntry->ID); SendFactionState(&(itr->second)); return true; @@ -6001,8 +6002,8 @@ bool Player::SetOneFactionReputation(FactionEntry const* factionEntry, int32 sta SetFactionAtWar(&itr->second,true); SendFactionState(&(itr->second)); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION,factionEntry->ID); + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION,factionEntry->ID); return true; } return false; @@ -7004,7 +7005,7 @@ void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply if(apply) { // Cannot be used in this stance/form - if(GetErrorAtShapeshiftedCast(spellInfo, m_form)!=0) + if(GetErrorAtShapeshiftedCast(spellInfo, m_form) != SPELL_CAST_OK) return; if(form_change) // check aura active state from other form @@ -7038,7 +7039,7 @@ void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply if(form_change) // check aura compatibility { // Cannot be used in this stance/form - if(GetErrorAtShapeshiftedCast(spellInfo, m_form)==0) + if(GetErrorAtShapeshiftedCast(spellInfo, m_form)==SPELL_CAST_OK) return; // and remove only not compatible at form change } @@ -7450,6 +7451,9 @@ void Player::SendLootRelease( uint64 guid ) void Player::SendLoot(uint64 guid, LootType loot_type) { + if (uint64 lguid = GetLootGUID()) + m_session->DoLootRelease(lguid); + Loot *loot = 0; PermissionTypes permission = ALL_PERMISSION; @@ -10667,6 +10671,8 @@ Item* Player::EquipItem( uint16 pos, Item *pItem, bool update ) } } + // only for full equip instead adding to stack + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry()); return pItem; } @@ -10913,60 +10919,60 @@ void Player::DestroyItem( uint8 bag, uint8 slot, bool update ) void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool unequip_check) { sLog.outDebug( "STORAGE: DestroyItemCount item = %u, count = %u", item, count); - Item *pItem; - ItemPrototype const *pProto; uint32 remcount = 0; // in inventory for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetEntry() == item ) + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { - if( pItem->GetCount() + remcount <= count ) + if (pItem->GetEntry() == item) { - // all items in inventory can unequipped - remcount += pItem->GetCount(); - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + if (pItem->GetCount() + remcount <= count) + { + // all items in inventory can unequipped + remcount += pItem->GetCount(); + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - if(remcount >=count) + if (remcount >=count) + return; + } + else + { + ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); + pItem->SetCount( pItem->GetCount() - count + remcount ); + if (IsInWorld() & update) + pItem->SendUpdateToPlayer( this ); + pItem->SetState(ITEM_CHANGED, this); return; - } - else - { - pProto = pItem->GetProto(); - ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); - pItem->SetCount( pItem->GetCount() - count + remcount ); - if( IsInWorld() & update ) - pItem->SendUpdateToPlayer( this ); - pItem->SetState(ITEM_CHANGED, this); - return; + } } } } for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetEntry() == item ) + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { - if( pItem->GetCount() + remcount <= count ) + if (pItem->GetEntry() == item) { - // all keys can be unequipped - remcount += pItem->GetCount(); - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + if (pItem->GetCount() + remcount <= count) + { + // all keys can be unequipped + remcount += pItem->GetCount(); + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - if(remcount >=count) + if (remcount >=count) + return; + } + else + { + ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); + pItem->SetCount( pItem->GetCount() - count + remcount ); + if (IsInWorld() & update) + pItem->SendUpdateToPlayer( this ); + pItem->SetState(ITEM_CHANGED, this); return; - } - else - { - pProto = pItem->GetProto(); - ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); - pItem->SetCount( pItem->GetCount() - count + remcount ); - if( IsInWorld() & update ) - pItem->SendUpdateToPlayer( this ); - pItem->SetState(ITEM_CHANGED, this); - return; + } } } } @@ -10978,27 +10984,28 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq { for(uint32 j = 0; j < pBag->GetBagSize(); j++) { - pItem = pBag->GetItemByPos(j); - if( pItem && pItem->GetEntry() == item ) + if(Item* pItem = pBag->GetItemByPos(j)) { - // all items in bags can be unequipped - if( pItem->GetCount() + remcount <= count ) + if (pItem->GetEntry() == item) { - remcount += pItem->GetCount(); - DestroyItem( i, j, update ); + // all items in bags can be unequipped + if (pItem->GetCount() + remcount <= count) + { + remcount += pItem->GetCount(); + DestroyItem( i, j, update ); - if(remcount >=count) + if (remcount >=count) + return; + } + else + { + ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); + pItem->SetCount( pItem->GetCount() - count + remcount ); + if (IsInWorld() && update) + pItem->SendUpdateToPlayer( this ); + pItem->SetState(ITEM_CHANGED, this); return; - } - else - { - pProto = pItem->GetProto(); - ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); - pItem->SetCount( pItem->GetCount() - count + remcount ); - if( IsInWorld() && update ) - pItem->SendUpdateToPlayer( this ); - pItem->SetState(ITEM_CHANGED, this); - return; + } } } } @@ -11008,29 +11015,30 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq // in equipment and bag list for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++) { - pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetEntry() == item ) + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) { - if( pItem->GetCount() + remcount <= count ) + if (pItem && pItem->GetEntry() == item) { - if(!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i,false) == EQUIP_ERR_OK ) + if (pItem->GetCount() + remcount <= count) { - remcount += pItem->GetCount(); - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + if (!unequip_check || CanUnequipItem(INVENTORY_SLOT_BAG_0 << 8 | i,false) == EQUIP_ERR_OK ) + { + remcount += pItem->GetCount(); + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - if(remcount >=count) - return; + if (remcount >=count) + return; + } + } + else + { + ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); + pItem->SetCount( pItem->GetCount() - count + remcount ); + if (IsInWorld() & update) + pItem->SendUpdateToPlayer( this ); + pItem->SetState(ITEM_CHANGED, this); + return; } - } - else - { - pProto = pItem->GetProto(); - ItemRemovedQuestCheck( pItem->GetEntry(), count - remcount ); - pItem->SetCount( pItem->GetCount() - count + remcount ); - if( IsInWorld() & update ) - pItem->SendUpdateToPlayer( this ); - pItem->SetState(ITEM_CHANGED, this); - return; } } } @@ -11042,40 +11050,28 @@ void Player::DestroyZoneLimitedItem( bool update, uint32 new_zone ) // in inventory for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone)) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); + for(int i = KEYRING_SLOT_START; i < QUESTBAG_SLOT_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone)) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); // in inventory bags for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) - { - Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pBag ) - { + if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) for(uint32 j = 0; j < pBag->GetBagSize(); j++) - { - Item* pItem = pBag->GetItemByPos(j); - if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) - DestroyItem( i, j, update); - } - } - } + if (Item* pItem = pBag->GetItemByPos(j)) + if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone)) + DestroyItem( i, j, update); // in equipment and bag list for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(),new_zone)) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); } void Player::DestroyConjuredItems( bool update ) @@ -11086,40 +11082,23 @@ void Player::DestroyConjuredItems( bool update ) // in inventory for(int i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetProto() && - (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) && - (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsConjuredConsumable()) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); // in inventory bags for(int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++) - { - Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pBag ) - { + if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i )) for(uint32 j = 0; j < pBag->GetBagSize(); j++) - { - Item* pItem = pBag->GetItemByPos(j); - if( pItem && pItem->GetProto() && - (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) && - (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) ) - DestroyItem( i, j, update); - } - } - } + if (Item* pItem = pBag->GetItemByPos(j)) + if (pItem->IsConjuredConsumable()) + DestroyItem( i, j, update); // in equipment and bag list for(int i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++) - { - Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ); - if( pItem && pItem->GetProto() && - (pItem->GetProto()->Class == ITEM_CLASS_CONSUMABLE) && - (pItem->GetProto()->Flags & ITEM_FLAGS_CONJURED) ) - DestroyItem( INVENTORY_SLOT_BAG_0, i, update); - } + if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i )) + if (pItem->IsConjuredConsumable()) + DestroyItem( INVENTORY_SLOT_BAG_0, i, update); } void Player::DestroyItemCount( Item* pItem, uint32 &count, bool update ) @@ -12250,7 +12229,7 @@ void Player::SendNewItem(Item *item, uint32 count, bool received, bool created, data << GetItemCount(item->GetEntry()); // count of items in inventory if (broadcast && GetGroup()) - GetGroup()->BroadcastPacket(&data); + GetGroup()->BroadcastPacket(&data, true); else GetSession()->SendPacket(&data); } @@ -12817,7 +12796,7 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver GiveXP( XP , NULL ); else { - int32 money = int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)); + uint32 money = uint32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)); ModifyMoney( money ); GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, money); } @@ -12826,7 +12805,9 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver if(pQuest->GetRewOrReqMoney()) { ModifyMoney( pQuest->GetRewOrReqMoney() ); - GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney()); + + if(pQuest->GetRewOrReqMoney() > 0) + GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney()); } // honor reward @@ -14785,9 +14766,15 @@ void Player::_LoadActions(QueryResult *result) uint8 button = fields[0].GetUInt8(); - addActionButton(button, fields[1].GetUInt16(), fields[2].GetUInt8(), fields[3].GetUInt8()); + if(addActionButton(button, fields[1].GetUInt16(), fields[2].GetUInt8(), fields[3].GetUInt8())) + m_actionButtons[button].uState = ACTIONBUTTON_UNCHANGED; + else + { + sLog.outError( " ...at loading, and will deleted in DB also"); - m_actionButtons[button].uState = ACTIONBUTTON_UNCHANGED; + // Will deleted in DB at next save (it can create data until save but marked as deleted) + m_actionButtons[button].uState = ACTIONBUTTON_DELETED; + } } while( result->NextRow() ); @@ -18261,7 +18248,8 @@ void Player::ClearComboPoints() void Player::SetGroup(Group *group, int8 subgroup) { - if(group == NULL) m_group.unlink(); + if(group == NULL) + m_group.unlink(); else { // never use SetGroup without a subgroup unless you specify NULL for group @@ -19215,7 +19203,7 @@ void Player::UpdateAreaDependentAuras( uint32 newArea ) for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();) { // use m_zoneUpdateId for speed: UpdateArea called from UpdateZone or instead UpdateZone in both cases m_zoneUpdateId up-to-date - if(spellmgr.GetSpellAllowedInLocationError(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea,this)!=0) + if(spellmgr.GetSpellAllowedInLocationError(iter->second->GetSpellProto(),GetMapId(),m_zoneUpdateId,newArea,this) != SPELL_CAST_OK) RemoveAura(iter); else ++iter; @@ -19348,6 +19336,41 @@ PartyResult Player::CanUninviteFromGroup() const return PARTY_RESULT_OK; } +void Player::SetBattleGroundRaid(Group* group, int8 subgroup) +{ + //we must move references from m_group to m_originalGroup + SetOriginalGroup(GetGroup(), GetSubGroup()); + + m_group.unlink(); + m_group.link(group, this); + m_group.setSubGroup((uint8)subgroup); +} + +void Player::RemoveFromBattleGroundRaid() +{ + //remove existing reference + m_group.unlink(); + if( Group* group = GetOriginalGroup() ) + { + m_group.link(group, this); + m_group.setSubGroup(GetOriginalSubGroup()); + } + SetOriginalGroup(NULL); +} + +void Player::SetOriginalGroup(Group *group, int8 subgroup) +{ + if( group == NULL ) + m_originalGroup.unlink(); + else + { + // never use SetOriginalGroup without a subgroup unless you specify NULL for group + assert(subgroup >= 0); + m_originalGroup.link(group, this); + m_originalGroup.setSubGroup((uint8)subgroup); + } +} + void Player::UpdateUnderwaterState( Map* m, float x, float y, float z ) { LiquidData liquid_status; @@ -19425,6 +19448,7 @@ bool Player::CanUseBattleGroundObject() { return ( //InBattleGround() && // in battleground - not need, check in other cases //!IsMounted() && - not correct, player is dismounted when he clicks on flag + //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, 0) && // can't pickup @@ -19742,8 +19766,20 @@ void Player::_LoadSkills() else SetUInt32Value(PLAYER_SKILL_INDEX(i), MAKE_PAIR32(id,0)); - uint32 vskill = SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))); + // set fixed skill ranges + switch(GetSkillRangeType(pSkill,false)) + { + case SKILL_RANGE_LANGUAGE: // 300..300 + SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(300,300)); + break; + case SKILL_RANGE_MONO: // 1..1, grey monolite bar + SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(1,1)); + break; + default: + break; + } + uint32 vskill = SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i))); learnSkillRewardedSpells(id, vskill); } diff --git a/src/game/Player.h b/src/game/Player.h index 0f1fd16d6..fc000230d 100644 --- a/src/game/Player.h +++ b/src/game/Player.h @@ -1189,7 +1189,6 @@ class MANGOS_DLL_SPEC Player : public Unit void AddArmorProficiency(uint32 newflag) { m_ArmorProficiency |= newflag; } uint32 GetWeaponProficiency() const { return m_WeaponProficiency; } uint32 GetArmorProficiency() const { return m_ArmorProficiency; } - bool IsInFeralForm() const { return m_form == FORM_CAT || m_form == FORM_BEAR || m_form == FORM_DIREBEAR; } bool IsUseEquipedWeapon( bool mainhand ) const { // disarm applied only to mainhand weapon @@ -1548,7 +1547,7 @@ class MANGOS_DLL_SPEC Player : public Unit m_cinematic = cine; } - void addActionButton(uint8 button, uint16 action, uint8 type, uint8 misc); + bool addActionButton(uint8 button, uint16 action, uint8 type, uint8 misc); void removeActionButton(uint8 button); void SendInitialActionButtons(); @@ -2153,6 +2152,13 @@ class MANGOS_DLL_SPEC Player : public Unit void SetAuraUpdateMask(uint8 slot) { m_auraUpdateMask |= (uint64(1) << slot); } Player* GetNextRandomRaidMember(float radius); PartyResult CanUninviteFromGroup() const; + // BattleGround Group System + void SetBattleGroundRaid(Group *group, int8 subgroup = -1); + void RemoveFromBattleGroundRaid(); + Group * GetOriginalGroup() { return m_originalGroup.getTarget(); } + GroupReference& GetOriginalGroupRef() { return m_originalGroup; } + uint8 GetOriginalSubGroup() const { return m_originalGroup.getSubGroup(); } + void SetOriginalGroup(Group *group, int8 subgroup = -1); GridReference &GetGridRef() { return m_gridRef; } MapReference &GetMapRef() { return m_mapRef; } @@ -2393,6 +2399,7 @@ class MANGOS_DLL_SPEC Player : public Unit // Groups GroupReference m_group; + GroupReference m_originalGroup; Group *m_groupInvite; uint32 m_groupUpdateMask; uint64 m_auraUpdateMask; diff --git a/src/game/ScriptCalls.cpp b/src/game/ScriptCalls.cpp index b4b953f3c..ec67773ec 100644 --- a/src/game/ScriptCalls.cpp +++ b/src/game/ScriptCalls.cpp @@ -74,6 +74,9 @@ bool LoadScriptingModule(char const* libName) ||!(testScript->GOQuestAccept =(scriptCallGOQuestAccept )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GOQuestAccept" )) ||!(testScript->ReceiveEmote =(scriptCallReceiveEmote )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ReceiveEmote" )) ||!(testScript->ItemUse =(scriptCallItemUse )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"ItemUse" )) + ||!(testScript->EffectDummyGameObj =(scriptCallEffectDummyGameObj )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"EffectDummyGameObj" )) + ||!(testScript->EffectDummyCreature =(scriptCallEffectDummyCreature )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"EffectDummyCreature" )) + ||!(testScript->EffectDummyItem =(scriptCallEffectDummyItem )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"EffectDummyItem" )) ||!(testScript->GetAI =(scriptCallGetAI )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"GetAI" )) ||!(testScript->CreateInstanceData =(scriptCallCreateInstanceData )MANGOS_GET_PROC_ADDR(testScript->hScriptsLib,"CreateInstanceData" )) ) diff --git a/src/game/ScriptCalls.h b/src/game/ScriptCalls.h index b075693f8..11aade7e7 100644 --- a/src/game/ScriptCalls.h +++ b/src/game/ScriptCalls.h @@ -56,6 +56,9 @@ typedef bool(MANGOS_IMPORT * scriptCallGOQuestAccept)(Player *player, GameObject typedef bool(MANGOS_IMPORT * scriptCallGOChooseReward)(Player *player, GameObject *, Quest const*, uint32 opt ); typedef bool(MANGOS_IMPORT * scriptCallReceiveEmote) ( Player *player, Creature *_Creature, uint32 emote ); typedef bool(MANGOS_IMPORT * scriptCallItemUse) (Player *player, Item *_Item, SpellCastTargets const& targets); +typedef bool(MANGOS_IMPORT * scriptCallEffectDummyGameObj) (Unit *caster, uint32 spellId, uint32 effIndex, GameObject *gameObjTarget); +typedef bool(MANGOS_IMPORT * scriptCallEffectDummyCreature) (Unit *caster, uint32 spellId, uint32 effIndex, Creature *crTarget); +typedef bool(MANGOS_IMPORT * scriptCallEffectDummyItem) (Unit *caster, uint32 spellId, uint32 effIndex, Item *itemTarget); typedef CreatureAI* (MANGOS_IMPORT * scriptCallGetAI) ( Creature *_Creature ); typedef InstanceData* (MANGOS_IMPORT * scriptCallCreateInstanceData) (Map *map); @@ -82,6 +85,9 @@ typedef struct scriptCallGOQuestAccept GOQuestAccept; scriptCallReceiveEmote ReceiveEmote; scriptCallItemUse ItemUse; + scriptCallEffectDummyGameObj EffectDummyGameObj; + scriptCallEffectDummyCreature EffectDummyCreature; + scriptCallEffectDummyItem EffectDummyItem; scriptCallGetAI GetAI; scriptCallCreateInstanceData CreateInstanceData; diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h index 1fb8090d1..6a296ad4b 100644 --- a/src/game/SharedDefines.h +++ b/src/game/SharedDefines.h @@ -684,6 +684,194 @@ enum SpellEffects TOTAL_SPELL_EFFECTS = 163 }; +enum SpellCastResult +{ + SPELL_FAILED_AFFECTING_COMBAT = 0, + SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 1, + SPELL_FAILED_ALREADY_AT_FULL_MANA = 2, + SPELL_FAILED_ALREADY_AT_FULL_POWER = 3, + SPELL_FAILED_ALREADY_BEING_TAMED = 4, + SPELL_FAILED_ALREADY_HAVE_CHARM = 5, + SPELL_FAILED_ALREADY_HAVE_SUMMON = 6, + SPELL_FAILED_ALREADY_OPEN = 7, + SPELL_FAILED_AURA_BOUNCED = 8, + SPELL_FAILED_AUTOTRACK_INTERRUPTED = 9, + SPELL_FAILED_BAD_IMPLICIT_TARGETS = 10, + SPELL_FAILED_BAD_TARGETS = 11, + SPELL_FAILED_CANT_BE_CHARMED = 12, + SPELL_FAILED_CANT_BE_DISENCHANTED = 13, + SPELL_FAILED_CANT_BE_DISENCHANTED_SKILL = 14, + SPELL_FAILED_CANT_BE_MILLED = 15, + SPELL_FAILED_CANT_BE_PROSPECTED = 16, + SPELL_FAILED_CANT_CAST_ON_TAPPED = 17, + SPELL_FAILED_CANT_DUEL_WHILE_INVISIBLE = 18, + SPELL_FAILED_CANT_DUEL_WHILE_STEALTHED = 19, + SPELL_FAILED_CANT_STEALTH = 20, + SPELL_FAILED_CASTER_AURASTATE = 21, + SPELL_FAILED_CASTER_DEAD = 22, + SPELL_FAILED_CHARMED = 23, + SPELL_FAILED_CHEST_IN_USE = 24, + SPELL_FAILED_CONFUSED = 25, + SPELL_FAILED_DONT_REPORT = 26, + SPELL_FAILED_EQUIPPED_ITEM = 27, + SPELL_FAILED_EQUIPPED_ITEM_CLASS = 28, + SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND = 29, + SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 30, + SPELL_FAILED_ERROR = 31, + SPELL_FAILED_FIZZLE = 32, + SPELL_FAILED_FLEEING = 33, + SPELL_FAILED_FOOD_LOWLEVEL = 34, + SPELL_FAILED_HIGHLEVEL = 35, + SPELL_FAILED_HUNGER_SATIATED = 36, + SPELL_FAILED_IMMUNE = 37, + SPELL_FAILED_INCORRECT_AREA = 38, + SPELL_FAILED_INTERRUPTED = 39, + SPELL_FAILED_INTERRUPTED_COMBAT = 40, + SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 41, + SPELL_FAILED_ITEM_GONE = 42, + SPELL_FAILED_ITEM_NOT_FOUND = 43, + SPELL_FAILED_ITEM_NOT_READY = 44, + SPELL_FAILED_LEVEL_REQUIREMENT = 45, + SPELL_FAILED_LINE_OF_SIGHT = 46, + SPELL_FAILED_LOWLEVEL = 47, + SPELL_FAILED_LOW_CASTLEVEL = 48, + SPELL_FAILED_MAINHAND_EMPTY = 49, + SPELL_FAILED_MOVING = 50, + SPELL_FAILED_NEED_AMMO = 51, + SPELL_FAILED_NEED_AMMO_POUCH = 52, + SPELL_FAILED_NEED_EXOTIC_AMMO = 53, + SPELL_FAILED_NEED_MORE_ITEMS = 54, + SPELL_FAILED_NOPATH = 55, + SPELL_FAILED_NOT_BEHIND = 56, + SPELL_FAILED_NOT_FISHABLE = 57, + SPELL_FAILED_NOT_FLYING = 58, + SPELL_FAILED_NOT_HERE = 59, + SPELL_FAILED_NOT_INFRONT = 60, + SPELL_FAILED_NOT_IN_CONTROL = 61, + SPELL_FAILED_NOT_KNOWN = 62, + SPELL_FAILED_NOT_MOUNTED = 63, + SPELL_FAILED_NOT_ON_TAXI = 64, + SPELL_FAILED_NOT_ON_TRANSPORT = 65, + SPELL_FAILED_NOT_READY = 66, + SPELL_FAILED_NOT_SHAPESHIFT = 67, + SPELL_FAILED_NOT_STANDING = 68, + SPELL_FAILED_NOT_TRADEABLE = 69, + SPELL_FAILED_NOT_TRADING = 70, + SPELL_FAILED_NOT_UNSHEATHED = 71, + SPELL_FAILED_NOT_WHILE_GHOST = 72, + SPELL_FAILED_NOT_WHILE_LOOTING = 73, + SPELL_FAILED_NO_AMMO = 74, + SPELL_FAILED_NO_CHARGES_REMAIN = 75, + SPELL_FAILED_NO_CHAMPION = 76, + SPELL_FAILED_NO_COMBO_POINTS = 77, + SPELL_FAILED_NO_DUELING = 78, + SPELL_FAILED_NO_ENDURANCE = 79, + SPELL_FAILED_NO_FISH = 80, + SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED = 81, + SPELL_FAILED_NO_MOUNTS_ALLOWED = 82, + SPELL_FAILED_NO_PET = 83, + SPELL_FAILED_NO_POWER = 84, + SPELL_FAILED_NOTHING_TO_DISPEL = 85, + SPELL_FAILED_NOTHING_TO_STEAL = 86, + SPELL_FAILED_ONLY_ABOVEWATER = 87, + SPELL_FAILED_ONLY_DAYTIME = 88, + SPELL_FAILED_ONLY_INDOORS = 89, + SPELL_FAILED_ONLY_MOUNTED = 90, + SPELL_FAILED_ONLY_NIGHTTIME = 91, + SPELL_FAILED_ONLY_OUTDOORS = 92, + SPELL_FAILED_ONLY_SHAPESHIFT = 93, + SPELL_FAILED_ONLY_STEALTHED = 94, + SPELL_FAILED_ONLY_UNDERWATER = 95, + SPELL_FAILED_OUT_OF_RANGE = 96, + SPELL_FAILED_PACIFIED = 97, + SPELL_FAILED_POSSESSED = 98, + SPELL_FAILED_REAGENTS = 99, + SPELL_FAILED_REQUIRES_AREA = 100, + SPELL_FAILED_REQUIRES_SPELL_FOCUS = 101, + SPELL_FAILED_ROOTED = 102, + SPELL_FAILED_SILENCED = 103, + SPELL_FAILED_SPELL_IN_PROGRESS = 104, + SPELL_FAILED_SPELL_LEARNED = 105, + SPELL_FAILED_SPELL_UNAVAILABLE = 106, + SPELL_FAILED_STUNNED = 107, + SPELL_FAILED_TARGETS_DEAD = 108, + SPELL_FAILED_TARGET_AFFECTING_COMBAT = 109, + SPELL_FAILED_TARGET_AURASTATE = 110, + SPELL_FAILED_TARGET_DUELING = 111, + SPELL_FAILED_TARGET_ENEMY = 112, + SPELL_FAILED_TARGET_ENRAGED = 113, + SPELL_FAILED_TARGET_FRIENDLY = 114, + SPELL_FAILED_TARGET_IN_COMBAT = 115, + SPELL_FAILED_TARGET_IS_PLAYER = 116, + SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED = 117, + SPELL_FAILED_TARGET_NOT_DEAD = 118, + SPELL_FAILED_TARGET_NOT_IN_PARTY = 119, + SPELL_FAILED_TARGET_NOT_LOOTED = 120, + SPELL_FAILED_TARGET_NOT_PLAYER = 121, + SPELL_FAILED_TARGET_NO_POCKETS = 122, + SPELL_FAILED_TARGET_NO_WEAPONS = 123, + SPELL_FAILED_TARGET_NO_RANGED_WEAPONS = 124, + SPELL_FAILED_TARGET_UNSKINNABLE = 125, + SPELL_FAILED_THIRST_SATIATED = 126, + SPELL_FAILED_TOO_CLOSE = 127, + SPELL_FAILED_TOO_MANY_OF_ITEM = 128, + SPELL_FAILED_TOTEM_CATEGORY = 129, + SPELL_FAILED_TOTEMS = 130, + SPELL_FAILED_TRY_AGAIN = 131, + SPELL_FAILED_UNIT_NOT_BEHIND = 132, + SPELL_FAILED_UNIT_NOT_INFRONT = 133, + SPELL_FAILED_WRONG_PET_FOOD = 134, + SPELL_FAILED_NOT_WHILE_FATIGUED = 135, + SPELL_FAILED_TARGET_NOT_IN_INSTANCE = 136, + SPELL_FAILED_NOT_WHILE_TRADING = 137, + SPELL_FAILED_TARGET_NOT_IN_RAID = 138, + SPELL_FAILED_TARGET_FREEFORALL = 139, + SPELL_FAILED_NO_EDIBLE_CORPSES = 140, + SPELL_FAILED_ONLY_BATTLEGROUNDS = 141, + SPELL_FAILED_TARGET_NOT_GHOST = 142, + SPELL_FAILED_TRANSFORM_UNUSABLE = 143, + SPELL_FAILED_WRONG_WEATHER = 144, + SPELL_FAILED_DAMAGE_IMMUNE = 145, + SPELL_FAILED_PREVENTED_BY_MECHANIC = 146, + SPELL_FAILED_PLAY_TIME = 147, + SPELL_FAILED_REPUTATION = 148, + SPELL_FAILED_MIN_SKILL = 149, + SPELL_FAILED_NOT_IN_ARENA = 150, + SPELL_FAILED_NOT_ON_SHAPESHIFT = 151, + SPELL_FAILED_NOT_ON_STEALTHED = 152, + SPELL_FAILED_NOT_ON_DAMAGE_IMMUNE = 153, + SPELL_FAILED_NOT_ON_MOUNTED = 154, + SPELL_FAILED_TOO_SHALLOW = 155, + SPELL_FAILED_TARGET_NOT_IN_SANCTUARY = 156, + SPELL_FAILED_TARGET_IS_TRIVIAL = 157, + SPELL_FAILED_BM_OR_INVISGOD = 158, + SPELL_FAILED_EXPERT_RIDING_REQUIREMENT = 159, + SPELL_FAILED_ARTISAN_RIDING_REQUIREMENT = 160, + SPELL_FAILED_NOT_IDLE = 161, + SPELL_FAILED_NOT_INACTIVE = 162, + SPELL_FAILED_PARTIAL_PLAYTIME = 163, + SPELL_FAILED_NO_PLAYTIME = 164, + SPELL_FAILED_NOT_IN_BATTLEGROUND = 165, + SPELL_FAILED_NOT_IN_RAID_INSTANCE = 166, + SPELL_FAILED_ONLY_IN_ARENA = 167, + SPELL_FAILED_TARGET_LOCKED_TO_RAID_INSTANCE = 168, + SPELL_FAILED_ON_USE_ENCHANT = 169, + SPELL_FAILED_NOT_ON_GROUND = 170, + SPELL_FAILED_CUSTOM_ERROR = 171, + SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW = 172, + SPELL_FAILED_TOO_MANY_SOCKETS = 173, + SPELL_FAILED_INVALID_GLYPH = 174, + SPELL_FAILED_UNIQUE_GLYPH = 175, + SPELL_FAILED_GLYPH_SOCKET_LOCKED = 176, + SPELL_FAILED_NO_VALID_TARGETS = 177, + SPELL_FAILED_ITEM_AT_MAX_CHARGES = 178, + SPELL_FAILED_NOT_IN_BARBERSHOP = 179, + SPELL_FAILED_FISHING_TOO_LOW = 180, + SPELL_FAILED_UNKNOWN = 181, + + SPELL_CAST_OK = 255 //custom value, don't must be send to client +}; + // Spell aura states enum AuraState { // (C) used in caster aura state (T) used in target aura state @@ -841,10 +1029,11 @@ enum Targets TARGET_SINGLE_FRIEND_2 = 57, TARGET_AREAEFFECT_PARTY_AND_CLASS = 61, TARGET_DUELVSPLAYER_COORDINATES = 63, - TARGET_BEHIND_VICTIM = 65, // uses in teleport behind spells + TARGET_BEHIND_VICTIM = 65, // uses in teleport behind spells, caster/target dependent from spell effect TARGET_DYNAMIC_OBJECT_COORDINATES = 76, TARGET_SINGLE_ENEMY = 77, TARGET_SELF2 = 87, + TARGET_DIRECTLY_FORWARD = 89, TARGET_NONCOMBAT_PET = 90, }; @@ -1698,6 +1887,8 @@ inline uint8 ClassByQuestSort(int32 QuestSort) enum SkillType { + SKILL_NONE = 0, + SKILL_FROST = 6, SKILL_FIRE = 8, SKILL_ARMS = 26, @@ -1852,6 +2043,20 @@ enum SkillType #define MAX_SKILL_TYPE 789 +inline SkillType SkillByLockType(LockType locktype) +{ + switch(locktype) + { + case LOCKTYPE_PICKLOCK: return SKILL_LOCKPICKING; + case LOCKTYPE_HERBALISM: return SKILL_HERBALISM; + case LOCKTYPE_MINING: return SKILL_MINING; + case LOCKTYPE_FISHING: return SKILL_FISHING; + case LOCKTYPE_INSCRIPTION: return SKILL_INSCRIPTION; + default: break; + } + return SKILL_NONE; +} + inline uint32 SkillByQuestSort(int32 QuestSort) { switch(QuestSort) diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp index 1ce483bfb..317fc3250 100644 --- a/src/game/Spell.cpp +++ b/src/game/Spell.cpp @@ -49,6 +49,31 @@ extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; +class PrioritizeManaPlayerWraper +{ + friend struct PrioritizeMana; + + public: + explicit PrioritizeManaPlayerWraper(Player* player) : player(player) + { + uint32 maxmana = player->GetMaxPower(POWER_MANA); + percentMana = maxmana ? player->GetPower(POWER_MANA) * 100 / maxmana : 101; + } + Player* getPlayer() const { return player; } + private: + Player* player; + uint32 percentMana; +}; + +struct PrioritizeMana +{ + int operator()( PrioritizeManaPlayerWraper const& x, PrioritizeManaPlayerWraper const& y ) const + { + return x.percentMana < y.percentMana; + } +}; + + bool IsQuestTameSpell(uint32 spellId) { SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId); @@ -409,7 +434,7 @@ void Spell::FillTargetMap() if(m_spellInfo->Effect[i]==0) continue; - // targets for TARGET_SCRIPT_COORDINATES (A) and TARGET_SCRIPT filled in Spell::canCast call + // targets for TARGET_SCRIPT_COORDINATES (A) and TARGET_SCRIPT filled in Spell::CheckCast call if( m_spellInfo->EffectImplicitTargetA[i] == TARGET_SCRIPT_COORDINATES || m_spellInfo->EffectImplicitTargetA[i] == TARGET_SCRIPT || m_spellInfo->EffectImplicitTargetB[i] == TARGET_SCRIPT && m_spellInfo->EffectImplicitTargetA[i] != TARGET_SELF ) @@ -426,6 +451,21 @@ void Spell::FillTargetMap() // but need it support in some know cases switch(m_spellInfo->EffectImplicitTargetA[i]) { + case TARGET_SELF: + switch(m_spellInfo->EffectImplicitTargetB[i]) + { + case 0: + SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap); + break; + case TARGET_BEHIND_VICTIM: // use B case that not dependent from from A in fact + SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap); + break; + default: + SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap); + SetTargetMap(i,m_spellInfo->EffectImplicitTargetB[i],tmpUnitMap); + break; + } + break; case TARGET_CASTER_COORDINATES: // Note: this hack with search required until GO casting not implemented // environment damage spells already have around enemies targeting but this not help in case not existed GO casting support @@ -448,7 +488,10 @@ void Spell::FillTargetMap() default: switch(m_spellInfo->EffectImplicitTargetB[i]) { - case TARGET_SCRIPT_COORDINATES: // B case filled in canCast but we need fill unit list base at A case + case 0: + SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap); + break; + case TARGET_SCRIPT_COORDINATES: // B case filled in CheckCast but we need fill unit list base at A case SetTargetMap(i,m_spellInfo->EffectImplicitTargetA[i],tmpUnitMap); break; default: @@ -1582,7 +1625,6 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list &TagUnitMap) case TARGET_ALL_PARTY_AROUND_CASTER: case TARGET_ALL_PARTY_AROUND_CASTER_2: case TARGET_ALL_PARTY: - case TARGET_ALL_RAID_AROUND_CASTER: { Player *pTarget = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself(); Group *pGroup = pTarget ? pTarget->GetGroup() : NULL; @@ -1596,9 +1638,7 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list &TagUnitMap) Player* Target = itr->getSource(); // IsHostileTo check duel and controlled by enemy - if( Target && - (cur==TARGET_ALL_RAID_AROUND_CASTER || Target->GetSubGroup()==subgroup) && - !m_caster->IsHostileTo(Target) ) + if( Target && Target->GetSubGroup()==subgroup && !m_caster->IsHostileTo(Target) ) { if( m_caster->IsWithinDistInMap(Target, radius) ) TagUnitMap.push_back(Target); @@ -1618,7 +1658,81 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list &TagUnitMap) if( m_caster->IsWithinDistInMap(pet, radius) ) TagUnitMap.push_back(pet); } - }break; + break; + } + case TARGET_ALL_RAID_AROUND_CASTER: + { + Player *pTarget = m_caster->GetCharmerOrOwnerPlayerOrPlayerItself(); + Group *pGroup = pTarget ? pTarget->GetGroup() : NULL; + + if(m_spellInfo->Id==57669) //Replenishment (special target selection) + { + if(pGroup) + { + typedef std::priority_queue, PrioritizeMana> Top10; + Top10 manaUsers; + + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL && manaUsers.size() < 10; itr = itr->next()) + { + Player* Target = itr->getSource(); + if (m_caster->GetGUID() != Target->GetGUID() && Target->getPowerType() == POWER_MANA && + !Target->isDead() && m_caster->IsWithinDistInMap(Target, radius)) + { + PrioritizeManaPlayerWraper WTarget(Target); + manaUsers.push(WTarget); + } + } + + while(!manaUsers.empty()) + { + TagUnitMap.push_back(manaUsers.top().getPlayer()); + manaUsers.pop(); + } + } + else + { + Unit* ownerOrSelf = pTarget ? pTarget : m_caster->GetCharmerOrOwnerOrSelf(); + if ((ownerOrSelf==m_caster || m_caster->IsWithinDistInMap(ownerOrSelf, radius)) && + ownerOrSelf->getPowerType() == POWER_MANA) + TagUnitMap.push_back(ownerOrSelf); + + if(Pet* pet = ownerOrSelf->GetPet()) + if( m_caster->IsWithinDistInMap(pet, radius) && pet->getPowerType() == POWER_MANA ) + TagUnitMap.push_back(pet); + } + } + else + { + if(pGroup) + { + for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next()) + { + Player* Target = itr->getSource(); + + // IsHostileTo check duel and controlled by enemy + if( Target && !m_caster->IsHostileTo(Target) ) + { + if( m_caster->IsWithinDistInMap(Target, radius) ) + TagUnitMap.push_back(Target); + + if(Pet* pet = Target->GetPet()) + if( m_caster->IsWithinDistInMap(pet, radius) ) + TagUnitMap.push_back(pet); + } + } + } + else + { + Unit* ownerOrSelf = pTarget ? pTarget : m_caster->GetCharmerOrOwnerOrSelf(); + if(ownerOrSelf==m_caster || m_caster->IsWithinDistInMap(ownerOrSelf, radius)) + TagUnitMap.push_back(ownerOrSelf); + if(Pet* pet = ownerOrSelf->GetPet()) + if( m_caster->IsWithinDistInMap(pet, radius) ) + TagUnitMap.push_back(pet); + } + } + break; + } case TARGET_SINGLE_FRIEND: case TARGET_SINGLE_FRIEND_2: { @@ -2048,8 +2162,15 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list &TagUnitMap) }break; case TARGET_BEHIND_VICTIM: { - Unit *pTarget = m_caster->getVictim(); - if(!pTarget && m_caster->GetTypeId() == TYPEID_PLAYER) + Unit *pTarget = NULL; + + // explicit cast data from client or server-side cast + // some spell at client send caster + if(m_targets.getUnitTarget() && m_targets.getUnitTarget()!=m_caster) + pTarget = m_targets.getUnitTarget(); + else if(m_caster->getVictim()) + pTarget = m_caster->getVictim(); + else if(m_caster->GetTypeId() == TYPEID_PLAYER) pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection()); if(pTarget) @@ -2057,15 +2178,37 @@ void Spell::SetTargetMap(uint32 i,uint32 cur,std::list &TagUnitMap) float _target_x, _target_y, _target_z; pTarget->GetClosePoint(_target_x, _target_y, _target_z, m_caster->GetObjectSize(), CONTACT_DISTANCE, M_PI); if(pTarget->IsWithinLOS(_target_x,_target_y,_target_z)) + { + TagUnitMap.push_back(m_caster); m_targets.setDestination(_target_x, _target_y, _target_z); + } } - }break; + break; + } case TARGET_DYNAMIC_OBJECT_COORDINATES: { // if parent spell create dynamic object extract area from it if(DynamicObject* dynObj = m_caster->GetDynObject(m_triggeredByAuraSpell ? m_triggeredByAuraSpell->Id : m_spellInfo->Id)) m_targets.setDestination(dynObj->GetPositionX(), dynObj->GetPositionY(), dynObj->GetPositionZ()); - }break; + break; + } + case TARGET_DIRECTLY_FORWARD: + { + if (!(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)) + { + SpellRangeEntry const* rEntry = sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex); + float minRange = GetSpellMinRange(rEntry); + float maxRange = GetSpellMaxRange(rEntry); + float dist = minRange+ rand_norm()*(maxRange-minRange); + + float _target_x, _target_y, _target_z; + m_caster->GetClosePoint(_target_x, _target_y, _target_z, m_caster->GetObjectSize(), dist); + m_targets.setDestination(_target_x, _target_y, _target_z); + } + + TagUnitMap.push_back(m_caster); + break; + } default: break; } @@ -2135,8 +2278,8 @@ void Spell::prepare(SpellCastTargets const* targets, Aura* triggeredByAura) // Fill cost data m_powerCost = CalculatePowerCost(); - uint8 result = CanCast(true); - if(result != 0 && !IsAutoRepeat()) //always cast autorepeat dummy for triggering + SpellCastResult result = CheckCast(true); + if(result != SPELL_CAST_OK && !IsAutoRepeat()) //always cast autorepeat dummy for triggering { if(triggeredByAura) { @@ -2151,7 +2294,7 @@ void Spell::prepare(SpellCastTargets const* targets, Aura* triggeredByAura) // Prepare data for triggers prepareDataForTriggerSystem(); - // calculate cast time (calculated after first CanCast check to prevent charge counting for first CanCast fail) + // calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail) m_casttime = GetSpellCastTime(m_spellInfo, this); // set timer base at cast time @@ -2222,8 +2365,6 @@ void Spell::cast(bool skipCheck) { SetExecutedCurrently(true); - uint8 castResult = 0; - // update pointers base at GUIDs to prevent access to non-existed already object UpdatePointers(); @@ -2238,8 +2379,8 @@ void Spell::cast(bool skipCheck) if(m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster) m_caster->SetInFront(m_targets.getUnitTarget()); - castResult = CheckPower(); - if(castResult != 0) + SpellCastResult castResult = CheckPower(); + if(castResult != SPELL_CAST_OK) { SendCastResult(castResult); finish(false); @@ -2250,8 +2391,8 @@ void Spell::cast(bool skipCheck) // triggered cast called from Spell::prepare where it was already checked if(!skipCheck) { - castResult = CanCast(false); - if(castResult != 0) + castResult = CheckCast(false); + if(castResult != SPELL_CAST_OK) { SendCastResult(castResult); finish(false); @@ -2520,12 +2661,11 @@ void Spell::SendSpellCooldown() Player* _player = (Player*)m_caster; - // mana/health potions, disabled by client - if (m_spellInfo->Category==SPELLCATEGORY_HEALTH_MANA_POTIONS) + // mana/health/etc potions, disabled by client (until combat out as declarate) + if (m_CastItem && m_CastItem->IsPotion()) { // need in some way provided data for Spell::finish SendCooldownEvent - if(m_CastItem) - _player->SetLastPotionId(m_CastItem->GetEntry()); + _player->SetLastPotionId(m_CastItem->GetEntry()); return; } @@ -2724,8 +2864,8 @@ void Spell::finish(bool ok) ((Player*)m_caster)->ClearComboPoints(); } - // mana/health potions, disabled by client, send event "not in combat" - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->Category == SPELLCATEGORY_HEALTH_MANA_POTIONS) + // potions disabled by client, send event "not in combat" if need + if (m_caster->GetTypeId() == TYPEID_PLAYER) ((Player*)m_caster)->UpdatePotionCooldown(this); // call triggered spell only at successful cast (after clear combo points -> for add some if need) @@ -2737,65 +2877,65 @@ void Spell::finish(bool ok) m_caster->AttackStop(); } -void Spell::SendCastResult(uint8 result) +void Spell::SendCastResult(SpellCastResult result) { + if(result == SPELL_CAST_OK) + return; + if (m_caster->GetTypeId() != TYPEID_PLAYER) return; if(((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time return; - if(result != 0) + WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); + data << uint8(m_cast_count); // single cast or multi 2.3 (0/1) + data << uint32(m_spellInfo->Id); + data << uint8(result); // problem + switch (result) { - WorldPacket data(SMSG_CAST_FAILED, (4+1+1)); - data << uint8(m_cast_count); // single cast or multi 2.3 (0/1) - data << uint32(m_spellInfo->Id); - data << uint8(result); // problem - switch (result) - { - case SPELL_FAILED_REQUIRES_SPELL_FOCUS: - data << uint32(m_spellInfo->RequiresSpellFocus); - break; - case SPELL_FAILED_REQUIRES_AREA: - // hardcode areas limitation case - switch(m_spellInfo->Id) - { - case 41617: // Cenarion Mana Salve - case 41619: // Cenarion Healing Salve - data << uint32(3905); - break; - case 41618: // Bottled Nethergon Energy - case 41620: // Bottled Nethergon Vapor - data << uint32(3842); - break; - case 45373: // Bloodberry Elixir - data << uint32(4075); - break; - default: // default case (don't must be) - data << uint32(0); - break; - } - break; - case SPELL_FAILED_TOTEMS: - if(m_spellInfo->Totem[0]) - data << uint32(m_spellInfo->Totem[0]); - if(m_spellInfo->Totem[1]) - data << uint32(m_spellInfo->Totem[1]); - break; - case SPELL_FAILED_TOTEM_CATEGORY: - if(m_spellInfo->TotemCategory[0]) - data << uint32(m_spellInfo->TotemCategory[0]); - if(m_spellInfo->TotemCategory[1]) - data << uint32(m_spellInfo->TotemCategory[1]); - break; - case SPELL_FAILED_EQUIPPED_ITEM_CLASS: - data << uint32(m_spellInfo->EquippedItemClass); - data << uint32(m_spellInfo->EquippedItemSubClassMask); - //data << uint32(m_spellInfo->EquippedItemInventoryTypeMask); - break; - } - ((Player*)m_caster)->GetSession()->SendPacket(&data); + case SPELL_FAILED_REQUIRES_SPELL_FOCUS: + data << uint32(m_spellInfo->RequiresSpellFocus); + break; + case SPELL_FAILED_REQUIRES_AREA: + // hardcode areas limitation case + switch(m_spellInfo->Id) + { + case 41617: // Cenarion Mana Salve + case 41619: // Cenarion Healing Salve + data << uint32(3905); + break; + case 41618: // Bottled Nethergon Energy + case 41620: // Bottled Nethergon Vapor + data << uint32(3842); + break; + case 45373: // Bloodberry Elixir + data << uint32(4075); + break; + default: // default case (don't must be) + data << uint32(0); + break; + } + break; + case SPELL_FAILED_TOTEMS: + if(m_spellInfo->Totem[0]) + data << uint32(m_spellInfo->Totem[0]); + if(m_spellInfo->Totem[1]) + data << uint32(m_spellInfo->Totem[1]); + break; + case SPELL_FAILED_TOTEM_CATEGORY: + if(m_spellInfo->TotemCategory[0]) + data << uint32(m_spellInfo->TotemCategory[0]); + if(m_spellInfo->TotemCategory[1]) + data << uint32(m_spellInfo->TotemCategory[1]); + break; + case SPELL_FAILED_EQUIPPED_ITEM_CLASS: + data << uint32(m_spellInfo->EquippedItemClass); + data << uint32(m_spellInfo->EquippedItemSubClassMask); + //data << uint32(m_spellInfo->EquippedItemInventoryTypeMask); + break; } + ((Player*)m_caster)->GetSession()->SendPacket(&data); } void Spell::SendSpellStart() @@ -3328,30 +3468,28 @@ void Spell::TakePower() m_caster->SetLastManaUse(getMSTime()); } -uint8 Spell::CheckRuneCost(uint32 runeCostID) +SpellCastResult Spell::CheckRuneCost(uint32 runeCostID) { if(m_caster->GetTypeId() != TYPEID_PLAYER) - return 0; + return SPELL_CAST_OK; Player *plr = (Player*)m_caster; if(plr->getClass() != CLASS_DEATH_KNIGHT) - return 0; + return SPELL_CAST_OK; SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(runeCostID); if(!src) - return 0; + return SPELL_CAST_OK; if(src->NoRuneCost()) - return 0; + return SPELL_CAST_OK; int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death for(uint32 i = 0; i < RUNE_DEATH; ++i) - { runeCost[i] = src->RuneCost[i]; - } runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later @@ -3359,23 +3497,17 @@ uint8 Spell::CheckRuneCost(uint32 runeCostID) { uint8 rune = plr->GetCurrentRune(i); if((plr->GetRuneCooldown(i) == 0) && (runeCost[rune] > 0)) - { runeCost[rune]--; - } } for(uint32 i = 0; i < RUNE_DEATH; ++i) - { if(runeCost[i] > 0) - { runeCost[RUNE_DEATH] += runeCost[i]; - } - } if(runeCost[RUNE_DEATH] > MAX_RUNES) return SPELL_FAILED_NO_POWER; // not sure if result code is correct - return 0; + return SPELL_CAST_OK; } void Spell::TakeRunePower() @@ -3545,7 +3677,7 @@ void Spell::TriggerSpell() } } -uint8 Spell::CanCast(bool strict) +SpellCastResult Spell::CheckCast(bool strict) { // check cooldowns to prevent cheating if(m_caster->GetTypeId()==TYPEID_PLAYER && ((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id)) @@ -3579,7 +3711,8 @@ uint8 Spell::CanCast(bool strict) if (checkForm) { // Cannot be used in this stance/form - if(uint8 shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form)) + SpellCastResult shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form); + if(shapeError != SPELL_CAST_OK) return shapeError; if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura())) @@ -3609,9 +3742,7 @@ uint8 Spell::CanCast(bool strict) return SPELL_FAILED_MOVING; } - Unit *target = m_targets.getUnitTarget(); - - if(target) + if(Unit *target = m_targets.getUnitTarget()) { // target state requirements (not allowed state), apply to self also if(m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot))) @@ -3639,14 +3770,10 @@ uint8 Spell::CanCast(bool strict) // auto selection spell rank implemented in WorldSession::HandleCastSpellOpcode // this case can be triggered if rank not found (too low-level target for first rank) if(m_caster->GetTypeId() == TYPEID_PLAYER && !IsPassiveSpell(m_spellInfo->Id) && !m_CastItem) - { for(int i=0;i<3;i++) - { if(IsPositiveEffect(m_spellInfo->Id, i) && m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA) if(target->getLevel() + 10 < m_spellInfo->spellLevel) return SPELL_FAILED_LOWLEVEL; - } - } } else if (m_caster->GetTypeId()==TYPEID_PLAYER) // Target - is player caster { @@ -3711,10 +3838,8 @@ uint8 Spell::CanCast(bool strict) } if(IsPositiveSpell(m_spellInfo->Id)) - { if(target->IsImmunedToSpell(m_spellInfo)) return SPELL_FAILED_TARGET_AURASTATE; - } //Must be behind the target. if( m_spellInfo->AttributesEx2 == 0x100000 && (m_spellInfo->AttributesEx & 0x200) == 0x200 && target->HasInArc(M_PI, m_caster) ) @@ -3736,10 +3861,9 @@ uint8 Spell::CanCast(bool strict) // check if target is in combat if (target != m_caster && (m_spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_IN_COMBAT_TARGET) && target->isInCombat()) - { return SPELL_FAILED_TARGET_AFFECTING_COMBAT; - } } + // Spell casted only on battleground if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_BATTLEGROUND) && m_caster->GetTypeId()==TYPEID_PLAYER) if(!((Player*)m_caster)->InBattleGround()) @@ -3758,9 +3882,10 @@ uint8 Spell::CanCast(bool strict) uint32 zone, area; m_caster->GetZoneAndAreaId(zone,area); - if (uint8 res= spellmgr.GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),zone,area, - m_caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)m_caster) : NULL)) - return res; + SpellCastResult locRes= spellmgr.GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),zone,area, + m_caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)m_caster) : NULL); + if(locRes != SPELL_CAST_OK) + return locRes; // not let players cast spells at mount (and let do it to creatures) if( m_caster->IsMounted() && m_caster->GetTypeId()==TYPEID_PLAYER && !m_IsTriggeredSpell && @@ -3774,11 +3899,14 @@ uint8 Spell::CanCast(bool strict) // always (except passive spells) check items (focus object can be required for any type casts) if(!IsPassiveSpell(m_spellInfo->Id)) - if(uint8 castResult = CheckItems()) + { + SpellCastResult castResult = CheckItems(); + if(castResult != SPELL_CAST_OK) return castResult; + } //ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38 - if(m_UniqueTargetInfo.empty()) // skip second canCast apply (for delayed spells for example) + if(m_UniqueTargetInfo.empty()) // skip second CheckCast apply (for delayed spells for example) { for(uint8 j = 0; j < 3; j++) { @@ -3913,17 +4041,24 @@ uint8 Spell::CanCast(bool strict) } if(!m_IsTriggeredSpell) - if(uint8 castResult = CheckRange(strict)) + { + SpellCastResult castResult = CheckRange(strict); + if(castResult != SPELL_CAST_OK) return castResult; + } { - if(uint8 castResult = CheckPower()) + SpellCastResult castResult = CheckPower(); + if(castResult != SPELL_CAST_OK) return castResult; } if(!m_IsTriggeredSpell) // triggered spell not affected by stun/etc - if(uint8 castResult = CheckCasterAuras()) + { + SpellCastResult castResult = CheckCasterAuras(); + if(castResult != SPELL_CAST_OK) return castResult; + } for (int i = 0; i < 3; i++) { @@ -3946,7 +4081,7 @@ uint8 Spell::CanCast(bool strict) { // spell different for friends and enemies // hart version required facing - if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, target )) + if(m_targets.getUnitTarget() && !m_caster->IsFriendlyTo(m_targets.getUnitTarget()) && !m_caster->HasInArc( M_PI, m_targets.getUnitTarget() )) return SPELL_FAILED_UNIT_NOT_INFRONT; } break; @@ -4114,120 +4249,31 @@ uint8 Spell::CanCast(bool strict) return SPELL_FAILED_TRY_AGAIN; // get the lock entry - LockEntry const *lockInfo = NULL; + uint32 lockId = 0; if (GameObject* go=m_targets.getGOTarget()) - lockInfo = sLockStore.LookupEntry(go->GetLockId()); + lockId = go->GetLockId(); else if(Item* itm=m_targets.getItemTarget()) - lockInfo = sLockStore.LookupEntry(itm->GetProto()->LockID); + lockId = itm->GetProto()->LockID; + + SkillType skillId =SKILL_NONE; + int32 reqSkillValue = 0; + int32 skillValue = 0; // check lock compatibility - if (lockInfo) - { - // check for lock - key pair (checked by client also, just prevent cheating - bool ok_key = false; - for(int it = 0; it < 8; ++it) - { - switch(lockInfo->Type[it]) - { - case LOCK_KEY_NONE: - break; - case LOCK_KEY_ITEM: - { - if(lockInfo->Index[it]) - { - if(m_CastItem && m_CastItem->GetEntry()==lockInfo->Index[it]) - ok_key =true; - break; - } - } - case LOCK_KEY_SKILL: - { - if(uint32(m_spellInfo->EffectMiscValue[i])!=lockInfo->Index[it]) - break; - - switch(lockInfo->Index[it]) - { - case LOCKTYPE_HERBALISM: - if(((Player*)m_caster)->HasSkill(SKILL_HERBALISM)) - ok_key =true; - break; - case LOCKTYPE_MINING: - if(((Player*)m_caster)->HasSkill(SKILL_MINING)) - ok_key =true; - break; - default: - ok_key =true; - break; - } - } - } - if(ok_key) - break; - } - - if(!ok_key) - return SPELL_FAILED_BAD_TARGETS; - } + SpellCastResult res = CanOpenLock(i,lockId,skillId,reqSkillValue,skillValue); + if(res != SPELL_CAST_OK) + return res; // chance for fail at orange mining/herb/LockPicking gathering attempt - if (!m_selfContainer || ((*m_selfContainer) != this)) - break; - - // get the skill value of the player - int32 SkillValue = 0; - bool canFailAtMax = true; - if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_HERBALISM) + // second check prevent fail at rechecks + if(skillId != SKILL_NONE && (!m_selfContainer || ((*m_selfContainer) != this))) { - SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_HERBALISM); - canFailAtMax = false; + bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING; + + // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill) + if((canFailAtMax || skillValue < sWorld.GetConfigMaxSkillValue()) && reqSkillValue > irand(skillValue-25, skillValue+37)) + return SPELL_FAILED_TRY_AGAIN; } - else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_MINING) - { - SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_MINING); - canFailAtMax = false; - } - else if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK) - SkillValue = ((Player*)m_caster)->GetSkillValue(SKILL_LOCKPICKING); - - // castitem check: rogue using skeleton keys. the skill values should not be added in this case. - if(m_CastItem) - SkillValue = 0; - - // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value) - SkillValue += m_currentBasePoints[i]+1; - - // get the required lock value - int32 ReqValue=0; - if (lockInfo) - { - // check for lock - key pair - bool ok = false; - for(int it = 0; it < 8; ++it) - { - if(lockInfo->Type[it]==LOCK_KEY_ITEM && lockInfo->Index[it] && m_CastItem && m_CastItem->GetEntry()==lockInfo->Index[it]) - { - // if so, we're good to go - ok = true; - break; - } - } - if(ok) - break; - - if (m_spellInfo->EffectMiscValue[i] == LOCKTYPE_PICKLOCK) - ReqValue = lockInfo->Skill[1]; - else - ReqValue = lockInfo->Skill[0]; - } - - // skill doesn't meet the required value - if (ReqValue > SkillValue) - return SPELL_FAILED_LOW_CASTLEVEL; - - // chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill) - if((canFailAtMax || SkillValue < sWorld.GetConfigMaxSkillValue()) && ReqValue > irand(SkillValue-25, SkillValue+37)) - return SPELL_FAILED_TRY_AGAIN; - break; } case SPELL_EFFECT_SUMMON_DEAD_PET: @@ -4354,6 +4400,20 @@ uint8 Spell::CanCast(bool strict) { switch(m_spellInfo->EffectApplyAuraName[i]) { + case SPELL_AURA_DUMMY: + { + //custom check + switch(m_spellInfo->Id) + { + case 61336: + if(m_caster->GetTypeId()!=TYPEID_PLAYER || !((Player*)m_caster)->IsInFeralForm()) + return SPELL_FAILED_ONLY_SHAPESHIFT; + break; + default: + break; + } + break; + } case SPELL_AURA_MOD_POSSESS: case SPELL_AURA_MOD_CHARM: { @@ -4440,10 +4500,10 @@ uint8 Spell::CanCast(bool strict) } // all ok - return 0; + return SPELL_CAST_OK; } -int16 Spell::PetCanCast(Unit* target) +SpellCastResult Spell::CheckPetCast(Unit* target) { if(!m_caster->isAlive()) return SPELL_FAILED_CASTER_DEAD; @@ -4507,20 +4567,16 @@ int16 Spell::PetCanCast(Unit* target) return SPELL_FAILED_NOT_READY; } - uint16 result = CanCast(true); - if(result != 0) - return result; - else - return -1; //this allows to check spell fail 0, in combat + return CheckCast(true); } -uint8 Spell::CheckCasterAuras() const +SpellCastResult Spell::CheckCasterAuras() const { // Flag drop spells totally immuned to caster auras // FIXME: find more nice check for all totally immuned spells // AttributesEx3 & 0x10000000? if(m_spellInfo->Id==23336 || m_spellInfo->Id==23334 || m_spellInfo->Id==34991) - return 0; + return SPELL_CAST_OK; uint8 school_immune = 0; uint32 mechanic_immune = 0; @@ -4545,7 +4601,7 @@ uint8 Spell::CheckCasterAuras() const } //Check whether the cast should be prevented by any state you might have. - uint8 prevented_reason = 0; + SpellCastResult prevented_reason = SPELL_CAST_OK; // Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out uint32 unitflag = m_caster->GetUInt32Value(UNIT_FIELD_FLAGS); // Get unit state if(unitflag & UNIT_FLAG_STUNNED && !(m_spellInfo->AttributesEx5 & SPELL_ATTR_EX5_USABLE_WHILE_STUNNED)) @@ -4609,7 +4665,7 @@ uint8 Spell::CheckCasterAuras() const else return prevented_reason; } - return 0; // all ok + return SPELL_CAST_OK; } bool Spell::CanAutoCast(Unit* target) @@ -4638,9 +4694,9 @@ bool Spell::CanAutoCast(Unit* target) } } - int16 result = PetCanCast(target); + SpellCastResult result = CheckPetCast(target); - if(result == -1 || result == SPELL_FAILED_UNIT_NOT_INFRONT) + if(result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT) { FillTargetMap(); //check if among target units, our WANTED target is as well (->only self cast spells return false) @@ -4651,12 +4707,13 @@ bool Spell::CanAutoCast(Unit* target) return false; //target invalid } -uint8 Spell::CheckRange(bool strict) +SpellCastResult Spell::CheckRange(bool strict) { float range_mod; // self cast doesn't need range checking -- also for Starshards fix - if (m_spellInfo->rangeIndex == 1) return 0; + if (m_spellInfo->rangeIndex == 1) + return SPELL_CAST_OK; if (strict) //add radius of caster range_mod = 1.25; @@ -4694,7 +4751,7 @@ uint8 Spell::CheckRange(bool strict) return SPELL_FAILED_TOO_CLOSE; } - return 0; // ok + return SPELL_CAST_OK; } int32 Spell::CalculatePowerCost() @@ -4765,18 +4822,18 @@ int32 Spell::CalculatePowerCost() return powerCost; } -uint8 Spell::CheckPower() +SpellCastResult Spell::CheckPower() { // item cast not used power if(m_CastItem) - return 0; + return SPELL_CAST_OK; // health as power used - need check health amount if(m_spellInfo->powerType == POWER_HEALTH) { if(m_caster->GetHealth() <= m_powerCost) return SPELL_FAILED_CASTER_AURASTATE; - return 0; + return SPELL_CAST_OK; } // Check valid power type if( m_spellInfo->powerType >= MAX_POWERS ) @@ -4785,8 +4842,8 @@ uint8 Spell::CheckPower() return SPELL_FAILED_UNKNOWN; } - uint8 failReason = CheckRuneCost(m_spellInfo->runeCostID); - if(failReason) + SpellCastResult failReason = CheckRuneCost(m_spellInfo->runeCostID); + if(failReason != SPELL_CAST_OK) return failReason; // Check power amount @@ -4794,90 +4851,85 @@ uint8 Spell::CheckPower() if(m_caster->GetPower(powerType) < m_powerCost) return SPELL_FAILED_NO_POWER; else - return 0; + return SPELL_CAST_OK; } -uint8 Spell::CheckItems() +SpellCastResult Spell::CheckItems() { if (m_caster->GetTypeId() != TYPEID_PLAYER) - return 0; + return SPELL_CAST_OK; - uint32 itemid, itemcount; Player* p_caster = (Player*)m_caster; + // cast item checks if(m_CastItem) { - itemid = m_CastItem->GetEntry(); + uint32 itemid = m_CastItem->GetEntry(); if( !p_caster->HasItemCount(itemid,1) ) return SPELL_FAILED_ITEM_NOT_READY; - else + + ItemPrototype const *proto = m_CastItem->GetProto(); + if(!proto) + return SPELL_FAILED_ITEM_NOT_READY; + + for (int i = 0; i<5; i++) + if (proto->Spells[i].SpellCharges) + if(m_CastItem->GetSpellCharges(i)==0) + return SPELL_FAILED_NO_CHARGES_REMAIN; + + // consumable cast item checks + if (proto->Class == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget()) { - ItemPrototype const *proto = m_CastItem->GetProto(); - if(!proto) - return SPELL_FAILED_ITEM_NOT_READY; - - for (int i = 0; i<5; i++) + // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example + SpellCastResult failReason = SPELL_CAST_OK; + for (int i = 0; i < 3; i++) { - if (proto->Spells[i].SpellCharges) - { - if(m_CastItem->GetSpellCharges(i)==0) - return SPELL_FAILED_NO_CHARGES_REMAIN; - } - } + // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster + if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET) + continue; - uint32 ItemClass = proto->Class; - if (ItemClass == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget()) - { - // such items should only fail if there is no suitable effect at all - see Rejuvenation Potions for example - uint8 failReason = 0; - for (int i = 0; i < 3; i++) + if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL) { - // skip check, pet not required like checks, and for TARGET_PET m_targets.getUnitTarget() is not the real target but the caster - if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_PET) + if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth()) + { + failReason = SPELL_FAILED_ALREADY_AT_FULL_HEALTH; continue; - - if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL) - { - if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth()) - { - failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_HEALTH; - continue; - } - else - { - failReason = 0; - break; - } } - - // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... - if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE) + else { - if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) - { - failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER; - continue; - } - - Powers power = Powers(m_spellInfo->EffectMiscValue[i]); - if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power)) - { - failReason = (uint8)SPELL_FAILED_ALREADY_AT_FULL_POWER; - continue; - } - else - { - failReason = 0; - break; - } + failReason = SPELL_CAST_OK; + break; + } + } + + // Mana Potion, Rage Potion, Thistle Tea(Rogue), ... + if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE) + { + if(m_spellInfo->EffectMiscValue[i] < 0 || m_spellInfo->EffectMiscValue[i] >= MAX_POWERS) + { + failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER; + continue; + } + + Powers power = Powers(m_spellInfo->EffectMiscValue[i]); + if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power)) + { + failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER; + continue; + } + else + { + failReason = SPELL_CAST_OK; + break; } } - if (failReason) - return failReason; } + if (failReason != SPELL_CAST_OK) + return failReason; } } + // check target item if(m_targets.getItemTargetGUID()) { if(m_caster->GetTypeId() != TYPEID_PLAYER) @@ -4896,6 +4948,7 @@ uint8 Spell::CheckItems() return SPELL_FAILED_EQUIPPED_ITEM_CLASS; } + // check spell focus object if(m_spellInfo->RequiresSpellFocus) { CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY())); @@ -4911,11 +4964,12 @@ uint8 Spell::CheckItems() cell_lock->Visit(cell_lock, object_checker, *m_caster->GetMap()); if(!ok) - return (uint8)SPELL_FAILED_REQUIRES_SPELL_FOCUS; + return SPELL_FAILED_REQUIRES_SPELL_FOCUS; focusObject = ok; // game object found in range } + // check reagents if (!p_caster->CanNoReagentCast(m_spellInfo)) { for(uint32 i=0;i<8;i++) @@ -4923,8 +4977,8 @@ uint8 Spell::CheckItems() if(m_spellInfo->Reagent[i] <= 0) continue; - itemid = m_spellInfo->Reagent[i]; - itemcount = m_spellInfo->ReagentCount[i]; + uint32 itemid = m_spellInfo->Reagent[i]; + uint32 itemcount = m_spellInfo->ReagentCount[i]; // if CastItem is also spell reagent if( m_CastItem && m_CastItem->GetEntry() == itemid ) @@ -4944,10 +4998,11 @@ uint8 Spell::CheckItems() } } if( !p_caster->HasItemCount(itemid,itemcount) ) - return (uint8)SPELL_FAILED_ITEM_NOT_READY; //0x54 + return SPELL_FAILED_ITEM_NOT_READY; //0x54 } } + // check totem-item requirements (items presence in inventory) uint32 totems = 2; for(int i=0;i<2;++i) { @@ -4962,9 +5017,9 @@ uint8 Spell::CheckItems() totems -= 1; } if(totems != 0) - return (uint8)SPELL_FAILED_TOTEMS; //0x7C + return SPELL_FAILED_TOTEMS; //0x7C - //Check items for TotemCategory + // Check items for TotemCategory (items presence in inventory) uint32 TotemCategory = 2; for(int i=0;i<2;++i) { @@ -4980,8 +5035,9 @@ uint8 Spell::CheckItems() TotemCategory -= 1; } if(TotemCategory != 0) - return (uint8)SPELL_FAILED_TOTEM_CATEGORY; //0x7B + return SPELL_FAILED_TOTEM_CATEGORY; //0x7B + // special checks for spell effects for(int i = 0; i < 3; i++) { switch (m_spellInfo->Effect[i]) @@ -5183,7 +5239,7 @@ uint8 Spell::CheckItems() } } - return uint8(0); + return SPELL_CAST_OK; } void Spell::Delayed() @@ -5590,3 +5646,65 @@ bool SpellEvent::IsDeletable() const { return m_Spell->IsDeletable(); } + +SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue) +{ + if(!lockId) // possible case for GO and maybe for items. + return SPELL_CAST_OK; + + // Get LockInfo + LockEntry const *lockInfo = sLockStore.LookupEntry(lockId); + + if (!lockInfo) + return SPELL_FAILED_BAD_TARGETS; + + bool reqKey = false; // some locks not have reqs + + for(int j = 0; j < 8; ++j) + { + switch(lockInfo->Type[j]) + { + // check key item (many fit cases can be) + case LOCK_KEY_ITEM: + if(lockInfo->Index[j] && m_CastItem && m_CastItem->GetEntry()==lockInfo->Index[j]) + return SPELL_CAST_OK; + reqKey = true; + break; + // check key skill (only single first fit case can be) + case LOCK_KEY_SKILL: + { + reqKey = true; + + // wrong locktype, skip + if(uint32(m_spellInfo->EffectMiscValue[effIndex]) != lockInfo->Index[j]) + continue; + + skillId = SkillByLockType(LockType(lockInfo->Index[j])); + + if ( skillId != SKILL_NONE ) + { + // skill bonus provided by casting spell (mostly item spells) + // add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value) + uint32 spellSkillBonus = uint32(m_currentBasePoints[effIndex]+1); + reqSkillValue = lockInfo->Skill[j]; + + // castitem check: rogue using skeleton keys. the skill values should not be added in this case. + skillValue = m_CastItem || m_caster->GetTypeId()!= TYPEID_PLAYER ? + 0 : ((Player*)m_caster)->GetSkillValue(skillId); + + skillValue += spellSkillBonus; + + if (skillValue < reqSkillValue) + return SPELL_FAILED_LOW_CASTLEVEL; + } + + return SPELL_CAST_OK; + } + } + } + + if(reqKey) + return SPELL_FAILED_BAD_TARGETS; + + return SPELL_CAST_OK; +} diff --git a/src/game/Spell.h b/src/game/Spell.h index 2b70422ac..468ce8884 100644 --- a/src/game/Spell.h +++ b/src/game/Spell.h @@ -20,6 +20,7 @@ #define __SPELL_H #include "GridDefines.h" +#include "SharedDefines.h" class WorldSession; class Unit; @@ -249,6 +250,7 @@ class Spell void EffectSummonWild(uint32 i); void EffectSummonGuardian(uint32 i); void EffectHealMechanical(uint32 i); + void EffectJump(uint32 i); void EffectTeleUnitsFaceCaster(uint32 i); void EffectLearnSkill(uint32 i); void EffectAddHonor(uint32 i); @@ -331,14 +333,13 @@ class Spell void cast(bool skipCheck = false); void finish(bool ok = true); void TakePower(); - uint8 CheckRuneCost(uint32 runeCostID); void TakeRunePower(); void TakeReagents(); void TakeCastItem(); void TriggerSpell(); - uint8 CanCast(bool strict); - int16 PetCanCast(Unit* target); - bool CanAutoCast(Unit* target); + + SpellCastResult CheckCast(bool strict); + SpellCastResult CheckPetCast(Unit* target); // handlers void handle_immediate(); @@ -347,10 +348,11 @@ class Spell void _handle_immediate_phase(); void _handle_finish_phase(); - uint8 CheckItems(); - uint8 CheckRange(bool strict); - uint8 CheckPower(); - uint8 CheckCasterAuras() const; + SpellCastResult CheckItems(); + SpellCastResult CheckRange(bool strict); + SpellCastResult CheckPower(); + SpellCastResult CheckRuneCost(uint32 runeCostID); + SpellCastResult CheckCasterAuras() const; int32 CalculateDamage(uint8 i, Unit* target) { return m_caster->CalculateSpellDamage(m_spellInfo,i,m_currentBasePoints[i],target); } int32 CalculatePowerCost(); @@ -370,8 +372,9 @@ class Spell Unit* SelectMagnetTarget(); bool CheckTarget( Unit* target, uint32 eff ); + bool CanAutoCast(Unit* target); - void SendCastResult(uint8 result); + void SendCastResult(SpellCastResult result); void SendSpellStart(); void SendSpellGo(); void SendSpellCooldown(); @@ -546,6 +549,7 @@ class Spell void DoAllEffectOnTarget(GOTargetInfo *target); void DoAllEffectOnTarget(ItemTargetInfo *target); bool IsAliveUnitPresentInTargetList(); + SpellCastResult CanOpenLock(uint32 effIndex, uint32 lockid, SkillType& skillid, int32& reqSkillValue, int32& skillValue); // ------------------------------------------- //List For Triggered Spells diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp index 5574408f1..9c9e40eb3 100644 --- a/src/game/SpellAuras.cpp +++ b/src/game/SpellAuras.cpp @@ -39,6 +39,7 @@ #include "Formulas.h" #include "BattleGround.h" #include "CreatureAI.h" +#include "ScriptCalls.h" #include "Util.h" #include "GridNotifiers.h" #include "GridNotifiersImpl.h" @@ -1442,7 +1443,7 @@ void Aura::TriggerSpell() caster->ModifyPower( POWER_MANA, mana ); caster->SendEnergizeSpellLog(caster, 23493, mana, POWER_MANA); } - break; + return; } // // Stoneclaw Totem Passive TEST // case 23792: break; @@ -1510,8 +1511,11 @@ void Aura::TriggerSpell() // case 28522: break; // // Silithyst // case 29519: break; -// // Inoculate Nestlewood Owlkin - case 29528: trigger_spell_id = 28713; break; + // Inoculate Nestlewood Owlkin + case 29528: + if(target->GetTypeId()!=TYPEID_UNIT)// prevent error reports in case ignored player target + return; + break; // // Overload // case 29768: break; // // Return Fire @@ -1553,7 +1557,6 @@ void Aura::TriggerSpell() creature->SetHealth(0); // just for nice GM-mode view } return; - break; } // Quake case 30576: trigger_spell_id = 30571; break; @@ -1934,13 +1937,9 @@ void Aura::TriggerSpell() default: break; } + // Reget trigger spell proto triggeredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id); - if(triggeredSpellInfo == NULL) - { - sLog.outError("Aura::TriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",GetId(),GetEffIndex()); - return; - } } else { @@ -1992,8 +1991,12 @@ void Aura::TriggerSpell() } } } + // All ok cast by default case - caster->CastSpell(target, triggeredSpellInfo, true, 0, this); + if(triggeredSpellInfo) + caster->CastSpell(target, triggeredSpellInfo, true, 0, this); + else if(target->GetTypeId()!=TYPEID_UNIT || !Script->EffectDummyCreature(caster, GetId(), GetEffIndex(), (Creature*)target)) + sLog.outError("Aura::TriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",GetId(),GetEffIndex()); } void Aura::TriggerSpellWithValue() @@ -2261,6 +2264,45 @@ void Aura::HandleAuraDummy(bool apply, bool Real) } case SPELLFAMILY_DRUID: { + switch(GetId()) + { + case 34246: // Idol of the Emerald Queen + { + if (m_target->GetTypeId() != TYPEID_PLAYER) + return; + + if(apply) + { + SpellModifier *mod = new SpellModifier; + mod->op = SPELLMOD_DOT; + mod->value = m_modifier.m_amount/7; + mod->type = SPELLMOD_FLAT; + mod->spellId = GetId(); + mod->mask = 0x001000000000LL; + mod->mask2= 0LL; + + m_spellmod = mod; + } + + ((Player*)m_target)->AddSpellMod(m_spellmod, apply); + return; + } + case 61336: // Survival Instincts + { + if(apply) + { + if (!m_target->IsInFeralForm()) + return; + + int32 bp0 = int32(m_target->GetMaxHealth() * m_modifier.m_amount / 100); + m_target->CastCustomSpell(m_target, 50322, &bp0, NULL, NULL, true); + } + else + m_target-> RemoveAurasDueToSpell(50322); + return; + } + } + // Lifebloom if ( GetSpellProto()->SpellFamilyFlags & 0x1000000000LL ) { @@ -2300,25 +2342,6 @@ void Aura::HandleAuraDummy(bool apply, bool Real) ((Player*)m_target)->UpdateAttackPowerAndDamage(); return; } - // Idol of the Emerald Queen - if ( GetId() == 34246 && m_target->GetTypeId()==TYPEID_PLAYER ) - { - if(apply) - { - SpellModifier *mod = new SpellModifier; - mod->op = SPELLMOD_DOT; - mod->value = m_modifier.m_amount/7; - mod->type = SPELLMOD_FLAT; - mod->spellId = GetId(); - mod->mask = 0x001000000000LL; - mod->mask2= 0LL; - - m_spellmod = mod; - } - - ((Player*)m_target)->AddSpellMod(m_spellmod, apply); - return; - } break; } case SPELLFAMILY_HUNTER: @@ -3329,7 +3352,7 @@ void Aura::HandleAuraModDisarm(bool apply, bool Real) return; // main-hand attack speed already set to special value for feral form already and don't must change and reset at remove. - if (((Player *)m_target)->IsInFeralForm()) + if (m_target->IsInFeralForm()) return; if (apply) @@ -4693,6 +4716,7 @@ void Aura::HandleAuraModIncreaseHealth(bool apply, bool Real) case 28726: // Nightmare Seed ( Nightmare Seed ) case 34511: // Valor (Bulwark of Kings, Bulwark of the Ancient Kings) case 44055: // Tremendous Fortitude (Battlemaster's Alacrity) + case 50322: // Survival Instincts { if(Real) { @@ -5924,6 +5948,15 @@ void Aura::PeriodicTick() } case SPELL_AURA_PERIODIC_MANA_LEECH: { + if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue >= MAX_POWERS) + return; + + Powers power = Powers(m_modifier.m_miscvalue); + + // power type might have changed between aura applying and tick (druid's shapeshift) + if(m_target->getPowerType() != power) + return; + Unit *pCaster = GetCaster(); if(!pCaster) return; @@ -5942,18 +5975,20 @@ void Aura::PeriodicTick() // ignore non positive values (can be result apply spellmods to aura damage uint32 pdamage = m_modifier.m_amount > 0 ? m_modifier.m_amount : 0; + // Special case: draining x% of mana (up to a maximum of 2*x% of the caster's maximum mana) + // It's mana percent cost spells, m_modifier.m_amount is percent drain from target + if (m_spellProto->ManaCostPercentage) + { + // max value + uint32 maxmana = pCaster->GetMaxPower(power) * pdamage * 2 / 100; + pdamage = m_target->GetMaxPower(power) * pdamage / 100; + if(pdamage > maxmana) + pdamage = maxmana; + } + sLog.outDetail("PeriodicTick: %u (TypeId: %u) power leech of %u (TypeId: %u) for %u dmg inflicted by %u", GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId()); - if(m_modifier.m_miscvalue < 0 || m_modifier.m_miscvalue >= MAX_POWERS) - break; - - Powers power = Powers(m_modifier.m_miscvalue); - - // power type might have changed between aura applying and tick (druid's shapeshift) - if(m_target->getPowerType() != power) - break; - int32 drain_amount = m_target->GetPower(power) > pdamage ? pdamage : m_target->GetPower(power); // resilience reduce mana draining effect at spell crit damage reduction (added in 2.4) diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp index d754219d2..aaa0ed905 100644 --- a/src/game/SpellEffects.cpp +++ b/src/game/SpellEffects.cpp @@ -99,7 +99,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectUnused, // 39 SPELL_EFFECT_LANGUAGE &Spell::EffectDualWield, // 40 SPELL_EFFECT_DUAL_WIELD &Spell::EffectUnused, // 41 SPELL_EFFECT_JUMP - &Spell::EffectUnused, // 42 SPELL_EFFECT_JUMP2 + &Spell::EffectJump, // 42 SPELL_EFFECT_JUMP2 &Spell::EffectTeleUnitsFaceCaster, // 43 SPELL_EFFECT_TELEPORT_UNITS_FACE_CASTER &Spell::EffectLearnSkill, // 44 SPELL_EFFECT_SKILL_STEP &Spell::EffectAddHonor, // 45 SPELL_EFFECT_ADD_HONOR honor/pvp related @@ -1119,7 +1119,7 @@ void Spell::EffectDummy(uint32 i) return; unitTarget->CastSpell(unitTarget, 58419, true); - break; + return; } case 58420: // Portal to Stormwind { @@ -1127,7 +1127,7 @@ void Spell::EffectDummy(uint32 i) return; unitTarget->CastSpell(unitTarget, 58421, true); - break; + return; } } @@ -1750,6 +1750,15 @@ void Spell::EffectDummy(uint32 i) m_caster->AddPetAura(petSpell); return; } + + // Script based implementation. Must be used only for not good for implementation in core spell effects + // So called only for not proccessed cases + if(gameObjTarget) + Script->EffectDummyGameObj(m_caster, m_spellInfo->Id, i, gameObjTarget); + else if(unitTarget && unitTarget->GetTypeId()==TYPEID_UNIT) + Script->EffectDummyCreature(m_caster, m_spellInfo->Id, i, (Creature*)unitTarget); + else if(itemTarget) + Script->EffectDummyItem(m_caster, m_spellInfo->Id, i, itemTarget); } void Spell::EffectTriggerSpellWithValue(uint32 i) @@ -2000,6 +2009,55 @@ void Spell::EffectTriggerMissileSpell(uint32 effect_idx) m_caster->CastSpell(m_targets.m_destX, m_targets.m_destY, m_targets.m_destZ, spellInfo, true, m_CastItem, 0, m_originalCasterGUID); } +void Spell::EffectJump(uint32 i) +{ + if(m_caster->isInFlight()) + return; + + // Init dest coordinates + float x,y,z,o; + if(m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION) + { + x = m_targets.m_destX; + y = m_targets.m_destY; + z = m_targets.m_destZ; + + if(m_spellInfo->EffectImplicitTargetA[i] == TARGET_BEHIND_VICTIM) + { + // explicit cast data from client or server-side cast + // some spell at client send caster + Unit* pTarget = NULL; + if(m_targets.getUnitTarget() && m_targets.getUnitTarget()!=m_caster) + pTarget = m_targets.getUnitTarget(); + else if(unitTarget->getVictim()) + pTarget = m_caster->getVictim(); + else if(m_caster->GetTypeId() == TYPEID_PLAYER) + pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection()); + + o = pTarget ? pTarget->GetOrientation() : m_caster->GetOrientation(); + } + else + o = m_caster->GetOrientation(); + } + else if(unitTarget) + { + unitTarget->GetContactPoint(m_caster,x,y,z,CONTACT_DISTANCE); + o = m_caster->GetOrientation(); + } + else if(gameObjTarget) + { + gameObjTarget->GetContactPoint(m_caster,x,y,z,CONTACT_DISTANCE); + o = m_caster->GetOrientation(); + } + else + { + sLog.outError( "Spell::EffectJump - unsupported target mode for spell ID %u", m_spellInfo->Id ); + return; + } + + m_caster->NearTeleportTo(x,y,z,o,true); +} + void Spell::EffectTeleportUnits(uint32 i) { if(!unitTarget || unitTarget->isInFlight()) @@ -2018,45 +2076,38 @@ void Spell::EffectTeleportUnits(uint32 i) } case TARGET_TABLE_X_Y_Z_COORDINATES: { - // TODO: Only players can teleport? - if (unitTarget->GetTypeId() != TYPEID_PLAYER) - return; SpellTargetPosition const* st = spellmgr.GetSpellTargetPosition(m_spellInfo->Id); if(!st) { sLog.outError( "Spell::EffectTeleportUnits - unknown Teleport coordinates for spell ID %u", m_spellInfo->Id ); return; } - ((Player*)unitTarget)->TeleportTo(st->target_mapId,st->target_X,st->target_Y,st->target_Z,st->target_Orientation,unitTarget==m_caster ? TELE_TO_SPELL : 0); + + if(st->target_mapId==unitTarget->GetMapId()) + unitTarget->NearTeleportTo(st->target_X,st->target_Y,st->target_Z,st->target_Orientation,unitTarget==m_caster); + else if(unitTarget->GetTypeId()==TYPEID_PLAYER) + ((Player*)unitTarget)->TeleportTo(st->target_mapId,st->target_X,st->target_Y,st->target_Z,st->target_Orientation,unitTarget==m_caster ? TELE_TO_SPELL : 0); break; } case TARGET_BEHIND_VICTIM: { - // Get selected target for player (or victim for units) Unit *pTarget = NULL; - if(m_caster->GetTypeId() == TYPEID_PLAYER) - pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection()); - else - pTarget = m_caster->getVictim(); - // No target present - return - if (!pTarget) - return; + + // explicit cast data from client or server-side cast + // some spell at client send caster + if(m_targets.getUnitTarget() && m_targets.getUnitTarget()!=unitTarget) + pTarget = m_targets.getUnitTarget(); + else if(unitTarget->getVictim()) + pTarget = unitTarget->getVictim(); + else if(unitTarget->GetTypeId() == TYPEID_PLAYER) + pTarget = ObjectAccessor::GetUnit(*unitTarget, ((Player*)unitTarget)->GetSelection()); + // Init dest coordinates - uint32 mapid = m_caster->GetMapId(); float x = m_targets.m_destX; float y = m_targets.m_destY; float z = m_targets.m_destZ; - float orientation = pTarget->GetOrientation(); - // Teleport - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); - else - { - m_caster->GetMap()->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation); - WorldPacket data; - unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation); - unitTarget->SendMessageToSet(&data, false); - } + float orientation = pTarget ? pTarget->GetOrientation() : unitTarget->GetOrientation(); + unitTarget->NearTeleportTo(x,y,z,orientation,unitTarget==m_caster); return; } default: @@ -2074,15 +2125,7 @@ void Spell::EffectTeleportUnits(uint32 i) float z = m_targets.m_destZ; float orientation = unitTarget->GetOrientation(); // Teleport - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); - else - { - m_caster->GetMap()->CreatureRelocation((Creature*)unitTarget, x, y, z, orientation); - WorldPacket data; - unitTarget->BuildTeleportAckMsg(&data, x, y, z, orientation); - unitTarget->SendMessageToSet(&data, false); - } + unitTarget->NearTeleportTo(x,y,z,orientation,unitTarget==m_caster); return; } } @@ -2903,7 +2946,7 @@ void Spell::SendLoot(uint64 guid, LootType loottype) player->SendLoot(guid, loottype); } -void Spell::EffectOpenLock(uint32 /*i*/) +void Spell::EffectOpenLock(uint32 effIndex) { if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER) { @@ -2913,7 +2956,6 @@ void Spell::EffectOpenLock(uint32 /*i*/) Player* player = (Player*)m_caster; - LootType loottype = LOOT_CORPSE; uint32 lockId = 0; uint64 guid = 0; @@ -2925,7 +2967,7 @@ void Spell::EffectOpenLock(uint32 /*i*/) if( goInfo->type == GAMEOBJECT_TYPE_BUTTON && goInfo->button.noDamageImmune || goInfo->type == GAMEOBJECT_TYPE_GOOBER && goInfo->goober.losOK ) { - //CanUseBattleGroundObject() already called in CanCast() + //CanUseBattleGroundObject() already called in CheckCast() // in battleground check if(BattleGround *bg = player->GetBattleGround()) { @@ -2937,7 +2979,7 @@ void Spell::EffectOpenLock(uint32 /*i*/) } else if (goInfo->type == GAMEOBJECT_TYPE_FLAGSTAND) { - //CanUseBattleGroundObject() already called in CanCast() + //CanUseBattleGroundObject() already called in CheckCast() // in battleground check if(BattleGround *bg = player->GetBattleGround()) { @@ -2960,91 +3002,39 @@ void Spell::EffectOpenLock(uint32 /*i*/) return; } - if(!lockId) // possible case for GO and maybe for items. + SkillType skillId = SKILL_NONE; + int32 reqSkillValue = 0; + int32 skillValue; + + SpellCastResult res = CanOpenLock(effIndex,lockId,skillId,reqSkillValue,skillValue); + if(res != SPELL_CAST_OK) { - SendLoot(guid, loottype); + SendCastResult(res); return; } - // Get LockInfo - LockEntry const *lockInfo = sLockStore.LookupEntry(lockId); + SendLoot(guid, LOOT_SKINNING); - if (!lockInfo) + // not allow use skill grow at item base open + if(!m_CastItem && skillId != SKILL_NONE) { - sLog.outError( "Spell::EffectOpenLock: %s [guid = %u] has an unknown lockId: %u!", - (gameObjTarget ? "gameobject" : "item"), GUID_LOPART(guid), lockId); - SendCastResult(SPELL_FAILED_BAD_TARGETS); - return; - } - - // check key - for(int i = 0; i < 8; ++i) - { - // Type==1 This means lockInfo->Index[i] is an item - if(lockInfo->Type[i]==LOCK_KEY_ITEM && lockInfo->Index[i] && m_CastItem && m_CastItem->GetEntry()==lockInfo->Index[i]) - { - SendLoot(guid, loottype); - return; - } - } - - uint32 SkillId = 0; - // Check and skill-up skill - if( m_spellInfo->Effect[1] == SPELL_EFFECT_SKILL ) - SkillId = m_spellInfo->EffectMiscValue[1]; - // pickpocketing spells - else if( m_spellInfo->EffectMiscValue[0] == LOCKTYPE_PICKLOCK ) - SkillId = SKILL_LOCKPICKING; - - // skill bonus provided by casting spell (mostly item spells) - uint32 spellSkillBonus = uint32(m_currentBasePoints[0]+1); - - uint32 reqSkillValue = lockInfo->Skill[0]; - - if(lockInfo->Skill[1]) // required pick lock skill applying - { - if(SkillId != SKILL_LOCKPICKING) // wrong skill (cheating?) - { - SendCastResult(SPELL_FAILED_FIZZLE); - return; - } - - reqSkillValue = lockInfo->Skill[1]; - } - else if(SkillId == SKILL_LOCKPICKING) // apply picklock skill to wrong target - { - SendCastResult(SPELL_FAILED_BAD_TARGETS); - return; - } - - if ( SkillId ) - { - loottype = LOOT_SKINNING; - if ( player->GetSkillValue(SkillId) + spellSkillBonus < reqSkillValue ) - { - SendCastResult(SPELL_FAILED_LOW_CASTLEVEL); - return; - } - // update skill if really known - if(uint32 SkillValue = player->GetPureSkillValue(SkillId)) + if(uint32 pureSkillValue = player->GetPureSkillValue(skillId)) { if(gameObjTarget) { // Allow one skill-up until respawned if ( !gameObjTarget->IsInSkillupList( player->GetGUIDLow() ) && - player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue) ) + player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue) ) gameObjTarget->AddToSkillupList( player->GetGUIDLow() ); } else if(itemTarget) { // Do one skill-up - player->UpdateGatherSkill(SkillId, SkillValue, reqSkillValue); + player->UpdateGatherSkill(skillId, pureSkillValue, reqSkillValue); } } } - - SendLoot(guid, loottype); } void Spell::EffectSummonChangeItem(uint32 i) @@ -3743,16 +3733,12 @@ void Spell::EffectTeleUnitsFaceCaster(uint32 i) if(unitTarget->isInFlight()) return; - uint32 mapid = m_caster->GetMapId(); float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); float fx,fy,fz; m_caster->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis); - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, -m_caster->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); - else - m_caster->GetMap()->CreatureRelocation((Creature*)m_caster, fx, fy, fz, -m_caster->GetOrientation()); + unitTarget->NearTeleportTo(fx,fy,fz,-m_caster->GetOrientation(),unitTarget==m_caster); } void Spell::EffectLearnSkill(uint32 i) @@ -3773,17 +3759,26 @@ void Spell::EffectAddHonor(uint32 /*i*/) if(unitTarget->GetTypeId() != TYPEID_PLAYER) return; - uint32 honor_reward = MaNGOS::Honor::hk_honor_at_level(unitTarget->getLevel(), damage); - sLog.outDebug("SpellEffect::AddHonor called for spell_id %u, that rewards %u honor points to player: %u", m_spellInfo->Id, honor_reward, ((Player*)unitTarget)->GetGUIDLow()); + // not scale value for item based reward (/10 value expected) + if(m_CastItem) + { + ((Player*)unitTarget)->RewardHonor(NULL, 1, damage/10); + sLog.outError("SpellEffect::AddHonor (spell_id %u) rewards %d honor points (item %u) for player: %u", m_spellInfo->Id, damage/10, m_CastItem->GetEntry(),((Player*)unitTarget)->GetGUIDLow()); + return; + } // do not allow to add too many honor for player (50 * 21) = 1040 at level 70, or (50 * 31) = 1550 at level 80 - if( damage <= 50 ) + if( damage <= 50) + { + uint32 honor_reward = MaNGOS::Honor::hk_honor_at_level(unitTarget->getLevel(), damage); ((Player*)unitTarget)->RewardHonor(NULL, 1, honor_reward); + sLog.outDebug("SpellEffect::AddHonor (spell_id %u) rewards %u honor points (scale) to player: %u", m_spellInfo->Id, honor_reward, ((Player*)unitTarget)->GetGUIDLow()); + } else { //maybe we have correct honor_gain in damage already ((Player*)unitTarget)->RewardHonor(NULL, 1, damage); - sLog.outError("SpellEffect::AddHonor called for spell_id %u, that rewards %d * honor for one honorable kill and it is too much (%u of honor) for player: %u", m_spellInfo->Id, damage, honor_reward, ((Player*)unitTarget)->GetGUIDLow()); + sLog.outError("SpellEffect::AddHonor (spell_id %u) rewards %u honor points (non scale) for player: %u", m_spellInfo->Id, damage, ((Player*)unitTarget)->GetGUIDLow()); } } @@ -5757,7 +5752,6 @@ void Spell::EffectMomentMove(uint32 i) if( m_spellInfo->rangeIndex== 1) //self range { - uint32 mapid = m_caster->GetMapId(); float dis = GetSpellRadius(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i])); // before caster @@ -5767,7 +5761,7 @@ void Spell::EffectMomentMove(uint32 i) unitTarget->GetPosition(ox,oy,oz); float fx2,fy2,fz2; // getObjectHitPos overwrite last args in any result case - if(VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(mapid, ox,oy,oz+0.5, fx,fy,oz+0.5,fx2,fy2,fz2, -0.5)) + if(VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(unitTarget->GetMapId(), ox,oy,oz+0.5, fx,fy,oz+0.5,fx2,fy2,fz2, -0.5)) { fx = fx2; fy = fy2; @@ -5775,10 +5769,7 @@ void Spell::EffectMomentMove(uint32 i) unitTarget->UpdateGroundPositionZ(fx,fy,fz); } - if(unitTarget->GetTypeId() == TYPEID_PLAYER) - ((Player*)unitTarget)->TeleportTo(mapid, fx, fy, fz, unitTarget->GetOrientation(), TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (unitTarget==m_caster ? TELE_TO_SPELL : 0)); - else - m_caster->GetMap()->CreatureRelocation((Creature*)unitTarget, fx, fy, fz, unitTarget->GetOrientation()); + unitTarget->NearTeleportTo(fx, fy, fz, unitTarget->GetOrientation(),unitTarget==m_caster); } } diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp index f333db815..124448f50 100644 --- a/src/game/SpellMgr.cpp +++ b/src/game/SpellMgr.cpp @@ -317,6 +317,7 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex) { case 13139: // net-o-matic special effect case 23445: // evil twin + case 35679: // Protectorate Demolitionist case 38637: // Nether Exhaustion (red) case 38638: // Nether Exhaustion (green) case 38639: // Nether Exhaustion (blue) @@ -535,13 +536,13 @@ bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId) return false; } -uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) +SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) { // talents that learn spells can have stance requirements that need ignore // (this requirement only for client-side stance show in talent description) if( GetTalentSpellCost(spellInfo->Id) > 0 && (spellInfo->Effect[0]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[1]==SPELL_EFFECT_LEARN_SPELL || spellInfo->Effect[2]==SPELL_EFFECT_LEARN_SPELL) ) - return 0; + return SPELL_CAST_OK; uint32 stanceMask = (form ? 1 << (form - 1) : 0); @@ -549,7 +550,7 @@ uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) return SPELL_FAILED_NOT_SHAPESHIFT; if (stanceMask & spellInfo->Stances) // can explicitly be casted in this stance - return 0; + return SPELL_CAST_OK; bool actAsShifted = false; if (form > 0) @@ -558,7 +559,7 @@ uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) if (!shapeInfo) { sLog.outError("GetErrorAtShapeshiftedCast: unknown shapeshift %u", form); - return 0; + return SPELL_CAST_OK; } actAsShifted = !(shapeInfo->flags1 & 1); // shapeshift acts as normal form for spells } @@ -577,7 +578,7 @@ uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form) return SPELL_FAILED_ONLY_SHAPESHIFT; } - return 0; + return SPELL_CAST_OK; } void SpellMgr::LoadSpellTargetPositions() @@ -1336,6 +1337,10 @@ bool SpellMgr::IsNoStackSpellDueToSpell(uint32 spellId_1, uint32 spellId_2) cons // Cat Energy (Feral T4 (2)) and Omen of Clarity if( spellInfo_1->Id == 16864 && spellInfo_2->Id == 37311 || spellInfo_2->Id == 16864 && spellInfo_1->Id == 37311 ) return false; + + // Survival Instincts and Survival Instincts + if( spellInfo_1->Id == 61336 && spellInfo_2->Id == 50322 || spellInfo_2->Id == 61336 && spellInfo_1->Id == 50322 ) + return false; } // Leader of the Pack and Scroll of Stamina (multi-family check) @@ -2559,7 +2564,7 @@ void SpellMgr::LoadSpellAreas() sLog.outString( ">> Loaded %u spell area requirements", count ); } -uint8 SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player) +SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player) { // normal case if( spellInfo->AreaGroupId > 0) @@ -2585,7 +2590,7 @@ uint8 SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint for(SpellAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) { if(itr->second.IsFitToRequirements(player,zone_id,area_id)) - return 0; + return SPELL_CAST_OK; } return SPELL_FAILED_INCORRECT_AREA; } @@ -2595,9 +2600,9 @@ uint8 SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint { case 23333: // Warsong Flag case 23335: // Silverwing Flag - return map_id == 489 && player && player->InBattleGround() ? 0 : SPELL_FAILED_REQUIRES_AREA; + return map_id == 489 && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; case 34976: // Netherstorm Flag - return map_id == 566 && player && player->InBattleGround() ? 0 : SPELL_FAILED_REQUIRES_AREA; + return map_id == 566 && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; case 2584: // Waiting to Resurrect case 22011: // Spirit Heal Channel case 22012: // Spirit Heal @@ -2610,7 +2615,7 @@ uint8 SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint if(!mapEntry) return SPELL_FAILED_INCORRECT_AREA; - return mapEntry->IsBattleGround() && player && player->InBattleGround() ? 0 : SPELL_FAILED_REQUIRES_AREA; + return mapEntry->IsBattleGround() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 44521: // Preparation { @@ -2625,7 +2630,7 @@ uint8 SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint return SPELL_FAILED_REQUIRES_AREA; BattleGround* bg = player->GetBattleGround(); - return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? 0 : SPELL_FAILED_REQUIRES_AREA; + return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 32724: // Gold Team (Alliance) case 32725: // Green Team (Alliance) @@ -2636,7 +2641,7 @@ uint8 SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint if(!mapEntry) return SPELL_FAILED_INCORRECT_AREA; - return mapEntry->IsBattleArena() && player && player->InBattleGround() ? 0 : SPELL_FAILED_REQUIRES_AREA; + return mapEntry->IsBattleArena() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } case 32727: // Arena Preparation { @@ -2651,11 +2656,11 @@ uint8 SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint return SPELL_FAILED_REQUIRES_AREA; BattleGround* bg = player->GetBattleGround(); - return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? 0 : SPELL_FAILED_REQUIRES_AREA; + return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA; } } - return 0; + return SPELL_CAST_OK; } void SpellMgr::LoadSkillLineAbilityMap() diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h index 952b42c19..2f0a6afc4 100644 --- a/src/game/SpellMgr.h +++ b/src/game/SpellMgr.h @@ -37,192 +37,6 @@ class Spell; extern SQLStorage sSpellThreatStore; -enum SpellFailedReason -{ - SPELL_FAILED_AFFECTING_COMBAT = 0, - SPELL_FAILED_ALREADY_AT_FULL_HEALTH = 1, - SPELL_FAILED_ALREADY_AT_FULL_MANA = 2, - SPELL_FAILED_ALREADY_AT_FULL_POWER = 3, - SPELL_FAILED_ALREADY_BEING_TAMED = 4, - SPELL_FAILED_ALREADY_HAVE_CHARM = 5, - SPELL_FAILED_ALREADY_HAVE_SUMMON = 6, - SPELL_FAILED_ALREADY_OPEN = 7, - SPELL_FAILED_AURA_BOUNCED = 8, - SPELL_FAILED_AUTOTRACK_INTERRUPTED = 9, - SPELL_FAILED_BAD_IMPLICIT_TARGETS = 10, - SPELL_FAILED_BAD_TARGETS = 11, - SPELL_FAILED_CANT_BE_CHARMED = 12, - SPELL_FAILED_CANT_BE_DISENCHANTED = 13, - SPELL_FAILED_CANT_BE_DISENCHANTED_SKILL = 14, - SPELL_FAILED_CANT_BE_MILLED = 15, - SPELL_FAILED_CANT_BE_PROSPECTED = 16, - SPELL_FAILED_CANT_CAST_ON_TAPPED = 17, - SPELL_FAILED_CANT_DUEL_WHILE_INVISIBLE = 18, - SPELL_FAILED_CANT_DUEL_WHILE_STEALTHED = 19, - SPELL_FAILED_CANT_STEALTH = 20, - SPELL_FAILED_CASTER_AURASTATE = 21, - SPELL_FAILED_CASTER_DEAD = 22, - SPELL_FAILED_CHARMED = 23, - SPELL_FAILED_CHEST_IN_USE = 24, - SPELL_FAILED_CONFUSED = 25, - SPELL_FAILED_DONT_REPORT = 26, - SPELL_FAILED_EQUIPPED_ITEM = 27, - SPELL_FAILED_EQUIPPED_ITEM_CLASS = 28, - SPELL_FAILED_EQUIPPED_ITEM_CLASS_MAINHAND = 29, - SPELL_FAILED_EQUIPPED_ITEM_CLASS_OFFHAND = 30, - SPELL_FAILED_ERROR = 31, - SPELL_FAILED_FIZZLE = 32, - SPELL_FAILED_FLEEING = 33, - SPELL_FAILED_FOOD_LOWLEVEL = 34, - SPELL_FAILED_HIGHLEVEL = 35, - SPELL_FAILED_HUNGER_SATIATED = 36, - SPELL_FAILED_IMMUNE = 37, - SPELL_FAILED_INCORRECT_AREA = 38, - SPELL_FAILED_INTERRUPTED = 39, - SPELL_FAILED_INTERRUPTED_COMBAT = 40, - SPELL_FAILED_ITEM_ALREADY_ENCHANTED = 41, - SPELL_FAILED_ITEM_GONE = 42, - SPELL_FAILED_ITEM_NOT_FOUND = 43, - SPELL_FAILED_ITEM_NOT_READY = 44, - SPELL_FAILED_LEVEL_REQUIREMENT = 45, - SPELL_FAILED_LINE_OF_SIGHT = 46, - SPELL_FAILED_LOWLEVEL = 47, - SPELL_FAILED_LOW_CASTLEVEL = 48, - SPELL_FAILED_MAINHAND_EMPTY = 49, - SPELL_FAILED_MOVING = 50, - SPELL_FAILED_NEED_AMMO = 51, - SPELL_FAILED_NEED_AMMO_POUCH = 52, - SPELL_FAILED_NEED_EXOTIC_AMMO = 53, - SPELL_FAILED_NEED_MORE_ITEMS = 54, - SPELL_FAILED_NOPATH = 55, - SPELL_FAILED_NOT_BEHIND = 56, - SPELL_FAILED_NOT_FISHABLE = 57, - SPELL_FAILED_NOT_FLYING = 58, - SPELL_FAILED_NOT_HERE = 59, - SPELL_FAILED_NOT_INFRONT = 60, - SPELL_FAILED_NOT_IN_CONTROL = 61, - SPELL_FAILED_NOT_KNOWN = 62, - SPELL_FAILED_NOT_MOUNTED = 63, - SPELL_FAILED_NOT_ON_TAXI = 64, - SPELL_FAILED_NOT_ON_TRANSPORT = 65, - SPELL_FAILED_NOT_READY = 66, - SPELL_FAILED_NOT_SHAPESHIFT = 67, - SPELL_FAILED_NOT_STANDING = 68, - SPELL_FAILED_NOT_TRADEABLE = 69, - SPELL_FAILED_NOT_TRADING = 70, - SPELL_FAILED_NOT_UNSHEATHED = 71, - SPELL_FAILED_NOT_WHILE_GHOST = 72, - SPELL_FAILED_NOT_WHILE_LOOTING = 73, - SPELL_FAILED_NO_AMMO = 74, - SPELL_FAILED_NO_CHARGES_REMAIN = 75, - SPELL_FAILED_NO_CHAMPION = 76, - SPELL_FAILED_NO_COMBO_POINTS = 77, - SPELL_FAILED_NO_DUELING = 78, - SPELL_FAILED_NO_ENDURANCE = 79, - SPELL_FAILED_NO_FISH = 80, - SPELL_FAILED_NO_ITEMS_WHILE_SHAPESHIFTED = 81, - SPELL_FAILED_NO_MOUNTS_ALLOWED = 82, - SPELL_FAILED_NO_PET = 83, - SPELL_FAILED_NO_POWER = 84, - SPELL_FAILED_NOTHING_TO_DISPEL = 85, - SPELL_FAILED_NOTHING_TO_STEAL = 86, - SPELL_FAILED_ONLY_ABOVEWATER = 87, - SPELL_FAILED_ONLY_DAYTIME = 88, - SPELL_FAILED_ONLY_INDOORS = 89, - SPELL_FAILED_ONLY_MOUNTED = 90, - SPELL_FAILED_ONLY_NIGHTTIME = 91, - SPELL_FAILED_ONLY_OUTDOORS = 92, - SPELL_FAILED_ONLY_SHAPESHIFT = 93, - SPELL_FAILED_ONLY_STEALTHED = 94, - SPELL_FAILED_ONLY_UNDERWATER = 95, - SPELL_FAILED_OUT_OF_RANGE = 96, - SPELL_FAILED_PACIFIED = 97, - SPELL_FAILED_POSSESSED = 98, - SPELL_FAILED_REAGENTS = 99, - SPELL_FAILED_REQUIRES_AREA = 100, - SPELL_FAILED_REQUIRES_SPELL_FOCUS = 101, - SPELL_FAILED_ROOTED = 102, - SPELL_FAILED_SILENCED = 103, - SPELL_FAILED_SPELL_IN_PROGRESS = 104, - SPELL_FAILED_SPELL_LEARNED = 105, - SPELL_FAILED_SPELL_UNAVAILABLE = 106, - SPELL_FAILED_STUNNED = 107, - SPELL_FAILED_TARGETS_DEAD = 108, - SPELL_FAILED_TARGET_AFFECTING_COMBAT = 109, - SPELL_FAILED_TARGET_AURASTATE = 110, - SPELL_FAILED_TARGET_DUELING = 111, - SPELL_FAILED_TARGET_ENEMY = 112, - SPELL_FAILED_TARGET_ENRAGED = 113, - SPELL_FAILED_TARGET_FRIENDLY = 114, - SPELL_FAILED_TARGET_IN_COMBAT = 115, - SPELL_FAILED_TARGET_IS_PLAYER = 116, - SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED = 117, - SPELL_FAILED_TARGET_NOT_DEAD = 118, - SPELL_FAILED_TARGET_NOT_IN_PARTY = 119, - SPELL_FAILED_TARGET_NOT_LOOTED = 120, - SPELL_FAILED_TARGET_NOT_PLAYER = 121, - SPELL_FAILED_TARGET_NO_POCKETS = 122, - SPELL_FAILED_TARGET_NO_WEAPONS = 123, - SPELL_FAILED_TARGET_NO_RANGED_WEAPONS = 124, - SPELL_FAILED_TARGET_UNSKINNABLE = 125, - SPELL_FAILED_THIRST_SATIATED = 126, - SPELL_FAILED_TOO_CLOSE = 127, - SPELL_FAILED_TOO_MANY_OF_ITEM = 128, - SPELL_FAILED_TOTEM_CATEGORY = 129, - SPELL_FAILED_TOTEMS = 130, - SPELL_FAILED_TRY_AGAIN = 131, - SPELL_FAILED_UNIT_NOT_BEHIND = 132, - SPELL_FAILED_UNIT_NOT_INFRONT = 133, - SPELL_FAILED_WRONG_PET_FOOD = 134, - SPELL_FAILED_NOT_WHILE_FATIGUED = 135, - SPELL_FAILED_TARGET_NOT_IN_INSTANCE = 136, - SPELL_FAILED_NOT_WHILE_TRADING = 137, - SPELL_FAILED_TARGET_NOT_IN_RAID = 138, - SPELL_FAILED_TARGET_FREEFORALL = 139, - SPELL_FAILED_NO_EDIBLE_CORPSES = 140, - SPELL_FAILED_ONLY_BATTLEGROUNDS = 141, - SPELL_FAILED_TARGET_NOT_GHOST = 142, - SPELL_FAILED_TRANSFORM_UNUSABLE = 143, - SPELL_FAILED_WRONG_WEATHER = 144, - SPELL_FAILED_DAMAGE_IMMUNE = 145, - SPELL_FAILED_PREVENTED_BY_MECHANIC = 146, - SPELL_FAILED_PLAY_TIME = 147, - SPELL_FAILED_REPUTATION = 148, - SPELL_FAILED_MIN_SKILL = 149, - SPELL_FAILED_NOT_IN_ARENA = 150, - SPELL_FAILED_NOT_ON_SHAPESHIFT = 151, - SPELL_FAILED_NOT_ON_STEALTHED = 152, - SPELL_FAILED_NOT_ON_DAMAGE_IMMUNE = 153, - SPELL_FAILED_NOT_ON_MOUNTED = 154, - SPELL_FAILED_TOO_SHALLOW = 155, - SPELL_FAILED_TARGET_NOT_IN_SANCTUARY = 156, - SPELL_FAILED_TARGET_IS_TRIVIAL = 157, - SPELL_FAILED_BM_OR_INVISGOD = 158, - SPELL_FAILED_EXPERT_RIDING_REQUIREMENT = 159, - SPELL_FAILED_ARTISAN_RIDING_REQUIREMENT = 160, - SPELL_FAILED_NOT_IDLE = 161, - SPELL_FAILED_NOT_INACTIVE = 162, - SPELL_FAILED_PARTIAL_PLAYTIME = 163, - SPELL_FAILED_NO_PLAYTIME = 164, - SPELL_FAILED_NOT_IN_BATTLEGROUND = 165, - SPELL_FAILED_NOT_IN_RAID_INSTANCE = 166, - SPELL_FAILED_ONLY_IN_ARENA = 167, - SPELL_FAILED_TARGET_LOCKED_TO_RAID_INSTANCE = 168, - SPELL_FAILED_ON_USE_ENCHANT = 169, - SPELL_FAILED_NOT_ON_GROUND = 170, - SPELL_FAILED_CUSTOM_ERROR = 171, - SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW = 172, - SPELL_FAILED_TOO_MANY_SOCKETS = 173, - SPELL_FAILED_INVALID_GLYPH = 174, - SPELL_FAILED_UNIQUE_GLYPH = 175, - SPELL_FAILED_GLYPH_SOCKET_LOCKED = 176, - SPELL_FAILED_NO_VALID_TARGETS = 177, - SPELL_FAILED_ITEM_AT_MAX_CHARGES = 178, - SPELL_FAILED_NOT_IN_BARBERSHOP = 179, - SPELL_FAILED_FISHING_TOO_LOW = 180, - SPELL_FAILED_UNKNOWN = 181 -}; - // only used in code enum SpellCategories { @@ -428,7 +242,7 @@ inline bool IsAutoRepeatRangedSpell(SpellEntry const* spellInfo) return (spellInfo->Attributes & SPELL_ATTR_RANGED) && (spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG); } -uint8 GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form); +SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form); inline bool IsChanneledSpell(SpellEntry const* spellInfo) { @@ -996,7 +810,7 @@ class SpellMgr return NULL; } - uint8 GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = NULL); + SpellCastResult GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = NULL); SpellAreaMapBounds GetSpellAreaMapBounds(uint32 spell_id) const { diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp index 6f4aa4c73..b2f5ad827 100644 --- a/src/game/TargetedMovementGenerator.cpp +++ b/src/game/TargetedMovementGenerator.cpp @@ -92,8 +92,6 @@ template void TargetedMovementGenerator::Initialize(T &owner) { - if(!&owner) - return; owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); if (owner.GetTypeId() == TYPEID_UNIT && ((Creature*)&owner)->canFly()) diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp index 07138366c..de66ff241 100644 --- a/src/game/Unit.cpp +++ b/src/game/Unit.cpp @@ -2830,7 +2830,7 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target) if(attType != BASE_ATTACK && !item ) return 0; - if(((Player*)this)->IsInFeralForm()) + if(IsInFeralForm()) return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact // weapon skill or (unarmed for base attack) @@ -2956,7 +2956,7 @@ void Unit::_UpdateAutoRepeatSpell() if (isAttackReady(RANGED_ATTACK)) { // Check if able to cast - if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CanCast(true)) + if(m_currentSpells[CURRENT_AUTOREPEAT_SPELL]->CheckCast(true) != SPELL_CAST_OK) { InterruptSpell(CURRENT_AUTOREPEAT_SPELL); return; @@ -7120,6 +7120,7 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) if(HasAuraType(SPELL_AURA_MOD_UNATTACKABLE)) RemoveSpellsCausingAura(SPELL_AURA_MOD_UNATTACKABLE); + // in fighting already if (m_attacking) { if (m_attacking == victim) @@ -7133,7 +7134,16 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) } return false; } - AttackStop(); + + // remove old target data + AttackStop(true); + } + // new battle + else + { + // set position before any AI calls/assistance + if(GetTypeId()==TYPEID_UNIT) + ((Creature*)this)->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ()); } //Set our target @@ -7142,10 +7152,6 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) if(meleeAttack) addUnitState(UNIT_STAT_MELEE_ATTACKING); - // set position before any AI calls/assistance - if(GetTypeId()==TYPEID_UNIT) - ((Creature*)this)->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ()); - m_attacking = victim; m_attacking->_addAttacker(this); @@ -7172,7 +7178,7 @@ bool Unit::Attack(Unit *victim, bool meleeAttack) return true; } -bool Unit::AttackStop() +bool Unit::AttackStop(bool targetSwitch /*=false*/) { if (!m_attacking) return false; @@ -7189,11 +7195,9 @@ bool Unit::AttackStop() InterruptSpell(CURRENT_MELEE_SPELL); - if( GetTypeId()==TYPEID_UNIT ) - { - // reset call assistance + // reset only at real combat stop + if(!targetSwitch && GetTypeId()==TYPEID_UNIT ) ((Creature*)this)->SetNoCallAssistance(false); - } SendAttackStop(victim); @@ -9629,7 +9633,7 @@ uint32 Unit::GetCreatureType() const { if(GetTypeId() == TYPEID_PLAYER) { - SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(((Player*)this)->m_form); + SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(m_form); if(ssEntry && ssEntry->creatureType > 0) return ssEntry->creatureType; else @@ -10599,8 +10603,11 @@ Player* Unit::GetSpellModOwner() } ///----------Pet responses methods----------------- -void Unit::SendPetCastFail(uint32 spellid, uint8 msg) +void Unit::SendPetCastFail(uint32 spellid, SpellCastResult msg) { + if(msg == SPELL_CAST_OK) + return; + Unit *owner = GetCharmerOrOwner(); if(!owner || owner->GetTypeId() != TYPEID_PLAYER) return; @@ -11361,3 +11368,19 @@ void Unit::SetPhaseMask(uint32 newPhaseMask, bool update) if(Pet* pet = GetPet()) pet->SetPhaseMask(newPhaseMask,true); } + +void Unit::NearTeleportTo( float x, float y, float z, float orientation, bool casting /*= false*/ ) +{ + if(GetTypeId() == TYPEID_PLAYER) + ((Player*)this)->TeleportTo(GetMapId(), x, y, z, orientation, TELE_TO_NOT_LEAVE_TRANSPORT | TELE_TO_NOT_LEAVE_COMBAT | TELE_TO_NOT_UNSUMMON_PET | (casting ? TELE_TO_SPELL : 0)); + else + { + GetMap()->CreatureRelocation((Creature*)this, x, y, z, orientation); + + WorldPacket data; + // Work strange for many spells: triggered active mover set for targeted player to creature + //BuildTeleportAckMsg(&data, x, y, z, orientation); + BuildHeartBeatMsg(&data); + SendMessageToSet(&data, false); + } +} \ No newline at end of file diff --git a/src/game/Unit.h b/src/game/Unit.h index 362ead84b..92116fb69 100644 --- a/src/game/Unit.h +++ b/src/game/Unit.h @@ -75,7 +75,7 @@ enum SpellAuraInterruptFlags AURA_INTERRUPT_FLAG_MOUNTING = 0x00020000, // 17 removed by mounting AURA_INTERRUPT_FLAG_NOT_SEATED = 0x00040000, // 18 removed by standing up AURA_INTERRUPT_FLAG_CHANGE_MAP = 0x00080000, // 19 leaving map/getting teleported - AURA_INTERRUPT_FLAG_UNK20 = 0x00100000, // 20 + AURA_INTERRUPT_FLAG_IMMUNE_OR_STEALTH = 0x00100000, // 20 removed when player on himself casts immunity spell or vanish? AURA_INTERRUPT_FLAG_UNK21 = 0x00200000, // 21 AURA_INTERRUPT_FLAG_UNK22 = 0x00400000, // 22 AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT = 0x00800000, // 23 removed by entering pvp combat @@ -850,7 +850,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject } bool Attack(Unit *victim, bool meleeAttack); void CastStop(uint32 except_spellid = 0); - bool AttackStop(); + bool AttackStop(bool targetSwitch = false); void RemoveAllAttackers(); AttackerSet const& getAttackers() const { return m_attackers; } bool isAttackingPlayer() const; @@ -1066,6 +1066,8 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID,uint32 Damage, SpellSchoolMask damageSchoolMask,uint32 AbsorbedDamage, uint32 Resist,bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false); void SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo); + void NearTeleportTo(float x, float y, float z, float orientation, bool casting = false); + void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player = NULL); void SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end, uint32 MovementFlags); void SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime = 0, Player* player = NULL); @@ -1195,8 +1197,11 @@ class MANGOS_DLL_SPEC Unit : public WorldObject uint64 m_ObjectSlot[4]; uint32 m_detectInvisibilityMask; uint32 m_invisibilityMask; + uint32 m_ShapeShiftFormSpellId; ShapeshiftForm m_form; + bool IsInFeralForm() const { return m_form == FORM_CAT || m_form == FORM_BEAR || m_form == FORM_DIREBEAR; } + float m_modMeleeHitChance; float m_modRangedHitChance; float m_modSpellHitChance; @@ -1401,7 +1406,7 @@ class MANGOS_DLL_SPEC Unit : public WorldObject void ClearComboPointHolders(); ///----------Pet responses methods----------------- - void SendPetCastFail(uint32 spellid, uint8 msg); + void SendPetCastFail(uint32 spellid, SpellCastResult msg); void SendPetActionFeedback (uint8 msg); void SendPetTalk (uint32 pettalk); void SendPetSpellCooldown (uint32 spellid, time_t cooltime); diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 0d83f461e..d5711b2db 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -245,11 +245,13 @@ void FlightPathMovementGenerator::Initialize(Player &player) void FlightPathMovementGenerator::Finalize(Player & player) { + // remove flag to prevent send object build movement packets for flight state and crash (movement generator already not at top of stack) + player.clearUnitState(UNIT_STAT_IN_FLIGHT); + float x, y, z; i_destinationHolder.GetLocationNow(player.GetMapId(), x, y, z); player.SetPosition(x, y, z, player.GetOrientation()); - player.clearUnitState(UNIT_STAT_IN_FLIGHT); player.Unmount(); player.RemoveFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); diff --git a/src/shared/Database/DBCStructure.h b/src/shared/Database/DBCStructure.h index 42ea449ae..6f9b187e2 100644 --- a/src/shared/Database/DBCStructure.h +++ b/src/shared/Database/DBCStructure.h @@ -374,6 +374,7 @@ struct AchievementCriteriaEntry struct { uint32 itemID; // 3 + uint32 count; // 4 } equip_item; // ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD= 62 @@ -975,13 +976,15 @@ struct ItemSetEntry uint32 required_skill_value; // 52 m_requiredSkillRank }; +#define MAX_LOCK_CASE 8 + struct LockEntry { uint32 ID; // 0 m_ID - uint32 Type[8]; // 1-8 m_Type - uint32 Index[8]; // 9-16 m_Index - uint32 Skill[8]; // 17-24 m_Skill - //uint32 Action[8]; // 25-32 m_Action + uint32 Type[MAX_LOCK_CASE]; // 1-8 m_Type + uint32 Index[MAX_LOCK_CASE]; // 9-16 m_Index + uint32 Skill[MAX_LOCK_CASE]; // 17-24 m_Skill + //uint32 Action[MAX_LOCK_CASE]; // 25-32 m_Action }; struct MailTemplateEntry diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 96986cb2e..b84930cc1 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "7440" + #define REVISION_NR "7483" #endif // __REVISION_NR_H__