2 * Copyright (C) 2005-2010 MaNGOS <http://getmangos.com/>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 #include "Database/DatabaseEnv.h"
20 #include "GridDefines.h"
21 #include "Policies/SingletonImp.h"
22 #include "WaypointManager.h"
23 #include "ProgressBar.h"
24 #include "MapManager.h"
25 #include "ObjectMgr.h"
27 INSTANTIATE_SINGLETON_1(WaypointManager
);
29 bool WaypointBehavior::isEmpty()
31 if (emote
|| spell
|| model1
|| model2
)
34 for(int i
= 0; i
< MAX_WAYPOINT_TEXT
; ++i
)
41 WaypointBehavior::WaypointBehavior(const WaypointBehavior
&b
)
47 for(int i
=0; i
< MAX_WAYPOINT_TEXT
; ++i
)
48 textid
[i
] = b
.textid
[i
];
51 void WaypointManager::Load()
55 uint32 total_paths
= 0;
56 uint32 total_nodes
= 0;
57 uint32 total_behaviors
= 0;
59 QueryResult
*result
= WorldDatabase
.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id");
66 sLog
.outString( ">> Loaded 0 paths. DB table `creature_movement` is empty." );
69 total_paths
= result
->GetRowCount();
70 barGoLink
bar( total_paths
);
74 Field
*fields
= result
->Fetch();
75 uint32 id
= fields
[0].GetUInt32();
76 uint32 count
= fields
[1].GetUInt32();
77 m_pathMap
[id
].resize(count
);
79 } while( result
->NextRow() );
83 sLog
.outString( ">> Paths loaded" );
87 result
= WorldDatabase
.Query("SELECT position_x, position_y, position_z, orientation, model1, model2,"
88 // 6 7 8 9 10 11 12 13 14 15
89 "waittime, emote, spell, textid1, textid2, textid3, textid4, textid5, id, point FROM creature_movement");
91 barGoLink
bar( result
->GetRowCount() );
95 Field
*fields
= result
->Fetch();
96 uint32 point
= fields
[15].GetUInt32();
97 uint32 id
= fields
[14].GetUInt32();
98 if (!sObjectMgr
.GetCreatureData(id
))
100 sLog
.outErrorDb("Table creature_movement references unknown creature %u. Skipping.", id
);
104 WaypointPath
&path
= m_pathMap
[id
];
105 // the cleanup queries make sure the following is true
106 assert(point
>= 1 && point
<= path
.size());
107 WaypointNode
&node
= path
[point
-1];
109 node
.x
= fields
[0].GetFloat();
110 node
.y
= fields
[1].GetFloat();
111 node
.z
= fields
[2].GetFloat();
112 node
.orientation
= fields
[3].GetFloat();
113 node
.delay
= fields
[6].GetUInt32();
115 // prevent using invalid coordinates
116 if(!MaNGOS::IsValidMapCoord(node
.x
, node
.y
, node
.z
, node
.orientation
))
118 QueryResult
*result1
= WorldDatabase
.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id
);
120 sLog
.outErrorDb("Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
121 id
, result1
->Fetch()[0].GetUInt32(), point
, node
.x
, node
.y
);
123 sLog
.outErrorDb("Waypoint path %d, have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
124 id
, point
, node
.x
, node
.y
);
126 MaNGOS::NormalizeMapCoord(node
.x
);
127 MaNGOS::NormalizeMapCoord(node
.y
);
130 node
.z
= MapManager::Instance ().CreateBaseMap(result1
->Fetch()[1].GetUInt32())->GetHeight(node
.x
, node
.y
, node
.z
);
133 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
);
136 be
.model1
= fields
[4].GetUInt32();
137 be
.model2
= fields
[5].GetUInt32();
138 be
.emote
= fields
[7].GetUInt32();
139 be
.spell
= fields
[8].GetUInt32();
140 for(int i
= 0; i
< MAX_WAYPOINT_TEXT
; ++i
)
142 be
.textid
[i
] = fields
[9+i
].GetUInt32();
145 if (be
.textid
[i
] < MIN_DB_SCRIPT_STRING_ID
|| be
.textid
[i
] >= MAX_DB_SCRIPT_STRING_ID
)
147 sLog
.outErrorDb( "Table `db_script_string` not have string id %u", be
.textid
[i
]);
153 if (be
.spell
&& ! sSpellStore
.LookupEntry(be
.spell
))
155 sLog
.outErrorDb("Table creature_movement references unknown spellid %u. Skipping id %u with point %u.", be
.spell
, id
, point
);
161 if (!sEmotesStore
.LookupEntry(be
.emote
))
162 sLog
.outErrorDb("Waypoint path %u (Point %u) are using emote %u, but emote does not exist.",id
, point
, be
.emote
);
165 // save memory by not storing empty behaviors
168 node
.behavior
= new WaypointBehavior(be
);
172 node
.behavior
= NULL
;
173 } while( result
->NextRow() );
177 sLog
.outString( ">> Waypoints and behaviors loaded" );
179 sLog
.outString( ">>> Loaded %u paths, %u nodes and %u behaviors", total_paths
, total_nodes
, total_behaviors
);
182 void WaypointManager::Cleanup()
184 // check if points need to be renumbered and do it
185 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"))
188 WorldDatabase
.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement");
189 WorldDatabase
.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement");
190 WorldDatabase
.DirectExecute("ALTER TABLE creature_movement DROP PRIMARY KEY");
191 WorldDatabase
.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)");
192 WorldDatabase
.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)");
193 WorldDatabase
.DirectExecute("DROP TABLE temp");
194 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")));
198 void WaypointManager::Unload()
200 for(WaypointPathMap::iterator itr
= m_pathMap
.begin(); itr
!= m_pathMap
.end(); ++itr
)
201 _clearPath(itr
->second
);
205 void WaypointManager::_clearPath(WaypointPath
&path
)
207 for(WaypointPath::const_iterator itr
= path
.begin(); itr
!= path
.end(); ++itr
)
209 delete itr
->behavior
;
213 /// - Insert after the last point
214 void WaypointManager::AddLastNode(uint32 id
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
216 _addNode(id
, GetLastPoint(id
, 0) + 1, x
, y
, z
, o
, delay
, wpGuid
);
219 /// - Insert after a certain point
220 void WaypointManager::AddAfterNode(uint32 id
, uint32 point
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
222 for(uint32 i
= GetLastPoint(id
, 0); i
> point
; i
--)
223 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET point=point+1 WHERE id='%u' AND point='%u'", id
, i
);
225 _addNode(id
, point
+ 1, x
, y
, z
, o
, delay
, wpGuid
);
228 /// - Insert without checking for collision
229 void WaypointManager::_addNode(uint32 id
, uint32 point
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
231 if(point
== 0) return; // counted from 1 in the DB
232 WorldDatabase
.PExecuteLog("INSERT INTO creature_movement (id,point,position_x,position_y,position_z,orientation,wpguid,waittime) VALUES ('%u','%u','%f', '%f', '%f', '%f', '%d', '%d')", id
, point
, x
, y
, z
, o
, wpGuid
, delay
);
233 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
234 if(itr
== m_pathMap
.end())
235 itr
= m_pathMap
.insert(WaypointPathMap::value_type(id
, WaypointPath())).first
;
236 itr
->second
.insert(itr
->second
.begin() + (point
- 1), WaypointNode(x
, y
, z
, o
, delay
, NULL
));
239 uint32
WaypointManager::GetLastPoint(uint32 id
, uint32 default_notfound
)
241 uint32 point
= default_notfound
;
242 /*QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'", id);
245 point = (*result)[0].GetUInt32()+1;
248 WaypointPathMap::const_iterator itr
= m_pathMap
.find(id
);
249 if(itr
!= m_pathMap
.end() && itr
->second
.size() != 0)
250 point
= itr
->second
.size();
254 void WaypointManager::DeleteNode(uint32 id
, uint32 point
)
256 if(point
== 0) return; // counted from 1 in the DB
257 WorldDatabase
.PExecuteLog("DELETE FROM creature_movement WHERE id='%u' AND point='%u'", id
, point
);
258 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET point=point-1 WHERE id='%u' AND point>'%u'", id
, point
);
259 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
260 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
261 itr
->second
.erase(itr
->second
.begin() + (point
-1));
264 void WaypointManager::DeletePath(uint32 id
)
266 WorldDatabase
.PExecuteLog("DELETE FROM creature_movement WHERE id='%u'", id
);
267 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
268 if(itr
!= m_pathMap
.end())
269 _clearPath(itr
->second
);
270 // the path is not removed from the map, just cleared
271 // WMGs have pointers to the path, so deleting them would crash
272 // this wastes some memory, but these functions are
273 // only meant to be called by GM commands
276 void WaypointManager::SetNodePosition(uint32 id
, uint32 point
, float x
, float y
, float z
)
278 if(point
== 0) return; // counted from 1 in the DB
279 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET position_x = '%f',position_y = '%f',position_z = '%f' where id = '%u' AND point='%u'", x
, y
, z
, id
, point
);
280 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
281 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
283 itr
->second
[point
-1].x
= x
;
284 itr
->second
[point
-1].y
= y
;
285 itr
->second
[point
-1].z
= z
;
289 void WaypointManager::SetNodeText(uint32 id
, uint32 point
, const char *text_field
, const char *text
)
291 if(point
== 0) return; // counted from 1 in the DB
292 if(!text_field
) return;
293 std::string field
= text_field
;
294 WorldDatabase
.escape_string(field
);
298 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field
.c_str(), id
, point
);
302 std::string text2
= text
;
303 WorldDatabase
.escape_string(text2
);
304 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET %s='%s' WHERE id='%u' AND point='%u'", field
.c_str(), text2
.c_str(), id
, point
);
307 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
308 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
310 WaypointNode
&node
= itr
->second
[point
-1];
311 if(!node
.behavior
) node
.behavior
= new WaypointBehavior();
313 // if(field == "text1") node.behavior->text[0] = text ? text : "";
314 // if(field == "text2") node.behavior->text[1] = text ? text : "";
315 // if(field == "text3") node.behavior->text[2] = text ? text : "";
316 // if(field == "text4") node.behavior->text[3] = text ? text : "";
317 // if(field == "text5") node.behavior->text[4] = text ? text : "";
318 if(field
== "emote") node
.behavior
->emote
= text
? atoi(text
) : 0;
319 if(field
== "spell") node
.behavior
->spell
= text
? atoi(text
) : 0;
320 if(field
== "model1") node
.behavior
->model1
= text
? atoi(text
) : 0;
321 if(field
== "model2") node
.behavior
->model2
= text
? atoi(text
) : 0;
325 void WaypointManager::CheckTextsExistance(std::set
<int32
>& ids
)
327 WaypointPathMap::const_iterator pmItr
= m_pathMap
.begin();
328 for ( ; pmItr
!= m_pathMap
.end(); ++pmItr
)
330 for (size_t i
= 0; i
< pmItr
->second
.size(); ++i
)
332 WaypointBehavior
* be
= pmItr
->second
[i
].behavior
;
336 // Now we check text existence and put all zero texts ids to the end of array
338 // Counting leading zeros for futher textid shift
340 for (int j
= 0; j
< MAX_WAYPOINT_TEXT
; ++j
)
349 if (!sObjectMgr
.GetMangosStringLocale(be
->textid
[j
]))
351 sLog
.outErrorDb("Some waypoint has textid%u with not existing %u text.", j
, be
->textid
[j
]);
357 ids
.erase(be
->textid
[j
]);
362 // Correct textid but some zeros leading, so move it forward.
363 be
->textid
[j
-zeroCount
] = be
->textid
[j
];