2 * Copyright (C) 2005-2008 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"
26 INSTANTIATE_SINGLETON_1(WaypointManager
);
28 bool WaypointBehavior::isEmpty()
30 return emote
== 0 && spell
== 0 && model1
== 0 && model2
== 0 && text
[0].empty() &&
31 text
[1].empty() && text
[2].empty() && text
[3].empty() && text
[4].empty();
34 WaypointBehavior::WaypointBehavior(const WaypointBehavior
&b
)
36 emote
= b
.emote
; spell
= b
.spell
; model1
= b
.model1
; model2
= b
.model2
;
37 text
[0] = b
.text
[0]; text
[1] = b
.text
[1]; text
[2] = b
.text
[2];
38 text
[3] = b
.text
[3]; text
[4] = b
.text
[4];
41 void WaypointManager::Load()
45 uint32 total_paths
= 0;
46 uint32 total_nodes
= 0;
47 uint32 total_behaviors
= 0;
49 QueryResult
*result
= WorldDatabase
.Query("SELECT id, COUNT(point) FROM creature_movement GROUP BY id");
52 total_paths
= result
->GetRowCount();
53 barGoLink
bar( total_paths
);
56 Field
*fields
= result
->Fetch();
57 uint32 id
= fields
[0].GetUInt32();
58 uint32 count
= fields
[1].GetUInt32();
59 m_pathMap
[id
].resize(count
);
63 } while( result
->NextRow() );
67 result
= WorldDatabase
.Query("SELECT position_x, position_y, position_z, orientation, model1, model2, waittime, emote, spell, text1, text2, text3, text4, text5, id, point FROM creature_movement");
70 barGoLink
bar( result
->GetRowCount() );
73 Field
*fields
= result
->Fetch();
74 uint32 point
= fields
[15].GetUInt32();
75 uint32 id
= fields
[14].GetUInt32();
77 WaypointPath
&path
= m_pathMap
[id
];
78 // the cleanup queries make sure the following is true
79 assert(point
>= 1 && point
<= path
.size());
80 WaypointNode
&node
= path
[point
-1];
82 node
.x
= fields
[0].GetFloat();
83 node
.y
= fields
[1].GetFloat();
84 node
.z
= fields
[2].GetFloat();
85 node
.orientation
= fields
[3].GetFloat();
86 node
.delay
= fields
[6].GetUInt16();
88 // prevent using invalid coordinates
89 if(!MaNGOS::IsValidMapCoord(node
.x
, node
.y
, node
.z
, node
.orientation
))
91 QueryResult
*result1
= WorldDatabase
.PQuery("SELECT id, map FROM creature WHERE guid = '%u'", id
);
93 sLog
.outErrorDb("ERROR: Creature (guidlow %d, entry %d) have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
94 id
, result1
->Fetch()[0].GetUInt32(), point
, node
.x
, node
.y
);
96 sLog
.outErrorDb("ERROR: Waypoint path %d, have invalid coordinates in his waypoint %d (X: %f, Y: %f).",
97 id
, point
, node
.x
, node
.y
);
99 MaNGOS::NormalizeMapCoord(node
.x
);
100 MaNGOS::NormalizeMapCoord(node
.y
);
103 node
.z
= MapManager::Instance ().GetBaseMap(result1
->Fetch()[1].GetUInt32())->GetHeight(node
.x
, node
.y
, node
.z
);
106 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
);
110 be
.model1
= fields
[4].GetUInt32();
111 be
.model2
= fields
[5].GetUInt32();
112 be
.emote
= fields
[7].GetUInt32();
113 be
.spell
= fields
[8].GetUInt32();
114 be
.text
[0] = fields
[9].GetCppString();
115 be
.text
[1] = fields
[10].GetCppString();
116 be
.text
[2] = fields
[11].GetCppString();
117 be
.text
[3] = fields
[12].GetCppString();
118 be
.text
[4] = fields
[13].GetCppString();
120 // save memory by not storing empty behaviors
123 node
.behavior
= new WaypointBehavior(be
);
127 node
.behavior
= NULL
;
129 } while( result
->NextRow() );
132 sLog
.outString( ">> Loaded %u paths, %u nodes and %u behaviors", total_paths
, total_nodes
, total_behaviors
);
135 void WaypointManager::Cleanup()
137 // check if points need to be renumbered and do it
138 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"))
141 WorldDatabase
.DirectExecute("CREATE TEMPORARY TABLE temp LIKE creature_movement");
142 WorldDatabase
.DirectExecute("INSERT INTO temp SELECT * FROM creature_movement");
143 WorldDatabase
.DirectExecute("ALTER TABLE creature_movement DROP PRIMARY KEY");
144 WorldDatabase
.DirectExecute("UPDATE creature_movement AS T SET point = (SELECT COUNT(*) FROM temp WHERE id = T.id AND point <= T.point)");
145 WorldDatabase
.DirectExecute("ALTER TABLE creature_movement ADD PRIMARY KEY (id, point)");
146 WorldDatabase
.DirectExecute("DROP TABLE temp");
147 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")));
151 void WaypointManager::Unload()
153 for(WaypointPathMap::iterator itr
= m_pathMap
.begin(); itr
!= m_pathMap
.end(); ++itr
)
154 _clearPath(itr
->second
);
158 void WaypointManager::_clearPath(WaypointPath
&path
)
160 for(WaypointPath::iterator itr
= path
.begin(); itr
!= path
.end(); ++itr
)
162 delete itr
->behavior
;
166 /// - Insert after the last point
167 void WaypointManager::AddLastNode(uint32 id
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
169 _addNode(id
, GetLastPoint(id
, 0) + 1, x
, y
, z
, o
, delay
, wpGuid
);
172 /// - Insert after a certain point
173 void WaypointManager::AddAfterNode(uint32 id
, uint32 point
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
175 for(uint32 i
= GetLastPoint(id
, 0); i
> point
; i
--)
176 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET point=point+1 WHERE id='%u' AND point='%u'", id
, i
);
178 _addNode(id
, point
+ 1, x
, y
, z
, o
, delay
, wpGuid
);
181 /// - Insert without checking for collision
182 void WaypointManager::_addNode(uint32 id
, uint32 point
, float x
, float y
, float z
, float o
, uint32 delay
, uint32 wpGuid
)
184 if(point
== 0) return; // counted from 1 in the DB
185 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
);
186 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
187 if(itr
== m_pathMap
.end())
188 itr
= m_pathMap
.insert(WaypointPathMap::value_type(id
, WaypointPath())).first
;
189 itr
->second
.insert(itr
->second
.begin() + (point
- 1), WaypointNode(x
, y
, z
, o
, delay
, NULL
));
192 uint32
WaypointManager::GetLastPoint(uint32 id
, uint32 default_notfound
)
194 uint32 point
= default_notfound
;
195 /*QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM creature_movement WHERE id = '%u'", id);
198 point = (*result)[0].GetUInt32()+1;
201 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
202 if(itr
!= m_pathMap
.end() && itr
->second
.size() != 0)
203 point
= itr
->second
.size();
207 void WaypointManager::DeleteNode(uint32 id
, uint32 point
)
209 if(point
== 0) return; // counted from 1 in the DB
210 WorldDatabase
.PExecuteLog("DELETE FROM creature_movement WHERE id='%u' AND point='%u'", id
, point
);
211 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET point=point-1 WHERE id='%u' AND point>'%u'", id
, point
);
212 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
213 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
214 itr
->second
.erase(itr
->second
.begin() + (point
-1));
217 void WaypointManager::DeletePath(uint32 id
)
219 WorldDatabase
.PExecuteLog("DELETE FROM creature_movement WHERE id='%u'", id
);
220 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
221 if(itr
!= m_pathMap
.end())
222 _clearPath(itr
->second
);
223 // the path is not removed from the map, just cleared
224 // WMGs have pointers to the path, so deleting them would crash
225 // this wastes some memory, but these functions are
226 // only meant to be called by GM commands
229 void WaypointManager::SetNodePosition(uint32 id
, uint32 point
, float x
, float y
, float z
)
231 if(point
== 0) return; // counted from 1 in the DB
232 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
);
233 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
234 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
236 itr
->second
[point
-1].x
= x
;
237 itr
->second
[point
-1].y
= y
;
238 itr
->second
[point
-1].z
= z
;
242 void WaypointManager::SetNodeText(uint32 id
, uint32 point
, const char *text_field
, const char *text
)
244 if(point
== 0) return; // counted from 1 in the DB
245 if(!text_field
) return;
246 std::string field
= text_field
;
247 WorldDatabase
.escape_string(field
);
251 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET %s=NULL WHERE id='%u' AND point='%u'", field
.c_str(), id
, point
);
255 std::string text2
= text
;
256 WorldDatabase
.escape_string(text2
);
257 WorldDatabase
.PExecuteLog("UPDATE creature_movement SET %s='%s' WHERE id='%u' AND point='%u'", field
.c_str(), text2
.c_str(), id
, point
);
260 WaypointPathMap::iterator itr
= m_pathMap
.find(id
);
261 if(itr
!= m_pathMap
.end() && point
<= itr
->second
.size())
263 WaypointNode
&node
= itr
->second
[point
-1];
264 if(!node
.behavior
) node
.behavior
= new WaypointBehavior();
266 if(field
== "text1") node
.behavior
->text
[0] = text
? text
: "";
267 if(field
== "text2") node
.behavior
->text
[1] = text
? text
: "";
268 if(field
== "text3") node
.behavior
->text
[2] = text
? text
: "";
269 if(field
== "text4") node
.behavior
->text
[3] = text
? text
: "";
270 if(field
== "text5") node
.behavior
->text
[4] = text
? text
: "";
271 if(field
== "emote") node
.behavior
->emote
= text
? atoi(text
) : 0;
272 if(field
== "spell") node
.behavior
->spell
= text
? atoi(text
) : 0;
273 if(field
== "model1") node
.behavior
->model1
= text
? atoi(text
) : 0;
274 if(field
== "model2") node
.behavior
->model2
= text
? atoi(text
) : 0;