2 * Copyright (C) 2005-2009 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" );
86 result
= WorldDatabase
.Query("SELECT position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, textid1, textid2, textid3, textid4, textid5, id, point FROM creature_movement");
88 barGoLink
bar( result
->GetRowCount() );
92 Field
*fields
= result
->Fetch();
93 uint32 point
= fields
[15].GetUInt32();
94 uint32 id
= fields
[14].GetUInt32();
95 WaypointPath
&path
= m_pathMap
[id
];
96 // the cleanup queries make sure the following is true
97 assert(point
>= 1 && point
<= path
.size());
98 WaypointNode
&node
= path
[point
-1];
100 node
.x
= fields
[0].GetFloat();
101 node
.y
= fields
[1].GetFloat();
102 node
.z
= fields
[2].GetFloat();
103 node
.orientation
= fields
[3].GetFloat();
104 node
.delay
= fields
[6].GetUInt32();
106 // prevent using invalid coordinates
107 if(!MaNGOS::IsValidMapCoord(node
.x
, node
.y
, node
.z
, node
.orientation
))
109 QueryResult
*result1
= WorldDatabase
.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id
);
111 sLog
.outErrorDb("Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
112 id
, result1
->Fetch()[0].GetUInt32(), point
, node
.x
, node
.y
);
114 sLog
.outErrorDb("Waypoint path %d, have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
115 id
, point
, node
.x
, node
.y
);
117 MaNGOS::NormalizeMapCoord(node
.x
);
118 MaNGOS::NormalizeMapCoord(node
.y
);
121 node
.z
= MapManager::Instance ().GetBaseMap(result1
->Fetch()[1].GetUInt32())->GetHeight(node
.x
, node
.y
, node
.z
);
124 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
);
127 be
.model1
= fields
[4].GetUInt32();
128 be
.model2
= fields
[5].GetUInt32();
129 be
.emote
= fields
[7].GetUInt32();
130 be
.spell
= fields
[8].GetUInt32();
131 for(int i
= 0; i
< MAX_WAYPOINT_TEXT
; ++i
)
133 be
.textid
[i
] = fields
[9+i
].GetUInt32();
136 if (be
.textid
[i
] < MIN_DB_SCRIPT_STRING_ID
|| be
.textid
[i
] >= MAX_DB_SCRIPT_STRING_ID
)
138 sLog
.outErrorDb( "Table `db_script_string` not have string id %u", be
.textid
[i
]);
146 if (!sEmotesStore
.LookupEntry(be
.emote
))
147 sLog
.outErrorDb("Waypoint path %u (Point %u) are using emote %u, but emote does not exist.",id
, point
, be
.emote
);
150 // save memory by not storing empty behaviors
153 node
.behavior
= new WaypointBehavior(be
);
157 node
.behavior
= NULL
;
158 } while( result
->NextRow() );
162 sLog
.outString( ">> Waypoints and behaviors loaded" );
164 sLog
.outString( ">>> Loaded %u paths, %u nodes and %u behaviors", total_paths
, total_nodes
, total_behaviors
);
167 void WaypointManager::Cleanup()
169 // check if points need to be renumbered and do it
170 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"))
173 WorldDatabase
.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement");
174 WorldDatabase
.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement");
175 WorldDatabase
.DirectExecute("ALTER TABLE creature_movement DROP PRIMARY KEY");
176 WorldDatabase
.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)");
177 WorldDatabase
.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)");
178 WorldDatabase
.DirectExecute("DROP TABLE temp");
179 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")));
183 void WaypointManager::Unload()
185 for(WaypointPathMap::iterator itr
= m_pathMap
.begin(); itr
!= m_pathMap
.end(); ++itr
)
186 _clearPath(itr
->second
);
190 void WaypointManager::_clearPath(WaypointPath
&path
)
192 for(WaypointPath::const_iterator itr
= path
.begin(); itr
!= path
.end(); ++itr
)
194 delete itr
->behavior
;
198 /// - Insert after the last point
199 void WaypointManager::AddLastNode(uint32 id
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
201 _addNode(id
, GetLastPoint(id
, 0) + 1, x
, y
, z
, o
, delay
, wpGuid
);
204 /// - Insert after a certain point
205 void WaypointManager::AddAfterNode(uint32 id
, uint32 point
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
207 for(uint32 i
= GetLastPoint(id
, 0); i
> point
; i
--)
208 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET point=point+1 WHERE id='%u' AND point='%u'", id
, i
);
210 _addNode(id
, point
+ 1, x
, y
, z
, o
, delay
, wpGuid
);
213 /// - Insert without checking for collision
214 void WaypointManager::_addNode(uint32 id
, uint32 point
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
216 if(point
== 0) return; // counted from 1 in the DB
217 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
);
218 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
219 if(itr
== m_pathMap
.end())
220 itr
= m_pathMap
.insert(WaypointPathMap::value_type(id
, WaypointPath())).first
;
221 itr
->second
.insert(itr
->second
.begin() + (point
- 1), WaypointNode(x
, y
, z
, o
, delay
, NULL
));
224 uint32
WaypointManager::GetLastPoint(uint32 id
, uint32 default_notfound
)
226 uint32 point
= default_notfound
;
227 /*QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'", id);
230 point = (*result)[0].GetUInt32()+1;
233 WaypointPathMap::const_iterator itr
= m_pathMap
.find(id
);
234 if(itr
!= m_pathMap
.end() && itr
->second
.size() != 0)
235 point
= itr
->second
.size();
239 void WaypointManager::DeleteNode(uint32 id
, uint32 point
)
241 if(point
== 0) return; // counted from 1 in the DB
242 WorldDatabase
.PExecuteLog("DELETE FROM creature_movement WHERE id='%u' AND point='%u'", id
, point
);
243 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET point=point-1 WHERE id='%u' AND point>'%u'", id
, point
);
244 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
245 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
246 itr
->second
.erase(itr
->second
.begin() + (point
-1));
249 void WaypointManager::DeletePath(uint32 id
)
251 WorldDatabase
.PExecuteLog("DELETE FROM creature_movement WHERE id='%u'", id
);
252 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
253 if(itr
!= m_pathMap
.end())
254 _clearPath(itr
->second
);
255 // the path is not removed from the map, just cleared
256 // WMGs have pointers to the path, so deleting them would crash
257 // this wastes some memory, but these functions are
258 // only meant to be called by GM commands
261 void WaypointManager::SetNodePosition(uint32 id
, uint32 point
, float x
, float y
, float z
)
263 if(point
== 0) return; // counted from 1 in the DB
264 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
);
265 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
266 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
268 itr
->second
[point
-1].x
= x
;
269 itr
->second
[point
-1].y
= y
;
270 itr
->second
[point
-1].z
= z
;
274 void WaypointManager::SetNodeText(uint32 id
, uint32 point
, const char *text_field
, const char *text
)
276 if(point
== 0) return; // counted from 1 in the DB
277 if(!text_field
) return;
278 std::string field
= text_field
;
279 WorldDatabase
.escape_string(field
);
283 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field
.c_str(), id
, point
);
287 std::string text2
= text
;
288 WorldDatabase
.escape_string(text2
);
289 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET %s='%s' WHERE id='%u' AND point='%u'", field
.c_str(), text2
.c_str(), id
, point
);
292 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
293 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
295 WaypointNode
&node
= itr
->second
[point
-1];
296 if(!node
.behavior
) node
.behavior
= new WaypointBehavior();
298 // if(field == "text1") node.behavior->text[0] = text ? text : "";
299 // if(field == "text2") node.behavior->text[1] = text ? text : "";
300 // if(field == "text3") node.behavior->text[2] = text ? text : "";
301 // if(field == "text4") node.behavior->text[3] = text ? text : "";
302 // if(field == "text5") node.behavior->text[4] = text ? text : "";
303 if(field
== "emote") node
.behavior
->emote
= text
? atoi(text
) : 0;
304 if(field
== "spell") node
.behavior
->spell
= text
? atoi(text
) : 0;
305 if(field
== "model1") node
.behavior
->model1
= text
? atoi(text
) : 0;
306 if(field
== "model2") node
.behavior
->model2
= text
? atoi(text
) : 0;
310 void WaypointManager::CheckTextsExistance(std::set
<int32
>& ids
)
312 WaypointPathMap::const_iterator pmItr
= m_pathMap
.begin();
313 for ( ; pmItr
!= m_pathMap
.end(); ++pmItr
)
315 for (int i
= 0; i
< pmItr
->second
.size(); ++i
)
317 WaypointBehavior
* be
= pmItr
->second
[i
].behavior
;
321 // Now we check text existence and put all zero texts ids to the end of array
323 // Counting leading zeros for futher textid shift
325 for (int j
= 0; j
< MAX_WAYPOINT_TEXT
; ++j
)
334 if (!objmgr
.GetMangosStringLocale(be
->textid
[j
]))
336 sLog
.outErrorDb("Some waypoint has textid%u with not existing %u text.", j
, be
->textid
[j
]);
342 ids
.erase(be
->textid
[j
]);
347 // Correct textid but some zeros leading, so move it forward.
348 be
->textid
[j
-zeroCount
] = be
->textid
[j
];