[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 <schmoozerd@scriptdev2.com>
This commit is contained in:
Schmoozerd 2012-04-15 22:40:13 +02:00
parent f3b5e1e4bc
commit 8c29893310
18 changed files with 729 additions and 132 deletions

View file

@ -0,0 +1,332 @@
#
# Copyright (C) 2005-2012 MaNGOS <http://getmangos.com/>
#
# 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()

View file

@ -0,0 +1,33 @@
#
# Copyright (C) 2005-2012 MaNGOS <http://getmangos.com/>
#
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)
<DB_NAME>_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!

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<uint8>::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<PlayerCondition>(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())

View file

@ -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<PlayerCondition>(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<PlayerCondition>(m_value1)->Meets(player) || sConditionStorage.LookupEntry<PlayerCondition>(m_value2)->Meets(player);
case CONDITION_AND:
// Checked on load
return sConditionStorage.LookupEntry<PlayerCondition>(m_value1)->Meets(player) && sConditionStorage.LookupEntry<PlayerCondition>(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<PlayerCondition>(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<PlayerCondition>(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<uint32>& 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<uint32>& 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<uint32>& 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<uint32>& 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<PlayerCondition>(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<uint32>& 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<uint32>& 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<uint32>& 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<uint32>& 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<PlayerCondition>(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;

View file

@ -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<uint32,GossipMenus> GossipMenusMap;
@ -336,6 +338,8 @@ typedef std::pair<GraveYardMap::const_iterator, GraveYardMap::const_iterator> 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<PlayerCondition>(conditionId))
return condition->Meets(pPlayer);
return false;
}
GameTele const* GetGameTele(uint32 id) const
{
GameTeleMap::const_iterator itr = m_GameTeleMap.find(id);

View file

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

View file

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

View file

@ -33,5 +33,6 @@ extern SQLStorage sPageTextStore;
extern SQLStorage sItemStorage;
extern SQLStorage sInstanceTemplate;
extern SQLStorage sWorldTemplate;
extern SQLStorage sConditionStorage;
#endif

View file

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

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "11963"
#define REVISION_NR "11964"
#endif // __REVISION_NR_H__

View file

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