[8923] Restructure gossip menus and make it possible to build selections by database

As result, gossip for GO is now possible. Moved related data structures and remove useless from code.
Please note that after some time, table npc_gossip will be fully removed (use menuId in _template in relation to gossip_menu as replacement).
Special thanks to GriffonHeart for help with research, discussions and ideas of code and thanks to Vladimir for helpful input.

Signed-off-by: NoFantasy <nofantasy@nf.no>
This commit is contained in:
NoFantasy 2009-12-06 02:26:29 +01:00
parent 8b0ce112ba
commit 090c8b8854
19 changed files with 689 additions and 455 deletions

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_8917_01_mangos_spell_proc_event` bit(1) default NULL
`required_8923_01_mangos_gossip` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes';
--
@ -1899,6 +1899,88 @@ LOCK TABLES `gameobject_template` WRITE;
/*!40000 ALTER TABLE `gameobject_template` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `gossip_menu`
--
DROP TABLE IF EXISTS gossip_menu;
CREATE TABLE gossip_menu (
entry smallint(6) unsigned NOT NULL default '0',
text_id mediumint(8) unsigned NOT NULL default '0',
cond_1 tinyint(3) unsigned NOT NULL default '0',
cond_1_val_1 mediumint(8) unsigned NOT NULL default '0',
cond_1_val_2 mediumint(8) unsigned NOT NULL default '0',
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',
PRIMARY KEY (entry, text_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `gossip_menu`
--
LOCK TABLES `gossip_menu` WRITE;
/*!40000 ALTER TABLE `gossip_menu` DISABLE KEYS */;
/*!40000 ALTER TABLE `gossip_menu` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `gossip_menu_option`
--
DROP TABLE IF EXISTS gossip_menu_option;
CREATE TABLE gossip_menu_option (
menu_id smallint(6) unsigned NOT NULL default '0',
id smallint(6) unsigned NOT NULL default '0',
option_icon mediumint(8) unsigned NOT NULL default '0',
option_text text,
option_id tinyint(3) unsigned NOT NULL default '0',
npc_option_npcflag int(10) unsigned NOT NULL default '0',
action_menu_id mediumint(8) unsigned NOT NULL default '0',
action_poi_id mediumint(8) unsigned NOT NULL default '0',
action_script_id mediumint(8) unsigned NOT NULL default '0',
box_coded tinyint(3) unsigned NOT NULL default '0',
box_money int(11) unsigned NOT NULL default '0',
box_text text,
cond_1 tinyint(3) unsigned NOT NULL default '0',
cond_1_val_1 mediumint(8) unsigned NOT NULL default '0',
cond_1_val_2 mediumint(8) unsigned NOT NULL default '0',
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',
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',
PRIMARY KEY (menu_id, id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `gossip_menu_option`
--
LOCK TABLES `gossip_menu_option` WRITE;
/*!40000 ALTER TABLE `gossip_menu_option` DISABLE KEYS */;
INSERT INTO gossip_menu_option VALUES
(0,0,0,'GOSSIP_OPTION_QUESTGIVER',2,2,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,1,1,'GOSSIP_OPTION_VENDOR',3,128,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,2,2,'GOSSIP_OPTION_TAXIVENDOR',4,8192,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,3,3,'GOSSIP_OPTION_TRAINER',5,16,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,4,4,'GOSSIP_OPTION_SPIRITHEALER',6,16384,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,5,4,'GOSSIP_OPTION_SPIRITGUIDE',7,32768,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,6,5,'GOSSIP_OPTION_INNKEEPER',8,65536,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,7,6,'GOSSIP_OPTION_BANKER',9,131072,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,8,7,'GOSSIP_OPTION_PETITIONER',10,262144,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,9,8,'GOSSIP_OPTION_TABARDDESIGNER',11,524288,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,10,9,'GOSSIP_OPTION_BATTLEFIELD',12,1048576,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,11,6,'GOSSIP_OPTION_AUCTIONEER',13,2097152,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,12,0,'GOSSIP_OPTION_STABLEPET',14,4194304,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,13,1,'GOSSIP_OPTION_ARMORER',15,4096,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,14,2,'GOSSIP_OPTION_UNLEARNTALENTS',16,16,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,15,2,'GOSSIP_OPTION_UNLEARNPETSKILLS',17,16,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0);
/*!40000 ALTER TABLE `gossip_menu` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `instance_template`
--
@ -2344,6 +2426,42 @@ LOCK TABLES `locales_gameobject` WRITE;
/*!40000 ALTER TABLE `locales_gameobject` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `locales_gossip_menu_option`
--
DROP TABLE IF EXISTS `locales_gossip_menu_option`;
CREATE TABLE `locales_gossip_menu_option` (
`menu_id` smallint(6) unsigned NOT NULL default '0',
`id` smallint(6) unsigned NOT NULL default '0',
`option_text_loc1` text,
`option_text_loc2` text,
`option_text_loc3` text,
`option_text_loc4` text,
`option_text_loc5` text,
`option_text_loc6` text,
`option_text_loc7` text,
`option_text_loc8` text,
`box_text_loc1` text,
`box_text_loc2` text,
`box_text_loc3` text,
`box_text_loc4` text,
`box_text_loc5` text,
`box_text_loc6` text,
`box_text_loc7` text,
`box_text_loc8` text,
PRIMARY KEY (`menu_id`, `id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `locales_gossip_menu_option`
--
LOCK TABLES `locales_gossip_menu_option` WRITE;
/*!40000 ALTER TABLE `locales_gossip_menu_option` DISABLE KEYS */;
/*!40000 ALTER TABLE `locales_gossip_menu_option` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `locales_item`
--
@ -2379,42 +2497,6 @@ LOCK TABLES `locales_item` WRITE;
/*!40000 ALTER TABLE `locales_item` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `locales_npc_option`
--
DROP TABLE IF EXISTS `locales_npc_option`;
CREATE TABLE `locales_npc_option` (
`entry` mediumint(8) unsigned NOT NULL default '0',
`option_text_loc1` text,
`option_text_loc2` text,
`option_text_loc3` text,
`option_text_loc4` text,
`option_text_loc5` text,
`option_text_loc6` text,
`option_text_loc7` text,
`option_text_loc8` text,
`box_text_loc1` text,
`box_text_loc2` text,
`box_text_loc3` text,
`box_text_loc4` text,
`box_text_loc5` text,
`box_text_loc6` text,
`box_text_loc7` text,
`box_text_loc8` text,
PRIMARY KEY (`entry`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `locales_npc_option`
--
LOCK TABLES `locales_npc_option` WRITE;
/*!40000 ALTER TABLE `locales_npc_option` DISABLE KEYS */;
/*!40000 ALTER TABLE `locales_npc_option` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `locales_npc_text`
--
@ -3530,54 +3612,6 @@ LOCK TABLES `npc_gossip` WRITE;
/*!40000 ALTER TABLE `npc_gossip` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `npc_gossip_textid`
--
DROP TABLE IF EXISTS `npc_gossip_textid`;
CREATE TABLE `npc_gossip_textid` (
`zoneid` smallint(5) unsigned NOT NULL default '0',
`action` smallint(5) unsigned NOT NULL default '0',
`textid` mediumint(8) unsigned NOT NULL default '0',
KEY `zoneid` (`zoneid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `npc_gossip_textid`
--
LOCK TABLES `npc_gossip_textid` WRITE;
/*!40000 ALTER TABLE `npc_gossip_textid` DISABLE KEYS */;
/*!40000 ALTER TABLE `npc_gossip_textid` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `npc_option`
--
DROP TABLE IF EXISTS `npc_option`;
CREATE TABLE `npc_option` (
`id` mediumint(8) unsigned NOT NULL default '0',
`gossip_id` mediumint(8) unsigned NOT NULL default '0',
`npcflag` int(10) unsigned NOT NULL default '0',
`icon` tinyint(3) unsigned NOT NULL default '0',
`action` mediumint(8) unsigned NOT NULL default '0',
`box_money` int(10) unsigned NOT NULL default '0',
`coded` tinyint(3) unsigned NOT NULL default '0',
`option_text` text,
`box_text` text,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
--
-- Dumping data for table `npc_option`
--
LOCK TABLES `npc_option` WRITE;
/*!40000 ALTER TABLE `npc_option` DISABLE KEYS */;
/*!40000 ALTER TABLE `npc_option` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `npc_spellclick_spells`
--

View file

@ -0,0 +1,72 @@
ALTER TABLE db_version CHANGE COLUMN required_8917_01_mangos_spell_proc_event required_8923_01_mangos_gossip bit;
DROP TABLE IF EXISTS gossip_menu;
CREATE TABLE gossip_menu (
entry smallint(6) unsigned NOT NULL default '0',
text_id mediumint(8) unsigned NOT NULL default '0',
cond_1 tinyint(3) unsigned NOT NULL default '0',
cond_1_val_1 mediumint(8) unsigned NOT NULL default '0',
cond_1_val_2 mediumint(8) unsigned NOT NULL default '0',
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',
PRIMARY KEY (entry, text_id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS gossip_menu_option;
CREATE TABLE gossip_menu_option (
menu_id smallint(6) unsigned NOT NULL default '0',
id smallint(6) unsigned NOT NULL default '0',
option_icon mediumint(8) unsigned NOT NULL default '0',
option_text text,
option_id tinyint(3) unsigned NOT NULL default '0',
npc_option_npcflag int(10) unsigned NOT NULL default '0',
action_menu_id mediumint(8) unsigned NOT NULL default '0',
action_poi_id mediumint(8) unsigned NOT NULL default '0',
action_script_id mediumint(8) unsigned NOT NULL default '0',
box_coded tinyint(3) unsigned NOT NULL default '0',
box_money int(11) unsigned NOT NULL default '0',
box_text text,
cond_1 tinyint(3) unsigned NOT NULL default '0',
cond_1_val_1 mediumint(8) unsigned NOT NULL default '0',
cond_1_val_2 mediumint(8) unsigned NOT NULL default '0',
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',
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',
PRIMARY KEY (menu_id, id)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
DELETE FROM gossip_menu_option WHERE menu_id=0;
INSERT INTO gossip_menu_option VALUES
(0,0,0,'GOSSIP_OPTION_QUESTGIVER',2,2,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,1,1,'GOSSIP_OPTION_VENDOR',3,128,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,2,2,'GOSSIP_OPTION_TAXIVENDOR',4,8192,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,3,3,'GOSSIP_OPTION_TRAINER',5,16,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,4,4,'GOSSIP_OPTION_SPIRITHEALER',6,16384,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,5,4,'GOSSIP_OPTION_SPIRITGUIDE',7,32768,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,6,5,'GOSSIP_OPTION_INNKEEPER',8,65536,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,7,6,'GOSSIP_OPTION_BANKER',9,131072,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,8,7,'GOSSIP_OPTION_PETITIONER',10,262144,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,9,8,'GOSSIP_OPTION_TABARDDESIGNER',11,524288,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,10,9,'GOSSIP_OPTION_BATTLEFIELD',12,1048576,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,11,6,'GOSSIP_OPTION_AUCTIONEER',13,2097152,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,12,0,'GOSSIP_OPTION_STABLEPET',14,4194304,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,13,1,'GOSSIP_OPTION_ARMORER',15,4096,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,14,2,'GOSSIP_OPTION_UNLEARNTALENTS',16,16,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0),
(0,15,2,'GOSSIP_OPTION_UNLEARNPETSKILLS',17,16,0,0,0,0,0,NULL,0,0,0,0,0,0,0,0,0);
ALTER TABLE creature_template ADD gossip_menu_id mediumint(8) unsigned NOT NULL default 0 AFTER IconName;
ALTER TABLE locales_npc_option CHANGE COLUMN entry id smallint(6) unsigned NOT NULL default '0';
ALTER TABLE locales_npc_option ADD menu_id smallint(6) unsigned NOT NULL default '0' FIRST;
ALTER TABLE locales_npc_option DROP PRIMARY KEY;
ALTER TABLE locales_npc_option ADD PRIMARY KEY (menu_id, id);
RENAME TABLE locales_npc_option TO locales_gossip_menu_option;
DROP TABLE IF EXISTS npc_option;
DROP TABLE IF EXISTS npc_gossip_textid;

View file

@ -189,6 +189,7 @@ pkgdata_DATA = \
8909_01_mangos_spell_proc_event.sql \
8912_01_mangos_spell_proc_event.sql \
8917_01_mangos_spell_proc_event.sql \
8923_01_mangos_gossip.sql \
README
## Additional files to include when running 'make dist'
@ -358,4 +359,5 @@ EXTRA_DIST = \
8909_01_mangos_spell_proc_event.sql \
8912_01_mangos_spell_proc_event.sql \
8917_01_mangos_spell_proc_event.sql \
8923_01_mangos_gossip.sql \
README

View file

@ -110,7 +110,7 @@ Unit(), i_AI(NULL),
lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0),
m_lootMoney(0), m_lootRecipient(0),
m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(0.0f),
m_gossipOptionLoaded(false), m_isPet(false), m_isVehicle(false), m_isTotem(false),
m_isPet(false), m_isVehicle(false), m_isTotem(false),
m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0),
m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false),
m_regenHealth(true), m_AI_locked(false), m_isDeadByDefault(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL),
@ -744,21 +744,6 @@ bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const
&& pPlayer->getClass() == GetCreatureInfo()->trainer_class;
}
void Creature::LoadGossipOptions()
{
if(m_gossipOptionLoaded)
return;
uint32 npcflags=GetUInt32Value(UNIT_NPC_FLAGS);
CacheNpcOptionList const& noList = sObjectMgr.GetNpcOptions ();
for (CacheNpcOptionList::const_iterator i = noList.begin (); i != noList.end (); ++i)
if(i->NpcFlag & npcflags)
addGossipOption(*i);
m_gossipOptionLoaded = true;
}
void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, MonsterMovementFlags flags, uint8 type)
{
/* uint32 timeElap = getMSTime();

View file

@ -36,102 +36,6 @@ class Quest;
class Player;
class WorldSession;
enum Gossip_Option
{
GOSSIP_OPTION_NONE = 0, //UNIT_NPC_FLAG_NONE = 0,
GOSSIP_OPTION_GOSSIP = 1, //UNIT_NPC_FLAG_GOSSIP = 1,
GOSSIP_OPTION_QUESTGIVER = 2, //UNIT_NPC_FLAG_QUESTGIVER = 2,
GOSSIP_OPTION_VENDOR = 3, //UNIT_NPC_FLAG_VENDOR = 4,
GOSSIP_OPTION_TAXIVENDOR = 4, //UNIT_NPC_FLAG_TAXIVENDOR = 8,
GOSSIP_OPTION_TRAINER = 5, //UNIT_NPC_FLAG_TRAINER = 16,
GOSSIP_OPTION_SPIRITHEALER = 6, //UNIT_NPC_FLAG_SPIRITHEALER = 32,
GOSSIP_OPTION_SPIRITGUIDE = 7, //UNIT_NPC_FLAG_SPIRITGUIDE = 64,
GOSSIP_OPTION_INNKEEPER = 8, //UNIT_NPC_FLAG_INNKEEPER = 128,
GOSSIP_OPTION_BANKER = 9, //UNIT_NPC_FLAG_BANKER = 256,
GOSSIP_OPTION_PETITIONER = 10, //UNIT_NPC_FLAG_PETITIONER = 512,
GOSSIP_OPTION_TABARDDESIGNER = 11, //UNIT_NPC_FLAG_TABARDDESIGNER = 1024,
GOSSIP_OPTION_BATTLEFIELD = 12, //UNIT_NPC_FLAG_BATTLEFIELDPERSON = 2048,
GOSSIP_OPTION_AUCTIONEER = 13, //UNIT_NPC_FLAG_AUCTIONEER = 4096,
GOSSIP_OPTION_STABLEPET = 14, //UNIT_NPC_FLAG_STABLE = 8192,
GOSSIP_OPTION_ARMORER = 15, //UNIT_NPC_FLAG_ARMORER = 16384,
GOSSIP_OPTION_UNLEARNTALENTS = 16, //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
GOSSIP_OPTION_UNLEARNPETSKILLS = 17 //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
};
enum Gossip_Guard
{
GOSSIP_GUARD_BANK = 32,
GOSSIP_GUARD_RIDE = 33,
GOSSIP_GUARD_GUILD = 34,
GOSSIP_GUARD_INN = 35,
GOSSIP_GUARD_MAIL = 36,
GOSSIP_GUARD_AUCTION = 37,
GOSSIP_GUARD_WEAPON = 38,
GOSSIP_GUARD_STABLE = 39,
GOSSIP_GUARD_BATTLE = 40,
GOSSIP_GUARD_SPELLTRAINER = 41,
GOSSIP_GUARD_SKILLTRAINER = 42
};
enum Gossip_Guard_Spell
{
GOSSIP_GUARD_SPELL_WARRIOR = 64,
GOSSIP_GUARD_SPELL_PALADIN = 65,
GOSSIP_GUARD_SPELL_HUNTER = 66,
GOSSIP_GUARD_SPELL_ROGUE = 67,
GOSSIP_GUARD_SPELL_PRIEST = 68,
GOSSIP_GUARD_SPELL_UNKNOWN1 = 69,
GOSSIP_GUARD_SPELL_SHAMAN = 70,
GOSSIP_GUARD_SPELL_MAGE = 71,
GOSSIP_GUARD_SPELL_WARLOCK = 72,
GOSSIP_GUARD_SPELL_UNKNOWN2 = 73,
GOSSIP_GUARD_SPELL_DRUID = 74
};
enum Gossip_Guard_Skill
{
GOSSIP_GUARD_SKILL_ALCHEMY = 80,
GOSSIP_GUARD_SKILL_BLACKSMITH = 81,
GOSSIP_GUARD_SKILL_COOKING = 82,
GOSSIP_GUARD_SKILL_ENCHANT = 83,
GOSSIP_GUARD_SKILL_FIRSTAID = 84,
GOSSIP_GUARD_SKILL_FISHING = 85,
GOSSIP_GUARD_SKILL_HERBALISM = 86,
GOSSIP_GUARD_SKILL_LEATHER = 87,
GOSSIP_GUARD_SKILL_MINING = 88,
GOSSIP_GUARD_SKILL_SKINNING = 89,
GOSSIP_GUARD_SKILL_TAILORING = 90,
GOSSIP_GUARD_SKILL_ENGINERING = 91
};
enum GossipOptionIcon
{
GOSSIP_ICON_CHAT = 0, //white chat bubble
GOSSIP_ICON_VENDOR = 1, //brown bag
GOSSIP_ICON_TAXI = 2, //flight
GOSSIP_ICON_TRAINER = 3, //book
GOSSIP_ICON_INTERACT_1 = 4, //interaction wheel
GOSSIP_ICON_INTERACT_2 = 5, //interaction wheel
GOSSIP_ICON_MONEY_BAG = 6, //brown bag with yellow dot
GOSSIP_ICON_TALK = 7, //white chat bubble with black dots
GOSSIP_ICON_TABARD = 8, //tabard
GOSSIP_ICON_BATTLE = 9, //two swords
GOSSIP_ICON_DOT = 10 //yellow dot
};
struct GossipOption
{
uint32 Id;
uint32 GossipId;
uint32 NpcFlag;
uint32 Icon;
uint32 Action;
uint32 BoxMoney;
bool Coded;
std::string OptionText;
std::string BoxText;
};
enum CreatureFlagsExtra
{
CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
@ -165,6 +69,7 @@ struct CreatureInfo
char* Name;
char* SubName;
char* IconName;
uint32 GossipMenuId;
uint32 minlevel;
uint32 maxlevel;
uint32 minhealth;
@ -449,8 +354,6 @@ struct TrainerSpellData
void Clear() { spellList.clear(); }
};
typedef std::list<GossipOption> GossipOptionList;
typedef std::map<uint32,time_t> CreatureSpellCooldowns;
// max different by z coordinate for creature aggro reaction
@ -576,10 +479,6 @@ class MANGOS_DLL_SPEC Creature : public Unit
std::string GetScriptName() const;
uint32 GetScriptId() const;
void LoadGossipOptions();
void addGossipOption(GossipOption const& gso) { m_goptions.push_back(gso); }
GossipOptionList &GetGossipOptionList() { return m_goptions; }
void Say(int32 textId, uint32 language, uint64 TargetGuid) { MonsterSay(textId,language,TargetGuid); }
void Yell(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYell(textId,language,TargetGuid); }
void TextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote = false) { MonsterTextEmote(textId,TargetGuid,IsBossEmote); }
@ -708,9 +607,6 @@ class MANGOS_DLL_SPEC Creature : public Unit
uint32 m_corpseDelay; // (secs) delay between death and corpse disappearance
float m_respawnradius;
bool m_gossipOptionLoaded;
GossipOptionList m_goptions;
bool m_isPet; // set only in Pet::Pet
bool m_isVehicle; // set only in Vehicle::Vehicle
bool m_isTotem; // set only in Totem::Totem

View file

@ -872,13 +872,13 @@ void GameObject::Use(Unit* user)
case GAMEOBJECT_TYPE_QUESTGIVER: //2
{
if(user->GetTypeId()!=TYPEID_PLAYER)
if (user->GetTypeId() != TYPEID_PLAYER)
return;
Player* player = (Player*)user;
player->PrepareQuestMenu( GetGUID() );
player->SendPreparedQuest( GetGUID() );
player->PrepareGossipMenu(this, GetGOInfo()->questgiver.gossipID);
player->SendPreparedGossip(this);
return;
}
//Sitting: Wooden bench, chairs enzz
@ -952,12 +952,17 @@ void GameObject::Use(Unit* user)
Player* player = (Player*)user;
// show page
if(info->goober.pageId)
if (info->goober.pageId)
{
WorldPacket data(SMSG_GAMEOBJECT_PAGETEXT, 8);
data << GetGUID();
player->GetSession()->SendPacket(&data);
}
else if (info->questgiver.gossipID)
{
player->PrepareGossipMenu(this, info->goober.gossipID);
player->SendPreparedGossip(this);
}
// possible quest objective for active quests
player->CastedCreatureOrGO(info->id, GetGUID(), 0);

View file

@ -27,6 +27,7 @@
GossipMenu::GossipMenu()
{
m_gItems.reserve(16); // can be set for max from most often sizes to speedup push_back and less memory use
m_gMenuId = 0;
}
GossipMenu::~GossipMenu()
@ -44,13 +45,24 @@ void GossipMenu::AddMenuItem(uint8 Icon, const std::string& Message, uint32 dtSe
gItem.m_gMessage = Message;
gItem.m_gCoded = Coded;
gItem.m_gSender = dtSender;
gItem.m_gAction = dtAction;
gItem.m_gOptionId = dtAction;
gItem.m_gBoxMessage = BoxMessage;
gItem.m_gBoxMoney = BoxMoney;
m_gItems.push_back(gItem);
}
void GossipMenu::AddGossipMenuItemData(uint32 action_menu, uint32 action_poi, uint32 action_script)
{
GossipMenuItemData pItemData;
pItemData.m_gAction_menu = action_menu;
pItemData.m_gAction_poi = action_poi;
pItemData.m_gAction_script = action_script;
m_gItemsData.push_back(pItemData);
}
void GossipMenu::AddMenuItem(uint8 Icon, const std::string& Message, bool Coded)
{
AddMenuItem( Icon, Message, 0, 0, "", 0, Coded);
@ -77,7 +89,7 @@ uint32 GossipMenu::MenuItemAction( unsigned int ItemId )
{
if ( ItemId >= m_gItems.size() ) return 0;
return m_gItems[ ItemId ].m_gAction;
return m_gItems[ ItemId ].m_gOptionId;
}
bool GossipMenu::MenuItemCoded( unsigned int ItemId )
@ -90,6 +102,7 @@ bool GossipMenu::MenuItemCoded( unsigned int ItemId )
void GossipMenu::ClearMenu()
{
m_gItems.clear();
m_gItemsData.clear();
}
PlayerMenu::PlayerMenu( WorldSession *session ) : pSession(session)
@ -122,13 +135,13 @@ bool PlayerMenu::GossipOptionCoded( unsigned int Selection )
return mGossipMenu.MenuItemCoded( Selection );
}
void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
void PlayerMenu::SendGossipMenu(uint32 TitleTextId, uint64 objectGUID)
{
WorldPacket data( SMSG_GOSSIP_MESSAGE, (100) ); // guess size
data << uint64(npcGUID);
data << uint32(0); // new 2.4.0
data << uint32( TitleTextId );
data << uint32( mGossipMenu.MenuItemCount() ); // max count 0x10
WorldPacket data(SMSG_GOSSIP_MESSAGE, (100)); // guess size
data << uint64(objectGUID);
data << uint32(mGossipMenu.GetMenuId()); // new 2.4.0
data << uint32(TitleTextId);
data << uint32(mGossipMenu.MenuItemCount()); // max count 0x10
for (uint32 iI = 0; iI < mGossipMenu.MenuItemCount(); ++iI )
{

View file

@ -28,6 +28,43 @@ class WorldSession;
#define GOSSIP_MAX_MENU_ITEMS 64 // client supported items unknown, but provided number must be enough
#define DEFAULT_GOSSIP_MESSAGE 0xffffff
enum Gossip_Option
{
GOSSIP_OPTION_NONE = 0, //UNIT_NPC_FLAG_NONE (0)
GOSSIP_OPTION_GOSSIP = 1, //UNIT_NPC_FLAG_GOSSIP (1)
GOSSIP_OPTION_QUESTGIVER = 2, //UNIT_NPC_FLAG_QUESTGIVER (2)
GOSSIP_OPTION_VENDOR = 3, //UNIT_NPC_FLAG_VENDOR (128)
GOSSIP_OPTION_TAXIVENDOR = 4, //UNIT_NPC_FLAG_TAXIVENDOR (8192)
GOSSIP_OPTION_TRAINER = 5, //UNIT_NPC_FLAG_TRAINER (16)
GOSSIP_OPTION_SPIRITHEALER = 6, //UNIT_NPC_FLAG_SPIRITHEALER (16384)
GOSSIP_OPTION_SPIRITGUIDE = 7, //UNIT_NPC_FLAG_SPIRITGUIDE (32768)
GOSSIP_OPTION_INNKEEPER = 8, //UNIT_NPC_FLAG_INNKEEPER (65536)
GOSSIP_OPTION_BANKER = 9, //UNIT_NPC_FLAG_BANKER (131072)
GOSSIP_OPTION_PETITIONER = 10, //UNIT_NPC_FLAG_PETITIONER (262144)
GOSSIP_OPTION_TABARDDESIGNER = 11, //UNIT_NPC_FLAG_TABARDDESIGNER (524288)
GOSSIP_OPTION_BATTLEFIELD = 12, //UNIT_NPC_FLAG_BATTLEFIELDPERSON (1048576)
GOSSIP_OPTION_AUCTIONEER = 13, //UNIT_NPC_FLAG_AUCTIONEER (2097152)
GOSSIP_OPTION_STABLEPET = 14, //UNIT_NPC_FLAG_STABLE (4194304)
GOSSIP_OPTION_ARMORER = 15, //UNIT_NPC_FLAG_ARMORER (4096)
GOSSIP_OPTION_UNLEARNTALENTS = 16, //UNIT_NPC_FLAG_TRAINER (16) (bonus option for GOSSIP_OPTION_TRAINER)
GOSSIP_OPTION_UNLEARNPETSKILLS = 17 //UNIT_NPC_FLAG_TRAINER (16) (bonus option for GOSSIP_OPTION_TRAINER)
};
enum GossipOptionIcon
{
GOSSIP_ICON_CHAT = 0, //white chat bubble
GOSSIP_ICON_VENDOR = 1, //brown bag
GOSSIP_ICON_TAXI = 2, //flight
GOSSIP_ICON_TRAINER = 3, //book
GOSSIP_ICON_INTERACT_1 = 4, //interaction wheel
GOSSIP_ICON_INTERACT_2 = 5, //interaction wheel
GOSSIP_ICON_MONEY_BAG = 6, //brown bag with yellow dot
GOSSIP_ICON_TALK = 7, //white chat bubble with black dots
GOSSIP_ICON_TABARD = 8, //tabard
GOSSIP_ICON_BATTLE = 9, //two swords
GOSSIP_ICON_DOT = 10 //yellow dot
};
//POI icons. Many more exist, list not complete.
enum Poi_Icon
{
@ -80,13 +117,22 @@ struct GossipMenuItem
bool m_gCoded;
std::string m_gMessage;
uint32 m_gSender;
uint32 m_gAction;
uint32 m_gOptionId;
std::string m_gBoxMessage;
uint32 m_gBoxMoney;
};
typedef std::vector<GossipMenuItem> GossipMenuItemList;
struct GossipMenuItemData
{
uint32 m_gAction_menu;
uint32 m_gAction_poi;
uint32 m_gAction_script;
};
typedef std::vector<GossipMenuItemData> GossipMenuItemDataList;
struct QuestMenuItem
{
uint32 m_qId;
@ -108,6 +154,11 @@ class MANGOS_DLL_SPEC GossipMenu
void AddMenuItem(uint8 Icon, char const* Message, bool Coded = false);
void AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded = false);
void SetMenuId(uint32 menu_id) { m_gMenuId = menu_id; }
uint32 GetMenuId() { return m_gMenuId; }
void AddGossipMenuItemData(uint32 action_menu, uint32 action_poi, uint32 action_script);
unsigned int MenuItemCount() const
{
return m_gItems.size();
@ -123,6 +174,11 @@ class MANGOS_DLL_SPEC GossipMenu
return m_gItems[ Id ];
}
GossipMenuItemData const& GetItemData(unsigned int indexId)
{
return m_gItemsData[indexId];
}
uint32 MenuItemSender( unsigned int ItemId );
uint32 MenuItemAction( unsigned int ItemId );
bool MenuItemCoded( unsigned int ItemId );
@ -131,6 +187,9 @@ class MANGOS_DLL_SPEC GossipMenu
protected:
GossipMenuItemList m_gItems;
GossipMenuItemDataList m_gItemsData;
uint32 m_gMenuId;
};
class QuestMenu

View file

@ -407,7 +407,8 @@ bool ChatHandler::HandleReloadMangosStringCommand(const char*)
bool ChatHandler::HandleReloadNpcOptionCommand(const char*)
{
sLog.outString( "Re-Loading `npc_option` Table!" );
sObjectMgr.LoadNpcOptions();
//sObjectMgr.LoadGossipMenu();
//sObjectMgr.LoadGossipMenuItems();
SendGlobalSysMessage("DB table `npc_option` reloaded.");
return true;
}

View file

@ -277,7 +277,7 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket & recv_data)
if (!Script->GossipHello(_player, pCreature))
{
_player->TalkedToCreature(pCreature->GetEntry(), pCreature->GetGUID());
_player->PrepareGossipMenu(pCreature);
_player->PrepareGossipMenu(pCreature, pCreature->GetCreatureInfo()->GossipMenuId);
_player->SendPreparedGossip(pCreature);
}
}
@ -286,14 +286,14 @@ void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_GOSSIP_SELECT_OPTION");
uint32 option;
uint32 gossipListId;
uint32 menuId;
uint64 guid;
std::string code = "";
recv_data >> guid >> menuId >> option;
recv_data >> guid >> menuId >> gossipListId;
if (_player->PlayerTalkClass->GossipOptionCoded(option))
if (_player->PlayerTalkClass->GossipOptionCoded(gossipListId))
{
sLog.outBasic("reading string");
recv_data >> code;
@ -314,13 +314,13 @@ void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
if (!code.empty())
{
if (!Script->GossipSelectWithCode(_player, pCreature, _player->PlayerTalkClass->GossipOptionSender(option), _player->PlayerTalkClass->GossipOptionAction(option), code.c_str()))
_player->OnGossipSelect(pCreature, option);
if (!Script->GossipSelectWithCode(_player, pCreature, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId), code.c_str()))
_player->OnGossipSelect(pCreature, gossipListId, menuId);
}
else
{
if (!Script->GossipSelect(_player, pCreature, _player->PlayerTalkClass->GossipOptionSender(option), _player->PlayerTalkClass->GossipOptionAction(option)))
_player->OnGossipSelect(pCreature, option);
if (!Script->GossipSelect(_player, pCreature, _player->PlayerTalkClass->GossipOptionSender(gossipListId), _player->PlayerTalkClass->GossipOptionAction(gossipListId)))
_player->OnGossipSelect(pCreature, gossipListId, menuId);
}
}

View file

@ -342,12 +342,12 @@ void ObjectMgr::LoadNpcOptionLocales()
{
mNpcOptionLocaleMap.clear(); // need for reload case
QueryResult *result = WorldDatabase.Query("SELECT entry,"
QueryResult *result = WorldDatabase.Query("SELECT menu_id,id,"
"option_text_loc1,box_text_loc1,option_text_loc2,box_text_loc2,"
"option_text_loc3,box_text_loc3,option_text_loc4,box_text_loc4,"
"option_text_loc5,box_text_loc5,option_text_loc6,box_text_loc6,"
"option_text_loc7,box_text_loc7,option_text_loc8,box_text_loc8 "
"FROM locales_npc_option");
"FROM locales_gossip_menu_option");
if(!result)
{
@ -356,7 +356,7 @@ void ObjectMgr::LoadNpcOptionLocales()
bar.step();
sLog.outString();
sLog.outString(">> Loaded 0 npc_option locale strings. DB table `locales_npc_option` is empty.");
sLog.outString(">> Loaded 0 gossip_menu_option locale strings. DB table `locales_gossip_menu_option` is empty.");
return;
}
@ -367,9 +367,10 @@ void ObjectMgr::LoadNpcOptionLocales()
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
uint16 menuId = fields[0].GetUInt16();
uint16 id = fields[1].GetUInt16();
NpcOptionLocale& data = mNpcOptionLocaleMap[entry];
NpcOptionLocale& data = mNpcOptionLocaleMap[MAKE_PAIR32(menuId,id)];
for(int i = 1; i < MAX_LOCALE; ++i)
{
@ -403,7 +404,7 @@ void ObjectMgr::LoadNpcOptionLocales()
delete result;
sLog.outString();
sLog.outString( ">> Loaded %lu npc_option locale strings", (unsigned long)mNpcOptionLocaleMap.size() );
sLog.outString( ">> Loaded %lu gossip_menu_option locale strings", (unsigned long)mNpcOptionLocaleMap.size() );
}
void ObjectMgr::LoadPointOfInterestLocales()
@ -7825,23 +7826,21 @@ void ObjectMgr::LoadNpcTextId()
sLog.outString( ">> Loaded %d NpcTextId ", count );
}
void ObjectMgr::LoadNpcOptions()
void ObjectMgr::LoadGossipMenu()
{
m_mCacheNpcOptionList.clear(); // For reload case
m_mGossipMenusMap.clear();
QueryResult *result = WorldDatabase.Query(
// 0 1 2 3 4 5 6 7 8
"SELECT id,gossip_id,npcflag,icon,action,box_money,coded,option_text,box_text "
"FROM npc_option");
QueryResult* result = WorldDatabase.Query("SELECT entry, text_id, "
"cond_1, cond_1_val_1, cond_1_val_2, cond_2, cond_2_val_1, cond_2_val_2 FROM gossip_menu");
if( !result )
if (!result)
{
barGoLink bar( 1 );
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outErrorDb(">> Loaded `npc_option`, table is empty!");
sLog.outErrorDb(">> Loaded gossip_menu, table is empty!");
return;
}
@ -7855,26 +7854,146 @@ void ObjectMgr::LoadNpcOptions()
Field* fields = result->Fetch();
GossipOption go;
go.Id = fields[0].GetUInt32();
go.GossipId = fields[1].GetUInt32();
go.NpcFlag = fields[2].GetUInt32();
go.Icon = fields[3].GetUInt32();
go.Action = fields[4].GetUInt32();
go.BoxMoney = fields[5].GetUInt32();
go.Coded = fields[6].GetUInt8()!=0;
go.OptionText = fields[7].GetCppString();
go.BoxText = fields[8].GetCppString();
GossipMenus gMenu;
m_mCacheNpcOptionList.push_back(go);
gMenu.entry = fields[0].GetUInt32();
gMenu.text_id = fields[1].GetUInt32();
ConditionType cond_1 = (ConditionType)fields[2].GetUInt32();
uint32 cond_1_val_1 = fields[3].GetUInt32();
uint32 cond_1_val_2 = fields[4].GetUInt32();
ConditionType cond_2 = (ConditionType)fields[5].GetUInt32();
uint32 cond_2_val_1 = fields[6].GetUInt32();
uint32 cond_2_val_2 = fields[7].GetUInt32();
if (!GetGossipText(gMenu.text_id))
{
sLog.outErrorDb("Table gossip_menu entry %u are using non-existing text_id %u", gMenu.entry, gMenu.text_id);
continue;
}
if (!PlayerCondition::IsValid(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))
{
sLog.outErrorDb("Table gossip_menu entry %u, invalid condition 2 for id %u", gMenu.entry, gMenu.text_id);
continue;
}
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);
m_mGossipMenusMap.insert(GossipMenusMap::value_type(gMenu.entry, gMenu));
++count;
}
while(result->NextRow());
} while (result->NextRow());
delete result;
sLog.outString();
sLog.outString( ">> Loaded %d npc_option entries", count );
sLog.outString( ">> Loaded %u gossip_menu entries", count);
}
void ObjectMgr::LoadGossipMenuItems()
{
m_mGossipMenuItemsMap.clear();
QueryResult *result = WorldDatabase.Query(
"SELECT menu_id, id, option_icon, option_text, option_id, npc_option_npcflag, "
"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 "
"FROM gossip_menu_option");
if (!result)
{
barGoLink bar(1);
bar.step();
sLog.outString();
sLog.outErrorDb(">> Loaded gossip_menu_items, table is empty!");
return;
}
barGoLink bar(result->GetRowCount());
uint32 count = 0;
do
{
bar.step();
Field* fields = result->Fetch();
GossipMenuItems gMenuItem;
gMenuItem.menu_id = fields[0].GetUInt32();
gMenuItem.id = fields[1].GetUInt32();
gMenuItem.option_icon = fields[2].GetUInt8();
gMenuItem.option_text = fields[3].GetCppString();
gMenuItem.option_id = fields[4].GetUInt32();
gMenuItem.npc_option_npcflag = fields[5].GetUInt32();
gMenuItem.action_menu_id = fields[6].GetUInt32();
gMenuItem.action_poi_id = fields[7].GetUInt32();
gMenuItem.action_script_id = fields[8].GetUInt32();
gMenuItem.box_coded = fields[9].GetUInt8() != 0;
gMenuItem.box_money = fields[10].GetUInt32();
gMenuItem.box_text = fields[11].GetCppString();
ConditionType cond_1 = (ConditionType)fields[12].GetUInt32();
uint32 cond_1_val_1 = fields[13].GetUInt32();
uint32 cond_1_val_2 = fields[14].GetUInt32();
ConditionType cond_2 = (ConditionType)fields[15].GetUInt32();
uint32 cond_2_val_1 = fields[16].GetUInt32();
uint32 cond_2_val_2 = fields[17].GetUInt32();
ConditionType cond_3 = (ConditionType)fields[18].GetUInt32();
uint32 cond_3_val_1 = fields[19].GetUInt32();
uint32 cond_3_val_2 = fields[20].GetUInt32();
if (!PlayerCondition::IsValid(cond_1, cond_1_val_1, cond_1_val_2))
{
sLog.outErrorDb("Table gossip_menu_items 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))
{
sLog.outErrorDb("Table gossip_menu_items 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))
{
sLog.outErrorDb("Table gossip_menu_items menu %u, invalid condition 3 for id %u", gMenuItem.menu_id, gMenuItem.id);
continue;
}
if (gMenuItem.action_poi_id && !GetPointOfInterest(gMenuItem.action_poi_id))
{
sLog.outErrorDb("Table gossip_menu_items for menu %u, id %u use non-existing action_poi_id %u, ignoring", gMenuItem.menu_id, gMenuItem.id, gMenuItem.action_poi_id);
gMenuItem.action_poi_id = 0;
}
gMenuItem.cond_1 = GetConditionId(cond_1, cond_1_val_1, cond_1_val_2);
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);
m_mGossipMenuItemsMap.insert(GossipMenuItemsMap::value_type(gMenuItem.menu_id, gMenuItem));
++count;
}
while(result->NextRow());
delete result;
sLog.outString();
sLog.outString(">> Loaded %u gossip_menu_items entries", count);
}
void ObjectMgr::AddVendorItem( uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost )

View file

@ -216,6 +216,38 @@ struct PointOfInterest
std::string icon_name;
};
struct GossipMenuItems
{
uint32 menu_id;
uint32 id;
uint8 option_icon;
std::string option_text;
uint32 option_id;
uint32 npc_option_npcflag;
uint32 action_menu_id;
uint32 action_poi_id;
uint32 action_script_id;
bool box_coded;
uint32 box_money;
std::string box_text;
uint16 cond_1;
uint16 cond_2;
uint16 cond_3;
};
struct GossipMenus
{
uint32 entry;
uint32 text_id;
uint16 cond_1;
uint16 cond_2;
};
typedef std::multimap<uint32,GossipMenus> GossipMenusMap;
typedef std::pair<GossipMenusMap::const_iterator, GossipMenusMap::const_iterator> GossipMenusMapBounds;
typedef std::multimap<uint32,GossipMenuItems> GossipMenuItemsMap;
typedef std::pair<GossipMenuItemsMap::const_iterator, GossipMenuItemsMap::const_iterator> GossipMenuItemsMapBounds;
#define WEATHER_SEASONS 4
struct WeatherSeasonChances
{
@ -275,7 +307,6 @@ struct PlayerCondition
// NPC gossip text id
typedef UNORDERED_MAP<uint32, uint32> CacheNpcTextIdMap;
typedef std::list<GossipOption> CacheNpcOptionList;
typedef UNORDERED_MAP<uint32, VendorItemData> CacheVendorItemMap;
typedef UNORDERED_MAP<uint32, TrainerSpellData> CacheTrainerSpellMap;
@ -557,8 +588,11 @@ class ObjectMgr
void LoadWeatherZoneChances();
void LoadGameTele();
void LoadNpcOptions();
void LoadNpcTextId();
void LoadGossipMenu();
void LoadGossipMenuItems();
void LoadVendors();
void LoadTrainerSpell();
@ -751,8 +785,6 @@ class ObjectMgr
bool AddGameTele(GameTele& data);
bool DeleteGameTele(const std::string& name);
CacheNpcOptionList const& GetNpcOptions() const { return m_mCacheNpcOptionList; }
uint32 GetNpcGossip(uint32 entry) const
{
CacheNpcTextIdMap::const_iterator iter = m_mCacheNpcTextIdMap.find(entry);
@ -800,6 +832,16 @@ class ObjectMgr
return ItemRequiredTargetMapBounds(m_ItemRequiredTarget.lower_bound(uiItemEntry),m_ItemRequiredTarget.upper_bound(uiItemEntry));
}
GossipMenusMapBounds GetGossipMenusMapBounds(uint32 uiMenuId) const
{
return GossipMenusMapBounds(m_mGossipMenusMap.lower_bound(uiMenuId),m_mGossipMenusMap.upper_bound(uiMenuId));
}
GossipMenuItemsMapBounds GetGossipMenuItemsMapBounds(uint32 uiMenuId) const
{
return GossipMenuItemsMapBounds(m_mGossipMenuItemsMap.lower_bound(uiMenuId),m_mGossipMenuItemsMap.upper_bound(uiMenuId));
}
protected:
// first free id for selected id type
@ -841,6 +883,8 @@ class ObjectMgr
RepOnKillMap mRepOnKill;
GossipMenusMap m_mGossipMenusMap;
GossipMenuItemsMap m_mGossipMenuItemsMap;
PointOfInterestMap mPointsOfInterest;
WeatherZoneMap mWeatherZoneMap;
@ -914,7 +958,6 @@ class ObjectMgr
typedef std::vector<PlayerCondition> ConditionStore;
ConditionStore mConditions;
CacheNpcOptionList m_mCacheNpcOptionList;
CacheNpcTextIdMap m_mCacheNpcTextIdMap;
CacheVendorItemMap m_mCacheVendorItemMap;
CacheTrainerSpellMap m_mCacheTrainerSpellMap;

View file

@ -12179,77 +12179,71 @@ void Player::SendNewItem(Item *item, uint32 count, bool received, bool created,
/*** GOSSIP SYSTEM ***/
/*********************************************************/
void Player::PrepareGossipMenu(WorldObject *pSource, uint32 gossipid)
void Player::PrepareGossipMenu(WorldObject *pSource, uint32 menuId)
{
PlayerMenu* pMenu = PlayerTalkClass;
pMenu->ClearMenus();
if (pSource->GetTypeId() != TYPEID_UNIT)
return;
pMenu->GetGossipMenu().SetMenuId(menuId);
GossipMenuItemsMapBounds pMenuItemBounds = sObjectMgr.GetGossipMenuItemsMapBounds(menuId);
for(GossipMenuItemsMap::const_iterator itr = pMenuItemBounds.first; itr != pMenuItemBounds.second; ++itr)
{
bool bCanTalk = true;
if (itr->second.cond_1 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_1))
continue;
if (itr->second.cond_2 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_2))
continue;
if (itr->second.cond_3 && !sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_3))
continue;
if (pSource->GetTypeId() == TYPEID_UNIT)
{
Creature *pCreature = (Creature*)pSource;
// lazy loading single time at use
pCreature->LoadGossipOptions();
uint32 npcflags = pCreature->GetUInt32Value(UNIT_NPC_FLAGS);
GossipOptionList &iOptList = pCreature->GetGossipOptionList();
if (!(itr->second.npc_option_npcflag & npcflags))
continue;
for(GossipOptionList::iterator i = iOptList.begin( ); i != iOptList.end( ); ++i)
{
GossipOption* gso = &*i;
if (gso->GossipId == gossipid)
{
bool cantalking = true;
if (gso->Id == 1)
{
uint32 textid = GetGossipTextId(pSource);
GossipText const* gossiptext = sObjectMgr.GetGossipText(textid);
if (!gossiptext)
cantalking = false;
}
else
{
switch(gso->Action)
switch(itr->second.option_id)
{
case GOSSIP_OPTION_QUESTGIVER:
PrepareQuestMenu(pSource->GetGUID());
//if (pm->GetQuestMenu()->MenuItemCount() == 0)
cantalking = false;
//pm->GetQuestMenu()->ClearMenu();
bCanTalk = false;
break;
case GOSSIP_OPTION_ARMORER:
cantalking = false; // added in special mode
bCanTalk = false; // added in special mode
break;
case GOSSIP_OPTION_SPIRITHEALER:
if (!isDead())
cantalking = false;
bCanTalk = false;
break;
case GOSSIP_OPTION_VENDOR:
{
VendorItemData const* vItems = pCreature->GetVendorItems();
if (!vItems || vItems->Empty())
{
sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.",
pCreature->GetGUIDLow(), pCreature->GetEntry());
cantalking = false;
sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_VENDOR but have empty trading item list.", pCreature->GetGUIDLow(), pCreature->GetEntry());
bCanTalk = false;
}
break;
}
case GOSSIP_OPTION_TRAINER:
if (!pCreature->isCanTrainingOf(this, false))
cantalking = false;
bCanTalk = false;
break;
case GOSSIP_OPTION_UNLEARNTALENTS:
if (!pCreature->isCanTrainingAndResetTalentsOf(this))
cantalking = false;
bCanTalk = false;
break;
case GOSSIP_OPTION_UNLEARNPETSKILLS:
if(!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || pCreature->GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || pCreature->GetCreatureInfo()->trainer_class != CLASS_HUNTER)
cantalking = false;
if (!GetPet() || GetPet()->getPetType() != HUNTER_PET || GetPet()->m_spells.size() <= 1 || pCreature->GetCreatureInfo()->trainer_type != TRAINER_TYPE_PETS || pCreature->GetCreatureInfo()->trainer_class != CLASS_HUNTER)
bCanTalk = false;
break;
case GOSSIP_OPTION_TAXIVENDOR:
if (GetSession()->SendLearnNewTaxiNode(pCreature))
@ -12257,48 +12251,76 @@ void Player::PrepareGossipMenu(WorldObject *pSource, uint32 gossipid)
break;
case GOSSIP_OPTION_BATTLEFIELD:
if (!pCreature->isCanInteractWithBattleMaster(this, false))
cantalking = false;
bCanTalk = false;
break;
case GOSSIP_OPTION_STABLEPET:
if (!GetPet() || GetPet()->getPetType() != HUNTER_PET)
bCanTalk = false;
break;
case GOSSIP_OPTION_GOSSIP:
case GOSSIP_OPTION_SPIRITGUIDE:
case GOSSIP_OPTION_INNKEEPER:
case GOSSIP_OPTION_BANKER:
case GOSSIP_OPTION_PETITIONER:
case GOSSIP_OPTION_STABLEPET:
case GOSSIP_OPTION_TABARDDESIGNER:
case GOSSIP_OPTION_AUCTIONEER:
break; // no checks
default:
sLog.outErrorDb("Creature %u (entry: %u) have unknown gossip option %u", pCreature->GetDBTableGUIDLow(), pCreature->GetEntry(), gso->Action);
sLog.outErrorDb("Creature entry %u have unknown gossip option %u for menu %u", pCreature->GetEntry(), itr->second.option_id, itr->second.menu_id);
bCanTalk = false;
break;
}
}
else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
{
GameObject *pGo = (GameObject*)pSource;
switch(itr->second.option_id)
{
case GOSSIP_OPTION_QUESTGIVER:
if (pGo->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
PrepareQuestMenu(pSource->GetGUID());
bCanTalk = false;
break;
case GOSSIP_OPTION_GOSSIP:
if (pGo->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER || pGo->GetGoType() != GAMEOBJECT_TYPE_GOOBER)
bCanTalk = false;
break;
default:
sLog.outErrorDb("GameObject entry %u are using invalid gossip option %u for menu %u", pGo->GetEntry(), itr->second.option_id, itr->second.menu_id);
bCanTalk = false;
break;
}
}
//note for future dev: should have database fields for BoxMessage & BoxMoney
if (!gso->OptionText.empty() && cantalking)
if (bCanTalk)
{
std::string OptionText = gso->OptionText;
std::string BoxText = gso->BoxText;
std::string strOptionText = itr->second.option_text;
std::string strBoxText = itr->second.box_text;
int loc_idx = GetSession()->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
if (NpcOptionLocale const *no = sObjectMgr.GetNpcOptionLocale(gso->Id))
uint32 idxEntry = MAKE_PAIR32(menuId, itr->second.id);
if (NpcOptionLocale const *no = sObjectMgr.GetNpcOptionLocale(idxEntry))
{
if (no->OptionText.size() > (size_t)loc_idx && !no->OptionText[loc_idx].empty())
OptionText = no->OptionText[loc_idx];
strOptionText = no->OptionText[loc_idx];
if (no->BoxText.size() > (size_t)loc_idx && !no->BoxText[loc_idx].empty())
BoxText = no->BoxText[loc_idx];
strBoxText = no->BoxText[loc_idx];
}
}
pMenu->GetGossipMenu().AddMenuItem((uint8)gso->Icon,OptionText, gossipid,gso->Action,BoxText,gso->BoxMoney,gso->Coded);
}
pMenu->GetGossipMenu().AddMenuItem(itr->second.option_icon, strOptionText, 0, itr->second.option_id, strBoxText, itr->second.box_money, itr->second.box_coded);
pMenu->GetGossipMenu().AddGossipMenuItemData(itr->second.action_menu_id, itr->second.action_poi_id, itr->second.action_script_id);
}
}
///some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
if (pMenu->Empty())
// some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
/*if (pMenu->Empty())
{
if (pCreature->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_TRAINER))
{
@ -12311,59 +12333,86 @@ void Player::PrepareGossipMenu(WorldObject *pSource, uint32 gossipid)
// output error message if need
pCreature->isCanInteractWithBattleMaster(this, true);
}
}
}*/
}
void Player::SendPreparedGossip(WorldObject *pSource)
{
if (!pSource || pSource->GetTypeId() != TYPEID_UNIT)
if (!pSource)
return;
if (pSource->GetTypeId() == TYPEID_UNIT)
{
// in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
if (!((Creature*)pSource)->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_GOSSIP) && !PlayerTalkClass->GetQuestMenu().Empty())
{
SendPreparedQuest(pSource->GetGUID());
return;
}
}
else if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
{
// probably need to find a better way here
if (!PlayerTalkClass->GetGossipMenu().GetMenuId() && !PlayerTalkClass->GetQuestMenu().Empty())
{
SendPreparedQuest(pSource->GetGUID());
return;
}
}
// in case non empty gossip menu (that not included quests list size) show it
// (quest entries from quest menu will be included in list)
PlayerTalkClass->SendGossipMenu(GetGossipTextId(pSource), pSource->GetGUID());
uint32 textId = GetGossipTextId(pSource);
if (uint32 menuId = PlayerTalkClass->GetGossipMenu().GetMenuId())
textId = GetGossipTextId(menuId);
PlayerTalkClass->SendGossipMenu(textId, pSource->GetGUID());
}
void Player::OnGossipSelect(WorldObject* pSource, uint32 option)
void Player::OnGossipSelect(WorldObject* pSource, uint32 gossipListId, uint32 menuId)
{
GossipMenu& gossipmenu = PlayerTalkClass->GetGossipMenu();
if (option >= gossipmenu.MenuItemCount())
if (gossipListId >= gossipmenu.MenuItemCount())
return;
uint32 action = gossipmenu.GetItem(option).m_gAction;
uint32 zoneid = GetZoneId();
// if not same, then something funky is going on
if (menuId != gossipmenu.GetMenuId())
return;
uint32 gossipOptionId = gossipmenu.GetItem(gossipListId).m_gOptionId;
uint64 guid = pSource->GetGUID();
GossipOption const *gossip = GetGossipOption(pSource, action);
if (!gossip)
if (pSource->GetTypeId() == TYPEID_GAMEOBJECT)
{
zoneid = 0;
gossip = GetGossipOption(pSource, action);
if (!gossip)
if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER)
{
sLog.outError("Player guid %u request invalid gossip option for GameObject entry %u", GetGUIDLow(), pSource->GetEntry());
return;
}
}
switch(gossip->Action)
GossipMenuItemData pMenuData = gossipmenu.GetItemData(gossipListId);
switch(gossipOptionId)
{
case GOSSIP_OPTION_GOSSIP:
{
uint32 textid = GetGossipTextId(action, zoneid);
if (textid == 0)
textid = GetGossipTextId(pSource);
if (pMenuData.m_gAction_menu)
{
PrepareGossipMenu(pSource, pMenuData.m_gAction_menu);
SendPreparedGossip(pSource);
}
else
{
PlayerTalkClass->CloseGossip();
PlayerTalkClass->SendTalking(textid);
}
if (pMenuData.m_gAction_poi)
PlayerTalkClass->SendPointOfInterest(pMenuData.m_gAction_poi);
break;
}
case GOSSIP_OPTION_SPIRITHEALER:
@ -12414,9 +12463,7 @@ void Player::OnGossipSelect(WorldObject* pSource, uint32 option)
GetSession()->SendAuctionHello(guid, ((Creature*)pSource));
break;
case GOSSIP_OPTION_SPIRITGUIDE:
case GOSSIP_GUARD_SPELLTRAINER:
case GOSSIP_GUARD_SKILLTRAINER:
PrepareGossipMenu(pSource, gossip->Id);
PrepareGossipMenu(pSource);
SendPreparedGossip(pSource);
break;
case GOSSIP_OPTION_BATTLEFIELD:
@ -12432,59 +12479,13 @@ void Player::OnGossipSelect(WorldObject* pSource, uint32 option)
GetSession()->SendBattlegGroundList(guid, bgTypeId);
break;
}
default:
OnPoiSelect(pSource, gossip);
break;
}
}
void Player::OnPoiSelect(WorldObject *pSource, GossipOption const *gossip)
{
if(gossip->GossipId==GOSSIP_GUARD_SPELLTRAINER || gossip->GossipId==GOSSIP_GUARD_SKILLTRAINER)
{
Poi_Icon icon = ICON_POI_BLANK;
//need add more case.
switch(gossip->Action)
{
case GOSSIP_GUARD_BANK:
icon=ICON_POI_SMALL_HOUSE;
break;
case GOSSIP_GUARD_RIDE:
icon=ICON_POI_RWHORSE;
break;
case GOSSIP_GUARD_GUILD:
icon=ICON_POI_BLUETOWER;
break;
default:
icon=ICON_POI_GREYTOWER;
break;
}
uint32 textid = GetGossipTextId(gossip->Action, GetZoneId());
PlayerTalkClass->SendTalking(textid);
// std::string areaname= gossip->OptionText;
// how this could worked player->PlayerTalkClass->SendPointOfInterest( x, y, icon, 2, 15, areaname.c_str() );
}
}
uint32 Player::GetGossipTextId(uint32 action, uint32 zoneid)
{
QueryResult *result= WorldDatabase.PQuery("SELECT textid FROM npc_gossip_textid WHERE action = '%u' AND zoneid ='%u'", action, zoneid );
if (!result)
return 0;
Field *fields = result->Fetch();
uint32 id = fields[0].GetUInt32();
delete result;
return id;
}
uint32 Player::GetGossipTextId(WorldObject *pSource)
{
if (!pSource || pSource->GetTypeId() != TYPEID_UNIT || !((Creature*)pSource)->GetDBTableGUIDLow())
return DEFAULT_GOSSIP_MESSAGE;
return 0;
if (uint32 pos = sObjectMgr.GetNpcGossip(((Creature*)pSource)->GetDBTableGUIDLow()))
return pos;
@ -12492,19 +12493,22 @@ uint32 Player::GetGossipTextId(WorldObject *pSource)
return DEFAULT_GOSSIP_MESSAGE;
}
GossipOption const* Player::GetGossipOption(WorldObject *pSource, uint32 id) const
uint32 Player::GetGossipTextId(uint32 menuId)
{
if (pSource->GetTypeId() == TYPEID_UNIT)
{
GossipOptionList &iOptlist = ((Creature*)pSource)->GetGossipOptionList();
uint32 textId = DEFAULT_GOSSIP_MESSAGE;
for(GossipOptionList::const_iterator i = iOptlist.begin( ); i != iOptlist.end( ); ++i)
if (!menuId)
return textId;
GossipMenusMapBounds pMenuBounds = sObjectMgr.GetGossipMenusMapBounds(menuId);
for(GossipMenusMap::const_iterator itr = pMenuBounds.first; itr != pMenuBounds.second; ++itr)
{
if (i->Action == id)
return &*i;
if (sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_1) && sObjectMgr.IsPlayerMeetToCondition(this, itr->second.cond_2))
textId = itr->second.text_id;
}
}
return NULL;
return textId;
}
/*********************************************************/

