diff --git a/sql/mangos.sql b/sql/mangos.sql index e83b8d668..f17b1ddaf 100644 --- a/sql/mangos.sql +++ b/sql/mangos.sql @@ -24,7 +24,7 @@ CREATE TABLE `db_version` ( `version` varchar(120) default NULL, `creature_ai_version` varchar(120) default NULL, `cache_id` int(10) default '0', - `required_10353_02_mangos_command` bit(1) default NULL + `required_10362_01_mangos_creature_movement_template` bit(1) default NULL ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Used DB version notes'; -- @@ -1057,6 +1057,42 @@ LOCK TABLES `creature_movement_scripts` WRITE; /*!40000 ALTER TABLE `creature_movement_scripts` ENABLE KEYS */; UNLOCK TABLES; +-- +-- Table structure for table `creature_movement_template` +-- + +DROP TABLE IF EXISTS `creature_movement_template`; +CREATE TABLE `creature_movement_template` ( + `entry` mediumint(8) unsigned NOT NULL COMMENT 'Creature entry', + `point` mediumint(8) unsigned NOT NULL default '0', + `position_x` float NOT NULL default '0', + `position_y` float NOT NULL default '0', + `position_z` float NOT NULL default '0', + `waittime` int(10) unsigned NOT NULL default '0', + `script_id` mediumint(8) unsigned NOT NULL default '0', + `textid1` int(11) NOT NULL default '0', + `textid2` int(11) NOT NULL default '0', + `textid3` int(11) NOT NULL default '0', + `textid4` int(11) NOT NULL default '0', + `textid5` int(11) NOT NULL default '0', + `emote` mediumint(8) unsigned NOT NULL default '0', + `spell` mediumint(8) unsigned NOT NULL default '0', + `wpguid` int(11) NOT NULL default '0', + `orientation` float NOT NULL default '0', + `model1` mediumint(9) NOT NULL default '0', + `model2` mediumint(9) NOT NULL default '0', + PRIMARY KEY (`entry`,`point`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Creature waypoint system'; + +-- +-- Dumping data for table `creature_movement_template` +-- + +LOCK TABLES `creature_movement_template` WRITE; +/*!40000 ALTER TABLE `creature_movement_template` DISABLE KEYS */; +/*!40000 ALTER TABLE `creature_movement_template` ENABLE KEYS */; +UNLOCK TABLES; + -- -- Table structure for table `creature_onkill_reputation` -- diff --git a/sql/updates/10362_01_mangos_creature_movement_template.sql b/sql/updates/10362_01_mangos_creature_movement_template.sql new file mode 100644 index 000000000..8a9fe738f --- /dev/null +++ b/sql/updates/10362_01_mangos_creature_movement_template.sql @@ -0,0 +1,24 @@ +ALTER TABLE db_version CHANGE COLUMN required_10353_02_mangos_command required_10362_01_mangos_creature_movement_template bit; + +DROP TABLE IF EXISTS `creature_movement_template`; +CREATE TABLE `creature_movement_template` ( + `entry` mediumint(8) unsigned NOT NULL COMMENT 'Creature entry', + `point` mediumint(8) unsigned NOT NULL default '0', + `position_x` float NOT NULL default '0', + `position_y` float NOT NULL default '0', + `position_z` float NOT NULL default '0', + `waittime` int(10) unsigned NOT NULL default '0', + `script_id` mediumint(8) unsigned NOT NULL default '0', + `textid1` int(11) NOT NULL default '0', + `textid2` int(11) NOT NULL default '0', + `textid3` int(11) NOT NULL default '0', + `textid4` int(11) NOT NULL default '0', + `textid5` int(11) NOT NULL default '0', + `emote` mediumint(8) unsigned NOT NULL default '0', + `spell` mediumint(8) unsigned NOT NULL default '0', + `wpguid` int(11) NOT NULL default '0', + `orientation` float NOT NULL default '0', + `model1` mediumint(9) NOT NULL default '0', + `model2` mediumint(9) NOT NULL default '0', + PRIMARY KEY (`entry`,`point`) +) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Creature waypoint system'; diff --git a/sql/updates/Makefile.am b/sql/updates/Makefile.am index 3bffffb88..7670ea856 100644 --- a/sql/updates/Makefile.am +++ b/sql/updates/Makefile.am @@ -80,6 +80,7 @@ pkgdata_DATA = \ 10350_02_mangos_command.sql \ 10353_01_mangos_mangos_string.sql \ 10353_02_mangos_command.sql \ + 10362_01_mangos_creature_movement_template.sql \ README ## Additional files to include when running 'make dist' @@ -140,4 +141,5 @@ EXTRA_DIST = \ 10350_02_mangos_command.sql \ 10353_01_mangos_mangos_string.sql \ 10353_02_mangos_command.sql \ + 10362_01_mangos_creature_movement_template.sql \ README diff --git a/src/game/WaypointManager.cpp b/src/game/WaypointManager.cpp index 528611dc6..be27dc9a9 100644 --- a/src/game/WaypointManager.cpp +++ b/src/game/WaypointManager.cpp @@ -56,159 +56,347 @@ void WaypointManager::Load() uint32 total_nodes = 0; uint32 total_behaviors = 0; - QueryResult *result = WorldDatabase.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id"); - - if(!result) - { - barGoLink bar(1); - bar.step(); - sLog.outString(); - sLog.outString( ">> Loaded 0 paths. DB table `creature_movement` is empty." ); - return; - } else { - total_paths = (uint32)result->GetRowCount(); - barGoLink bar( total_paths ); - do - { - bar.step(); - Field *fields = result->Fetch(); - uint32 id = fields[0].GetUInt32(); - uint32 count = fields[1].GetUInt32(); - m_pathMap[id].resize(count); - total_nodes += count; - } while( result->NextRow() ); - delete result; - - sLog.outString(); - sLog.outString( ">> Paths loaded" ); - } - - // 0 1 2 3 4 5 - result = WorldDatabase.Query("SELECT position_x, position_y, position_z, orientation, model1, model2," - // 6 7 8 9 10 11 12 13 14 15 16 - "waittime, emote, spell, textid1, textid2, textid3, textid4, textid5, id, point, script_id FROM creature_movement"); - std::set movementScriptSet; for(ScriptMapMap::const_iterator itr = sCreatureMovementScripts.begin(); itr != sCreatureMovementScripts.end(); ++itr) movementScriptSet.insert(itr->first); - barGoLink bar( (int)result->GetRowCount() ); - do + // creature_movement + QueryResult *result = WorldDatabase.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id"); + + if (!result) { + barGoLink bar(1); bar.step(); - Field *fields = result->Fetch(); - uint32 point = fields[15].GetUInt32(); - uint32 id = fields[14].GetUInt32(); - if (!sObjectMgr.GetCreatureData(id)) + sLog.outString(); + sLog.outString( ">> Loaded 0 paths. DB table `creature_movement` is empty." ); + } + else + { + total_paths = (uint32)result->GetRowCount(); + barGoLink bar( total_paths ); + + do { - sLog.outErrorDb("Table creature_movement references unknown creature %u. Skipping.", id); - continue; + bar.step(); + Field *fields = result->Fetch(); + + uint32 id = fields[0].GetUInt32(); + uint32 count = fields[1].GetUInt32(); + + m_pathMap[id].resize(count); + total_nodes += count; } + while(result->NextRow()); - WaypointPath &path = m_pathMap[id]; - // the cleanup queries make sure the following is true - ASSERT(point >= 1 && point <= path.size()); - WaypointNode &node = path[point-1]; + sLog.outString(); + sLog.outString( ">> Paths loaded" ); - node.x = fields[0].GetFloat(); - node.y = fields[1].GetFloat(); - node.z = fields[2].GetFloat(); - node.orientation = fields[3].GetFloat(); - node.delay = fields[6].GetUInt32(); - node.script_id = fields[16].GetUInt32(); + delete result; - // prevent using invalid coordinates - if(!MaNGOS::IsValidMapCoord(node.x, node.y, node.z, node.orientation)) + // 0 1 2 3 4 5 6 + result = WorldDatabase.Query("SELECT id, point, position_x, position_y, position_z, waittime, script_id," + // 7 8 9 10 11 12 13 14 15 16 + "textid1, textid2, textid3, textid4, textid5, emote, spell, orientation, model1, model2 FROM creature_movement"); + + barGoLink barRow((int)result->GetRowCount()); + + // error after load, we check if creature guid corresponding to the path id has proper MovementType + std::set creatureNoMoveType; + + do { - QueryResult *result1 = WorldDatabase.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id); - if(result1) - sLog.outErrorDb("Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %f, Y: %f).", - id, result1->Fetch()[0].GetUInt32(), point, node.x, node.y); - else - sLog.outErrorDb("Waypoint path %d, have invalid coordinates in his waypoint %d (X: %f, Y: %f).", - id, point, node.x, node.y); + barRow.step(); + Field *fields = result->Fetch(); + uint32 id = fields[0].GetUInt32(); + uint32 point = fields[1].GetUInt32(); - MaNGOS::NormalizeMapCoord(node.x); - MaNGOS::NormalizeMapCoord(node.y); - if(result1) - { - node.z = MapManager::Instance ().CreateBaseMap(result1->Fetch()[1].GetUInt32())->GetHeight(node.x, node.y, node.z); - delete result1; - } - WorldDatabase.PExecute("UPDATE creature_movement SET position_x = '%f', position_y = '%f', position_z = '%f' WHERE id = '%u' AND point = '%u'", node.x, node.y, node.z, id, point); - } + const CreatureData* cData = sObjectMgr.GetCreatureData(id); - if (node.script_id) - { - if (sCreatureMovementScripts.find(node.script_id) == sCreatureMovementScripts.end()) + if (!cData) { - sLog.outErrorDb("Table creature_movement for id %u, point %u have script_id %u that does not exist in `creature_movement_scripts`, ignoring", id, point, node.script_id); + sLog.outErrorDb("Table creature_movement contain path for creature guid %u, but this creature guid does not exist. Skipping.", id); continue; } - movementScriptSet.erase(node.script_id); - } + if (cData->movementType != WAYPOINT_MOTION_TYPE) + creatureNoMoveType.insert(id); - // WaypointBehavior can be dropped in time. Script_id added may 2010 and can handle all the below behavior. + WaypointPath &path = m_pathMap[id]; - WaypointBehavior be; - be.model1 = fields[4].GetUInt32(); - be.model2 = fields[5].GetUInt32(); - be.emote = fields[7].GetUInt32(); - be.spell = fields[8].GetUInt32(); - for(int i = 0; i < MAX_WAYPOINT_TEXT; ++i) - { - be.textid[i] = fields[9+i].GetUInt32(); - if(be.textid[i]) + // the cleanup queries make sure the following is true + ASSERT(point >= 1 && point <= path.size()); + + WaypointNode &node = path[point-1]; + + node.x = fields[2].GetFloat(); + node.y = fields[3].GetFloat(); + node.z = fields[4].GetFloat(); + node.orientation = fields[14].GetFloat(); + node.delay = fields[5].GetUInt32(); + node.script_id = fields[6].GetUInt32(); + + // prevent using invalid coordinates + if (!MaNGOS::IsValidMapCoord(node.x, node.y, node.z, node.orientation)) { - if (be.textid[i] < MIN_DB_SCRIPT_STRING_ID || be.textid[i] >= MAX_DB_SCRIPT_STRING_ID) + QueryResult *result1 = WorldDatabase.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id); + if (result1) + sLog.outErrorDb("Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %f, Y: %f).", + id, result1->Fetch()[0].GetUInt32(), point, node.x, node.y); + else + sLog.outErrorDb("Waypoint path %d, have invalid coordinates in his waypoint %d (X: %f, Y: %f).", + id, point, node.x, node.y); + + MaNGOS::NormalizeMapCoord(node.x); + MaNGOS::NormalizeMapCoord(node.y); + + if (result1) { - sLog.outErrorDb( "Table `db_script_string` not have string id %u", be.textid[i]); + node.z = MapManager::Instance ().CreateBaseMap(result1->Fetch()[1].GetUInt32())->GetHeight(node.x, node.y, node.z); + delete result1; + } + + WorldDatabase.PExecute("UPDATE creature_movement SET position_x = '%f', position_y = '%f', position_z = '%f' WHERE id = '%u' AND point = '%u'", node.x, node.y, node.z, id, point); + } + + if (node.script_id) + { + if (sCreatureMovementScripts.find(node.script_id) == sCreatureMovementScripts.end()) + { + sLog.outErrorDb("Table creature_movement for id %u, point %u have script_id %u that does not exist in `creature_movement_scripts`, ignoring", id, point, node.script_id); continue; } + + movementScriptSet.erase(node.script_id); + } + + // WaypointBehavior can be dropped in time. Script_id added may 2010 and can handle all the below behavior. + + WaypointBehavior be; + be.model1 = fields[15].GetUInt32(); + be.model2 = fields[16].GetUInt32(); + be.emote = fields[12].GetUInt32(); + be.spell = fields[13].GetUInt32(); + + for(int i = 0; i < MAX_WAYPOINT_TEXT; ++i) + { + be.textid[i] = fields[7+i].GetUInt32(); + + if (be.textid[i]) + { + if (be.textid[i] < MIN_DB_SCRIPT_STRING_ID || be.textid[i] >= MAX_DB_SCRIPT_STRING_ID) + { + sLog.outErrorDb( "Table `db_script_string` not have string id %u", be.textid[i]); + continue; + } + } + } + + if (be.spell && ! sSpellStore.LookupEntry(be.spell)) + { + sLog.outErrorDb("Table creature_movement references unknown spellid %u. Skipping id %u with point %u.", be.spell, id, point); + be.spell = 0; + } + + if (be.emote) + { + if (!sEmotesStore.LookupEntry(be.emote)) + sLog.outErrorDb("Waypoint path %u (Point %u) are using emote %u, but emote does not exist.",id, point, be.emote); + } + + // save memory by not storing empty behaviors + if (!be.isEmpty()) + { + node.behavior = new WaypointBehavior(be); + ++total_behaviors; + } + else + node.behavior = NULL; + } + while(result->NextRow()); + + if (!creatureNoMoveType.empty()) + { + for(std::set::const_iterator itr = creatureNoMoveType.begin(); itr != creatureNoMoveType.end(); ++itr) + { + const CreatureData* cData = sObjectMgr.GetCreatureData(*itr); + const CreatureInfo* cInfo = sObjectMgr.GetCreatureTemplate(cData->id); + + sLog.outErrorDb("Table creature_movement has waypoint for creature guid %u (entry %u), but MovementType is not WAYPOINT_MOTION_TYPE(2). Creature will not use this path.", *itr, cData->id); + + if (cInfo->MovementType == WAYPOINT_MOTION_TYPE) + sLog.outErrorDb(" creature_template for this entry has MovementType WAYPOINT_MOTION_TYPE(2), did you intend to use creature_movement_template ?"); } } - if (be.spell && ! sSpellStore.LookupEntry(be.spell)) - { - sLog.outErrorDb("Table creature_movement references unknown spellid %u. Skipping id %u with point %u.", be.spell, id, point); - be.spell = 0; - } + sLog.outString(); + sLog.outString( ">> Waypoints and behaviors loaded" ); + sLog.outString(); + sLog.outString( ">>> Loaded %u paths, %u nodes and %u behaviors", total_paths, total_nodes, total_behaviors); - if (be.emote) - { - if (!sEmotesStore.LookupEntry(be.emote)) - sLog.outErrorDb("Waypoint path %u (Point %u) are using emote %u, but emote does not exist.",id, point, be.emote); - } + delete result; + } - // save memory by not storing empty behaviors - if(!be.isEmpty()) + // creature_movement_template + result = WorldDatabase.Query("SELECT entry, COUNT(point) FROM creature_movement_template GROUP BY entry"); + + if (!result) + { + barGoLink bar(1); + bar.step(); + sLog.outString(); + sLog.outString( ">> Loaded 0 path templates. DB table `creature_movement_template` is empty." ); + } + else + { + total_nodes = 0; + total_behaviors = 0; + total_paths = (uint32)result->GetRowCount(); + barGoLink barRow(total_paths); + + do { - node.behavior = new WaypointBehavior(be); - ++total_behaviors; + barRow.step(); + Field *fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + uint32 count = fields[1].GetUInt32(); + + m_pathTemplateMap[entry].resize(count); + total_nodes += count; } - else - node.behavior = NULL; - } while( result->NextRow() ); - delete result; + while(result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString(">> Path templates loaded"); + + // 0 1 2 3 4 5 6 + result = WorldDatabase.Query("SELECT entry, point, position_x, position_y, position_z, waittime, script_id," + // 7 8 9 10 11 12 13 14 15 16 + "textid1, textid2, textid3, textid4, textid5, emote, spell, orientation, model1, model2 FROM creature_movement_template"); + + barGoLink bar( (int)result->GetRowCount() ); + + do + { + bar.step(); + Field *fields = result->Fetch(); + + uint32 entry = fields[0].GetUInt32(); + uint32 point = fields[1].GetUInt32(); + + const CreatureInfo* cInfo = sObjectMgr.GetCreatureTemplate(entry); + + if (!cInfo) + { + sLog.outErrorDb("Table creature_movement_template references unknown creature template %u. Skipping.", entry); + continue; + } + + WaypointPath &path = m_pathTemplateMap[entry]; + + // the cleanup queries make sure the following is true + ASSERT(point >= 1 && point <= path.size()); + + WaypointNode &node = path[point-1]; + + node.x = fields[2].GetFloat(); + node.y = fields[3].GetFloat(); + node.z = fields[4].GetFloat(); + node.orientation = fields[14].GetFloat(); + node.delay = fields[5].GetUInt32(); + node.script_id = fields[6].GetUInt32(); + + // prevent using invalid coordinates + if (!MaNGOS::IsValidMapCoord(node.x, node.y, node.z, node.orientation)) + { + sLog.outErrorDb("Table creature_movement_template for entry %u (point %u) are using invalid coordinates position_x: %f, position_y: %f)", + entry, point, node.x, node.y); + + MaNGOS::NormalizeMapCoord(node.x); + MaNGOS::NormalizeMapCoord(node.y); + + sLog.outErrorDb("Table creature_movement_template for entry %u (point %u) are auto corrected to normalized position_x=%f, position_y=%f", + entry, point, node.x, node.y); + + WorldDatabase.PExecute("UPDATE creature_movement_template SET position_x = '%f', position_y = '%f' WHERE entry = %u AND point = %u", node.x, node.y, entry, point); + } + + if (node.script_id) + { + if (sCreatureMovementScripts.find(node.script_id) == sCreatureMovementScripts.end()) + { + sLog.outErrorDb("Table creature_movement_template for entry %u, point %u have script_id %u that does not exist in `creature_movement_scripts`, ignoring", entry, point, node.script_id); + continue; + } + + movementScriptSet.erase(node.script_id); + } + + WaypointBehavior be; + be.model1 = fields[15].GetUInt32(); + be.model2 = fields[16].GetUInt32(); + be.emote = fields[12].GetUInt32(); + be.spell = fields[13].GetUInt32(); + + for(int i = 0; i < MAX_WAYPOINT_TEXT; ++i) + { + be.textid[i] = fields[7+i].GetUInt32(); + + if (be.textid[i]) + { + if (be.textid[i] < MIN_DB_SCRIPT_STRING_ID || be.textid[i] >= MAX_DB_SCRIPT_STRING_ID) + { + sLog.outErrorDb( "Table `db_script_string` not have string id %u", be.textid[i]); + continue; + } + } + } + + if (be.spell && ! sSpellStore.LookupEntry(be.spell)) + { + sLog.outErrorDb("Table creature_movement_template references unknown spellid %u. Skipping id %u with point %u.", be.spell, entry, point); + be.spell = 0; + } + + if (be.emote) + { + if (!sEmotesStore.LookupEntry(be.emote)) + sLog.outErrorDb("Waypoint template path %u (point %u) are using emote %u, but emote does not exist.", entry, point, be.emote); + } + + // save memory by not storing empty behaviors + if (!be.isEmpty()) + { + node.behavior = new WaypointBehavior(be); + ++total_behaviors; + } + else + node.behavior = NULL; + } + while(result->NextRow()); + + delete result; + + sLog.outString(); + sLog.outString( ">> Waypoint templates loaded" ); + sLog.outString(); + sLog.outString( ">>> Loaded %u path templates with %u nodes and %u behaviors", total_paths, total_nodes, total_behaviors); + } if (!movementScriptSet.empty()) { for(std::set::const_iterator itr = movementScriptSet.begin(); itr != movementScriptSet.end(); ++itr) sLog.outErrorDb("Table `creature_movement_scripts` contain unused script, id %u.", *itr); } - - sLog.outString(); - sLog.outString( ">> Waypoints and behaviors loaded" ); - sLog.outString(); - sLog.outString( ">>> Loaded %u paths, %u nodes and %u behaviors", total_paths, total_nodes, total_behaviors); } void WaypointManager::Cleanup() { // check if points need to be renumbered and do it - if(QueryResult *result = WorldDatabase.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1")) + if (QueryResult *result = WorldDatabase.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1")) { delete result; WorldDatabase.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement"); @@ -217,8 +405,26 @@ void WaypointManager::Cleanup() WorldDatabase.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)"); WorldDatabase.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)"); WorldDatabase.DirectExecute("DROP TABLE temp"); + + sLog.outErrorDb("Table `creature_movement` was auto corrected for using points out of order (invalid or points missing)"); + ASSERT(!(result = WorldDatabase.Query("SELECT 1 from creature_movement As T WHERE point <> (SELECT COUNT(*) FROM creature_movement WHERE id = T.id AND point <= T.point) LIMIT 1"))); } + + if (QueryResult *result = WorldDatabase.Query("SELECT 1 from creature_movement_template As T WHERE point <> (SELECT COUNT(*) FROM creature_movement_template WHERE entry = T.entry AND point <= T.point) LIMIT 1")) + { + delete result; + WorldDatabase.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement_template"); + WorldDatabase.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement_template"); + WorldDatabase.DirectExecute("ALTER TABLE creature_movement_template DROP PRIMARY KEY"); + WorldDatabase.DirectExecute("UPDATE creature_movement_template AS T SET point = (SELECT COUNT(*) FROM temp WHERE entry = T.entry AND point <= T.point)"); + WorldDatabase.DirectExecute("ALTER TABLE creature_movement_template ADD PRIMARY KEY (entry, point)"); + WorldDatabase.DirectExecute("DROP TABLE temp"); + + sLog.outErrorDb("Table `creature_movement_template` was auto corrected for using points out of order (invalid or points missing)"); + + ASSERT(!(result = WorldDatabase.Query("SELECT 1 from creature_movement_template As T WHERE point <> (SELECT COUNT(*) FROM creature_movement_template WHERE entry = T.entry AND point <= T.point) LIMIT 1"))); + } } void WaypointManager::Unload() diff --git a/src/game/WaypointManager.h b/src/game/WaypointManager.h index e0c0eb4f5..f2b189fca 100644 --- a/src/game/WaypointManager.h +++ b/src/game/WaypointManager.h @@ -71,6 +71,12 @@ class WaypointManager return itr != m_pathMap.end() ? &itr->second : NULL; } + WaypointPath *GetPathTemplate(uint32 entry) + { + WaypointPathTemplateMap::iterator itr = m_pathTemplateMap.find(entry); + return itr != m_pathTemplateMap.end() ? &itr->second : NULL; + } + void AddLastNode(uint32 id, float x, float y, float z, float o, uint32 delay, uint32 wpGuid); void AddAfterNode(uint32 id, uint32 point, float x, float y, float z, float o, uint32 delay, uint32 wpGuid); uint32 GetLastPoint(uint32 id, uint32 default_notfound); @@ -86,6 +92,8 @@ class WaypointManager typedef UNORDERED_MAP WaypointPathMap; WaypointPathMap m_pathMap; + typedef UNORDERED_MAP WaypointPathTemplateMap; + WaypointPathTemplateMap m_pathTemplateMap; }; #define sWaypointMgr MaNGOS::Singleton::Instance() diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp index 4950a3614..6a9aa0597 100644 --- a/src/game/WaypointMovementGenerator.cpp +++ b/src/game/WaypointMovementGenerator.cpp @@ -50,11 +50,29 @@ void WaypointMovementGenerator::LoadPath(Creature &creature) i_path = sWaypointMgr.GetPath(creature.GetDBTableGUIDLow()); + // We may LoadPath() for several occasions: + + // 1: When creature.MovementType=2 + // 1a) Path is selected by creature.guid == creature_movement.id + // 1b) Path for 1a) does not exist and then use path from creature.GetEntry() == creature_movement_template.entry + + // 2: When creature_template.MovementType=2 + // 2a) Creature is summoned and has creature_template.MovementType=2 + // Creators need to be sure that creature_movement_template is always valid for summons. + // Mob that can be summoned anywhere should not have creature_movement_template for example. + + // No movement found for guid if (!i_path) { - sLog.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path", - creature.GetName(), creature.GetEntry(), creature.GetDBTableGUIDLow()); - return; + i_path = sWaypointMgr.GetPathTemplate(creature.GetEntry()); + + // No movement found for entry + if (!i_path) + { + sLog.outErrorDb("WaypointMovementGenerator::LoadPath: creature %s (Entry: %u GUID: %u) doesn't have waypoint path", + creature.GetName(), creature.GetEntry(), creature.GetDBTableGUIDLow()); + return; + } } // We have to set the destination here (for the first point), right after Initialize. Without, we may not have valid xyz for GetResetPosition @@ -266,7 +284,7 @@ bool WaypointMovementGenerator::Update(Creature &creature, const uint3 else { // If not stopped then stop it - creature.StopMoving(); + creature.clearUnitState(UNIT_STAT_ROAMING_MOVE); SetStoppedByPlayer(false); diff --git a/src/shared/revision_nr.h b/src/shared/revision_nr.h index 37464b6c7..1587ab146 100644 --- a/src/shared/revision_nr.h +++ b/src/shared/revision_nr.h @@ -1,4 +1,4 @@ #ifndef __REVISION_NR_H__ #define __REVISION_NR_H__ - #define REVISION_NR "10361" + #define REVISION_NR "10362" #endif // __REVISION_NR_H__ diff --git a/src/shared/revision_sql.h b/src/shared/revision_sql.h index 0a92ae43d..7fef34775 100644 --- a/src/shared/revision_sql.h +++ b/src/shared/revision_sql.h @@ -1,6 +1,6 @@ #ifndef __REVISION_SQL_H__ #define __REVISION_SQL_H__ #define REVISION_DB_CHARACTERS "required_10332_02_characters_pet_aura" - #define REVISION_DB_MANGOS "required_10353_02_mangos_command" + #define REVISION_DB_MANGOS "required_10362_01_mangos_creature_movement_template" #define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version" #endif // __REVISION_SQL_H__