From 8c298933105366aa1ac9f5fca06b047f2471f48a Mon Sep 17 00:00:00 2001 From: Schmoozerd Date: Sun, 15 Apr 2012 22:40:13 +0200 Subject: [PATCH] [11964] Implement new conditions system Tree-like design idea by Faramir118, thanks for that! * Add `conditions` table to store conditions. * REPLACE current handling of conditions for the *_loot_template tables Convert the old conditions in *_loot_template to the new system by SQL-Queries * ADD support for new conditions to gossip_menu and gossip_menu_option. If for these tables no condition_id (new system) is provided, the old conditions will still be used * Add a small helper python script to contrib/convertConditions, see README there for details * Add new command to reload the `conditions` table (.reload conditions) * Add two Meta-Condition types CONDITION_AND (-1) and CONDITION_OR (-2) which are used as: value1 (as condition_entry) AND / OR value2 (as condition_entry) With these meta-conditions it is possible to create tree like and very complicated combined conditions (like HasAura && (HasItem || HasQuest)) NOTE about conversion: For easier convertion all the old table data is still preserved, but will be removed eventually (within a circle of the moon approximately) The python script will not create an optimal initial fill of the `conditions` table. You might want to tweak it manually or suggest some optimized algorithm :) Signed-off-by: Schmoozerd --- .../convertConditions/ConvertConditions.py | 332 ++++++++++++++++++ contrib/convertConditions/README.txt | 33 ++ sql/mangos.sql | 37 +- sql/updates/11964_01_mangos_conditions.sql | 54 +++ src/game/Chat.cpp | 1 + src/game/Chat.h | 11 +- src/game/CreatureEventAI.cpp | 2 +- src/game/CreatureEventAIMgr.cpp | 2 +- src/game/Level3.cpp | 8 + src/game/LootMgr.cpp | 29 +- src/game/ObjectMgr.cpp | 263 +++++++++----- src/game/ObjectMgr.h | 54 ++- src/game/Player.cpp | 23 +- src/game/SQLStorages.cpp | 3 + src/game/SQLStorages.h | 1 + src/game/World.cpp | 4 + src/shared/revision_nr.h | 2 +- src/shared/revision_sql.h | 2 +- 18 files changed, 729 insertions(+), 132 deletions(-) create mode 100644 contrib/convertConditions/ConvertConditions.py create mode 100644 contrib/convertConditions/README.txt create mode 100644 sql/updates/11964_01_mangos_conditions.sql diff --git a/contrib/convertConditions/ConvertConditions.py b/contrib/convertConditions/ConvertConditions.py new file mode 100644 index 000000000..c5c423d75 --- /dev/null +++ b/contrib/convertConditions/ConvertConditions.py @@ -0,0 +1,332 @@ +# +# Copyright (C) 2005-2012 MaNGOS +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +import MySQLdb as mdb +import sys + +#global Variables (change as required) +host = "localhost" +user = "mangos" +passw = "mangos" +# databases format: list of [name, expansion] +databases = [ ["mangos", 2] ] +#databases = [ ["zero_db", 0], ["tbcdb", 1], ["udb_clean", 2], ["ytdb", 2] ] + +# Should the current conditions table be loaded? (usefull for appending custom content) +loadOldConditions = 0 +# database, from which the conditions table will be loaded +database = databases[0][0] +#database = "mangos_custom" + +# be very chatty with debug output +debug = 0 + +# global variables for internal use +false = 0 +true = 1 +processNumConditions = 0 +fUpdates = 0 + +# Some Helper functions, main code at the bottom +def isSameCondition(c1, v11, v12, c2, v21, v22): + return (c1 == c2) and (v11 == v21) and (v12 == v22) +# + +def compareCondition(c1, v11, v12, c2, v21, v22): + if (c1 > c2): + return true + if (c1 == c2): + if (v11 > v21): + return true + if (v11 == v21): + if (v12 > v22): + return true + + return false +# + +def insertCondition(c, v1, v2): + global old_max + old_max = old_max + 1 + linkedList.append( [old_max, c, v1, v2, database] ) + if (debug): + print "Inserted: [%d, %d, %d, %d], (%s)" % (old_max, c, v1, v2, database) +# + +def findCondition(c, v1, v2): + for entry in linkedList: + if (isSameCondition(c, v1, v2, entry[1], entry[2], entry[3])): + return entry[0] + return 0 +# + +# Function that processes table tableName for keys keyName1, keyName2, parses the conditions of conditionString, which must select numberOfConditions conditions +def progressTable(tableName, keyName1, keyName2, conditionString, numberOfConditions): + global old_max + global processNumConditions + global fUpdates + + try: + con = mdb.connect(host, user, passw, database); + cur = con.cursor() + cur.execute('SELECT %s, %s, %s FROM %s; ' % (keyName1, keyName2, conditionString, tableName)) + + result = cur.fetchall() + + if (debug): + print 'ProgressTable %s in database %s' % (tableName, database) + + for row in result: + key1 = row[0] + key2 = row[1] + + c1 = v11 = v12 = c2 = v21 = v22 = c3= v31 =v32 = 0 + + c1 = row[2] + v11 = row[3] + v12 = row[4] + if (numberOfConditions >= 2): + c2 = row[5] + v21 = row[6] + v22 = row[7] + if (numberOfConditions >= 3): + c3 = row[8] + v31 = row[9] + v32 = row[10] + + # Order the conditions of one row from big to slow + if (numberOfConditions >= 2) and (compareCondition(c2, v21, v22, c1, v11, v12)): + c1, v11, v12, c2, v21, v22 = c2, v21, v22, c1, v11, v12 + if (numberOfConditions >= 3): + if (compareCondition(c3, v31, v32, c2, v21, v22)): + c2, v21, v22, c3, v31, v32 = c3, v31, v32, c2, v21, v22 + if (compareCondition(c2, v21, v22, c1, v11, v12)): + c1, v11, v12, c2, v21, v22 = c2, v21, v22, c1, v11, v12 + + # How many conditions do we have? + rowConditionNumber = 0 + if (c1 > 0): + rowConditionNumber = rowConditionNumber + 1 + if (c2 > 0): + rowConditionNumber = rowConditionNumber + 1 + if (c3 > 0): + rowConditionNumber = rowConditionNumber + 1 + + if (rowConditionNumber == 0): #nothing to do + continue; + + if (debug): + print "Condition(s) for Key (%d, %d): %d, %d, %d -- %d, %d, %d -- %d, %d, %d" % (key1, key2, c1, v11, v12, c2, v21, v22, c3, v31, v32) + + # Just insert + if (processNumConditions == 0): + if (rowConditionNumber >= 1 and findCondition(c1, v11, v12) == 0): + insertCondition(c1, v11, v12) + if (rowConditionNumber >= 2 and findCondition(c2, v21, v22) == 0): + insertCondition(c2, v21, v22) + if (rowConditionNumber >= 3 and findCondition(c3, v31, v32) == 0): + insertCondition(c3, v31, v32) + continue + # + + # Currently processing? + if (processNumConditions != rowConditionNumber): + continue + + founds = [0, 0, 0] + countFound = 0 # helper for error + if (rowConditionNumber >= 1): + founds[0] = findCondition(c1, v11, v12) + if (founds[0] > 0): + countFound = countFound + 1 + if (rowConditionNumber >= 2): + founds[1] = findCondition(c2, v21, v22) + if (founds[1] > 0): + countFound = countFound + 1 + if (rowConditionNumber >= 3): + founds[2] = findCondition(c3, v31, v32) + if (founds[2] > 0): + countFound = countFound + 1 + + if (countFound != rowConditionNumber): + print 'An error happened for: Condition(s) for Key (%d, %d): %d, %d, %d -- %d, %d, %d -- %d, %d, %d' % (key1, key2, c1, v11, v12, c2, v21, v22, c3, v31, v32) + continue + + last_point = 0 + #3-vector condition + if (rowConditionNumber == 3): + # search for 2 match + notSearched = [0, 0, 0] + notSearched[2] = findCondition(-1, founds[0], founds[1]) + if (notSearched[2] == 0): + notSearched[2] = findCondition(-1, founds[1], founds[0]) + notSearched[1] = findCondition(-1, founds[0], founds[2]) + if (notSearched[1] == 0): + notSearched[1] = findCondition(-1, founds[2], founds[0]) + notSearched[0] = findCondition(-1, founds[1], founds[2]) + if (notSearched[0] == 0): + notSearched[0] = findCondition(-1, founds[2], founds[1]) + + if (notSearched == [0, 0, 0]): # nothing found + insertCondition(-1, founds[1], founds[2]) + notSearched[0] = old_max + for i in range(0, 3): + if (notSearched[i] > 0): + last_point = findCondition(-1, notSearched[i], founds[i]) + if (last_point == 0): + last_point = findCondition(-1, founds[i], notSearched[i]) + if (last_point > 0): + break + if (last_point == 0): + for i in range(0, 3): + if (notSearched[i] > 0): + insertCondition(-1, founds[i], notSearched[i]) + last_point = old_max + break + + #2-vector condition + if (rowConditionNumber == 2): + # search for 2 match + last_point = findCondition(-1, founds[1], founds[0]) + if (last_point == 0): + last_point = findCondition(-1, founds[0], founds[1]) + if (last_point == 0): + #Not found, insert list + insertCondition(-1, founds[1], founds[0]) + last_point = old_max + + #1-vector condition + if (rowConditionNumber == 1): + last_point = founds[0] + + # Now we must have last_point > 0 (for a condition), and linking to proper place + if (last_point > 0 and processNumConditions > 0): + #cur.execute('UPDATE %s SET condition_id=%d WHERE %s=%d AND %s=%d; ' % (tableName, last_point, keyName1, key1, keyName2, key2)) + print >> fUpdates, 'UPDATE %s SET condition_id=%d WHERE %s=%d AND %s=%d;' % (tableName, last_point, keyName1, key1, keyName2, key2) + + except mdb.Error, e: + print 'Error %d, %s' % (e.args[0], e.args[1]) + sys.exit(1) + + finally: + if con: + con.close() +## End of Helper function + +linkedList = [] +old_max = 0 +linkedList.append( [0, 0, 0, 0, 'initial fill'] ) + +# Extract old conditions +if (loadOldConditions): + try: + con = mdb.connect(host, user, passw, database); + cur = con.cursor() + cur.execute('SELECT condition_entry, type, value1, value2 FROM conditions') + + for row in cur: + linkedList.append( [row[0], row[1], row[2], row[3], 'reloaded from %s' % database ] ) + old_max = old_max + 1 + if (row[0] != old_max): + print 'An error happened at old_max=%d, entry=%d' % (old_max, row[0]) + + print 'Loaded %d values from %s conditions table' % (old_max, database) + + except mdb.Error, e: + print 'Error %d, %s' % (e.args[0], e.args[1]) + sys.exit(1) + + finally: + if con: + con.close() +# +start_entry=old_max + +def doTables(db): + global processNumConditions + global fUpdates + global database + + database = db[0] + print 'Processing database %s (%d vector conditions)' % (database, processNumConditions) + try: + if (processNumConditions == 0): + fUpdates = open("%s_updates.sql" % database, "w") + else: + fUpdates = open("%s_updates.sql" % database, "a") + + if (processNumConditions <= 1): + progressTable("reference_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + progressTable("creature_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + progressTable("gameobject_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + progressTable("pickpocketing_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + progressTable("item_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + progressTable("fishing_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + progressTable("skinning_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + progressTable("disenchant_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + progressTable("mail_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + # Not all expansions have all tables + if (db[1] >= 1): + progressTable("prospecting_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + if (db[1] >= 2): + progressTable("spell_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + progressTable("milling_loot_template", "entry", "item", "lootcondition, condition_value1, condition_value2", 1) + + if (processNumConditions < 3): + progressTable("gossip_menu", "entry", "text_id", "cond_1, cond_1_val_1, cond_1_val_2, cond_2, cond_2_val_1, cond_2_val_2", 2) + progressTable("gossip_menu_option", "menu_id", "id", "cond_1, cond_1_val_1, cond_1_val_2, cond_2, cond_2_val_1, cond_2_val_2, cond_3, cond_3_val_1, cond_3_val_2", 3) + + except: + print "An error happened here" + sys.exit(1) + + finally: + fUpdates.close() + +# end of helper function doTables + +try: + fConditions = open("conditions_dump.sql", "w") + if (debug): + print 'Opened conditions_dump.sql successfully' + + for i in range (0, 4): + processNumConditions = i + for db in databases: + doTables(db) + print 'Inserted %d rows for database %s' % (old_max - start_entry, database) + start_entry = old_max + + print 'Processed database(s): %s' % databases + #create dump + print >> fConditions, 'TRUNCATE conditions;' + print >> fConditions, 'INSERT INTO conditions VALUES' + for i in range(1, old_max): + if (linkedList[i][0] != i): + print 'AN ERROR HAPPENED for i=%d, liLi[i].entry=%d' % (i, linkedList[i][0]) + print >> fConditions, '(%d, %d, %d, %d), -- %s' % (linkedList[i][0], linkedList[i][1], linkedList[i][2], linkedList[i][3], linkedList[i][4]) + + i = old_max + print >> fConditions, '(%d, %d, %d, %d); -- %s' % (linkedList[i][0], linkedList[i][1], linkedList[i][2], linkedList[i][3], linkedList[i][4]) + +except: + print "An error happened" + sys.exit(1) + +finally: + fConditions.close() diff --git a/contrib/convertConditions/README.txt b/contrib/convertConditions/README.txt new file mode 100644 index 000000000..578cde512 --- /dev/null +++ b/contrib/convertConditions/README.txt @@ -0,0 +1,33 @@ +# +# Copyright (C) 2005-2012 MaNGOS +# + +This small Python script is intended to help with the convertion +of the old condition-system to the new conditions system. + +Requirements: +* Python 2.5 (at least tested with this version) +* The MySQLdb module (found on http://sourceforge.net/projects/mysql-python/) + +You need to edit the ConvertConditions.py file for configure. + +First variables that must be set are related to the database connection (host, user, passw). + +Then you must specify which databases you want to convert (databases) +For portability, you must provide the name of the database, and the client expansion for which it is. +(2 == WotLK, 1 == TBC, 0 == classic) + +You can also provide a list of databases to process multiple in one run. + +With the next option, you can specify if you want to load a current `conditions` table (loadOldConditions) +Setting = 1 will load old conditions (default = 0). +In case you want to load from a database, which is not the first database you want the script to work on, +you can provide its name with the database variable. + +The script will create (and overwrite existing!) sql-files: +conditions_dump.sql <-- this file stores a possible dump for the conditions table for the old conditions of your databases. (including the loaded) + +_updates.sql <-- these files store the UPDATE statements to fill the condition_id fields for the tables. + One file is created for each provided database. + +Backup your data, before you change anything! diff --git a/sql/mangos.sql b/sql/mangos.sql index 08ba90c6f..85d7b0894 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_11958_01_mangos_mangos_string` bit(1) default NULL + `required_11964_01_mangos_conditions` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -818,6 +818,27 @@ INSERT INTO `command` VALUES /*!40000 ALTER TABLE `command` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `conditions` +-- +DROP TABLE IF EXISTS `conditions`; +CREATE TABLE `conditions` ( + `condition_entry` mediumint(8) unsigned NOT NULL auto_increment COMMENT 'Identifier', + `type` tinyint(3) signed NOT NULL DEFAULT '0' COMMENT 'Type of the condition', + `value1` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT 'data field one for the condition', + `value2` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT 'data field two for the condition', + PRIMARY KEY (`condition_entry`), + CONSTRAINT unique_conditions UNIQUE (type,value1,value2) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Condition System'; + +-- +-- Dumping data for table `conditions` +-- +LOCK TABLES `conditions` WRITE; +/*!40000 ALTER TABLE `conditions` DISABLE KEYS */; +/*!40000 ALTER TABLE `conditions` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `creature` -- @@ -983,6 +1004,7 @@ CREATE TABLE `creature_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -1391,6 +1413,7 @@ CREATE TABLE `disenchant_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -1643,6 +1666,7 @@ CREATE TABLE `fishing_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -1980,6 +2004,7 @@ CREATE TABLE `gameobject_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -2122,6 +2147,7 @@ CREATE TABLE gossip_menu ( cond_2 tinyint(3) unsigned NOT NULL default '0', cond_2_val_1 mediumint(8) unsigned NOT NULL default '0', cond_2_val_2 mediumint(8) unsigned NOT NULL default '0', + condition_id MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (entry, text_id, script_id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; @@ -2161,6 +2187,7 @@ CREATE TABLE gossip_menu_option ( cond_3 tinyint(3) unsigned NOT NULL default '0', cond_3_val_1 mediumint(8) unsigned NOT NULL default '0', cond_3_val_2 mediumint(8) unsigned NOT NULL default '0', + condition_id MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (menu_id, id) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; @@ -2323,6 +2350,7 @@ CREATE TABLE `item_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -3123,6 +3151,7 @@ CREATE TABLE `mail_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -4003,6 +4032,7 @@ CREATE TABLE `milling_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -4599,6 +4629,7 @@ CREATE TABLE `pickpocketing_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -14063,6 +14094,7 @@ CREATE TABLE `prospecting_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -14365,6 +14397,7 @@ CREATE TABLE `reference_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -14568,6 +14601,7 @@ CREATE TABLE `skinning_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; @@ -17003,6 +17037,7 @@ CREATE TABLE `spell_loot_template` ( `lootcondition` tinyint(3) unsigned NOT NULL default '0', `condition_value1` mediumint(8) unsigned NOT NULL default '0', `condition_value2` mediumint(8) unsigned NOT NULL default '0', + `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0', PRIMARY KEY (`entry`,`item`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Loot System'; diff --git a/sql/updates/11964_01_mangos_conditions.sql b/sql/updates/11964_01_mangos_conditions.sql new file mode 100644 index 000000000..3da815b23 --- /dev/null +++ b/sql/updates/11964_01_mangos_conditions.sql @@ -0,0 +1,54 @@ +ALTER TABLE db_version CHANGE COLUMN required_11958_01_mangos_mangos_string required_11964_01_mangos_conditions bit; + +DROP TABLE IF EXISTS `conditions`; +CREATE TABLE `conditions` ( + `condition_entry` mediumint(8) unsigned NOT NULL auto_increment COMMENT 'Identifier', + `type` tinyint(3) signed NOT NULL DEFAULT '0' COMMENT 'Type of the condition', + `value1` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT 'data field one for the condition', + `value2` mediumint(8) unsigned NOT NULL DEFAULT '0' COMMENT 'data field two for the condition', + PRIMARY KEY (`condition_entry`), + CONSTRAINT unique_conditions UNIQUE (type,value1,value2) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Condition System'; + +ALTER TABLE gossip_menu_option ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER cond_3_val_2; +ALTER TABLE gossip_menu ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER cond_2_val_2; + +ALTER TABLE reference_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE creature_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE gameobject_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE pickpocketing_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE item_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE fishing_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE skinning_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE disenchant_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE mail_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE prospecting_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE spell_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; +ALTER TABLE milling_loot_template ADD COLUMN `condition_id` MEDIUMINT(8) UNSIGNED NOT NULL DEFAULT '0' AFTER condition_value2; + +-- convert *_loot_template conditions -- Note to DB-devs If you create a dump for the new system in an updatepack, these queries are not required +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM reference_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM creature_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM gameobject_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM pickpocketing_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM item_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM fishing_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM skinning_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM disenchant_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM mail_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM prospecting_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM spell_loot_template WHERE lootcondition>0; +INSERT IGNORE INTO conditions (type, value1, value2) SELECT lootcondition, condition_value1, condition_value2 FROM milling_loot_template WHERE lootcondition>0; + +UPDATE reference_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE creature_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE gameobject_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE pickpocketing_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE item_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE fishing_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE skinning_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE disenchant_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE mail_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE prospecting_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE spell_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; +UPDATE milling_loot_template SET condition_id=(SELECT condition_entry FROM conditions WHERE type=lootcondition AND value1=condition_value1 AND value2=condition_value2 LIMIT 1) WHERE lootcondition>0; diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp index 0a9deae93..1b25dd0bb 100644 --- a/src/game/Chat.cpp +++ b/src/game/Chat.cpp @@ -520,6 +520,7 @@ ChatCommand * ChatHandler::getCommandTable() { "areatrigger_tavern", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAreaTriggerTavernCommand, "", NULL }, { "areatrigger_teleport", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAreaTriggerTeleportCommand, "", NULL }, { "command", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadCommandCommand, "", NULL }, + { "conditions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadConditionsCommand, "", NULL }, { "creature_ai_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAIScriptsCommand, "", NULL }, { "creature_ai_summons", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAISummonsCommand, "", NULL }, { "creature_ai_texts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadEventAITextsCommand, "", NULL }, diff --git a/src/game/Chat.h b/src/game/Chat.h index 948df91f0..ffa529e13 100644 --- a/src/game/Chat.h +++ b/src/game/Chat.h @@ -401,15 +401,16 @@ class MANGOS_DLL_SPEC ChatHandler bool HandleReloadAchievementRewardCommand(char* args); bool HandleReloadAreaTriggerTavernCommand(char* args); bool HandleReloadAreaTriggerTeleportCommand(char* args); + bool HandleReloadBattleEventCommand(char* args); + bool HandleReloadCommandCommand(char* args); + bool HandleReloadConditionsCommand(char* args); + bool HandleReloadCreatureQuestRelationsCommand(char* args); + bool HandleReloadCreatureQuestInvRelationsCommand(char* args); + bool HandleReloadDbScriptStringCommand(char* args); bool HandleReloadEventScriptsCommand(char* args); bool HandleReloadEventAITextsCommand(char* args); bool HandleReloadEventAISummonsCommand(char* args); bool HandleReloadEventAIScriptsCommand(char* args); - bool HandleReloadCommandCommand(char* args); - bool HandleReloadBattleEventCommand(char* args); - bool HandleReloadCreatureQuestRelationsCommand(char* args); - bool HandleReloadCreatureQuestInvRelationsCommand(char* args); - bool HandleReloadDbScriptStringCommand(char* args); bool HandleReloadGameGraveyardZoneCommand(char* args); bool HandleReloadGameObjectScriptsCommand(char* args); bool HandleReloadGameTeleCommand(char* args); diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp index 11e691a09..c08d5edbc 100644 --- a/src/game/CreatureEventAI.cpp +++ b/src/game/CreatureEventAI.cpp @@ -1405,7 +1405,7 @@ void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote) if ((*itr).Event.receive_emote.emoteId != text_emote) return; - PlayerCondition pcon((*itr).Event.receive_emote.condition,(*itr).Event.receive_emote.conditionValue1,(*itr).Event.receive_emote.conditionValue2); + PlayerCondition pcon(0, (*itr).Event.receive_emote.condition,(*itr).Event.receive_emote.conditionValue1,(*itr).Event.receive_emote.conditionValue2); if (pcon.Meets(pPlayer)) { DEBUG_FILTER_LOG(LOG_FILTER_AI_AND_MOVEGENSS, "CreatureEventAI: ReceiveEmote CreatureEventAI: Condition ok, processing"); diff --git a/src/game/CreatureEventAIMgr.cpp b/src/game/CreatureEventAIMgr.cpp index 562376b22..c08a29e3b 100644 --- a/src/game/CreatureEventAIMgr.cpp +++ b/src/game/CreatureEventAIMgr.cpp @@ -445,7 +445,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts() continue; } - if (!PlayerCondition::IsValid(ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2)) + if (!PlayerCondition::IsValid(0, ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2)) { sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.receive_emote.condition); continue; diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp index 71e960955..bc5e0170c 100644 --- a/src/game/Level3.cpp +++ b/src/game/Level3.cpp @@ -438,6 +438,14 @@ bool ChatHandler::HandleReloadCreatureQuestInvRelationsCommand(char* /*args*/) return true; } +bool ChatHandler::HandleReloadConditionsCommand(char* /*args*/) +{ + sLog.outString( "Re-Loading `conditions`... " ); + sObjectMgr.LoadConditions(); + SendGlobalSysMessage("DB table `conditions` reloaded."); + return true; +} + bool ChatHandler::HandleReloadGossipMenuCommand(char* /*args*/) { sObjectMgr.LoadGossipMenus(); diff --git a/src/game/LootMgr.cpp b/src/game/LootMgr.cpp index ac8705420..bc28059b2 100644 --- a/src/game/LootMgr.cpp +++ b/src/game/LootMgr.cpp @@ -97,8 +97,8 @@ void LootStore::LoadLootTable() sLog.outString( "%s :", GetName()); - // 0 1 2 3 4 5 6 7 8 - QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, lootcondition, condition_value1, condition_value2 FROM %s",GetName()); + // 0 1 2 3 4 5 6 + QueryResult *result = WorldDatabase.PQuery("SELECT entry, item, ChanceOrQuestChance, groupid, mincountOrRef, maxcount, condition_id FROM %s",GetName()); if (result) { @@ -115,9 +115,7 @@ void LootStore::LoadLootTable() uint8 group = fields[3].GetUInt8(); int32 mincountOrRef = fields[4].GetInt32(); uint32 maxcount = fields[5].GetUInt32(); - ConditionType condition = (ConditionType)fields[6].GetUInt8(); - uint32 cond_value1 = fields[7].GetUInt32(); - uint32 cond_value2 = fields[8].GetUInt32(); + uint16 conditionId = fields[6].GetUInt16(); if (maxcount > std::numeric_limits::max()) { @@ -125,21 +123,22 @@ void LootStore::LoadLootTable() continue; // error already printed to log/console. } - if (mincountOrRef < 0 && condition != CONDITION_NONE) + if (mincountOrRef < 0 && conditionId) { sLog.outErrorDb("Table '%s' entry %u mincountOrRef %i < 0 and not allowed has condition, skipped", GetName(), entry, mincountOrRef); continue; } - if (!PlayerCondition::IsValid(condition,cond_value1, cond_value2)) + if (conditionId) { - sLog.outErrorDb("... in table '%s' entry %u item %u", GetName(), entry, item); - continue; // error already printed to log/console. + const PlayerCondition* condition = sConditionStorage.LookupEntry(conditionId); + if (!condition) + { + sLog.outErrorDb("Table `%s` for entry %u, item %u has condition_id %u that does not exist in `conditions`, ignoring", GetName(), entry,item, conditionId); + conditionId = 0; + } } - // (condition + cond_value1/2) are converted into single conditionId - uint16 conditionId = sObjectMgr.GetConditionId(condition, cond_value1, cond_value2); - LootStoreItem storeitem = LootStoreItem(item, chanceOrQuestChance, group, conditionId, mincountOrRef, maxcount); if (!storeitem.IsValid(*this,entry)) // Validity checks @@ -363,7 +362,7 @@ LootItem::LootItem(uint32 itemid_, uint32 count_, uint32 randomSuffix_, int32 ra bool LootItem::AllowedForPlayer(Player const * player) const { // DB conditions check - if (!sObjectMgr.IsPlayerMeetToCondition(player,conditionId)) + if (conditionId && !sObjectMgr.IsPlayerMeetToNEWCondition(player, conditionId)) return false; ItemPrototype const *pProto = ObjectMgr::GetItemPrototype(itemid); @@ -396,7 +395,7 @@ bool LootItem::AllowedForPlayer(Player const * player) const LootSlotType LootItem::GetSlotTypeForSharedLoot(PermissionTypes permission, Player* viewer, bool condition_ok /*= false*/) const { // ignore looted, FFA (each player get own copy) and not allowed items - if (is_looted || freeforall || conditionId && !condition_ok || !AllowedForPlayer(viewer)) + if (is_looted || freeforall || (conditionId && !condition_ok) || !AllowedForPlayer(viewer)) return MAX_LOOT_SLOT_TYPE; switch (permission) @@ -697,7 +696,7 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qite } } } - else if(item->conditionId) + else if (item->conditionId) { QuestItemMap::const_iterator itr = m_playerNonQuestNonFFAConditionalItems.find(player->GetGUIDLow()); if (itr != m_playerNonQuestNonFFAConditionalItems.end()) diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp index 48e61b7d0..a0e656eb0 100644 --- a/src/game/ObjectMgr.cpp +++ b/src/game/ObjectMgr.cpp @@ -2376,7 +2376,6 @@ void ObjectMgr::LoadItemPrototypes() sLog.outErrorDb("Item (Entry: %u) not exist in `item_template` but referenced in `CharStartOutfit.dbc`", *itr); } - void ObjectMgr::LoadItemConverts() { m_ItemConvert.clear(); // needed for reload case @@ -4703,7 +4702,7 @@ void ObjectMgr::LoadWorldTemplate() SQLWorldLoader loader; loader.Load(sWorldTemplate, false); - for(uint32 i = 0; i < sWorldTemplate.MaxEntry; i++) + for (uint32 i = 0; i < sWorldTemplate.MaxEntry; ++i) { WorldTemplate const* temp = GetWorldTemplate(i); if (!temp) @@ -4725,7 +4724,30 @@ void ObjectMgr::LoadWorldTemplate() } } - sLog.outString( ">> Loaded %u World Template definitions", sWorldTemplate.RecordCount ); + sLog.outString( ">> Loaded %u World Template definitions", sWorldTemplate.RecordCount); + sLog.outString(); +} + +void ObjectMgr::LoadConditions() +{ + SQLWorldLoader loader; + loader.Load(sConditionStorage, false); + + for (uint32 i = 0; i < sConditionStorage.MaxEntry; ++i) + { + const PlayerCondition* condition = sConditionStorage.LookupEntry(i); + if (!condition) + continue; + + if (!condition->IsValid()) + { + sLog.outErrorDb("ObjectMgr::LoadConditions: invalid condition_entry %u, skip", i); + sConditionStorage.EraseEntry(i); + continue; + } + } + + sLog.outString( ">> Loaded %u Condition definitions", sConditionStorage.RecordCount); sLog.outString(); } @@ -7425,7 +7447,7 @@ void ObjectMgr::LoadFishingBaseSkillLevel() // Returns Id if found, else adds it to Conditions and returns Id uint16 ObjectMgr::GetConditionId( ConditionType condition, uint32 value1, uint32 value2 ) { - PlayerCondition lc = PlayerCondition(condition, value1, value2); + PlayerCondition lc = PlayerCondition(0, condition, value1, value2); for (uint16 i=0; i < mConditions.size(); ++i) { if (lc == mConditions[i]) @@ -7460,39 +7482,45 @@ bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& n // Checks if player meets the condition bool PlayerCondition::Meets(Player const * player) const { - if( !player ) + if (!player) return false; // player not present, return false - switch (condition) + switch (m_condition) { + case CONDITION_OR: + // Checked on load + return sConditionStorage.LookupEntry(m_value1)->Meets(player) || sConditionStorage.LookupEntry(m_value2)->Meets(player); + case CONDITION_AND: + // Checked on load + return sConditionStorage.LookupEntry(m_value1)->Meets(player) && sConditionStorage.LookupEntry(m_value2)->Meets(player); case CONDITION_NONE: return true; // empty condition, always met case CONDITION_AURA: - return player->HasAura(value1, SpellEffectIndex(value2)); + return player->HasAura(m_value1, SpellEffectIndex(m_value2)); case CONDITION_ITEM: - return player->HasItemCount(value1, value2); + return player->HasItemCount(m_value1, m_value2); case CONDITION_ITEM_EQUIPPED: - return player->HasItemOrGemWithIdEquipped(value1,1); + return player->HasItemOrGemWithIdEquipped(m_value1,1); case CONDITION_AREAID: { uint32 zone, area; player->GetZoneAndAreaId(zone,area); - return (zone == value1 || area == value1) == (value2 == 0); + return (zone == m_value1 || area == m_value1) == (m_value2 == 0); } case CONDITION_REPUTATION_RANK: { - FactionEntry const* faction = sFactionStore.LookupEntry(value1); - return faction && player->GetReputationMgr().GetRank(faction) >= ReputationRank(value2); + FactionEntry const* faction = sFactionStore.LookupEntry(m_value1); + return faction && player->GetReputationMgr().GetRank(faction) >= ReputationRank(m_value2); } case CONDITION_TEAM: - return uint32(player->GetTeam()) == value1; + return uint32(player->GetTeam()) == m_value1; case CONDITION_SKILL: - return player->HasSkill(value1) && player->GetBaseSkillValue(value1) >= value2; + return player->HasSkill(m_value1) && player->GetBaseSkillValue(m_value1) >= m_value2; case CONDITION_QUESTREWARDED: - return player->GetQuestRewardStatus(value1); + return player->GetQuestRewardStatus(m_value1); case CONDITION_QUESTTAKEN: { - return player->IsCurrentQuest(value1, value2); + return player->IsCurrentQuest(m_value1, m_value2); } case CONDITION_AD_COMMISSION_AURA: { @@ -7503,72 +7531,72 @@ bool PlayerCondition::Meets(Player const * player) const return false; } case CONDITION_NO_AURA: - return !player->HasAura(value1, SpellEffectIndex(value2)); + return !player->HasAura(m_value1, SpellEffectIndex(m_value2)); case CONDITION_ACTIVE_GAME_EVENT: - return sGameEventMgr.IsActiveEvent(value1); + return sGameEventMgr.IsActiveEvent(m_value1); case CONDITION_AREA_FLAG: { if (AreaTableEntry const *pAreaEntry = GetAreaEntryByAreaID(player->GetAreaId())) { - if ((!value1 || (pAreaEntry->flags & value1)) && (!value2 || !(pAreaEntry->flags & value2))) + if ((!m_value1 || (pAreaEntry->flags & m_value1)) && (!m_value2 || !(pAreaEntry->flags & m_value2))) return true; } return false; } case CONDITION_RACE_CLASS: - if ((!value1 || (player->getRaceMask() & value1)) && (!value2 || (player->getClassMask() & value2))) + if ((!m_value1 || (player->getRaceMask() & m_value1)) && (!m_value2 || (player->getClassMask() & m_value2))) return true; return false; case CONDITION_LEVEL: { - switch(value2) + switch(m_value2) { - case 0: return player->getLevel() == value1; - case 1: return player->getLevel() >= value1; - case 2: return player->getLevel() <= value1; + case 0: return player->getLevel() == m_value1; + case 1: return player->getLevel() >= m_value1; + case 2: return player->getLevel() <= m_value1; } return false; } case CONDITION_NOITEM: - return !player->HasItemCount(value1, value2); + return !player->HasItemCount(m_value1, m_value2); case CONDITION_SPELL: { - switch(value2) + switch(m_value2) { - case 0: return player->HasSpell(value1); - case 1: return !player->HasSpell(value1); + case 0: return player->HasSpell(m_value1); + case 1: return !player->HasSpell(m_value1); } return false; } case CONDITION_INSTANCE_SCRIPT: { // have meaning only for specific map instance script so ignore other maps - if (player->GetMapId() != value1) + if (player->GetMapId() != m_value1) return false; if (InstanceData* data = player->GetInstanceData()) - return data->CheckConditionCriteriaMeet(player, value1, value2); + return data->CheckConditionCriteriaMeet(player, m_value1, m_value2); return false; } case CONDITION_QUESTAVAILABLE: { - if (Quest const* quest = sObjectMgr.GetQuestTemplate(value1)) + if (Quest const* quest = sObjectMgr.GetQuestTemplate(m_value1)) return player->CanTakeQuest(quest, false); else return false; } case CONDITION_ACHIEVEMENT: { - switch(value2) + switch(m_value2) { - case 0: return player->GetAchievementMgr().HasAchievement(value1); - case 1: return !player->GetAchievementMgr().HasAchievement(value1); + case 0: return player->GetAchievementMgr().HasAchievement(m_value1); + case 1: return !player->GetAchievementMgr().HasAchievement(m_value1); } return false; } case CONDITION_ACHIEVEMENT_REALM: { - AchievementEntry const* ach = sAchievementStore.LookupEntry(value1); - switch(value2) + AchievementEntry const* ach = sAchievementStore.LookupEntry(m_value1); + switch(m_value2) { case 0: return sAchievementMgr.IsRealmCompleted(ach); case 1: return !sAchievementMgr.IsRealmCompleted(ach); @@ -7577,39 +7605,39 @@ bool PlayerCondition::Meets(Player const * player) const } case CONDITION_QUEST_NONE: { - if (!player->IsCurrentQuest(value1) && !player->GetQuestRewardStatus(value1)) + if (!player->IsCurrentQuest(m_value1) && !player->GetQuestRewardStatus(m_value1)) return true; return false; } case CONDITION_ITEM_WITH_BANK: - return player->HasItemCount(value1, value2, true); + return player->HasItemCount(m_value1, m_value2, true); case CONDITION_NOITEM_WITH_BANK: - return !player->HasItemCount(value1, value2, true); + return !player->HasItemCount(m_value1, m_value2, true); case CONDITION_NOT_ACTIVE_GAME_EVENT: - return !sGameEventMgr.IsActiveEvent(value1); + return !sGameEventMgr.IsActiveEvent(m_value1); case CONDITION_ACTIVE_HOLIDAY: - return sGameEventMgr.IsActiveHoliday(HolidayIds(value1)); + return sGameEventMgr.IsActiveHoliday(HolidayIds(m_value1)); case CONDITION_NOT_ACTIVE_HOLIDAY: - return !sGameEventMgr.IsActiveHoliday(HolidayIds(value1)); + return !sGameEventMgr.IsActiveHoliday(HolidayIds(m_value1)); case CONDITION_LEARNABLE_ABILITY: { // Already know the spell - if (player->HasSpell(value1)) + if (player->HasSpell(m_value1)) return false; // If item defined, check if player has the item already. - if (value2) + if (m_value2) { // Hard coded item count. This should be ok, since the intention with this condition is to have // a all-in-one check regarding items that learn some ability (primary/secondary tradeskills). // Commonly, items like this is unique and/or are not expected to be obtained more than once. - if (player->HasItemCount(value2, 1, true)) + if (player->HasItemCount(m_value2, 1, true)) return false; } bool isSkillOk = false; - SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(value1); + SkillLineAbilityMapBounds bounds = sSpellMgr.GetSkillLineAbilityMapBounds(m_value1); for(SkillLineAbilityMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr) { @@ -7644,31 +7672,57 @@ bool PlayerCondition::Meets(Player const * player) const return false; } case CONDITION_SKILL_BELOW: - if (value2 == 1) - return !player->HasSkill(value1); + if (m_value2 == 1) + return !player->HasSkill(m_value1); else - return player->HasSkill(value1) && player->GetBaseSkillValue(value1) < value2; + return player->HasSkill(m_value1) && player->GetBaseSkillValue(m_value1) < m_value2; default: return false; } } // Verification of condition values validity -bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 value2) +bool PlayerCondition::IsValid(uint16 entry, ConditionType condition, uint32 value1, uint32 value2) { - switch(condition) + switch (condition) { + case CONDITION_OR: + case CONDITION_AND: + { + if (value1 >= entry) + { + sLog.outErrorDb("And or Or condition (entry %u, type %d) has invalid value1 %u, must be lower than entry, skipped", entry, condition, value1); + return false; + } + if (value2 >= entry) + { + sLog.outErrorDb("And or Or condition (entry %u, type %d) has invalid value2 %u, must be lower than entry, skipped", entry, condition, value2); + return false; + } + const PlayerCondition* condition1 = sConditionStorage.LookupEntry(value1); + if (!condition1) + { + sLog.outErrorDb("And or Or condition (entry %u, type %d) has value1 %u without proper condition, skipped", entry, condition, value1); + return false; + } + const PlayerCondition* condition2 = sConditionStorage.LookupEntry(value2); + if (!condition2) + { + sLog.outErrorDb("And or Or condition (entry %u, type %d) has value2 %u without proper condition, skipped", entry, condition, value2); + return false; + } + break; + } case CONDITION_AURA: { if (!sSpellStore.LookupEntry(value1)) { - sLog.outErrorDb("Aura condition (%u) requires to have non existing spell (Id: %d), skipped", condition, value1); + sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry, condition, value1); return false; } if (value2 >= MAX_EFFECT_INDEX) { - sLog.outErrorDb("Aura condition (%u) requires to have non existing effect index (%u) (must be 0..%u), skipped", - condition, value2, MAX_EFFECT_INDEX-1); + sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing effect index (%u) (must be 0..%u), skipped", entry, condition, value2, MAX_EFFECT_INDEX-1); return false; } break; @@ -7681,13 +7735,13 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val ItemPrototype const *proto = ObjectMgr::GetItemPrototype(value1); if (!proto) { - sLog.outErrorDb("Item condition (%u) requires to have non existing item (%u), skipped", condition, value1); + sLog.outErrorDb("Item condition (entry %u, type %u) requires to have non existing item (%u), skipped", entry, condition, value1); return false; } if (value2 < 1) { - sLog.outErrorDb("Item condition (%u) useless with count < 1, skipped", condition); + sLog.outErrorDb("Item condition (entry %u, type %u) useless with count < 1, skipped", entry, condition); return false; } break; @@ -7697,7 +7751,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val ItemPrototype const *proto = ObjectMgr::GetItemPrototype(value1); if (!proto) { - sLog.outErrorDb("ItemEquipped condition (%u) requires to have non existing item (%u) equipped, skipped", condition, value1); + sLog.outErrorDb("ItemEquipped condition (entry %u, type %u) requires to have non existing item (%u) equipped, skipped", entry, condition, value1); return false; } break; @@ -7707,13 +7761,13 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(value1); if (!areaEntry) { - sLog.outErrorDb("Zone condition (%u) requires to be in non existing area (%u), skipped", condition, value1); + sLog.outErrorDb("Zone condition (entry %u, type %u) requires to be in non existing area (%u), skipped", entry, condition, value1); return false; } if (value2 > 1) { - sLog.outErrorDb("Zone condition (%u) has invalid argument %u (must be 0..1), skipped", condition, value2); + sLog.outErrorDb("Zone condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry, condition, value2); return false; } break; @@ -7723,7 +7777,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val FactionEntry const* factionEntry = sFactionStore.LookupEntry(value1); if (!factionEntry) { - sLog.outErrorDb("Reputation condition (%u) requires to have reputation non existing faction (%u), skipped", condition, value1); + sLog.outErrorDb("Reputation condition (entry %u, type %u) requires to have reputation non existing faction (%u), skipped", entry, condition, value1); return false; } break; @@ -7732,7 +7786,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val { if (value1 != ALLIANCE && value1 != HORDE) { - sLog.outErrorDb("Team condition (%u) specifies unknown team (%u), skipped", condition, value1); + sLog.outErrorDb("Team condition (entry %u, type %u) specifies unknown team (%u), skipped", entry, condition, value1); return false; } break; @@ -7743,12 +7797,12 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val SkillLineEntry const *pSkill = sSkillLineStore.LookupEntry(value1); if (!pSkill) { - sLog.outErrorDb("Skill condition (%u) specifies non-existing skill (%u), skipped", condition, value1); + sLog.outErrorDb("Skill condition (entry %u, type %u) specifies non-existing skill (%u), skipped", entry, condition, value1); return false; } if (value2 < 1 || value2 > sWorld.GetConfigMaxSkillValue() ) { - sLog.outErrorDb("Skill condition (%u) specifies invalid skill value (%u), skipped", condition, value2); + sLog.outErrorDb("Skill condition (entry %u, type %u) specifies invalid skill value (%u), skipped", entry, condition, value2); return false; } break; @@ -7761,32 +7815,32 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val Quest const *Quest = sObjectMgr.GetQuestTemplate(value1); if (!Quest) { - sLog.outErrorDb("Quest condition (%u) specifies non-existing quest (%u), skipped", condition, value1); + sLog.outErrorDb("Quest condition (entry %u, type %u) specifies non-existing quest (%u), skipped", entry, condition, value1); return false; } if (value2 && condition != CONDITION_QUESTTAKEN) - sLog.outErrorDb("Quest condition (%u) has useless data in value2 (%u)!", condition, value2); + sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", entry, condition, value2); break; } case CONDITION_AD_COMMISSION_AURA: { if (value1) - sLog.outErrorDb("Quest condition (%u) has useless data in value1 (%u)!", condition, value1); + sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value1 (%u)!", entry, condition, value1); if (value2) - sLog.outErrorDb("Quest condition (%u) has useless data in value2 (%u)!", condition, value2); + sLog.outErrorDb("Quest condition (entry %u, type %u) has useless data in value2 (%u)!", entry, condition, value2); break; } case CONDITION_NO_AURA: { if (!sSpellStore.LookupEntry(value1)) { - sLog.outErrorDb("Aura condition (%u) requires to have non existing spell (Id: %d), skipped", condition, value1); + sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry, condition, value1); return false; } if (value2 > MAX_EFFECT_INDEX) { - sLog.outErrorDb("Aura condition (%u) requires to have non existing effect index (%u) (must be 0..%u), skipped", condition, value2, MAX_EFFECT_INDEX-1); + sLog.outErrorDb("Aura condition (entry %u, type %u) requires to have non existing effect index (%u) (must be 0..%u), skipped", entry, condition, value2, MAX_EFFECT_INDEX-1); return false; } break; @@ -7796,7 +7850,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val { if (!sGameEventMgr.IsValidEvent(value1)) { - sLog.outErrorDb("Active event (%u) condition requires existing event id (%u), skipped", condition, value1); + sLog.outErrorDb("(Not)Active event condition (entry %u, type %u) requires existing event id (%u), skipped", entry, condition, value1); return false; } break; @@ -7805,7 +7859,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val { if (!value1 && !value2) { - sLog.outErrorDb("Area flag (%u) condition has both values like 0, skipped", condition); + sLog.outErrorDb("Area flag condition (entry %u, type %u) has both values like 0, skipped", entry, condition); return false; } break; @@ -7814,19 +7868,19 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val { if (!value1 && !value2) { - sLog.outErrorDb("Race_class condition (%u) has both values like 0, skipped", condition); + sLog.outErrorDb("Race_class condition (entry %u, type %u) has both values like 0, skipped", entry, condition); return false; } if (value1 && !(value1 & RACEMASK_ALL_PLAYABLE)) { - sLog.outErrorDb("Race_class condition (%u) has invalid player class %u, skipped", condition, value1); + sLog.outErrorDb("Race_class condition (entry %u, type %u) has invalid player class %u, skipped", entry, condition, value1); return false; } if (value2 && !(value2 & CLASSMASK_ALL_PLAYABLE)) { - sLog.outErrorDb("Race_class condition (%u) has invalid race mask %u, skipped", condition, value2); + sLog.outErrorDb("Race_class condition (entry %u, type %u) has invalid race mask %u, skipped", entry, condition, value2); return false; } break; @@ -7835,13 +7889,13 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val { if (!value1 || value1 > sWorld.getConfig(CONFIG_UINT32_MAX_PLAYER_LEVEL)) { - sLog.outErrorDb("Level condition (%u) has invalid level %u, skipped", condition, value1); + sLog.outErrorDb("Level condition (entry %u, type %u)has invalid level %u, skipped", entry, condition, value1); return false; } if (value2 > 2) { - sLog.outErrorDb("Level condition (%u) has invalid argument %u (must be 0..2), skipped", condition, value2); + sLog.outErrorDb("Level condition (entry %u, type %u) has invalid argument %u (must be 0..2), skipped", entry, condition, value2); return false; } @@ -7851,13 +7905,13 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val { if (!sSpellStore.LookupEntry(value1)) { - sLog.outErrorDb("Spell condition (%u) requires to have non existing spell (Id: %d), skipped", condition, value1); + sLog.outErrorDb("Spell condition (entry %u, type %u) requires to have non existing spell (Id: %d), skipped", entry, condition, value1); return false; } if (value2 > 1) { - sLog.outErrorDb("Spell condition (%u) has invalid argument %u (must be 0..1), skipped", condition, value2); + sLog.outErrorDb("Spell condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry, condition, value2); return false; } @@ -7868,7 +7922,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val MapEntry const* mapEntry = sMapStore.LookupEntry(value1); if (!mapEntry || !mapEntry->IsDungeon()) { - sLog.outErrorDb("Instance script condition (%u) has nonexistent map id %u as first arg, skipped", condition, value1); + sLog.outErrorDb("Instance script condition (entry %u, type %u) has nonexistent map id %u as first arg, skipped", entry, condition, value1); return false; } @@ -7879,13 +7933,13 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val { if (!sAchievementStore.LookupEntry(value1)) { - sLog.outErrorDb("Achievement condition (%u) requires to have non existing achievement (Id: %d), skipped", condition, value1); + sLog.outErrorDb("Achievement condition (entry %u, type %u) requires to have non existing achievement (Id: %d), skipped", entry, condition, value1); return false; } if (value2 > 1) { - sLog.outErrorDb("Achievement condition (%u) has invalid argument %u (must be 0..1), skipped", condition, value2); + sLog.outErrorDb("Achievement condition (entry %u, type %u) has invalid argument %u (must be 0..1), skipped", entry, condition, value2); return false; } @@ -7896,7 +7950,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val { if (!sHolidaysStore.LookupEntry(value1)) { - sLog.outErrorDb("Active holiday (%u) condition requires existing holiday id (%u), skipped", condition, value1); + sLog.outErrorDb("(Not)Active holiday condition (entry %u, type %u) requires existing holiday id (%u), skipped", entry, condition, value1); return false; } break; @@ -7907,7 +7961,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val if (bounds.first == bounds.second) { - sLog.outErrorDb("CONDITION_LEARNABLE_ABILITY (%u) has spell id %u defined, but this spell is not listed in SkillLineAbility and can not be used, skipping.", condition, value1); + sLog.outErrorDb("Learnable ability conditon (entry %u, type %u) has spell id %u defined, but this spell is not listed in SkillLineAbility and can not be used, skipping.", entry, condition, value1); return false; } @@ -7916,7 +7970,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val ItemPrototype const *proto = ObjectMgr::GetItemPrototype(value2); if (!proto) { - sLog.outErrorDb("CONDITION_LEARNABLE_ABILITY (%u) has item entry %u defined but item does not exist, skipping.", condition, value2); + sLog.outErrorDb("Learnable ability conditon (entry %u, type %u) has item entry %u defined but item does not exist, skipping.", entry, condition, value2); return false; } } @@ -7926,7 +7980,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val case CONDITION_NONE: break; default: - sLog.outErrorDb("Condition has bad type of %u, skipped ", condition); + sLog.outErrorDb("Condition entry %u has bad type of %d, skipped ", entry, condition); return false; } return true; @@ -8488,8 +8542,8 @@ void ObjectMgr::LoadGossipMenu(std::set& gossipScriptSet) m_mGossipMenusMap.clear(); // 0 1 2 QueryResult* result = WorldDatabase.Query("SELECT entry, text_id, script_id, " - // 3 4 5 6 7 8 - "cond_1, cond_1_val_1, cond_1_val_2, cond_2, cond_2_val_1, cond_2_val_2 FROM gossip_menu"); + // 3 4 5 6 7 8 9 + "cond_1, cond_1_val_1, cond_1_val_2, cond_2, cond_2_val_1, cond_2_val_2, condition_id FROM gossip_menu"); if (!result) { @@ -8525,6 +8579,8 @@ void ObjectMgr::LoadGossipMenu(std::set& gossipScriptSet) uint32 cond_2_val_1 = fields[7].GetUInt32(); uint32 cond_2_val_2 = fields[8].GetUInt32(); + gMenu.conditionId = fields[9].GetUInt16(); + if (!GetGossipText(gMenu.text_id)) { sLog.outErrorDb("Table gossip_menu entry %u are using non-existing text_id %u", gMenu.entry, gMenu.text_id); @@ -8544,13 +8600,13 @@ void ObjectMgr::LoadGossipMenu(std::set& gossipScriptSet) gossipScriptSet.erase(gMenu.script_id); } - if (!PlayerCondition::IsValid(cond_1, cond_1_val_1, cond_1_val_2)) + if (!PlayerCondition::IsValid(0, cond_1, cond_1_val_1, cond_1_val_2)) { sLog.outErrorDb("Table gossip_menu entry %u, invalid condition 1 for id %u", gMenu.entry, gMenu.text_id); continue; } - if (!PlayerCondition::IsValid(cond_2, cond_2_val_1, cond_2_val_2)) + if (!PlayerCondition::IsValid(0, cond_2, cond_2_val_1, cond_2_val_2)) { sLog.outErrorDb("Table gossip_menu entry %u, invalid condition 2 for id %u", gMenu.entry, gMenu.text_id); continue; @@ -8559,6 +8615,16 @@ void ObjectMgr::LoadGossipMenu(std::set& gossipScriptSet) gMenu.cond_1 = GetConditionId(cond_1, cond_1_val_1, cond_1_val_2); gMenu.cond_2 = GetConditionId(cond_2, cond_2_val_1, cond_2_val_2); + if (gMenu.conditionId) + { + const PlayerCondition* condition = sConditionStorage.LookupEntry(gMenu.conditionId); + if (!condition) + { + sLog.outErrorDb("Table gossip_menu for menu %u, text-id %u has condition_id %u that does not exist in `conditions`, ignoring", gMenu.entry, gMenu.text_id, gMenu.conditionId); + gMenu.conditionId = 0; + } + } + m_mGossipMenusMap.insert(GossipMenusMap::value_type(gMenu.entry, gMenu)); ++count; @@ -8593,7 +8659,7 @@ void ObjectMgr::LoadGossipMenuItems(std::set& gossipScriptSet) "action_menu_id, action_poi_id, action_script_id, box_coded, box_money, box_text, " "cond_1, cond_1_val_1, cond_1_val_2, " "cond_2, cond_2_val_1, cond_2_val_2, " - "cond_3, cond_3_val_1, cond_3_val_2 " + "cond_3, cond_3_val_1, cond_3_val_2, condition_id " "FROM gossip_menu_option"); if (!result) @@ -8664,6 +8730,7 @@ void ObjectMgr::LoadGossipMenuItems(std::set& gossipScriptSet) ConditionType cond_3 = (ConditionType)fields[18].GetUInt32(); uint32 cond_3_val_1 = fields[19].GetUInt32(); uint32 cond_3_val_2 = fields[20].GetUInt32(); + gMenuItem.conditionId = fields[21].GetUInt16(); if (gMenuItem.menu_id) // == 0 id is special and not have menu_id data { @@ -8674,17 +8741,17 @@ void ObjectMgr::LoadGossipMenuItems(std::set& gossipScriptSet) } } - if (!PlayerCondition::IsValid(cond_1, cond_1_val_1, cond_1_val_2)) + if (!PlayerCondition::IsValid(0, cond_1, cond_1_val_1, cond_1_val_2)) { sLog.outErrorDb("Table gossip_menu_option menu %u, invalid condition 1 for id %u", gMenuItem.menu_id, gMenuItem.id); continue; } - if (!PlayerCondition::IsValid(cond_2, cond_2_val_1, cond_2_val_2)) + if (!PlayerCondition::IsValid(0, cond_2, cond_2_val_1, cond_2_val_2)) { sLog.outErrorDb("Table gossip_menu_option menu %u, invalid condition 2 for id %u", gMenuItem.menu_id, gMenuItem.id); continue; } - if (!PlayerCondition::IsValid(cond_3, cond_3_val_1, cond_3_val_2)) + if (!PlayerCondition::IsValid(0, cond_3, cond_3_val_1, cond_3_val_2)) { sLog.outErrorDb("Table gossip_menu_option menu %u, invalid condition 3 for id %u", gMenuItem.menu_id, gMenuItem.id); continue; @@ -8757,6 +8824,16 @@ void ObjectMgr::LoadGossipMenuItems(std::set& gossipScriptSet) gMenuItem.cond_2 = GetConditionId(cond_2, cond_2_val_1, cond_2_val_2); gMenuItem.cond_3 = GetConditionId(cond_3, cond_3_val_1, cond_3_val_2); + if (gMenuItem.conditionId) + { + const PlayerCondition* condition = sConditionStorage.LookupEntry(gMenuItem.conditionId); + if (!condition) + { + sLog.outErrorDb("Table gossip_menu_option for menu %u, id %u has condition_id %u that does not exist in `conditions`, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.conditionId); + gMenuItem.conditionId = 0; + } + } + m_mGossipMenuItemsMap.insert(GossipMenuItemsMap::value_type(gMenuItem.menu_id, gMenuItem)); ++count; diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h index c569b80bc..229d9b2c3 100644 --- a/src/game/ObjectMgr.h +++ b/src/game/ObjectMgr.h @@ -257,6 +257,7 @@ struct GossipMenuItems uint16 cond_1; uint16 cond_2; uint16 cond_3; + uint16 conditionId; }; struct GossipMenus @@ -266,6 +267,7 @@ struct GossipMenus uint32 script_id; uint16 cond_1; uint16 cond_2; + uint16 conditionId; }; typedef std::multimap GossipMenusMap; @@ -336,6 +338,8 @@ typedef std::pair Gr enum ConditionType { // value1 value2 for the Condition enumed + CONDITION_OR = -2, // cond-id-1 cond-id-2 returns cond-id-1 OR cond-id-2 + CONDITION_AND = -1, // cond-id-1 cond-id-2 returns cond-id-1 AND cond-id-2 CONDITION_NONE = 0, // 0 0 CONDITION_AURA = 1, // spell_id effindex CONDITION_ITEM = 2, // item_id count check present req. amount items in inventory @@ -373,22 +377,32 @@ enum ConditionType // If skill_value == 1, then true if player has not skill skill_id }; -struct PlayerCondition +class PlayerCondition { - ConditionType condition; // additional condition type - uint32 value1; // data for the condition - see ConditionType definition - uint32 value2; + public: + // Default constructor, required for SQL Storage (Will give errors if used elsewise) + PlayerCondition() : m_entry(0), m_condition(CONDITION_AND), m_value1(0), m_value2(0) {} - PlayerCondition(uint8 _condition = 0, uint32 _value1 = 0, uint32 _value2 = 0) - : condition(ConditionType(_condition)), value1(_value1), value2(_value2) {} + PlayerCondition(uint16 _entry, int16 _condition, uint32 _value1, uint32 _value2) + : m_entry(_entry), m_condition(ConditionType(_condition)), m_value1(_value1), m_value2(_value2) {} - static bool IsValid(ConditionType condition, uint32 value1, uint32 value2); - // Checks correctness of values - bool Meets(Player const * APlayer) const; // Checks if the player meets the condition - bool operator == (PlayerCondition const& lc) const - { - return (lc.condition == condition && lc.value1 == value1 && lc.value2 == value2); - } + // Checks correctness of values + bool IsValid() const { return IsValid(m_entry, m_condition, m_value1, m_value2); } + static bool IsValid(uint16 entry, ConditionType condition, uint32 value1, uint32 value2); + + bool Meets(Player const* pPlayer) const; // Checks if the player meets the condition + + // TODO: old system, remove soon! + bool operator == (PlayerCondition const& lc) const + { + return (lc.m_condition == m_condition && lc.m_value1 == m_value1 && lc.m_value2 == m_value2); + } + + private: + uint16 m_entry; // entry of the condition + ConditionType m_condition; // additional condition type + uint32 m_value1; // data for the condition - see ConditionType definition + uint32 m_value2; }; // NPC gossip text id @@ -673,6 +687,7 @@ class ObjectMgr void LoadInstanceEncounters(); void LoadInstanceTemplate(); void LoadWorldTemplate(); + void LoadConditions(); void LoadMailLevelRewards(); void LoadGossipText(); @@ -927,6 +942,7 @@ class ObjectMgr int GetIndexForLocale(LocaleConstant loc); LocaleConstant GetLocaleForIndex(int i); + // TODO: Outdated version, rename NEW and remove soon uint16 GetConditionId(ConditionType condition, uint32 value1, uint32 value2); bool IsPlayerMeetToCondition(Player const* player, uint16 condition_id) const { @@ -936,6 +952,18 @@ class ObjectMgr return mConditions[condition_id].Meets(player); } + // Check if a player meets condition conditionId + bool IsPlayerMeetToNEWCondition(Player const* pPlayer, uint16 conditionId) const + { + if (!pPlayer) + return false; // player not present, return false + + if (const PlayerCondition* condition = sConditionStorage.LookupEntry(conditionId)) + return condition->Meets(pPlayer); + + return false; + } + GameTele const* GetGameTele(uint32 id) const { GameTeleMap::const_iterator itr = m_GameTeleMap.find(id); diff --git a/src/game/Player.cpp b/src/game/Player.cpp index 2c378d82e..f7746e228 100644 --- a/src/game/Player.cpp +++ b/src/game/Player.cpp @@ -12969,6 +12969,14 @@ void Player::PrepareGossipMenu(WorldObject *pSource, uint32 menuId) bool hasMenuItem = true; if (!isGameMaster()) // Let GM always see menu items regardless of conditions + { + if (itr->second.conditionId && !sObjectMgr.IsPlayerMeetToNEWCondition(this, itr->second.conditionId)) + { + if (itr->second.option_id == GOSSIP_OPTION_QUESTGIVER) + canSeeQuests = false; + continue; + } + else if (!itr->second.conditionId) { if (itr->second.cond_1 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_1)) { @@ -12991,6 +12999,7 @@ void Player::PrepareGossipMenu(WorldObject *pSource, uint32 menuId) continue; } } + } if (pSource->GetTypeId() == TYPEID_UNIT) { @@ -13340,7 +13349,18 @@ uint32 Player::GetGossipTextId(uint32 menuId, WorldObject* pSource) GossipMenusMapBounds pMenuBounds = sObjectMgr.GetGossipMenusMapBounds(menuId); - for(GossipMenusMap::const_iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr) + for (GossipMenusMap::const_iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr) + { + if (itr->second.conditionId && sObjectMgr.IsPlayerMeetToNEWCondition(this, itr->second.conditionId)) + { + textId = itr->second.text_id; + + // Start related script + if (itr->second.script_id) + GetMap()->ScriptsStart(sGossipScripts, itr->second.script_id, this, pSource); + break; + } + else if (!itr->second.conditionId) { if (sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_1) && sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_2)) { @@ -13352,6 +13372,7 @@ uint32 Player::GetGossipTextId(uint32 menuId, WorldObject* pSource) break; } } + } return textId; } diff --git a/src/game/SQLStorages.cpp b/src/game/SQLStorages.cpp index 109582d7e..9a93eb320 100644 --- a/src/game/SQLStorages.cpp +++ b/src/game/SQLStorages.cpp @@ -37,6 +37,8 @@ const char InstanceTemplatesrcfmt[]="iiiis"; const char InstanceTemplatedstfmt[]="iiiii"; const char WorldTemplatesrcfmt[]="is"; const char WorldTemplatedstfmt[]="ii"; +const char ConditionsSrcFmt[]="iiii"; +const char ConditionsDstFmt[]="iiii"; SQLStorage sCreatureStorage(CreatureInfosrcfmt, CreatureInfodstfmt, "entry","creature_template"); SQLStorage sCreatureDataAddonStorage(CreatureDataAddonInfofmt,"guid","creature_addon"); @@ -49,3 +51,4 @@ SQLStorage sItemStorage(ItemPrototypesrcfmt, ItemPrototypedstfmt, "entry","item_ SQLStorage sPageTextStore(PageTextfmt,"entry","page_text"); SQLStorage sInstanceTemplate(InstanceTemplatesrcfmt, InstanceTemplatedstfmt, "map","instance_template"); SQLStorage sWorldTemplate(WorldTemplatesrcfmt, WorldTemplatedstfmt, "map","world_template"); +SQLStorage sConditionStorage(ConditionsSrcFmt, ConditionsDstFmt, "condition_entry", "conditions"); diff --git a/src/game/SQLStorages.h b/src/game/SQLStorages.h index f844c2b4f..7d313b995 100644 --- a/src/game/SQLStorages.h +++ b/src/game/SQLStorages.h @@ -33,5 +33,6 @@ extern SQLStorage sPageTextStore; extern SQLStorage sItemStorage; extern SQLStorage sInstanceTemplate; extern SQLStorage sWorldTemplate; +extern SQLStorage sConditionStorage; #endif diff --git a/src/game/World.cpp b/src/game/World.cpp index 5a4335e28..a72ec8d71 100644 --- a/src/game/World.cpp +++ b/src/game/World.cpp @@ -1105,6 +1105,10 @@ void World::SetInitialWorldSettings() sLog.outString( ">>> Game Event Data loaded" ); sLog.outString(); + // Load Conditions + sLog.outString( "Loading Conditions..." ); + sObjectMgr.LoadConditions(); + sLog.outString( "Creating map persistent states for non-instanceable maps..." ); // must be after PackInstances(), LoadCreatures(), sPoolMgr.LoadFromDB(), sGameEventMgr.LoadFromDB(); sMapPersistentStateMgr.InitWorldMaps(); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 16315479d..fac601066 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 "11963" + #define REVISION_NR "11964" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index cbcaf821d..499a16727 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ #define REVISION_DB_CHARACTERS "required_11785_02_characters_instance" - #define REVISION_DB_MANGOS "required_11958_01_mangos_mangos_string" + #define REVISION_DB_MANGOS "required_11964_01_mangos_conditions" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #endif // __REVISION_SQL_H__