View file

@ -1299,14 +1299,12 @@ class MANGOS_DLL_SPEC Player : public Unit
/*** GOSSIP SYSTEM ***/
/*********************************************************/
void PrepareGossipMenu(WorldObject *pSource, uint32 gossipid = 0);
void PrepareGossipMenu(WorldObject *pSource, uint32 menuId = 0);
void SendPreparedGossip(WorldObject *pSource);
void OnGossipSelect(WorldObject *pSource, uint32 option);
void OnPoiSelect(WorldObject *pSource, GossipOption const *gossip);
void OnGossipSelect(WorldObject *pSource, uint32 gossipListId, uint32 menuId);
uint32 GetGossipTextId(uint32 action, uint32 zoneid);
uint32 GetGossipTextId(uint32 menuId);
uint32 GetGossipTextId(WorldObject *pSource);
GossipOption const* GetGossipOption(WorldObject *pSource, uint32 id) const;
/*********************************************************/
/*** QUEST SYSTEM ***/

View file

@ -102,7 +102,7 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket & recv_data)
if (Script->GossipHello(_player, pCreature))
return;
_player->PrepareGossipMenu(pCreature);
_player->PrepareGossipMenu(pCreature, pCreature->GetCreatureInfo()->GossipMenuId);
_player->SendPreparedGossip(pCreature);
}

