[9529] Make Player::IsValidPos const
[getmangos.git] / src / game / WaypointManager.cpp
blobb6e649ebec6551f59f8c3d8ceb5f6a923c64d240
1 /*
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)
32 return false;
34 for(int i = 0; i < MAX_WAYPOINT_TEXT; ++i)
35 if(textid[i])
36 return false;
38 return true;
41 WaypointBehavior::WaypointBehavior(const WaypointBehavior &b)
43 emote = b.emote;
44 spell = b.spell;
45 model1 = b.model1;
46 model2 = b.model2;
47 for(int i=0; i < MAX_WAYPOINT_TEXT; ++i)
48 textid[i] = b.textid[i];
51 void WaypointManager::Load()
53 Cleanup();
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");
61 if(!result)
63 barGoLink bar(1);
64 bar.step();
65 sLog.outString();
66 sLog.outString( ">> Loaded 0 paths. DB table `creature_movement` is empty." );
67 return;
68 } else {
69 total_paths = (uint32)result->GetRowCount();
70 barGoLink bar( total_paths );
73 bar.step();
74 Field *fields = result->Fetch();
75 uint32 id = fields[0].GetUInt32();
76 uint32 count = fields[1].GetUInt32();
77 m_pathMap[id].resize(count);
78 total_nodes += count;
79 } while( result->NextRow() );
80 delete result;
82 sLog.outString();
83 sLog.outString( ">> Paths loaded" );
86 // 0 1 2 3 4 5
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( (int)result->GetRowCount() );
94 bar.step();
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);
101 continue;
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);
119 if(result1)
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);
122 else
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);
128 if(result1)
130 node.z = MapManager::Instance ().CreateBaseMap(result1->Fetch()[1].GetUInt32())->GetHeight(node.x, node.y, node.z);
131 delete result1;
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);
135 WaypointBehavior be;
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();
143 if(be.textid[i])
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]);
148 continue;
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);
156 be.spell = 0;
159 if (be.emote)
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
166 if(!be.isEmpty())
168 node.behavior = new WaypointBehavior(be);
169 ++total_behaviors;
171 else
172 node.behavior = NULL;
173 } while( result->NextRow() );
174 delete result;
176 sLog.outString();
177 sLog.outString( ">> Waypoints and behaviors loaded" );
178 sLog.outString();
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"))
187 delete result;
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);
202 m_pathMap.clear();
205 void WaypointManager::_clearPath(WaypointPath &path)
207 for(WaypointPath::const_iterator itr = path.begin(); itr != path.end(); ++itr)
208 if(itr->behavior)
209 delete itr->behavior;
210 path.clear();
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);
243 if( result )
245 point = (*result)[0].GetUInt32()+1;
246 delete result;
248 WaypointPathMap::const_iterator itr = m_pathMap.find(id);
249 if(itr != m_pathMap.end() && itr->second.size() != 0)
250 point = itr->second.size();
251 return point;
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);
296 if(!text)
298 WorldDatabase.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field.c_str(), id, point);
300 else
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;
333 if (!be)
334 continue;
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
339 int zeroCount = 0;
340 for (int j = 0; j < MAX_WAYPOINT_TEXT; ++j)
342 if (!be->textid[j])
344 ++zeroCount;
345 continue;
347 else
349 if (!sObjectMgr.GetMangosStringLocale(be->textid[j]))
351 sLog.outErrorDb("Some waypoint has textid%u with not existing %u text.", j, be->textid[j]);
352 be->textid[j] = 0;
353 ++zeroCount;
354 continue;
356 else
357 ids.erase(be->textid[j]);
359 // Shifting check
360 if (zeroCount)
362 // Correct textid but some zeros leading, so move it forward.
363 be->textid[j-zeroCount] = be->textid[j];
364 be->textid[j] = 0;