View file

@ -1402,8 +1402,11 @@ void World::SetInitialWorldSettings()
sLog.outString( "Loading Npc Text Id..." );
sObjectMgr.LoadNpcTextId(); // must be after load Creature and NpcText
sLog.outString( "Loading Npc Options..." );
sObjectMgr.LoadNpcOptions();
sLog.outString( "Loading Gossip menus..." );
sObjectMgr.LoadGossipMenu();
sLog.outString( "Loading Gossip menu options..." );
sObjectMgr.LoadGossipMenuItems();
sLog.outString( "Loading Vendors..." );
sObjectMgr.LoadVendors(); // must be after load CreatureTemplate and ItemTemplate

View file

@ -25,8 +25,8 @@ extern DatabasePostgre WorldDatabase;
extern DatabaseMysql WorldDatabase;
#endif
const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiis";
const char CreatureInfodstfmt[]="iiiiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiii";
const char CreatureInfosrcfmt[]="iiiiiiiiiisssiiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiis";
const char CreatureInfodstfmt[]="iiiiiiiiiisssiiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiisiiffliiiiiiiliiii";
const char CreatureDataAddonInfofmt[]="iiiiiis";
const char CreatureModelfmt[]="iffbi";
const char CreatureInfoAddonInfofmt[]="iiiiiis";

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "8922"
#define REVISION_NR "8923"
#endif // __REVISION_NR_H__

View file

@ -1,6 +1,6 @@
#ifndef __REVISION_SQL_H__
#define __REVISION_SQL_H__
#define REVISION_DB_CHARACTERS "required_8874_01_characters_character_skills"
#define REVISION_DB_MANGOS "required_8917_01_mangos_spell_proc_event"
#define REVISION_DB_MANGOS "required_8923_01_mangos_gossip"
#define REVISION_DB_REALMD "required_8728_01_realmd_account"
#endif // __REVISION_SQL_H__